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/response.rb6
-rw-r--r--actionpack/lib/action_dispatch/journey/gtg/transition_table.rb35
-rw-r--r--actionpack/lib/action_dispatch/journey/router/utils.rb5
-rw-r--r--actionpack/lib/action_dispatch/journey/visitors.rb50
-rw-r--r--actionpack/lib/action_dispatch/middleware/remote_ip.rb2
-rw-r--r--actionpack/lib/action_dispatch/middleware/show_exceptions.rb7
-rw-r--r--actionpack/lib/action_dispatch/routing/mapper.rb25
-rw-r--r--actionpack/lib/action_dispatch/routing/polymorphic_routes.rb13
-rw-r--r--actionpack/lib/action_dispatch/routing/redirection.rb20
-rw-r--r--actionpack/lib/action_dispatch/routing/route_set.rb26
-rw-r--r--actionpack/lib/action_dispatch/routing/url_for.rb2
-rw-r--r--actionpack/lib/action_dispatch/testing/assertions/response.rb20
12 files changed, 127 insertions, 84 deletions
diff --git a/actionpack/lib/action_dispatch/http/response.rb b/actionpack/lib/action_dispatch/http/response.rb
index d3696cbb8a..5247e61a23 100644
--- a/actionpack/lib/action_dispatch/http/response.rb
+++ b/actionpack/lib/action_dispatch/http/response.rb
@@ -41,7 +41,7 @@ module ActionDispatch # :nodoc:
# Get and set headers for this response.
attr_accessor :header
-
+
alias_method :headers=, :header=
alias_method :headers, :header
@@ -181,9 +181,9 @@ module ActionDispatch # :nodoc:
end
alias_method :status_message, :message
- def respond_to?(method)
+ def respond_to?(method, include_private = false)
if method.to_s == 'to_path'
- stream.respond_to?(:to_path)
+ stream.respond_to?(method)
else
super
end
diff --git a/actionpack/lib/action_dispatch/journey/gtg/transition_table.rb b/actionpack/lib/action_dispatch/journey/gtg/transition_table.rb
index da0cddd93c..971cb3447f 100644
--- a/actionpack/lib/action_dispatch/journey/gtg/transition_table.rb
+++ b/actionpack/lib/action_dispatch/journey/gtg/transition_table.rb
@@ -9,8 +9,8 @@ module ActionDispatch
attr_reader :memos
def initialize
- @regexp_states = Hash.new { |h,k| h[k] = {} }
- @string_states = Hash.new { |h,k| h[k] = {} }
+ @regexp_states = {}
+ @string_states = {}
@accepting = {}
@memos = Hash.new { |h,k| h[k] = [] }
end
@@ -111,14 +111,8 @@ module ActionDispatch
end
def []=(from, to, sym)
- case sym
- when String
- @string_states[from][sym] = to
- when Regexp
- @regexp_states[from][sym] = to
- else
- raise ArgumentError, 'unknown symbol: %s' % sym.class
- end
+ to_mappings = states_hash_for(sym)[from] ||= {}
+ to_mappings[sym] = to
end
def states
@@ -137,18 +131,35 @@ module ActionDispatch
private
+ def states_hash_for(sym)
+ case sym
+ when String
+ @string_states
+ when Regexp
+ @regexp_states
+ else
+ raise ArgumentError, 'unknown symbol: %s' % sym.class
+ end
+ end
+
def move_regexp(t, a)
return [] if t.empty?
t.map { |s|
- @regexp_states[s].map { |re, v| re === a ? v : nil }
+ if states = @regexp_states[s]
+ states.map { |re, v| re === a ? v : nil }
+ end
}.flatten.compact.uniq
end
def move_string(t, a)
return [] if t.empty?
- t.map { |s| @string_states[s][a] }.compact
+ t.map do |s|
+ if states = @string_states[s]
+ states[a]
+ end
+ end.compact
end
end
end
diff --git a/actionpack/lib/action_dispatch/journey/router/utils.rb b/actionpack/lib/action_dispatch/journey/router/utils.rb
index 462f1a122d..d1a004af50 100644
--- a/actionpack/lib/action_dispatch/journey/router/utils.rb
+++ b/actionpack/lib/action_dispatch/journey/router/utils.rb
@@ -7,15 +7,18 @@ module ActionDispatch
# Normalizes URI path.
#
# Strips off trailing slash and ensures there is a leading slash.
+ # Also converts downcase url encoded string to uppercase.
#
# normalize_path("/foo") # => "/foo"
# normalize_path("/foo/") # => "/foo"
# normalize_path("foo") # => "/foo"
# normalize_path("") # => "/"
+ # normalize_path("/%ab") # => "/%AB"
def self.normalize_path(path)
path = "/#{path}"
path.squeeze!('/')
path.sub!(%r{/+\Z}, '')
+ path.gsub!(/(%[a-f0-9]{2})/) { $1.upcase }
path = '/' if path == ''
path
end
@@ -35,7 +38,7 @@ module ActionDispatch
UNSAFE_FRAGMENT = Regexp.new("[^#{safe_fragment}]", false).freeze
end
- Parser = URI.const_defined?(:Parser) ? URI::Parser.new : URI
+ Parser = URI::Parser.new
def self.escape_path(path)
Parser.escape(path.to_s, UriEscape::UNSAFE_SEGMENT)
diff --git a/actionpack/lib/action_dispatch/journey/visitors.rb b/actionpack/lib/action_dispatch/journey/visitors.rb
index 0a8cb1b4d4..9e66cab052 100644
--- a/actionpack/lib/action_dispatch/journey/visitors.rb
+++ b/actionpack/lib/action_dispatch/journey/visitors.rb
@@ -1,9 +1,12 @@
# encoding: utf-8
+
+require 'thread_safe'
+
module ActionDispatch
module Journey # :nodoc:
module Visitors # :nodoc:
class Visitor # :nodoc:
- DISPATCH_CACHE = Hash.new { |h,k|
+ DISPATCH_CACHE = ThreadSafe::Cache.new { |h,k|
h[k] = :"visit_#{k}"
}
@@ -84,44 +87,43 @@ module ActionDispatch
# Used for formatting urls (url_for)
class Formatter < Visitor # :nodoc:
- attr_reader :options, :consumed
+ attr_reader :options
def initialize(options)
@options = options
- @consumed = {}
end
private
- def visit_GROUP(node)
- if consumed == options
- nil
- else
- route = visit(node.left)
- route.include?("\0") ? nil : route
+ def visit(node, optional = false)
+ case node.type
+ when :LITERAL, :SLASH, :DOT
+ node.left
+ when :STAR
+ visit(node.left)
+ when :GROUP
+ visit(node.left, true)
+ when :CAT
+ visit_CAT(node, optional)
+ when :SYMBOL
+ visit_SYMBOL(node)
end
end
- def terminal(node)
- node.left
- end
-
- def binary(node)
- [visit(node.left), visit(node.right)].join
- end
+ def visit_CAT(node, optional)
+ left = visit(node.left, optional)
+ right = visit(node.right, optional)
- def nary(node)
- node.children.map { |c| visit(c) }.join
+ if optional && !(right && left)
+ ""
+ else
+ [left, right].join
+ end
end
def visit_SYMBOL(node)
- key = node.to_sym
-
- if value = options[key]
- consumed[key] = value
+ if value = options[node.to_sym]
Router::Utils.escape_path(value)
- else
- "\0"
end
end
end
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/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/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb
index 288ce3e867..db9c993590 100644
--- a/actionpack/lib/action_dispatch/routing/mapper.rb
+++ b/actionpack/lib/action_dispatch/routing/mapper.rb
@@ -362,8 +362,9 @@ module ActionDispatch
# # Yes, controller actions are just rack endpoints
# match 'photos/:id', to: PhotosController.action(:show)
#
- # Because request various HTTP verbs with a single action has security
- # implications, is recommendable use HttpHelpers[rdoc-ref:HttpHelpers]
+ # Because requesting various HTTP verbs with a single action has security
+ # implications, you must either specify the actions in
+ # the via options or use one of the HtttpHelpers[rdoc-ref:HttpHelpers]
# instead +match+
#
# === Options
@@ -432,10 +433,10 @@ module ActionDispatch
#
# match 'json_only', constraints: { format: 'json' }
#
- # class Blacklist
+ # class Whitelist
# def matches?(request) request.remote_ip == '1.2.3.4' end
# end
- # match 'path', to: 'c#a', constraints: Blacklist.new
+ # match 'path', to: 'c#a', constraints: Whitelist.new
#
# See <tt>Scoping#constraints</tt> for more examples with its scope
# equivalent.
@@ -1066,18 +1067,18 @@ module ActionDispatch
# a singular resource to map /profile (rather than /profile/:id) to
# the show action:
#
- # resource :geocoder
+ # resource :profile
#
# creates six different routes in your application, all mapping to
- # the +GeoCoders+ controller (note that the controller is named after
+ # the +Profiles+ controller (note that the controller is named after
# the plural):
#
- # GET /geocoder/new
- # POST /geocoder
- # GET /geocoder
- # GET /geocoder/edit
- # PATCH/PUT /geocoder
- # DELETE /geocoder
+ # GET /profile/new
+ # POST /profile
+ # GET /profile
+ # GET /profile/edit
+ # PATCH/PUT /profile
+ # DELETE /profile
#
# === Options
# Takes same options as +resources+.
diff --git a/actionpack/lib/action_dispatch/routing/polymorphic_routes.rb b/actionpack/lib/action_dispatch/routing/polymorphic_routes.rb
index 6d3f8da932..2fb03f2712 100644
--- a/actionpack/lib/action_dispatch/routing/polymorphic_routes.rb
+++ b/actionpack/lib/action_dispatch/routing/polymorphic_routes.rb
@@ -74,6 +74,19 @@ module ActionDispatch
# * <tt>:routing_type</tt> - Allowed values are <tt>:path</tt> or <tt>:url</tt>.
# Default is <tt>:url</tt>.
#
+ # Also includes all the options from <tt>url_for</tt>. These include such
+ # things as <tt>:anchor</tt> or <tt>:trailing_slash</tt>. Example usage
+ # is given below:
+ #
+ # polymorphic_url([blog, post], anchor: 'my_anchor')
+ # # => "http://example.com/blogs/1/posts/1#my_anchor"
+ # polymorphic_url([blog, post], anchor: 'my_anchor', script_name: "/my_app")
+ # # => "http://example.com/my_app/blogs/1/posts/1#my_anchor"
+ #
+ # For all of these options, see the documentation for <tt>url_for</tt>.
+ #
+ # ==== Functionality
+ #
# # an Article record
# polymorphic_url(record) # same as article_url(record)
#
diff --git a/actionpack/lib/action_dispatch/routing/redirection.rb b/actionpack/lib/action_dispatch/routing/redirection.rb
index d751e04e6a..3e54c7e71c 100644
--- a/actionpack/lib/action_dispatch/routing/redirection.rb
+++ b/actionpack/lib/action_dispatch/routing/redirection.rb
@@ -17,7 +17,7 @@ module ActionDispatch
def call(env)
req = Request.new(env)
- # If any of the path parameters has a invalid encoding then
+ # If any of the path parameters has an invalid encoding then
# raise since it's likely to trigger errors further on.
req.symbolized_path_parameters.each do |key, value|
unless value.valid_encoding?
@@ -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,6 +52,11 @@ module ActionDispatch
def inspect
"redirect(#{status})"
end
+
+ private
+ def relative_path?(path)
+ path && !path.empty? && path[0] != '/'
+ end
end
class PathRedirect < Redirect
@@ -81,6 +90,11 @@ 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
@@ -104,6 +118,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/route_set.rb b/actionpack/lib/action_dispatch/routing/route_set.rb
index 0e5dc1fc6c..b8abdabca5 100644
--- a/actionpack/lib/action_dispatch/routing/route_set.rb
+++ b/actionpack/lib/action_dispatch/routing/route_set.rb
@@ -28,7 +28,7 @@ module ActionDispatch
def call(env)
params = env[PARAMETERS_KEY]
- # If any of the path parameters has a invalid encoding then
+ # If any of the path parameters has an invalid encoding then
# raise since it's likely to trigger errors further on.
params.each do |key, value|
next unless value.respond_to?(:valid_encoding?)
@@ -514,11 +514,12 @@ module ActionDispatch
@recall = recall.dup
@set = set
+ normalize_recall!
normalize_options!
normalize_controller_action_id!
use_relative_controller!
normalize_controller!
- handle_nil_action!
+ normalize_action!
end
def controller
@@ -537,6 +538,11 @@ module ActionDispatch
end
end
+ # Set 'index' as default action for recall
+ def normalize_recall!
+ @recall[:action] ||= 'index'
+ end
+
def normalize_options!
# If an explicit :controller was given, always make :action explicit
# too, so that action expiry works as expected for things like
@@ -552,8 +558,8 @@ module ActionDispatch
options[:controller] = options[:controller].to_s
end
- if options[:action]
- options[:action] = options[:action].to_s
+ if options.key?(:action)
+ options[:action] = (options[:action] || 'index').to_s
end
end
@@ -563,8 +569,6 @@ module ActionDispatch
# :controller, :action or :id is not found, don't pull any
# more keys from the recall.
def normalize_controller_action_id!
- @recall[:action] ||= 'index' if current_controller
-
use_recall_for(:controller) or return
use_recall_for(:action) or return
use_recall_for(:id)
@@ -586,13 +590,11 @@ module ActionDispatch
@options[:controller] = controller.sub(%r{^/}, '') if controller
end
- # This handles the case of action: nil being explicitly passed.
- # It is identical to action: "index"
- def handle_nil_action!
- if options.has_key?(:action) && options[:action].nil?
- options[:action] = 'index'
+ # Move 'index' action from options to recall
+ def normalize_action!
+ if @options[:action] == 'index'
+ @recall[:action] = @options.delete(:action)
end
- recall[:action] = options.delete(:action) if options[:action] == 'index'
end
# Generates a path from routes, returns [path, params].
diff --git a/actionpack/lib/action_dispatch/routing/url_for.rb b/actionpack/lib/action_dispatch/routing/url_for.rb
index 8e19025722..bcebe532bf 100644
--- a/actionpack/lib/action_dispatch/routing/url_for.rb
+++ b/actionpack/lib/action_dispatch/routing/url_for.rb
@@ -20,7 +20,7 @@ module ActionDispatch
#
# <%= link_to('Click here', controller: 'users',
# action: 'new', message: 'Welcome!') %>
- # # => "/users/new?message=Welcome%21"
+ # # => <a href="/users/new?message=Welcome%21">Click here</a>
#
# link_to, and all other functions that require URL generation functionality,
# actually use ActionController::UrlFor under the hood. And in particular,
diff --git a/actionpack/lib/action_dispatch/testing/assertions/response.rb b/actionpack/lib/action_dispatch/testing/assertions/response.rb
index 44ed0ac1f3..93f9fab9c2 100644
--- a/actionpack/lib/action_dispatch/testing/assertions/response.rb
+++ b/actionpack/lib/action_dispatch/testing/assertions/response.rb
@@ -67,21 +67,11 @@ module ActionDispatch
end
def normalize_argument_to_redirection(fragment)
- normalized = case fragment
- when Regexp
- fragment
- when %r{^\w[A-Za-z\d+.-]*:.*}
- fragment
- when String
- @request.protocol + @request.host_with_port + fragment
- when :back
- raise RedirectBackError unless refer = @request.headers["Referer"]
- refer
- else
- @controller.url_for(fragment)
- end
-
- normalized.respond_to?(:delete) ? normalized.delete("\0\r\n") : normalized
+ if Regexp === fragment
+ fragment
+ else
+ @controller._compute_redirect_to_location(fragment)
+ end
end
end
end