aboutsummaryrefslogtreecommitdiffstats
path: root/actionpack/lib/action_dispatch
diff options
context:
space:
mode:
Diffstat (limited to 'actionpack/lib/action_dispatch')
-rw-r--r--actionpack/lib/action_dispatch/http/cache.rb64
-rw-r--r--actionpack/lib/action_dispatch/http/headers.rb4
-rw-r--r--actionpack/lib/action_dispatch/http/mime_types.rb2
-rw-r--r--actionpack/lib/action_dispatch/http/request.rb1
-rw-r--r--actionpack/lib/action_dispatch/middleware/flash.rb5
-rw-r--r--actionpack/lib/action_dispatch/middleware/params_parser.rb1
-rw-r--r--actionpack/lib/action_dispatch/middleware/ssl.rb9
-rw-r--r--actionpack/lib/action_dispatch/routing.rb6
-rw-r--r--actionpack/lib/action_dispatch/routing/mapper.rb2
-rw-r--r--actionpack/lib/action_dispatch/routing/route_set.rb4
-rw-r--r--actionpack/lib/action_dispatch/testing/assertions/routing.rb2
-rw-r--r--actionpack/lib/action_dispatch/testing/integration.rb59
12 files changed, 107 insertions, 52 deletions
diff --git a/actionpack/lib/action_dispatch/http/cache.rb b/actionpack/lib/action_dispatch/http/cache.rb
index 4bd727c14e..9fa2e38ae3 100644
--- a/actionpack/lib/action_dispatch/http/cache.rb
+++ b/actionpack/lib/action_dispatch/http/cache.rb
@@ -17,9 +17,7 @@ module ActionDispatch
end
def if_none_match_etags
- (if_none_match ? if_none_match.split(/\s*,\s*/) : []).collect do |etag|
- etag.gsub(/^\"|\"$/, "")
- end
+ if_none_match ? if_none_match.split(/\s*,\s*/) : []
end
def not_modified?(modified_at)
@@ -28,8 +26,8 @@ module ActionDispatch
def etag_matches?(etag)
if etag
- etag = etag.gsub(/^\"|\"$/, "")
- if_none_match_etags.include?(etag)
+ validators = if_none_match_etags
+ validators.include?(etag) || validators.include?('*')
end
end
@@ -80,27 +78,63 @@ module ActionDispatch
set_header DATE, utc_time.httpdate
end
- # This method allows you to set the ETag for cached content, which
- # will be returned to the end user.
+ # This method sets a weak ETag validator on the response so browsers
+ # and proxies may cache the response, keyed on the ETag. On subsequent
+ # requests, the If-None-Match header is set to the cached ETag. If it
+ # matches the current ETag, we can return a 304 Not Modified response
+ # with no body, letting the browser or proxy know that their cache is
+ # current. Big savings in request time and network bandwidth.
+ #
+ # Weak ETags are considered to be semantically equivalent but not
+ # byte-for-byte identical. This is perfect for browser caching of HTML
+ # pages where we don't care about exact equality, just what the user
+ # is viewing.
#
- # By default, Action Dispatch sets all ETags to be weak.
- # This ensures that if the content changes only semantically,
- # the whole page doesn't have to be regenerated from scratch
- # by the web server. With strong ETags, pages are compared
- # byte by byte, and are regenerated only if they are not exactly equal.
- def etag=(etag)
- key = ActiveSupport::Cache.expand_cache_key(etag)
- super %(W/"#{Digest::MD5.hexdigest(key)}")
+ # Strong ETags are considered byte-for-byte identical. They allow a
+ # browser or proxy cache to support Range requests, useful for paging
+ # through a PDF file or scrubbing through a video. Some CDNs only
+ # support strong ETags and will ignore weak ETags entirely.
+ #
+ # Weak ETags are what we almost always need, so they're the default.
+ # Check out `#strong_etag=` to provide a strong ETag validator.
+ def etag=(weak_validators)
+ self.weak_etag = weak_validators
+ end
+
+ def weak_etag=(weak_validators)
+ set_header 'ETag', generate_weak_etag(weak_validators)
+ end
+
+ def strong_etag=(strong_validators)
+ set_header 'ETag', generate_strong_etag(strong_validators)
end
def etag?; etag; end
+ # True if an ETag is set and it's a weak validator (preceded with W/)
+ def weak_etag?
+ etag? && etag.starts_with?('W/"')
+ end
+
+ # True if an ETag is set and it isn't a weak validator (not preceded with W/)
+ def strong_etag?
+ etag? && !weak_etag?
+ end
+
private
DATE = 'Date'.freeze
LAST_MODIFIED = "Last-Modified".freeze
SPECIAL_KEYS = Set.new(%w[extras no-cache max-age public private must-revalidate])
+ def generate_weak_etag(validators)
+ "W/#{generate_strong_etag(validators)}"
+ end
+
+ def generate_strong_etag(validators)
+ %("#{Digest::MD5.hexdigest(ActiveSupport::Cache.expand_cache_key(validators))}")
+ end
+
def cache_control_segments
if cache_control = _cache_control
cache_control.delete(' ').split(',')
diff --git a/actionpack/lib/action_dispatch/http/headers.rb b/actionpack/lib/action_dispatch/http/headers.rb
index 8e899174c6..69a934b7cd 100644
--- a/actionpack/lib/action_dispatch/http/headers.rb
+++ b/actionpack/lib/action_dispatch/http/headers.rb
@@ -5,7 +5,7 @@ module ActionDispatch
# env = { "CONTENT_TYPE" => "text/plain", "HTTP_USER_AGENT" => "curl/7.43.0" }
# headers = ActionDispatch::Http::Headers.new(env)
# headers["Content-Type"] # => "text/plain"
- # headers["User-Agent"] # => "curl/7/43/0"
+ # headers["User-Agent"] # => "curl/7.43.0"
#
# Also note that when headers are mapped to CGI-like variables by the Rack
# server, both dashes and underscores are converted to underscores. This
@@ -115,7 +115,7 @@ module ActionDispatch
private
- # Converts a HTTP header name to an environment variable name if it is
+ # Converts an 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
diff --git a/actionpack/lib/action_dispatch/http/mime_types.rb b/actionpack/lib/action_dispatch/http/mime_types.rb
index 66cea88256..8b04174f1f 100644
--- a/actionpack/lib/action_dispatch/http/mime_types.rb
+++ b/actionpack/lib/action_dispatch/http/mime_types.rb
@@ -21,7 +21,7 @@ Mime::Type.register "video/mpeg", :mpeg, [], %w(mpg mpeg mpe)
Mime::Type.register "application/xml", :xml, %w( text/xml application/x-xml )
Mime::Type.register "application/rss+xml", :rss
Mime::Type.register "application/atom+xml", :atom
-Mime::Type.register "application/x-yaml", :yaml, %w( text/yaml )
+Mime::Type.register "application/x-yaml", :yaml, %w( text/yaml ), %w(yml yaml)
Mime::Type.register "multipart/form-data", :multipart_form
Mime::Type.register "application/x-www-form-urlencoded", :url_encoded_form
diff --git a/actionpack/lib/action_dispatch/http/request.rb b/actionpack/lib/action_dispatch/http/request.rb
index 316a9f08b7..b0ed681623 100644
--- a/actionpack/lib/action_dispatch/http/request.rb
+++ b/actionpack/lib/action_dispatch/http/request.rb
@@ -337,7 +337,6 @@ module ActionDispatch
else
self.session = {}
end
- self.flash = nil
end
def session=(session) #:nodoc:
diff --git a/actionpack/lib/action_dispatch/middleware/flash.rb b/actionpack/lib/action_dispatch/middleware/flash.rb
index c51dcd542a..06038af571 100644
--- a/actionpack/lib/action_dispatch/middleware/flash.rb
+++ b/actionpack/lib/action_dispatch/middleware/flash.rb
@@ -70,6 +70,11 @@ module ActionDispatch
session.delete('flash')
end
end
+
+ def reset_session # :nodoc
+ super
+ self.flash = nil
+ end
end
class FlashNow #:nodoc:
diff --git a/actionpack/lib/action_dispatch/middleware/params_parser.rb b/actionpack/lib/action_dispatch/middleware/params_parser.rb
index 5841c978af..faf3262b8f 100644
--- a/actionpack/lib/action_dispatch/middleware/params_parser.rb
+++ b/actionpack/lib/action_dispatch/middleware/params_parser.rb
@@ -37,6 +37,7 @@ module ActionDispatch
# The +parsers+ argument can take Hash of parsers where key is identifying
# content mime type, and value is a lambda that is going to process data.
def self.new(app, parsers = {})
+ ActiveSupport::Deprecation.warn('ActionDispatch::ParamsParser is deprecated and will be removed in Rails 5.1. Configure the parameter parsing in ActionDispatch::Request.parameter_parsers.')
parsers = parsers.transform_keys { |key| key.respond_to?(:symbol) ? key.symbol : key }
ActionDispatch::Request.parameter_parsers = ActionDispatch::Request::DEFAULT_PARSERS.merge(parsers)
app
diff --git a/actionpack/lib/action_dispatch/middleware/ssl.rb b/actionpack/lib/action_dispatch/middleware/ssl.rb
index cb442af19b..ab3077b308 100644
--- a/actionpack/lib/action_dispatch/middleware/ssl.rb
+++ b/actionpack/lib/action_dispatch/middleware/ssl.rb
@@ -35,9 +35,9 @@ module ActionDispatch
# expire HSTS immediately. Setting `hsts: false` is a shortcut for
# `hsts: { expires: 0 }`.
#
- # Redirection can be constrained to only whitelisted requests with `constrain_to`:
+ # Requests can opt-out of redirection with `exclude`:
#
- # config.ssl_options = { redirect: { constrain_to: -> request { request.path !~ /healthcheck/ } } }
+ # config.ssl_options = { redirect: { exclude: -> request { request.path =~ /healthcheck/ } } }
class SSL
# Default to 180 days, the low end for https://www.ssllabs.com/ssltest/
# and greater than the 18-week requirement for browser preload lists.
@@ -59,7 +59,8 @@ module ActionDispatch
else
@redirect = redirect
end
- @constrain_to = @redirect && @redirect[:constrain_to] || proc { @redirect }
+
+ @exclude = @redirect && @redirect[:exclude] || proc { !@redirect }
@secure_cookies = secure_cookies
if hsts != true && hsts != false && hsts[:subdomains].nil?
@@ -84,7 +85,7 @@ module ActionDispatch
flag_cookies_as_secure! headers if @secure_cookies
end
else
- return redirect_to_https request if @constrain_to.call(request)
+ return redirect_to_https request unless @exclude.call(request)
@app.call(env)
end
end
diff --git a/actionpack/lib/action_dispatch/routing.rb b/actionpack/lib/action_dispatch/routing.rb
index dcf800b215..79d2f1f13c 100644
--- a/actionpack/lib/action_dispatch/routing.rb
+++ b/actionpack/lib/action_dispatch/routing.rb
@@ -73,14 +73,14 @@ module ActionDispatch
# get 'post/:id' => 'posts#show'
# post 'post/:id' => 'posts#create_comment'
#
+ # Now, if you POST to <tt>/posts/:id</tt>, it will route to the <tt>create_comment</tt> action. A GET on the same
+ # URL will route to the <tt>show</tt> action.
+ #
# If your route needs to respond to more than one HTTP method (or all methods) then using the
# <tt>:via</tt> option on <tt>match</tt> is preferable.
#
# match 'post/:id' => 'posts#show', via: [:get, :post]
#
- # Now, if you POST to <tt>/posts/:id</tt>, it will route to the <tt>create_comment</tt> action. A GET on the same
- # URL will route to the <tt>show</tt> action.
- #
# == Named routes
#
# Routes can be named by passing an <tt>:as</tt> option,
diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb
index c4621e386d..ffd5b83ad3 100644
--- a/actionpack/lib/action_dispatch/routing/mapper.rb
+++ b/actionpack/lib/action_dispatch/routing/mapper.rb
@@ -1597,7 +1597,7 @@ module ActionDispatch
route_options = options.dup
if _path && option_path
ActiveSupport::Deprecation.warn <<-eowarn
-Specifying strings for both :path and the route path is deprecated. Change things like this:
+Specifying strings for both :path and the route path is deprecated. Change things like this:
match #{_path.inspect}, :path => #{option_path.inspect}
diff --git a/actionpack/lib/action_dispatch/routing/route_set.rb b/actionpack/lib/action_dispatch/routing/route_set.rb
index 85f202b823..16237bd564 100644
--- a/actionpack/lib/action_dispatch/routing/route_set.rb
+++ b/actionpack/lib/action_dispatch/routing/route_set.rb
@@ -517,14 +517,14 @@ module ActionDispatch
if route.segment_keys.include?(:controller)
ActiveSupport::Deprecation.warn(<<-MSG.squish)
Using a dynamic :controller segment in a route is deprecated and
- will be removed in Rails 5.1
+ will be removed in Rails 5.1.
MSG
end
if route.segment_keys.include?(:action)
ActiveSupport::Deprecation.warn(<<-MSG.squish)
Using a dynamic :action segment in a route is deprecated and
- will be removed in Rails 5.1
+ will be removed in Rails 5.1.
MSG
end
diff --git a/actionpack/lib/action_dispatch/testing/assertions/routing.rb b/actionpack/lib/action_dispatch/testing/assertions/routing.rb
index e7af27463c..44ad2c10d8 100644
--- a/actionpack/lib/action_dispatch/testing/assertions/routing.rb
+++ b/actionpack/lib/action_dispatch/testing/assertions/routing.rb
@@ -117,7 +117,7 @@ module ActionDispatch
# # Tests a route, providing a defaults hash
# assert_routing 'controller/action/9', {id: "9", item: "square"}, {controller: "controller", action: "action"}, {}, {item: "square"}
#
- # # Tests a route with a HTTP method
+ # # Tests a route with an HTTP method
# assert_routing({ method: 'put', path: '/product/321' }, { controller: "product", action: "update", id: "321" })
def assert_routing(path, options, defaults={}, extras={}, message=nil)
assert_recognizes(options, path, extras, message)
diff --git a/actionpack/lib/action_dispatch/testing/integration.rb b/actionpack/lib/action_dispatch/testing/integration.rb
index f4534b4173..69ae5a8468 100644
--- a/actionpack/lib/action_dispatch/testing/integration.rb
+++ b/actionpack/lib/action_dispatch/testing/integration.rb
@@ -95,7 +95,7 @@ module ActionDispatch
ActiveSupport::Deprecation.warn(<<-MSG.strip_heredoc)
xhr and xml_http_request methods are deprecated in favor of
- `get "/posts", xhr: true` and `post "/posts/1", xhr: true`
+ `get "/posts", xhr: true` and `post "/posts/1", xhr: true`.
MSG
process(request_method, path, params: params, headers: headers, xhr: true)
@@ -735,34 +735,49 @@ module ActionDispatch
# Consult the Rails Testing Guide for more.
class IntegrationTest < ActiveSupport::TestCase
- include Integration::Runner
- include ActionController::TemplateAssertions
- include ActionDispatch::Routing::UrlFor
+ module UrlOptions
+ extend ActiveSupport::Concern
+ def url_options
+ integration_session.url_options
+ end
+ end
- @@app = nil
+ module Behavior
+ extend ActiveSupport::Concern
- def self.app
- @@app || ActionDispatch.test_app
- end
+ include Integration::Runner
+ include ActionController::TemplateAssertions
- def self.app=(app)
- @@app = app
- end
+ included do
+ include ActionDispatch::Routing::UrlFor
+ include UrlOptions # don't let UrlFor override the url_options method
+ ActiveSupport.run_load_hooks(:action_dispatch_integration_test, self)
+ @@app = nil
+ end
- def app
- super || self.class.app
- end
+ module ClassMethods
+ def app
+ defined?(@@app) ? @@app : ActionDispatch.test_app
+ end
- def url_options
- integration_session.url_options
- end
+ def app=(app)
+ @@app = app
+ end
- def document_root_element
- html_document.root
- end
+ def register_encoder(*args)
+ Integration::Session::RequestEncoder.register_encoder(*args)
+ end
+ end
- def self.register_encoder(*args)
- Integration::Session::RequestEncoder.register_encoder(*args)
+ def app
+ super || self.class.app
+ end
+
+ def document_root_element
+ html_document.root
+ end
end
+
+ include Behavior
end
end