diff options
Diffstat (limited to 'actionpack/lib/action_dispatch/middleware')
7 files changed, 49 insertions, 28 deletions
diff --git a/actionpack/lib/action_dispatch/middleware/cookies.rb b/actionpack/lib/action_dispatch/middleware/cookies.rb index b1f0f5fc28..dd1f140051 100644 --- a/actionpack/lib/action_dispatch/middleware/cookies.rb +++ b/actionpack/lib/action_dispatch/middleware/cookies.rb @@ -79,6 +79,9 @@ module ActionDispatch # domain: %w(.example.com .example.org) # Allow the cookie # # for concrete domain names. # + # * <tt>:tld_length</tt> - When using <tt>:domain => :all</tt>, this option can be used to explicitly + # set the TLD length when using a short (<= 3 character) domain that is being interpreted as part of a TLD. + # For example, to share cookies between user1.lvh.me and user2.lvh.me, set <tt>:tld_length</tt> to 1. # * <tt>:expires</tt> - The time at which this cookie expires, as a \Time object. # * <tt>:secure</tt> - Whether this cookie is only transmitted to HTTPS servers. # Default is +false+. @@ -181,7 +184,7 @@ module ActionDispatch # to the Message{Encryptor,Verifier} allows us to handle the # (de)serialization step within the cookie jar, which gives us the # opportunity to detect and migrate legacy cookies. - module VerifyAndUpgradeLegacySignedMessage + module VerifyAndUpgradeLegacySignedMessage # :nodoc: def initialize(*args) super @legacy_verifier = ActiveSupport::MessageVerifier.new(@options[:secret_token], serializer: ActiveSupport::MessageEncryptor::NullSerializer) @@ -392,7 +395,7 @@ module ActionDispatch end end - class JsonSerializer + class JsonSerializer # :nodoc: def self.load(value) ActiveSupport::JSON.decode(value) end @@ -402,7 +405,7 @@ module ActionDispatch end end - module SerializedCookieJars + module SerializedCookieJars # :nodoc: MARSHAL_SIGNATURE = "\x04\x08".freeze protected @@ -454,12 +457,16 @@ module ActionDispatch @verifier = ActiveSupport::MessageVerifier.new(secret, digest: digest, serializer: ActiveSupport::MessageEncryptor::NullSerializer) end + # Returns the value of the cookie by +name+ if it is untampered, + # returns +nil+ otherwise or if no such cookie exists. def [](name) if signed_message = @parent_jar[name] deserialize name, verify(signed_message) end end + # Signs and sets the cookie named +name+. The second argument may be the cookie's + # value or a hash of options as documented above. def []=(name, options) if options.is_a?(Hash) options.symbolize_keys! @@ -511,12 +518,16 @@ module ActionDispatch @encryptor = ActiveSupport::MessageEncryptor.new(secret, sign_secret, digest: digest, serializer: ActiveSupport::MessageEncryptor::NullSerializer) end + # Returns the value of the cookie by +name+ if it is untampered, + # returns +nil+ otherwise or if no such cookie exists. def [](name) if encrypted_message = @parent_jar[name] deserialize name, decrypt_and_verify(encrypted_message) end end + # Encrypts and sets the cookie named +name+. The second argument may be the cookie's + # value or a hash of options as documented above. def []=(name, options) if options.is_a?(Hash) options.symbolize_keys! diff --git a/actionpack/lib/action_dispatch/middleware/exception_wrapper.rb b/actionpack/lib/action_dispatch/middleware/exception_wrapper.rb index d176a73633..8c3d45584d 100644 --- a/actionpack/lib/action_dispatch/middleware/exception_wrapper.rb +++ b/actionpack/lib/action_dispatch/middleware/exception_wrapper.rb @@ -17,7 +17,9 @@ module ActionDispatch 'ActionController::InvalidCrossOriginRequest' => :unprocessable_entity, 'ActionDispatch::ParamsParser::ParseError' => :bad_request, 'ActionController::BadRequest' => :bad_request, - 'ActionController::ParameterMissing' => :bad_request + 'ActionController::ParameterMissing' => :bad_request, + 'Rack::Utils::ParameterTypeError' => :bad_request, + 'Rack::Utils::InvalidParameterError' => :bad_request ) cattr_accessor :rescue_templates diff --git a/actionpack/lib/action_dispatch/middleware/reloader.rb b/actionpack/lib/action_dispatch/middleware/reloader.rb index 15b5a48535..6c7fba00cb 100644 --- a/actionpack/lib/action_dispatch/middleware/reloader.rb +++ b/actionpack/lib/action_dispatch/middleware/reloader.rb @@ -11,9 +11,9 @@ module ActionDispatch # the response body. This is important for streaming responses such as the # following: # - # self.response_body = lambda { |response, output| + # self.response_body = -> (response, output) do # # code here which refers to application models - # } + # end # # Cleanup callbacks will not be called until after the response_body lambda # is evaluated, ensuring that it can refer to application models and other diff --git a/actionpack/lib/action_dispatch/middleware/remote_ip.rb b/actionpack/lib/action_dispatch/middleware/remote_ip.rb index 7c4236518d..9f894e2ec6 100644 --- a/actionpack/lib/action_dispatch/middleware/remote_ip.rb +++ b/actionpack/lib/action_dispatch/middleware/remote_ip.rb @@ -74,7 +74,7 @@ module ActionDispatch # requests. For those requests that do need to know the IP, the # GetIp#calculate_ip method will calculate the memoized client IP address. def call(env) - env["action_dispatch.remote_ip"] = GetIp.new(env, self) + env["action_dispatch.remote_ip"] = GetIp.new(env, check_ip, proxies) @app.call(env) end @@ -82,10 +82,10 @@ module ActionDispatch # into an actual IP address. If the ActionDispatch::Request#remote_ip method # is called, this class will calculate the value and then memoize it. class GetIp - def initialize(env, middleware) + def initialize(env, check_ip, proxies) @env = env - @check_ip = middleware.check_ip - @proxies = middleware.proxies + @check_ip = check_ip + @proxies = proxies end # Sort through the various IP address headers, looking for the IP most diff --git a/actionpack/lib/action_dispatch/middleware/request_id.rb b/actionpack/lib/action_dispatch/middleware/request_id.rb index b9ca524309..1555ff72af 100644 --- a/actionpack/lib/action_dispatch/middleware/request_id.rb +++ b/actionpack/lib/action_dispatch/middleware/request_id.rb @@ -13,22 +13,23 @@ module ActionDispatch # from multiple pieces of the stack. class RequestId X_REQUEST_ID = "X-Request-Id".freeze # :nodoc: - ACTION_DISPATCH_REQUEST_ID = "action_dispatch.request_id".freeze # :nodoc: - HTTP_X_REQUEST_ID = "HTTP_X_REQUEST_ID".freeze # :nodoc: def initialize(app) @app = app end def call(env) - env[ACTION_DISPATCH_REQUEST_ID] = external_request_id(env) || internal_request_id - @app.call(env).tap { |_status, headers, _body| headers[X_REQUEST_ID] = env[ACTION_DISPATCH_REQUEST_ID] } + req = ActionDispatch::Request.new env + req.request_id = make_request_id(req.x_request_id) + @app.call(env).tap { |_status, headers, _body| headers[X_REQUEST_ID] = req.request_id } end private - def external_request_id(env) - if request_id = env[HTTP_X_REQUEST_ID].presence + def make_request_id(request_id) + if request_id.presence request_id.gsub(/[^\w\-]/, "".freeze).first(255) + else + internal_request_id end end diff --git a/actionpack/lib/action_dispatch/middleware/ssl.rb b/actionpack/lib/action_dispatch/middleware/ssl.rb index 0c7caef25d..7b3d8bcc5b 100644 --- a/actionpack/lib/action_dispatch/middleware/ssl.rb +++ b/actionpack/lib/action_dispatch/middleware/ssl.rb @@ -22,7 +22,7 @@ module ActionDispatch if request.ssl? status, headers, body = @app.call(env) - headers = hsts_headers.merge(headers) + headers.reverse_merge!(hsts_headers) flag_cookies_as_secure!(headers) [status, headers, body] else diff --git a/actionpack/lib/action_dispatch/middleware/static.rb b/actionpack/lib/action_dispatch/middleware/static.rb index 9a92b690c7..b098ea389f 100644 --- a/actionpack/lib/action_dispatch/middleware/static.rb +++ b/actionpack/lib/action_dispatch/middleware/static.rb @@ -3,29 +3,36 @@ require 'active_support/core_ext/uri' module ActionDispatch # This middleware returns a file's contents from disk in the body response. - # When initialized it can accept an optional 'Cache-Control' header which + # When initialized, it can accept an optional 'Cache-Control' header, which # will be set when a response containing a file's contents is delivered. # # This middleware will render the file specified in `env["PATH_INFO"]` - # where the base path is in the +root+ directory. For example if the +root+ - # is set to `public/` then a request with `env["PATH_INFO"]` of - # `assets/application.js` will return a response with contents of a file + # where the base path is in the +root+ directory. For example, if the +root+ + # is set to `public/`, then a request with `env["PATH_INFO"]` of + # `assets/application.js` will return a response with the contents of a file # located at `public/assets/application.js` if the file exists. If the file - # does not exist a 404 "File not Found" response will be returned. + # does not exist, a 404 "File not Found" response will be returned. class FileHandler - def initialize(root, cache_control) + def initialize(root, cache_control, index: 'index') @root = root.chomp('/') @compiled_root = /^#{Regexp.escape(root)}/ headers = cache_control && { 'Cache-Control' => cache_control } @file_server = ::Rack::File.new(@root, headers) + @index = index end + # Takes a path to a file. If the file is found, has valid encoding, and has + # correct read permissions, the return value is a URI-escaped string + # representing the filename. Otherwise, false is returned. + # + # Used by the `Static` class to check the existence of a valid file + # in the server's `public/` directory (see Static#call). def match?(path) path = URI.parser.unescape(path) return false unless path.valid_encoding? path = Rack::Utils.clean_path_info path - paths = [path, "#{path}#{ext}", "#{path}/index#{ext}"] + paths = [path, "#{path}#{ext}", "#{path}/#{@index}#{ext}"] if match = paths.detect { |p| path = File.join(@root, p.force_encoding('UTF-8')) @@ -88,7 +95,7 @@ module ActionDispatch end # This middleware will attempt to return the contents of a file's body from - # disk in the response. If a file is not found on disk, the request will be + # disk in the response. If a file is not found on disk, the request will be # delegated to the application stack. This middleware is commonly initialized # to serve assets from a server's `public/` directory. # @@ -97,9 +104,9 @@ module ActionDispatch # produce a directory traversal using this middleware. Only 'GET' and 'HEAD' # requests will result in a file being returned. class Static - def initialize(app, path, cache_control=nil) + def initialize(app, path, cache_control = nil, index: 'index') @app = app - @file_handler = FileHandler.new(path, cache_control) + @file_handler = FileHandler.new(path, cache_control, index: index) end def call(env) @@ -107,7 +114,7 @@ module ActionDispatch when 'GET', 'HEAD' path = env['PATH_INFO'].chomp('/') if match = @file_handler.match?(path) - env["PATH_INFO"] = match + env['PATH_INFO'] = match return @file_handler.call(env) end end |