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/cache.rb19
-rw-r--r--actionpack/lib/action_dispatch/http/mime_negotiation.rb16
-rw-r--r--actionpack/lib/action_dispatch/http/mime_type.rb75
-rw-r--r--actionpack/lib/action_dispatch/http/mime_types.rb3
-rw-r--r--actionpack/lib/action_dispatch/http/parameters.rb2
-rw-r--r--actionpack/lib/action_dispatch/http/request.rb11
-rw-r--r--actionpack/lib/action_dispatch/http/response.rb49
7 files changed, 102 insertions, 73 deletions
diff --git a/actionpack/lib/action_dispatch/http/cache.rb b/actionpack/lib/action_dispatch/http/cache.rb
index 6b25ee9a70..30ade14c26 100644
--- a/actionpack/lib/action_dispatch/http/cache.rb
+++ b/actionpack/lib/action_dispatch/http/cache.rb
@@ -82,24 +82,19 @@ module ActionDispatch
def etag=(etag)
key = ActiveSupport::Cache.expand_cache_key(etag)
- set_header ETAG, %("#{Digest::MD5.hexdigest(key)}")
+ super %("#{Digest::MD5.hexdigest(key)}")
end
- def etag
- get_header ETAG
- end
- alias :etag? :etag
+ def etag?; etag; end
private
DATE = 'Date'.freeze
LAST_MODIFIED = "Last-Modified".freeze
- ETAG = "ETag".freeze
- CACHE_CONTROL = "Cache-Control".freeze
SPECIAL_KEYS = Set.new(%w[extras no-cache max-age public must-revalidate])
def cache_control_segments
- if cache_control = get_header(CACHE_CONTROL)
+ if cache_control = _cache_control
cache_control.delete(' ').split(',')
else
[]
@@ -153,11 +148,11 @@ module ActionDispatch
control.merge! cache_control
if control.empty?
- set_header CACHE_CONTROL, DEFAULT_CACHE_CONTROL
+ self._cache_control = DEFAULT_CACHE_CONTROL
elsif control[:no_cache]
- set_header CACHE_CONTROL, NO_CACHE
+ self._cache_control = NO_CACHE
if control[:extras]
- set_header(CACHE_CONTROL, get_header(CACHE_CONTROL) + ", #{control[:extras].join(', ')}")
+ self._cache_control = _cache_control + ", #{control[:extras].join(', ')}"
end
else
extras = control[:extras]
@@ -169,7 +164,7 @@ module ActionDispatch
options << MUST_REVALIDATE if control[:must_revalidate]
options.concat(extras) if extras
- set_header CACHE_CONTROL, options.join(", ")
+ self._cache_control = options.join(", ")
end
end
end
diff --git a/actionpack/lib/action_dispatch/http/mime_negotiation.rb b/actionpack/lib/action_dispatch/http/mime_negotiation.rb
index a966c5e452..7acf91902d 100644
--- a/actionpack/lib/action_dispatch/http/mime_negotiation.rb
+++ b/actionpack/lib/action_dispatch/http/mime_negotiation.rb
@@ -10,7 +10,7 @@ module ActionDispatch
self.ignore_accept_header = false
end
- # The MIME type of the HTTP request, such as Mime::Type[:XML].
+ # The MIME type of the HTTP request, such as Mime[:xml].
#
# For backward compatibility, the post \format is extracted from the
# X-Post-Data-Format HTTP header if present.
@@ -49,9 +49,9 @@ module ActionDispatch
# Returns the MIME type for the \format used in the request.
#
- # GET /posts/5.xml | request.format => Mime::Type[:XML]
- # GET /posts/5.xhtml | request.format => Mime::Type[:HTML]
- # GET /posts/5 | request.format => Mime::Type[:HTML] or Mime::Type[:JS], or request.accepts.first
+ # GET /posts/5.xml | request.format => Mime[:xml]
+ # GET /posts/5.xhtml | request.format => Mime[:html]
+ # GET /posts/5 | request.format => Mime[:html] or Mime[:js], or request.accepts.first
#
def format(view_path = [])
formats.first || Mime::NullType.instance
@@ -70,9 +70,9 @@ module ActionDispatch
elsif use_accept_header && valid_accept_header
accepts
elsif xhr?
- [Mime::Type[:JS]]
+ [Mime[:js]]
else
- [Mime::Type[:HTML]]
+ [Mime[:html]]
end
set_header k, v
end
@@ -138,14 +138,14 @@ module ActionDispatch
#
def negotiate_mime(order)
formats.each do |priority|
- if priority == Mime::Type[:ALL]
+ if priority == Mime::ALL
return order.first
elsif order.include?(priority)
return priority
end
end
- order.include?(Mime::Type[:ALL]) ? format : 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 36e90e5855..b64f660ec5 100644
--- a/actionpack/lib/action_dispatch/http/mime_type.rb
+++ b/actionpack/lib/action_dispatch/http/mime_type.rb
@@ -1,7 +1,6 @@
require 'singleton'
require 'active_support/core_ext/module/attribute_accessors'
require 'active_support/core_ext/string/starts_ends_with'
-require 'active_support/deprecation'
module Mime
class Mimes
@@ -46,7 +45,8 @@ module Mime
end
def const_missing(sym)
- if Mime::Type.registered?(sym)
+ ext = sym.downcase
+ if Mime[ext]
ActiveSupport::Deprecation.warn <<-eow
Accessing mime types via constants is deprecated. Please change:
@@ -54,16 +54,17 @@ Accessing mime types via constants is deprecated. Please change:
to:
- `Mime::Type[:#{sym}]`
+ `Mime[:#{ext}]`
eow
- Mime::Type[sym]
+ Mime[ext]
else
super
end
end
def const_defined?(sym, inherit = true)
- if Mime::Type.registered?(sym)
+ ext = sym.downcase
+ if Mime[ext]
ActiveSupport::Deprecation.warn <<-eow
Accessing mime types via constants is deprecated. Please change:
@@ -71,7 +72,7 @@ Accessing mime types via constants is deprecated. Please change:
to:
- `Mime::Type.registered?(:#{sym})`
+ `Mime[:#{ext}]`
eow
true
else
@@ -106,7 +107,7 @@ to:
def initialize(index, name, q = nil)
@index = index
@name = name
- q ||= 0.0 if @name == Mime::Type[:ALL].to_s # default wildcard match to end of list
+ q ||= 0.0 if @name == '*/*'.freeze # default wildcard match to end of list
@q = ((q || 1.0).to_f * 100).to_i
end
@@ -131,7 +132,7 @@ to:
exchange_xml_items if app_xml_idx > text_xml_idx # make sure app_xml is ahead of text_xml in the list
delete_at(text_xml_idx) # delete text_xml from the list
elsif text_xml_idx
- text_xml.name = Mime::Type[:XML].to_s
+ text_xml.name = Mime[:xml].to_s
end
# Look for more specific XML-based types and sort them ahead of app/xml
@@ -160,7 +161,7 @@ to:
end
def app_xml_idx
- @app_xml_idx ||= index(Mime::Type[:XML].to_s)
+ @app_xml_idx ||= index(Mime[:xml].to_s)
end
def text_xml
@@ -177,8 +178,6 @@ to:
end
end
- TYPES = {}
-
class << self
TRAILING_STAR_REGEXP = /(text|application)\/\*/
PARAMETER_SEPARATOR_REGEXP = /;\s*\w+="?\w+"?/
@@ -187,18 +186,6 @@ to:
@register_callbacks << block
end
- def registered?(symbol)
- TYPES.key? symbol
- end
-
- def [](symbol)
- TYPES[symbol]
- end
-
- def add_type(symbol, type)
- TYPES[symbol] = type
- end
-
def lookup(string)
LOOKUP[string]
end
@@ -215,7 +202,6 @@ to:
def register(string, symbol, mime_type_synonyms = [], extension_synonyms = [], skip_lookup = false)
new_mime = Type.new(string, symbol, mime_type_synonyms)
- add_type symbol.upcase, new_mime
SET << new_mime
@@ -255,13 +241,13 @@ to:
parse_data_with_trailing_star($1) if accept_header =~ TRAILING_STAR_REGEXP
end
- # For an input of <tt>'text'</tt>, returns <tt>[Mime::Type[:JSON], Mime::Type[:XML], Mime::Type[:ICS],
- # Mime::Type[:HTML], Mime::Type[:CSS], Mime::Type[:CSV], Mime::Type[:JS], Mime::Type[:YAML], Mime::Type[:TEXT]</tt>.
+ # For an input of <tt>'text'</tt>, returns <tt>[Mime[:json], Mime[:xml], Mime[:ics],
+ # Mime[:html], Mime[:css], Mime[:csv], Mime[:js], Mime[:yaml], Mime[:text]</tt>.
#
- # For an input of <tt>'application'</tt>, returns <tt>[Mime::Type[:HTML], Mime::Type[:JS],
- # Mime::Type[:XML], Mime::Type[:YAML], Mime::Type[:ATOM], Mime::Type[:JSON], Mime::Type[:RSS], Mime::Type[:URL_ENCODED_FORM]</tt>.
- def parse_data_with_trailing_star(input)
- Mime::SET.select { |m| m =~ input }
+ # For an input of <tt>'application'</tt>, returns <tt>[Mime[:html], Mime[:js],
+ # Mime[:xml], Mime[:yaml], Mime[:atom], Mime[:json], Mime[:rss], Mime[:url_encoded_form]</tt>.
+ def parse_data_with_trailing_star(type)
+ Mime::SET.select { |m| m =~ type }
end
# This method is opposite of register method.
@@ -270,12 +256,12 @@ to:
#
# Mime::Type.unregister(:mobile)
def unregister(symbol)
- symbol = symbol.upcase
- mime = TYPES.delete symbol
-
- SET.delete_if { |v| v.eql?(mime) }
- LOOKUP.delete_if { |_,v| v.eql?(mime) }
- EXTENSION_LOOKUP.delete_if { |_,v| v.eql?(mime) }
+ symbol = symbol.downcase
+ if mime = Mime[symbol]
+ SET.delete_if { |v| v.eql?(mime) }
+ LOOKUP.delete_if { |_, v| v.eql?(mime) }
+ EXTENSION_LOOKUP.delete_if { |_, v| v.eql?(mime) }
+ end
end
end
@@ -343,13 +329,24 @@ to:
def respond_to_missing?(method, include_private = false) #:nodoc:
method.to_s.ends_with? '?'
end
+ end
+
+ class AllType < Type
+ include Singleton
- class All < Type
- def all?; true; end
- def html?; true; end
+ def initialize
+ super '*/*', :all
end
+
+ def all?; true; end
+ def html?; true; end
end
+ # ALL isn't a real MIME type, so we don't register it for lookup with the
+ # other concrete types. It's a wildcard match that we use for `respond_to`
+ # negotiation internals.
+ ALL = AllType.instance
+
class NullType
include Singleton
diff --git a/actionpack/lib/action_dispatch/http/mime_types.rb b/actionpack/lib/action_dispatch/http/mime_types.rb
index 04828f7c87..87715205d9 100644
--- a/actionpack/lib/action_dispatch/http/mime_types.rb
+++ b/actionpack/lib/action_dispatch/http/mime_types.rb
@@ -31,6 +31,3 @@ Mime::Type.register "application/json", :json, %w( text/x-json application/jsonr
Mime::Type.register "application/pdf", :pdf, [], %w(pdf)
Mime::Type.register "application/zip", :zip, [], %w(zip)
-
-# Create Mime::Type[:ALL] but do not add it to the SET.
-Mime::Type.add_type :ALL, Mime::Type::All.new("*/*", :all, [])
diff --git a/actionpack/lib/action_dispatch/http/parameters.rb b/actionpack/lib/action_dispatch/http/parameters.rb
index e3c4392760..248ecfd676 100644
--- a/actionpack/lib/action_dispatch/http/parameters.rb
+++ b/actionpack/lib/action_dispatch/http/parameters.rb
@@ -4,7 +4,7 @@ module ActionDispatch
PARAMETERS_KEY = 'action_dispatch.request.path_parameters'
DEFAULT_PARSERS = {
- Mime::Type[:JSON] => lambda { |raw_post|
+ Mime[:json] => lambda { |raw_post|
data = ActiveSupport::JSON.decode(raw_post)
data.is_a?(Hash) ? data : {:_json => data}
}
diff --git a/actionpack/lib/action_dispatch/http/request.rb b/actionpack/lib/action_dispatch/http/request.rb
index bf20a33d36..35e3ac304f 100644
--- a/actionpack/lib/action_dispatch/http/request.rb
+++ b/actionpack/lib/action_dispatch/http/request.rb
@@ -65,7 +65,7 @@ module ActionDispatch
path_parameters.each do |key, value|
next unless value.respond_to?(:valid_encoding?)
unless value.valid_encoding?
- raise ActionController::BadRequest, "Invalid parameter: #{key} => #{value}"
+ raise ActionController::BadRequest, "Invalid parameter encoding: #{key} => #{value.inspect}"
end
end
end
@@ -338,10 +338,13 @@ module ActionDispatch
# Override Rack's GET method to support indifferent access
def GET
fetch_header("action_dispatch.request.query_parameters") do |k|
- set_header k, Request::Utils.normalize_encode_params(super || {})
+ rack_query_params = super || {}
+ # Check for non UTF-8 parameter values, which would cause errors later
+ Request::Utils.check_param_encoding(rack_query_params)
+ set_header k, Request::Utils.normalize_encode_params(rack_query_params)
end
rescue Rack::Utils::ParameterTypeError, Rack::Utils::InvalidParameterError => e
- raise ActionController::BadRequest.new(:query, e)
+ raise ActionController::BadRequest.new("Invalid query parameters: #{e.message}", e)
end
alias :query_parameters :GET
@@ -357,7 +360,7 @@ module ActionDispatch
self.request_parameters = Request::Utils.normalize_encode_params(super || {})
raise
rescue Rack::Utils::ParameterTypeError, Rack::Utils::InvalidParameterError => e
- raise ActionController::BadRequest.new(:request, e)
+ raise ActionController::BadRequest.new("Invalid request parameters: #{e.message}", e)
end
alias :request_parameters :POST
diff --git a/actionpack/lib/action_dispatch/http/response.rb b/actionpack/lib/action_dispatch/http/response.rb
index a27ff67114..f0127aa276 100644
--- a/actionpack/lib/action_dispatch/http/response.rb
+++ b/actionpack/lib/action_dispatch/http/response.rb
@@ -39,7 +39,7 @@ module ActionDispatch # :nodoc:
end
def []=(k,v)
- if @response.committed?
+ if @response.sending? || @response.sent?
raise ActionDispatch::IllegalStateError, 'header already sent'
end
@@ -78,6 +78,10 @@ module ActionDispatch # :nodoc:
cattr_accessor(:default_headers)
include Rack::Response::Helpers
+ # Aliasing these off because AD::Http::Cache::Response defines them
+ alias :_cache_control :cache_control
+ alias :_cache_control= :cache_control=
+
include ActionDispatch::Http::FilterRedirect
include ActionDispatch::Http::Cache::Response
include MonitorMixin
@@ -145,7 +149,6 @@ module ActionDispatch # :nodoc:
self.body, self.status = body, status
- @blank = false
@cv = new_cond
@committed = false
@sending = false
@@ -279,12 +282,12 @@ module ActionDispatch # :nodoc:
@stream.body
end
- EMPTY = " "
+ def write(string)
+ @stream.write string
+ end
# Allows you to manually set or override the response body.
def body=(body)
- @blank = true if body == EMPTY
-
if body.respond_to?(:to_path)
@stream = body
else
@@ -294,6 +297,40 @@ module ActionDispatch # :nodoc:
end
end
+ # Avoid having to pass an open file handle as the response body.
+ # Rack::Sendfile will usually intercept the response and uses
+ # the path directly, so there is no reason to open the file.
+ class FileBody #:nodoc:
+ attr_reader :to_path
+
+ def initialize(path)
+ @to_path = path
+ end
+
+ def body
+ File.binread(to_path)
+ end
+
+ # Stream the file's contents if Rack::Sendfile isn't present.
+ def each
+ File.open(to_path, 'rb') do |file|
+ while chunk = file.read(16384)
+ yield chunk
+ end
+ end
+ end
+ end
+
+ # Send the file stored at +path+ as the response body.
+ def send_file(path)
+ commit!
+ @stream = FileBody.new(path)
+ end
+
+ def reset_body!
+ @stream = build_buffer(self, [])
+ end
+
def body_parts
parts = []
@stream.each { |x| parts << x }
@@ -389,7 +426,7 @@ module ActionDispatch # :nodoc:
return if content_type
ct = parse_content_type
- set_content_type(ct.mime_type || Mime::Type[:HTML].to_s,
+ set_content_type(ct.mime_type || Mime[:html].to_s,
ct.charset || self.class.default_charset)
end