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/mime_negotiation.rb14
-rw-r--r--actionpack/lib/action_dispatch/http/mime_type.rb7
-rw-r--r--actionpack/lib/action_dispatch/http/request.rb27
-rw-r--r--actionpack/lib/action_dispatch/http/response.rb2
-rw-r--r--actionpack/lib/action_dispatch/journey/gtg/transition_table.rb8
-rw-r--r--actionpack/lib/action_dispatch/journey/router/utils.rb2
-rw-r--r--actionpack/lib/action_dispatch/middleware/callbacks.rb12
-rw-r--r--actionpack/lib/action_dispatch/middleware/exception_wrapper.rb4
-rw-r--r--actionpack/lib/action_dispatch/middleware/remote_ip.rb2
-rw-r--r--actionpack/lib/action_dispatch/middleware/session/cookie_store.rb4
-rw-r--r--actionpack/lib/action_dispatch/middleware/show_exceptions.rb7
-rw-r--r--actionpack/lib/action_dispatch/request/session.rb12
-rw-r--r--actionpack/lib/action_dispatch/routing/inspector.rb3
-rw-r--r--actionpack/lib/action_dispatch/routing/mapper.rb37
-rw-r--r--actionpack/lib/action_dispatch/routing/redirection.rb51
-rw-r--r--actionpack/lib/action_dispatch/routing/url_for.rb2
-rw-r--r--actionpack/lib/action_dispatch/testing/assertions/response.rb3
17 files changed, 140 insertions, 57 deletions
diff --git a/actionpack/lib/action_dispatch/http/mime_negotiation.rb b/actionpack/lib/action_dispatch/http/mime_negotiation.rb
index 40bb060d52..346598b6de 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
@@ -64,6 +66,18 @@ module ActionDispatch
end
end
+ # Sets the \variant for template.
+ def variant=(variant)
+ if variant.is_a? Symbol
+ @variant = variant
+ else
+ raise ArgumentError, "request.variant must be set to a Symbol, 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.
#
diff --git a/actionpack/lib/action_dispatch/http/mime_type.rb b/actionpack/lib/action_dispatch/http/mime_type.rb
index ef144c3c76..a398919ca7 100644
--- a/actionpack/lib/action_dispatch/http/mime_type.rb
+++ b/actionpack/lib/action_dispatch/http/mime_type.rb
@@ -1,5 +1,6 @@
require 'set'
-require 'active_support/core_ext/class/attribute_accessors'
+require 'singleton'
+require 'active_support/core_ext/module/attribute_accessors'
require 'active_support/core_ext/string/starts_ends_with'
module Mime
@@ -27,7 +28,7 @@ module Mime
class << self
def [](type)
return type if type.is_a?(Type)
- Type.lookup_by_extension(type) || NullType.new
+ Type.lookup_by_extension(type) || NullType.instance
end
def fetch(type)
@@ -292,6 +293,8 @@ module Mime
end
class NullType
+ include Singleton
+
def nil?
true
end
diff --git a/actionpack/lib/action_dispatch/http/request.rb b/actionpack/lib/action_dispatch/http/request.rb
index aba8f66118..1318c62fbe 100644
--- a/actionpack/lib/action_dispatch/http/request.rb
+++ b/actionpack/lib/action_dispatch/http/request.rb
@@ -271,7 +271,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 +279,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
@@ -299,17 +299,24 @@ module ActionDispatch
LOCALHOST =~ remote_addr && LOCALHOST =~ remote_ip
end
- protected
+ # Extracted into ActionDispatch::Request::Utils.deep_munge, but kept here for backwards compatibility.
+ def deep_munge(hash)
+ ActiveSupport::Deprecation.warn(
+ "This method has been extracted into ActionDispatch::Request::Utils.deep_munge. Please start using that instead."
+ )
- def parse_query(qs)
- Utils.deep_munge(super)
+ Utils.deep_munge(hash)
end
- private
+ protected
+ def parse_query(qs)
+ Utils.deep_munge(super)
+ end
- def check_method(name)
- HTTP_METHOD_LOOKUP[name] || raise(ActionController::UnknownHttpMethod, "#{name}, accepted HTTP methods are #{HTTP_METHODS.to_sentence(:locale => :en)}")
- name
- end
+ private
+ def check_method(name)
+ HTTP_METHOD_LOOKUP[name] || raise(ActionController::UnknownHttpMethod, "#{name}, accepted HTTP methods are #{HTTP_METHODS.to_sentence(:locale => :en)}")
+ name
+ end
end
end
diff --git a/actionpack/lib/action_dispatch/http/response.rb b/actionpack/lib/action_dispatch/http/response.rb
index 5247e61a23..7b2655b2d8 100644
--- a/actionpack/lib/action_dispatch/http/response.rb
+++ b/actionpack/lib/action_dispatch/http/response.rb
@@ -1,4 +1,4 @@
-require 'active_support/core_ext/class/attribute_accessors'
+require 'active_support/core_ext/module/attribute_accessors'
require 'monitor'
module ActionDispatch # :nodoc:
diff --git a/actionpack/lib/action_dispatch/journey/gtg/transition_table.rb b/actionpack/lib/action_dispatch/journey/gtg/transition_table.rb
index 971cb3447f..5a79059ed6 100644
--- a/actionpack/lib/action_dispatch/journey/gtg/transition_table.rb
+++ b/actionpack/lib/action_dispatch/journey/gtg/transition_table.rb
@@ -43,9 +43,7 @@ module ActionDispatch
move_string(t, a).concat(move_regexp(t, a))
end
- def to_json
- require 'json'
-
+ def as_json(options = nil)
simple_regexp = Hash.new { |h,k| h[k] = {} }
@regexp_states.each do |from, hash|
@@ -54,11 +52,11 @@ module ActionDispatch
end
end
- JSON.dump({
+ {
regexp_states: simple_regexp,
string_states: @string_states,
accepting: @accepting
- })
+ }
end
def to_svg
diff --git a/actionpack/lib/action_dispatch/journey/router/utils.rb b/actionpack/lib/action_dispatch/journey/router/utils.rb
index 1edf86cd88..d1a004af50 100644
--- a/actionpack/lib/action_dispatch/journey/router/utils.rb
+++ b/actionpack/lib/action_dispatch/journey/router/utils.rb
@@ -18,7 +18,7 @@ module ActionDispatch
path = "/#{path}"
path.squeeze!('/')
path.sub!(%r{/+\Z}, '')
- path.gsub!(/(%[a-f0-9]{2}+)/) { $1.upcase }
+ path.gsub!(/(%[a-f0-9]{2})/) { $1.upcase }
path = '/' if path == ''
path
end
diff --git a/actionpack/lib/action_dispatch/middleware/callbacks.rb b/actionpack/lib/action_dispatch/middleware/callbacks.rb
index 852f1cf6f5..baf9d5779e 100644
--- a/actionpack/lib/action_dispatch/middleware/callbacks.rb
+++ b/actionpack/lib/action_dispatch/middleware/callbacks.rb
@@ -8,14 +8,14 @@ module ActionDispatch
class << self
delegate :to_prepare, :to_cleanup, :to => "ActionDispatch::Reloader"
- end
- def self.before(*args, &block)
- set_callback(:call, :before, *args, &block)
- end
+ def before(*args, &block)
+ set_callback(:call, :before, *args, &block)
+ end
- def self.after(*args, &block)
- set_callback(:call, :after, *args, &block)
+ def after(*args, &block)
+ set_callback(:call, :after, *args, &block)
+ end
end
def initialize(app)
diff --git a/actionpack/lib/action_dispatch/middleware/exception_wrapper.rb b/actionpack/lib/action_dispatch/middleware/exception_wrapper.rb
index 1de3d14530..377f05c982 100644
--- a/actionpack/lib/action_dispatch/middleware/exception_wrapper.rb
+++ b/actionpack/lib/action_dispatch/middleware/exception_wrapper.rb
@@ -1,5 +1,5 @@
require 'action_controller/metal/exceptions'
-require 'active_support/core_ext/class/attribute_accessors'
+require 'active_support/core_ext/module/attribute_accessors'
module ActionDispatch
class ExceptionWrapper
@@ -96,7 +96,7 @@ module ActionDispatch
def source_fragment(path, line)
return unless Rails.respond_to?(:root) && Rails.root
full_path = Rails.root.join(path)
- if File.exists?(full_path)
+ if File.exist?(full_path)
File.open(full_path, "r") do |file|
start = [line - 3, 0].max
lines = file.each_line.drop(start).take(6)
diff --git a/actionpack/lib/action_dispatch/middleware/remote_ip.rb b/actionpack/lib/action_dispatch/middleware/remote_ip.rb
index 8879291dbd..57bc6d5cd0 100644
--- a/actionpack/lib/action_dispatch/middleware/remote_ip.rb
+++ b/actionpack/lib/action_dispatch/middleware/remote_ip.rb
@@ -143,7 +143,7 @@ module ActionDispatch
# proxies with incompatible IP header conventions, and there is no way
# for us to determine which header is the right one after the fact.
# Since we have no idea, we give up and explode.
- should_check_ip = @check_ip && client_ips.last
+ should_check_ip = @check_ip && client_ips.last && forwarded_ips.last
if should_check_ip && !forwarded_ips.include?(client_ips.last)
# We don't know which came from the proxy, and which from the user
raise IpSpoofAttackError, "IP spoofing attack?! " +
diff --git a/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb b/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb
index b9eb8036e9..11b42ee5be 100644
--- a/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb
+++ b/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb
@@ -15,8 +15,8 @@ module ActionDispatch
# best possible option given your application's configuration.
#
# If you only have secret_token set, your cookies will be signed, but
- # not encrypted. This means a user cannot alter his +user_id+ without
- # knowing your app's secret key, but can easily read his +user_id+. This
+ # not encrypted. This means a user cannot alter their +user_id+ without
+ # knowing your app's secret key, but can easily read their +user_id+. This
# was the default for Rails 3 apps.
#
# If you have secret_key_base set, your cookies will be encrypted. This
diff --git a/actionpack/lib/action_dispatch/middleware/show_exceptions.rb b/actionpack/lib/action_dispatch/middleware/show_exceptions.rb
index fcc5bc12c4..1d4f0f89a6 100644
--- a/actionpack/lib/action_dispatch/middleware/show_exceptions.rb
+++ b/actionpack/lib/action_dispatch/middleware/show_exceptions.rb
@@ -29,8 +29,11 @@ module ActionDispatch
def call(env)
@app.call(env)
rescue Exception => exception
- raise exception if env['action_dispatch.show_exceptions'] == false
- render_exception(env, exception)
+ if env['action_dispatch.show_exceptions'] == false
+ raise exception
+ else
+ render_exception(env, exception)
+ end
end
private
diff --git a/actionpack/lib/action_dispatch/request/session.rb b/actionpack/lib/action_dispatch/request/session.rb
index 7bc812fd22..6d911a75f1 100644
--- a/actionpack/lib/action_dispatch/request/session.rb
+++ b/actionpack/lib/action_dispatch/request/session.rb
@@ -127,6 +127,18 @@ module ActionDispatch
@delegate.delete key.to_s
end
+ def fetch(key, default=nil)
+ if self.key?(key)
+ self[key]
+ elsif default
+ self[key] = default
+ elsif block_given?
+ self[key] = yield(key)
+ else
+ raise KeyError
+ end
+ end
+
def inspect
if loaded?
super
diff --git a/actionpack/lib/action_dispatch/routing/inspector.rb b/actionpack/lib/action_dispatch/routing/inspector.rb
index cffb814e1e..120bc54333 100644
--- a/actionpack/lib/action_dispatch/routing/inspector.rb
+++ b/actionpack/lib/action_dispatch/routing/inspector.rb
@@ -179,7 +179,8 @@ module ActionDispatch
private
def draw_section(routes)
- name_width, verb_width, path_width = widths(routes)
+ header_lengths = ['Prefix', 'Verb', 'URI Pattern'].map(&:length)
+ name_width, verb_width, path_width = widths(routes).zip(header_lengths).map(&:max)
routes.map do |r|
"#{r[:name].rjust(name_width)} #{r[:verb].ljust(verb_width)} #{r[:path].ljust(path_width)} #{r[:reqs]}"
diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb
index db9c993590..846a6345cb 100644
--- a/actionpack/lib/action_dispatch/routing/mapper.rb
+++ b/actionpack/lib/action_dispatch/routing/mapper.rb
@@ -147,14 +147,16 @@ module ActionDispatch
@defaults.merge!(options[:defaults]) if options[:defaults]
options.each do |key, default|
- next if Regexp === default || IGNORE_OPTIONS.include?(key)
- @defaults[key] = default
+ unless Regexp === default || IGNORE_OPTIONS.include?(key)
+ @defaults[key] = default
+ end
end
if options[:constraints].is_a?(Hash)
options[:constraints].each do |key, default|
- next unless URL_OPTIONS.include?(key) && (String === default || Fixnum === default)
- @defaults[key] ||= default
+ if URL_OPTIONS.include?(key) && (String === default || Fixnum === default)
+ @defaults[key] ||= default
+ end
end
end
@@ -166,19 +168,21 @@ module ActionDispatch
end
def normalize_conditions!
- @conditions.merge!(:path_info => path)
+ @conditions[:path_info] = path
constraints.each do |key, condition|
- next if segment_keys.include?(key) || key == :controller
- @conditions[key] = condition
+ unless segment_keys.include?(key) || key == :controller
+ @conditions[key] = condition
+ end
end
- @conditions[:required_defaults] = []
+ required_defaults = []
options.each do |key, required_default|
- next if segment_keys.include?(key) || IGNORE_OPTIONS.include?(key)
- next if Regexp === required_default
- @conditions[:required_defaults] << key
+ unless segment_keys.include?(key) || IGNORE_OPTIONS.include?(key) || Regexp === required_default
+ required_defaults << key
+ end
end
+ @conditions[:required_defaults] = required_defaults
via_all = options.delete(:via) if options[:via] == :all
@@ -192,8 +196,7 @@ module ActionDispatch
end
if via = options[:via]
- list = Array(via).map { |m| m.to_s.dasherize.upcase }
- @conditions.merge!(:request_method => list)
+ @conditions[:request_method] = Array(via).map { |m| m.to_s.dasherize.upcase }
end
end
@@ -226,11 +229,13 @@ module ActionDispatch
action = action.to_s unless action.is_a?(Regexp)
if controller.blank? && segment_keys.exclude?(:controller)
- raise ArgumentError, "missing :controller"
+ message = "Missing :controller key on routes definition, please check your routes."
+ raise ArgumentError, message
end
if action.blank? && segment_keys.exclude?(:action)
- raise ArgumentError, "missing :action"
+ message = "Missing :action key on routes definition, please check your routes."
+ raise ArgumentError, message
end
if controller.is_a?(String) && controller !~ /\A[a-z_0-9\/]*\z/
@@ -384,7 +389,7 @@ module ActionDispatch
# The namespace for :controller.
#
# match 'path', to: 'c#a', module: 'sekret', controller: 'posts'
- # #=> Sekret::PostsController
+ # # => Sekret::PostsController
#
# See <tt>Scoping#namespace</tt> for its scope equivalent.
#
diff --git a/actionpack/lib/action_dispatch/routing/redirection.rb b/actionpack/lib/action_dispatch/routing/redirection.rb
index 68094f129f..cbf4c5aa8b 100644
--- a/actionpack/lib/action_dispatch/routing/redirection.rb
+++ b/actionpack/lib/action_dispatch/routing/redirection.rb
@@ -30,6 +30,10 @@ module ActionDispatch
uri.host ||= req.host
uri.port ||= req.port unless req.standard_port?
+ if relative_path?(uri.path)
+ uri.path = "#{req.script_name}/#{uri.path}"
+ end
+
body = %(<html><body>You are being <a href="#{ERB::Util.h(uri.to_s)}">redirected</a>.</body></html>)
headers = {
@@ -48,11 +52,38 @@ module ActionDispatch
def inspect
"redirect(#{status})"
end
+
+ private
+ def relative_path?(path)
+ path && !path.empty? && path[0] != '/'
+ end
+
+ def escape(params)
+ Hash[params.map{ |k,v| [k, Rack::Utils.escape(v)] }]
+ end
+
+ def escape_fragment(params)
+ Hash[params.map{ |k,v| [k, Journey::Router::Utils.escape_fragment(v)] }]
+ end
+
+ def escape_path(params)
+ Hash[params.map{ |k,v| [k, Journey::Router::Utils.escape_path(v)] }]
+ end
end
class PathRedirect < Redirect
+ URL_PARTS = /\A([^?]+)?(\?[^#]+)?(#.+)?\z/
+
def path(params, request)
- (params.empty? || !block.match(/%\{\w*\}/)) ? block : (block % escape(params))
+ if block.match(URL_PARTS)
+ path = interpolation_required?($1, params) ? $1 % escape_path(params) : $1
+ query = interpolation_required?($2, params) ? $2 % escape(params) : $2
+ fragment = interpolation_required?($3, params) ? $3 % escape_fragment(params) : $3
+
+ "#{path}#{query}#{fragment}"
+ else
+ interpolation_required?(block, params) ? block % escape(params) : block
+ end
end
def inspect
@@ -60,8 +91,8 @@ module ActionDispatch
end
private
- def escape(params)
- Hash[params.map{ |k,v| [k, Rack::Utils.escape(v)] }]
+ def interpolation_required?(string, params)
+ !params.empty? && string && string.match(/%\{\w*\}/)
end
end
@@ -81,17 +112,17 @@ module ActionDispatch
url_options[:path] = (url_options[:path] % escape_path(params))
end
+ if relative_path?(url_options[:path])
+ url_options[:path] = "/#{url_options[:path]}"
+ url_options[:script_name] = request.script_name
+ end
+
ActionDispatch::Http::URL.url_for url_options
end
def inspect
"redirect(#{status}, #{options.map{ |k,v| "#{k}: #{v}" }.join(', ')})"
end
-
- private
- def escape_path(params)
- Hash[params.map{ |k,v| [k, URI.parser.escape(v)] }]
- end
end
module Redirection
@@ -104,6 +135,10 @@ module ActionDispatch
#
# get 'docs/:article', to: redirect('/wiki/%{article}')
#
+ # Note that if you return a path without a leading slash then the url is prefixed with the
+ # current SCRIPT_NAME environment variable. This is typically '/' but may be different in
+ # a mounted engine or where the application is deployed to a subdirectory of a website.
+ #
# Alternatively you can use one of the other syntaxes:
#
# The block version of redirect allows for the easy encapsulation of any logic associated with
diff --git a/actionpack/lib/action_dispatch/routing/url_for.rb b/actionpack/lib/action_dispatch/routing/url_for.rb
index bcebe532bf..4a0ef40873 100644
--- a/actionpack/lib/action_dispatch/routing/url_for.rb
+++ b/actionpack/lib/action_dispatch/routing/url_for.rb
@@ -155,6 +155,8 @@ module ActionDispatch
_routes.url_for(options.symbolize_keys.reverse_merge!(url_options))
when String
options
+ when Array
+ polymorphic_url(options, options.extract_options!)
else
polymorphic_url(options)
end
diff --git a/actionpack/lib/action_dispatch/testing/assertions/response.rb b/actionpack/lib/action_dispatch/testing/assertions/response.rb
index 93f9fab9c2..68feb26936 100644
--- a/actionpack/lib/action_dispatch/testing/assertions/response.rb
+++ b/actionpack/lib/action_dispatch/testing/assertions/response.rb
@@ -27,6 +27,9 @@ module ActionDispatch
assert @response.send("#{type}?"), message
else
code = Rack::Utils::SYMBOL_TO_STATUS_CODE[type]
+ if code.nil?
+ raise ArgumentError, "Invalid response type :#{type}"
+ end
assert_equal code, @response.response_code, message
end
else