aboutsummaryrefslogtreecommitdiffstats
path: root/actionpack
diff options
context:
space:
mode:
Diffstat (limited to 'actionpack')
-rw-r--r--actionpack/lib/action_dispatch.rb1
-rw-r--r--actionpack/lib/action_dispatch/middleware/debug_exceptions.rb8
-rw-r--r--actionpack/lib/action_dispatch/middleware/debug_locks.rb122
-rw-r--r--actionpack/lib/action_dispatch/middleware/session/cookie_store.rb2
-rw-r--r--actionpack/lib/action_dispatch/middleware/ssl.rb9
-rw-r--r--actionpack/lib/action_dispatch/routing/mapper.rb6
-rw-r--r--actionpack/lib/action_dispatch/testing/integration.rb7
-rw-r--r--actionpack/test/controller/parameters/nested_parameters_permit_test.rb (renamed from actionpack/test/controller/parameters/nested_parameters_test.rb)2
-rw-r--r--actionpack/test/dispatch/debug_exceptions_test.rb23
-rw-r--r--actionpack/test/dispatch/routing_test.rb7
10 files changed, 179 insertions, 8 deletions
diff --git a/actionpack/lib/action_dispatch.rb b/actionpack/lib/action_dispatch.rb
index 01d49475de..17aa115f15 100644
--- a/actionpack/lib/action_dispatch.rb
+++ b/actionpack/lib/action_dispatch.rb
@@ -50,6 +50,7 @@ module ActionDispatch
autoload :Callbacks
autoload :Cookies
autoload :DebugExceptions
+ autoload :DebugLocks
autoload :ExceptionWrapper
autoload :Executor
autoload :Flash
diff --git a/actionpack/lib/action_dispatch/middleware/debug_exceptions.rb b/actionpack/lib/action_dispatch/middleware/debug_exceptions.rb
index 5f758d641a..8974258cf7 100644
--- a/actionpack/lib/action_dispatch/middleware/debug_exceptions.rb
+++ b/actionpack/lib/action_dispatch/middleware/debug_exceptions.rb
@@ -36,6 +36,14 @@ module ActionDispatch
def debug_hash(object)
object.to_hash.sort_by { |k, _| k.to_s }.map { |k, v| "#{k}: #{v.inspect rescue $!.message}" }.join("\n")
end
+
+ def render(*)
+ if logger = ActionView::Base.logger
+ logger.silence { super }
+ else
+ super
+ end
+ end
end
def initialize(app, routes_app = nil, response_format = :default)
diff --git a/actionpack/lib/action_dispatch/middleware/debug_locks.rb b/actionpack/lib/action_dispatch/middleware/debug_locks.rb
new file mode 100644
index 0000000000..13254d08de
--- /dev/null
+++ b/actionpack/lib/action_dispatch/middleware/debug_locks.rb
@@ -0,0 +1,122 @@
+module ActionDispatch
+ # This middleware can be used to diagnose deadlocks in the autoload interlock.
+ #
+ # To use it, insert it near the top of the middleware stack, using
+ # <tt>config/application.rb</tt>:
+ #
+ # config.middleware.insert_before Rack::Sendfile, ActionDispatch::DebugLocks
+ #
+ # After restarting the application and re-triggering the deadlock condition,
+ # <tt>/rails/locks</tt> will show a summary of all threads currently known to
+ # the interlock, which lock level they are holding or awaiting, and their
+ # current backtrace.
+ #
+ # Generally a deadlock will be caused by the interlock conflicting with some
+ # other external lock or blocking I/O call. These cannot be automatically
+ # identified, but should be visible in the displayed backtraces.
+ #
+ # NOTE: The formatting and content of this middleware's output is intended for
+ # human consumption, and should be expected to change between releases.
+ #
+ # This middleware exposes operational details of the server, with no access
+ # control. It should only be enabled when in use, and removed thereafter.
+ class DebugLocks
+ def initialize(app, path = '/rails/locks')
+ @app = app
+ @path = path
+ end
+
+ def call(env)
+ req = ActionDispatch::Request.new env
+
+ if req.get?
+ path = req.path_info.chomp('/'.freeze)
+ if path == @path
+ return render_details(req)
+ end
+ end
+
+ @app.call(env)
+ end
+
+ private
+ def render_details(req)
+ threads = ActiveSupport::Dependencies.interlock.raw_state do |threads|
+ # The Interlock itself comes to a complete halt as long as this block
+ # is executing. That gives us a more consistent picture of everything,
+ # but creates a pretty strong Observer Effect.
+ #
+ # Most directly, that means we need to do as little as possible in
+ # this block. More widely, it means this middleware should remain a
+ # strictly diagnostic tool (to be used when something has gone wrong),
+ # and not for any sort of general monitoring.
+
+ threads.each.with_index do |(thread, info), idx|
+ info[:index] = idx
+ info[:backtrace] = thread.backtrace
+ end
+
+ threads
+ end
+
+ str = threads.map do |thread, info|
+ if info[:exclusive]
+ lock_state = 'Exclusive'
+ elsif info[:sharing] > 0
+ lock_state = 'Sharing'
+ lock_state << " x#{info[:sharing]}" if info[:sharing] > 1
+ else
+ lock_state = 'No lock'
+ end
+
+ if info[:waiting]
+ lock_state << ' (yielded share)'
+ end
+
+ msg = "Thread #{info[:index]} [0x#{thread.__id__.to_s(16)} #{thread.status || 'dead'}] #{lock_state}\n"
+
+ if info[:sleeper]
+ msg << " Waiting in #{info[:sleeper]}"
+ msg << " to #{info[:purpose].to_s.inspect}" unless info[:purpose].nil?
+ msg << "\n"
+
+ if info[:compatible]
+ compat = info[:compatible].map { |c| c == false ? "share" : c.to_s.inspect }
+ msg << " may be pre-empted for: #{compat.join(', ')}\n"
+ end
+
+ blockers = threads.values.select { |binfo| blocked_by?(info, binfo, threads.values) }
+ msg << " blocked by: #{blockers.map {|i| i[:index] }.join(', ')}\n" if blockers.any?
+ end
+
+ blockees = threads.values.select { |binfo| blocked_by?(binfo, info, threads.values) }
+ msg << " blocking: #{blockees.map {|i| i[:index] }.join(', ')}\n" if blockees.any?
+
+ msg << "\n#{info[:backtrace].join("\n")}\n" if info[:backtrace]
+ end.join("\n\n---\n\n\n")
+
+ [200, { "Content-Type" => "text/plain", "Content-Length" => str.size }, [str]]
+ end
+
+ def blocked_by?(victim, blocker, all_threads)
+ return false if victim.equal?(blocker)
+
+ case victim[:sleeper]
+ when :start_sharing
+ blocker[:exclusive] ||
+ (!victim[:waiting] && blocker[:compatible] && !blocker[:compatible].include?(false))
+ when :start_exclusive
+ blocker[:sharing] > 0 ||
+ blocker[:exclusive] ||
+ (blocker[:compatible] && !blocker[:compatible].include?(victim[:purpose]))
+ when :yield_shares
+ blocker[:exclusive]
+ when :stop_exclusive
+ blocker[:exclusive] ||
+ victim[:compatible] &&
+ victim[:compatible].include?(blocker[:purpose]) &&
+ all_threads.all? { |other| !other[:compatible] || blocker.equal?(other) || other[:compatible].include?(blocker[:purpose]) }
+ end
+ end
+ end
+end
diff --git a/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb b/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb
index dec9c60ef2..380a24a367 100644
--- a/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb
+++ b/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb
@@ -64,7 +64,7 @@ module ActionDispatch
# <tt>:httponly</tt>.
class CookieStore < AbstractStore
def initialize(app, options={})
- super(app, options.merge!(:cookie_only => true))
+ super(app, options.merge!(cookie_only: true))
end
def delete_session(req, session_id, options)
diff --git a/actionpack/lib/action_dispatch/middleware/ssl.rb b/actionpack/lib/action_dispatch/middleware/ssl.rb
index ab3077b308..0e04d3a524 100644
--- a/actionpack/lib/action_dispatch/middleware/ssl.rb
+++ b/actionpack/lib/action_dispatch/middleware/ssl.rb
@@ -18,17 +18,18 @@ module ActionDispatch
# Enabled by default. Configure `config.ssl_options` with `hsts: false` to disable.
#
# Set `config.ssl_options` with `hsts: { … }` to configure HSTS:
- # * `expires`: How long, in seconds, these settings will stick. Defaults to
- # `180.days` (recommended). The minimum required to qualify for browser
- # preload lists is `18.weeks`.
+ # * `expires`: How long, in seconds, these settings will stick. The minimum
+ # required to qualify for browser preload lists is `18.weeks`. Defaults to
+ # `180.days` (recommended).
# * `subdomains`: Set to `true` to tell the browser to apply these settings
# to all subdomains. This protects your cookies from interception by a
- # vulnerable site on a subdomain. Defaults to `true`.
+ # vulnerable site on a subdomain. Defaults to `false`.
# * `preload`: Advertise that this site may be included in browsers'
# preloaded HSTS lists. HSTS protects your site on every visit *except the
# first visit* since it hasn't seen your HSTS header yet. To close this
# gap, browser vendors include a baked-in list of HSTS-enabled sites.
# Go to https://hstspreload.appspot.com to submit your site for inclusion.
+ # Defaults to `false`.
#
# To turn off HSTS, omitting the header is not enough. Browsers will remember the
# original HSTS directive until it expires. Instead, use the header to tell browsers to
diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb
index e2cf75da3a..73b4864e45 100644
--- a/actionpack/lib/action_dispatch/routing/mapper.rb
+++ b/actionpack/lib/action_dispatch/routing/mapper.rb
@@ -1562,6 +1562,12 @@ module ActionDispatch
options = path
path, to = options.find { |name, _value| name.is_a?(String) }
+ if path.nil?
+ ActiveSupport::Deprecation.warn 'Omitting the route path is deprecated. '\
+ 'Specify the path with a String or a Symbol instead.'
+ path = ''
+ end
+
case to
when Symbol
options[:action] = to
diff --git a/actionpack/lib/action_dispatch/testing/integration.rb b/actionpack/lib/action_dispatch/testing/integration.rb
index 5627e79bb7..10cd1e5787 100644
--- a/actionpack/lib/action_dispatch/testing/integration.rb
+++ b/actionpack/lib/action_dispatch/testing/integration.rb
@@ -423,8 +423,11 @@ module ActionDispatch
end
def append_format_to(path)
- path << @path_format unless @url_encoded_form
- path
+ if @url_encoded_form
+ path
+ else
+ path + @path_format
+ end
end
def content_type
diff --git a/actionpack/test/controller/parameters/nested_parameters_test.rb b/actionpack/test/controller/parameters/nested_parameters_permit_test.rb
index 7151a8567c..5cf6f0d4e8 100644
--- a/actionpack/test/controller/parameters/nested_parameters_test.rb
+++ b/actionpack/test/controller/parameters/nested_parameters_permit_test.rb
@@ -1,7 +1,7 @@
require 'abstract_unit'
require 'action_controller/metal/strong_parameters'
-class NestedParametersTest < ActiveSupport::TestCase
+class NestedParametersPermitTest < ActiveSupport::TestCase
def assert_filtered_out(params, key)
assert !params.has_key?(key), "key #{key.inspect} has not been filtered out"
end
diff --git a/actionpack/test/dispatch/debug_exceptions_test.rb b/actionpack/test/dispatch/debug_exceptions_test.rb
index 5a39db145e..6f3d30ebf9 100644
--- a/actionpack/test/dispatch/debug_exceptions_test.rb
+++ b/actionpack/test/dispatch/debug_exceptions_test.rb
@@ -362,6 +362,29 @@ class DebugExceptionsTest < ActionDispatch::IntegrationTest
assert_match(/puke/, output.rewind && output.read)
end
+ test 'logs only what is necessary' do
+ @app = DevelopmentApp
+ io = StringIO.new
+ logger = ActiveSupport::Logger.new(io)
+
+ _old, ActionView::Base.logger = ActionView::Base.logger, logger
+ begin
+ get "/", headers: { 'action_dispatch.show_exceptions' => true, 'action_dispatch.logger' => logger }
+ ensure
+ ActionView::Base.logger = _old
+ end
+
+ output = io.rewind && io.read
+ lines = output.lines
+
+ # Other than the first three...
+ assert_equal([" \n", "RuntimeError (puke!):\n", " \n"], lines.slice!(0, 3))
+ lines.each do |line|
+ # .. all the remaining lines should be from the backtrace
+ assert_match(/:\d+:in /, line)
+ end
+ end
+
test 'uses backtrace cleaner from env' do
@app = DevelopmentApp
backtrace_cleaner = ActiveSupport::BacktraceCleaner.new
diff --git a/actionpack/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb
index 75fdc9469f..d54cdf7247 100644
--- a/actionpack/test/dispatch/routing_test.rb
+++ b/actionpack/test/dispatch/routing_test.rb
@@ -373,6 +373,9 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
post "create", :as => ""
put "update"
get "remove", :action => :destroy, :as => :remove
+ tc.assert_deprecated do
+ get action: :show, as: :show
+ end
end
end
@@ -391,6 +394,10 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
get '/pagemark/remove'
assert_equal 'pagemarks#destroy', @response.body
assert_equal '/pagemark/remove', pagemark_remove_path
+
+ get '/pagemark'
+ assert_equal 'pagemarks#show', @response.body
+ assert_equal '/pagemark', pagemark_show_path
end
def test_admin