aboutsummaryrefslogtreecommitdiffstats
path: root/actionpack/lib/action_dispatch/http
diff options
context:
space:
mode:
Diffstat (limited to 'actionpack/lib/action_dispatch/http')
-rw-r--r--actionpack/lib/action_dispatch/http/filter_parameters.rb4
-rw-r--r--actionpack/lib/action_dispatch/http/filter_redirect.rb9
-rw-r--r--actionpack/lib/action_dispatch/http/headers.rb27
-rw-r--r--actionpack/lib/action_dispatch/http/mime_negotiation.rb20
-rw-r--r--actionpack/lib/action_dispatch/http/mime_type.rb8
-rw-r--r--actionpack/lib/action_dispatch/http/mime_types.rb1
-rw-r--r--actionpack/lib/action_dispatch/http/request.rb11
-rw-r--r--actionpack/lib/action_dispatch/http/response.rb40
-rw-r--r--actionpack/lib/action_dispatch/http/upload.rb1
-rw-r--r--actionpack/lib/action_dispatch/http/url.rb26
10 files changed, 116 insertions, 31 deletions
diff --git a/actionpack/lib/action_dispatch/http/filter_parameters.rb b/actionpack/lib/action_dispatch/http/filter_parameters.rb
index 289e204ac8..2b851cc28d 100644
--- a/actionpack/lib/action_dispatch/http/filter_parameters.rb
+++ b/actionpack/lib/action_dispatch/http/filter_parameters.rb
@@ -6,8 +6,8 @@ module ActionDispatch
module Http
# Allows you to specify sensitive parameters which will be replaced from
# the request log by looking in the query string of the request and all
- # subhashes of the params hash to filter. If a block is given, each key and
- # value of the params hash and all subhashes is passed to it, the value
+ # sub-hashes of the params hash to filter. If a block is given, each key and
+ # value of the params hash and all sub-hashes is passed to it, the value
# or key can be replaced using String#replace or similar method.
#
# env["action_dispatch.parameter_filter"] = [:password]
diff --git a/actionpack/lib/action_dispatch/http/filter_redirect.rb b/actionpack/lib/action_dispatch/http/filter_redirect.rb
index 900ce1c646..cd603649c3 100644
--- a/actionpack/lib/action_dispatch/http/filter_redirect.rb
+++ b/actionpack/lib/action_dispatch/http/filter_redirect.rb
@@ -5,7 +5,8 @@ module ActionDispatch
FILTERED = '[FILTERED]'.freeze # :nodoc:
def filtered_location
- if !location_filter.empty? && location_filter_match?
+ filters = location_filter
+ if !filters.empty? && location_filter_match?(filters)
FILTERED
else
location
@@ -15,15 +16,15 @@ module ActionDispatch
private
def location_filter
- if request.present?
+ if request
request.env['action_dispatch.redirect_filter'] || []
else
[]
end
end
- def location_filter_match?
- location_filter.any? do |filter|
+ def location_filter_match?(filters)
+ filters.any? do |filter|
if String === filter
location.include?(filter)
elsif Regexp === filter
diff --git a/actionpack/lib/action_dispatch/http/headers.rb b/actionpack/lib/action_dispatch/http/headers.rb
index 2666cd4b0a..3e607bbde1 100644
--- a/actionpack/lib/action_dispatch/http/headers.rb
+++ b/actionpack/lib/action_dispatch/http/headers.rb
@@ -1,5 +1,10 @@
module ActionDispatch
module Http
+ # Provides access to the request's HTTP headers from the environment.
+ #
+ # env = { "CONTENT_TYPE" => "text/plain" }
+ # headers = ActionDispatch::Http::Headers.new(env)
+ # headers["Content-Type"] # => "text/plain"
class Headers
CGI_VARIABLES = %w(
CONTENT_TYPE CONTENT_LENGTH
@@ -14,21 +19,32 @@ module ActionDispatch
include Enumerable
attr_reader :env
- def initialize(env = {})
+ def initialize(env = {}) # :nodoc:
@env = env
end
+ # Returns the value for the given key mapped to @env.
def [](key)
@env[env_name(key)]
end
+ # Sets the given value for the key mapped to @env.
def []=(key, value)
@env[env_name(key)] = value
end
- def key?(key); @env.key? key; end
+ def key?(key)
+ @env.key? env_name(key)
+ end
alias :include? :key?
+ # Returns the value for the given key mapped to @env.
+ #
+ # If the key is not found and an optional code block is not provided,
+ # raises a <tt>KeyError</tt> exception.
+ #
+ # If the code block is provided, then it will be run and
+ # its result returned.
def fetch(key, *args, &block)
@env.fetch env_name(key), *args, &block
end
@@ -37,12 +53,17 @@ module ActionDispatch
@env.each(&block)
end
+ # Returns a new Http::Headers instance containing the contents of
+ # <tt>headers_or_env</tt> and the original instance.
def merge(headers_or_env)
headers = Http::Headers.new(env.dup)
headers.merge!(headers_or_env)
headers
end
+ # Adds the contents of <tt>headers_or_env</tt> to original instance
+ # entries; duplicate keys are overwritten with the values from
+ # <tt>headers_or_env</tt>.
def merge!(headers_or_env)
headers_or_env.each do |key, value|
self[env_name(key)] = value
@@ -50,6 +71,8 @@ module ActionDispatch
end
private
+ # Converts a HTTP header name to an environment variable name if it is
+ # not contained within the headers hash.
def env_name(key)
key = key.to_s
if key =~ HTTP_HEADER
diff --git a/actionpack/lib/action_dispatch/http/mime_negotiation.rb b/actionpack/lib/action_dispatch/http/mime_negotiation.rb
index 40bb060d52..0b2b60d2e4 100644
--- a/actionpack/lib/action_dispatch/http/mime_negotiation.rb
+++ b/actionpack/lib/action_dispatch/http/mime_negotiation.rb
@@ -10,6 +10,8 @@ module ActionDispatch
self.ignore_accept_header = false
end
+ attr_reader :variant
+
# The MIME type of the HTTP request, such as Mime::XML.
#
# For backward compatibility, the post \format is extracted from the
@@ -48,7 +50,7 @@ module ActionDispatch
# GET /posts/5 | request.format => Mime::HTML or MIME::JS, or request.accepts.first
#
def format(view_path = [])
- formats.first
+ formats.first || Mime::NullType.instance
end
def formats
@@ -64,6 +66,20 @@ module ActionDispatch
end
end
+ # Sets the \variant for template.
+ def variant=(variant)
+ if variant.is_a?(Symbol)
+ @variant = [variant]
+ elsif variant.is_a?(Array) && variant.any? && variant.all?{ |v| v.is_a?(Symbol) }
+ @variant = variant
+ else
+ raise ArgumentError, "request.variant must be set to a Symbol or an Array of Symbols, not a #{variant.class}. " \
+ "For security reasons, never directly set the variant to a user-provided value, " \
+ "like params[:variant].to_sym. Check user-provided value against a whitelist first, " \
+ "then set the variant: request.variant = :tablet if params[:variant] == 'tablet'"
+ end
+ end
+
# Sets the \format by string extension, which can be used to force custom formats
# that are not controlled by the extension.
#
@@ -113,7 +129,7 @@ module ActionDispatch
end
end
- order.include?(Mime::ALL) ? formats.first : nil
+ order.include?(Mime::ALL) ? format : nil
end
protected
diff --git a/actionpack/lib/action_dispatch/http/mime_type.rb b/actionpack/lib/action_dispatch/http/mime_type.rb
index a398919ca7..9450be838c 100644
--- a/actionpack/lib/action_dispatch/http/mime_type.rb
+++ b/actionpack/lib/action_dispatch/http/mime_type.rb
@@ -28,7 +28,7 @@ module Mime
class << self
def [](type)
return type if type.is_a?(Type)
- Type.lookup_by_extension(type) || NullType.instance
+ Type.lookup_by_extension(type)
end
def fetch(type)
@@ -174,7 +174,7 @@ module Mime
end
def parse(accept_header)
- if accept_header !~ /,/
+ if !accept_header.include?(',')
accept_header = accept_header.split(PARAMETER_SEPARATOR_REGEXP).first
parse_trailing_star(accept_header) || [Mime::Type.lookup(accept_header)].compact
else
@@ -299,9 +299,7 @@ module Mime
true
end
- def ref
- nil
- end
+ def ref; end
def respond_to_missing?(method, include_private = false)
method.to_s.ends_with? '?'
diff --git a/actionpack/lib/action_dispatch/http/mime_types.rb b/actionpack/lib/action_dispatch/http/mime_types.rb
index a6b3aee5e7..0e4da36038 100644
--- a/actionpack/lib/action_dispatch/http/mime_types.rb
+++ b/actionpack/lib/action_dispatch/http/mime_types.rb
@@ -7,6 +7,7 @@ Mime::Type.register "text/javascript", :js, %w( application/javascript applicati
Mime::Type.register "text/css", :css
Mime::Type.register "text/calendar", :ics
Mime::Type.register "text/csv", :csv
+Mime::Type.register "text/vcard", :vcf
Mime::Type.register "image/png", :png, [], %w(png)
Mime::Type.register "image/jpeg", :jpeg, [], %w(jpg jpeg jpe pjpeg)
diff --git a/actionpack/lib/action_dispatch/http/request.rb b/actionpack/lib/action_dispatch/http/request.rb
index 99b81c898f..daa06e96e6 100644
--- a/actionpack/lib/action_dispatch/http/request.rb
+++ b/actionpack/lib/action_dispatch/http/request.rb
@@ -152,6 +152,13 @@ module ActionDispatch
Http::Headers.new(@env)
end
+ # Returns a +String+ with the last requested path including their params.
+ #
+ # # get '/foo'
+ # request.original_fullpath # => '/foo'
+ #
+ # # get '/foo?bar'
+ # request.original_fullpath # => '/foo?bar'
def original_fullpath
@original_fullpath ||= (env["ORIGINAL_FULLPATH"] || fullpath)
end
@@ -271,7 +278,7 @@ module ActionDispatch
# Override Rack's GET method to support indifferent access
def GET
- @env["action_dispatch.request.query_parameters"] ||= (normalize_encode_params(super) || {})
+ @env["action_dispatch.request.query_parameters"] ||= Utils.deep_munge((normalize_encode_params(super) || {}))
rescue TypeError => e
raise ActionController::BadRequest.new(:query, e)
end
@@ -279,7 +286,7 @@ module ActionDispatch
# Override Rack's POST method to support indifferent access
def POST
- @env["action_dispatch.request.request_parameters"] ||= (normalize_encode_params(super) || {})
+ @env["action_dispatch.request.request_parameters"] ||= Utils.deep_munge((normalize_encode_params(super) || {}))
rescue TypeError => e
raise ActionController::BadRequest.new(:request, e)
end
diff --git a/actionpack/lib/action_dispatch/http/response.rb b/actionpack/lib/action_dispatch/http/response.rb
index 7b2655b2d8..3d27ff2b24 100644
--- a/actionpack/lib/action_dispatch/http/response.rb
+++ b/actionpack/lib/action_dispatch/http/response.rb
@@ -1,4 +1,5 @@
require 'active_support/core_ext/module/attribute_accessors'
+require 'action_dispatch/http/filter_redirect'
require 'monitor'
module ActionDispatch # :nodoc:
@@ -90,7 +91,10 @@ module ActionDispatch # :nodoc:
end
def each(&block)
- @buf.each(&block)
+ @response.sending!
+ x = @buf.each(&block)
+ @response.sent!
+ x
end
def close
@@ -117,6 +121,8 @@ module ActionDispatch # :nodoc:
@blank = false
@cv = new_cond
@committed = false
+ @sending = false
+ @sent = false
@content_type = nil
@charset = nil
@@ -137,17 +143,37 @@ module ActionDispatch # :nodoc:
end
end
+ def await_sent
+ synchronize { @cv.wait_until { @sent } }
+ end
+
def commit!
synchronize do
+ before_committed
@committed = true
@cv.broadcast
end
end
- def committed?
- @committed
+ def sending!
+ synchronize do
+ before_sending
+ @sending = true
+ @cv.broadcast
+ end
end
+ def sent!
+ synchronize do
+ @sent = true
+ @cv.broadcast
+ end
+ end
+
+ def sending?; synchronize { @sending }; end
+ def committed?; synchronize { @committed }; end
+ def sent?; synchronize { @sent }; end
+
# Sets the HTTP status code.
def status=(status)
@status = Rack::Utils.status_code(status)
@@ -272,6 +298,12 @@ module ActionDispatch # :nodoc:
private
+ def before_committed
+ end
+
+ def before_sending
+ end
+
def merge_default_headers(original, default)
return original unless default.respond_to?(:merge)
@@ -312,7 +344,7 @@ module ActionDispatch # :nodoc:
header.delete CONTENT_TYPE
[status, header, []]
else
- [status, header, self]
+ [status, header, Rack::BodyProxy.new(self){}]
end
end
end
diff --git a/actionpack/lib/action_dispatch/http/upload.rb b/actionpack/lib/action_dispatch/http/upload.rb
index a8d2dc3950..45bf751d09 100644
--- a/actionpack/lib/action_dispatch/http/upload.rb
+++ b/actionpack/lib/action_dispatch/http/upload.rb
@@ -18,6 +18,7 @@ module ActionDispatch
# A +Tempfile+ object with the actual uploaded file. Note that some of
# its interface is available directly.
attr_accessor :tempfile
+ alias :to_io :tempfile
# A string with the headers of the multipart request.
attr_accessor :headers
diff --git a/actionpack/lib/action_dispatch/http/url.rb b/actionpack/lib/action_dispatch/http/url.rb
index 6f5a52c568..c9860af909 100644
--- a/actionpack/lib/action_dispatch/http/url.rb
+++ b/actionpack/lib/action_dispatch/http/url.rb
@@ -29,15 +29,12 @@ module ActionDispatch
extract_subdomains(host, tld_length).join('.')
end
- def url_for(options = {})
- options = options.dup
- path = options.delete(:script_name).to_s.chomp("/")
- path << options.delete(:path).to_s
-
- params = options[:params].is_a?(Hash) ? options[:params] : options.slice(:params)
- params.reject! { |_,v| v.to_param.nil? }
+ def url_for(options)
+ path = options[:script_name].to_s.chomp("/")
+ path << options[:path].to_s
result = build_host_url(options)
+
if options[:trailing_slash]
if path.include?('?')
result << path.sub(/\?/, '/\&')
@@ -47,7 +44,16 @@ module ActionDispatch
else
result << path
end
- result << "?#{params.to_query}" unless params.empty?
+
+ if options.key? :params
+ params = options[:params].is_a?(Hash) ?
+ options[:params] :
+ { params: options[:params] }
+
+ params.reject! { |_,v| v.to_param.nil? }
+ result << "?#{params.to_query}" unless params.empty?
+ end
+
result << "##{Journey::Router::Utils.escape_fragment(options[:anchor].to_param.to_s)}" if options[:anchor]
result
end
@@ -55,7 +61,7 @@ module ActionDispatch
private
def build_host_url(options)
- if options[:host].blank? && options[:only_path].blank?
+ unless options[:host] || options[:only_path]
raise ArgumentError, 'Missing host to link to! Please provide the :host parameter, set default_url_options[:host], or set :only_path to true'
end
@@ -130,7 +136,7 @@ module ActionDispatch
case options[:protocol]
when "//"
- nil
+ options[:port]
when "https://"
options[:port].to_i == 443 ? nil : options[:port]
else