actionpack 3.1.12 → 3.2.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of actionpack might be problematic. Click here for more details.
- data/CHANGELOG.md +5503 -108
- data/README.rdoc +3 -3
- data/lib/abstract_controller/asset_paths.rb +1 -1
- data/lib/abstract_controller/base.rb +1 -1
- data/lib/abstract_controller/callbacks.rb +102 -18
- data/lib/abstract_controller/helpers.rb +1 -1
- data/lib/abstract_controller/layouts.rb +116 -50
- data/lib/abstract_controller/logger.rb +1 -1
- data/lib/abstract_controller/railties/routes_helpers.rb +2 -2
- data/lib/abstract_controller/rendering.rb +1 -6
- data/lib/abstract_controller/view_paths.rb +6 -5
- data/lib/action_controller.rb +0 -15
- data/lib/action_controller/caching.rb +0 -1
- data/lib/action_controller/caching/actions.rb +5 -6
- data/lib/action_controller/caching/fragments.rb +18 -18
- data/lib/action_controller/caching/pages.rb +7 -6
- data/lib/action_controller/caching/sweeping.rb +1 -1
- data/lib/action_controller/log_subscriber.rb +8 -4
- data/lib/action_controller/metal.rb +7 -1
- data/lib/action_controller/metal/conditional_get.rb +49 -4
- data/lib/action_controller/metal/data_streaming.rb +17 -5
- data/lib/action_controller/metal/force_ssl.rb +8 -5
- data/lib/action_controller/metal/helpers.rb +7 -4
- data/lib/action_controller/metal/http_authentication.rb +9 -12
- data/lib/action_controller/metal/instrumentation.rb +9 -4
- data/lib/action_controller/metal/mime_responds.rb +4 -4
- data/lib/action_controller/metal/params_wrapper.rb +12 -8
- data/lib/action_controller/metal/redirecting.rb +7 -6
- data/lib/action_controller/metal/renderers.rb +9 -11
- data/lib/action_controller/metal/request_forgery_protection.rb +2 -1
- data/lib/action_controller/metal/rescue.rb +13 -0
- data/lib/action_controller/metal/responder.rb +11 -23
- data/lib/action_controller/metal/streaming.rb +0 -25
- data/lib/action_controller/railtie.rb +1 -0
- data/lib/action_controller/railties/paths.rb +4 -3
- data/lib/action_controller/record_identifier.rb +4 -4
- data/lib/action_controller/test_case.rb +60 -56
- data/lib/action_controller/vendor/html-scanner/html/sanitizer.rb +6 -6
- data/lib/action_dispatch.rb +5 -1
- data/lib/action_dispatch/http/cache.rb +27 -15
- data/lib/action_dispatch/http/filter_parameters.rb +3 -1
- data/lib/action_dispatch/http/headers.rb +3 -5
- data/lib/action_dispatch/http/mime_negotiation.rb +2 -1
- data/lib/action_dispatch/http/mime_type.rb +7 -3
- data/lib/action_dispatch/http/mime_types.rb +12 -0
- data/lib/action_dispatch/http/parameter_filter.rb +3 -1
- data/lib/action_dispatch/http/parameters.rb +0 -4
- data/lib/action_dispatch/http/request.rb +18 -68
- data/lib/action_dispatch/http/response.rb +11 -32
- data/lib/action_dispatch/http/upload.rb +3 -14
- data/lib/action_dispatch/http/url.rb +1 -1
- data/lib/action_dispatch/middleware/callbacks.rb +1 -2
- data/lib/action_dispatch/middleware/cookies.rb +20 -16
- data/lib/action_dispatch/middleware/debug_exceptions.rb +82 -0
- data/lib/action_dispatch/middleware/exception_wrapper.rb +78 -0
- data/lib/action_dispatch/middleware/flash.rb +6 -9
- data/lib/action_dispatch/middleware/params_parser.rb +6 -11
- data/lib/action_dispatch/middleware/public_exceptions.rb +30 -0
- data/lib/action_dispatch/middleware/reloader.rb +38 -14
- data/lib/action_dispatch/middleware/remote_ip.rb +66 -36
- data/lib/action_dispatch/middleware/request_id.rb +39 -0
- data/lib/action_dispatch/middleware/session/abstract_store.rb +4 -16
- data/lib/action_dispatch/middleware/session/cache_store.rb +50 -0
- data/lib/action_dispatch/middleware/session/cookie_store.rb +1 -1
- data/lib/action_dispatch/middleware/show_exceptions.rb +58 -142
- data/lib/action_dispatch/middleware/static.rb +2 -10
- data/lib/action_dispatch/middleware/templates/rescues/layout.erb +1 -0
- data/lib/action_dispatch/middleware/templates/rescues/routing_error.erb +13 -8
- data/lib/action_dispatch/railtie.rb +15 -1
- data/lib/action_dispatch/routing.rb +1 -2
- data/lib/action_dispatch/routing/mapper.rb +108 -107
- data/lib/action_dispatch/routing/redirection.rb +63 -69
- data/lib/action_dispatch/routing/route_set.rb +75 -43
- data/lib/action_dispatch/routing/routes_proxy.rb +0 -4
- data/lib/action_dispatch/routing/url_for.rb +3 -3
- data/lib/action_dispatch/testing/assertions/response.rb +5 -7
- data/lib/action_dispatch/testing/assertions/routing.rb +10 -9
- data/lib/action_dispatch/testing/integration.rb +8 -25
- data/lib/action_dispatch/testing/test_process.rb +3 -2
- data/lib/action_dispatch/testing/test_request.rb +4 -23
- data/lib/action_pack/version.rb +3 -3
- data/lib/action_view.rb +1 -5
- data/lib/action_view/asset_paths.rb +7 -8
- data/lib/action_view/base.rb +7 -5
- data/lib/action_view/helpers/asset_paths.rb +1 -1
- data/lib/action_view/helpers/asset_tag_helper.rb +4 -8
- data/lib/action_view/helpers/asset_tag_helpers/stylesheet_tag_helpers.rb +3 -0
- data/lib/action_view/helpers/atom_feed_helper.rb +2 -2
- data/lib/action_view/helpers/capture_helper.rb +3 -3
- data/lib/action_view/helpers/controller_helper.rb +1 -1
- data/lib/action_view/helpers/date_helper.rb +26 -18
- data/lib/action_view/helpers/debug_helper.rb +1 -1
- data/lib/action_view/helpers/form_helper.rb +71 -13
- data/lib/action_view/helpers/form_options_helper.rb +65 -34
- data/lib/action_view/helpers/form_tag_helper.rb +24 -18
- data/lib/action_view/helpers/javascript_helper.rb +12 -3
- data/lib/action_view/helpers/number_helper.rb +3 -2
- data/lib/action_view/helpers/record_tag_helper.rb +51 -5
- data/lib/action_view/helpers/rendering_helper.rb +2 -2
- data/lib/action_view/helpers/sanitize_helper.rb +6 -7
- data/lib/action_view/helpers/tag_helper.rb +1 -1
- data/lib/action_view/helpers/text_helper.rb +5 -4
- data/lib/action_view/helpers/url_helper.rb +19 -11
- data/lib/action_view/locale/en.yml +6 -0
- data/lib/action_view/log_subscriber.rb +1 -1
- data/lib/action_view/lookup_context.rb +123 -125
- data/lib/action_view/path_set.rb +60 -13
- data/lib/action_view/renderer/abstract_renderer.rb +16 -11
- data/lib/action_view/renderer/partial_renderer.rb +59 -40
- data/lib/action_view/renderer/template_renderer.rb +29 -17
- data/lib/action_view/template.rb +0 -1
- data/lib/action_view/template/error.rb +6 -5
- data/lib/action_view/template/handlers.rb +0 -6
- data/lib/action_view/template/handlers/builder.rb +10 -1
- data/lib/action_view/template/handlers/erb.rb +2 -2
- data/lib/action_view/template/resolver.rb +20 -31
- data/lib/action_view/test_case.rb +7 -10
- data/lib/sprockets/assets.rake +1 -1
- data/lib/sprockets/bootstrap.rb +3 -31
- data/lib/sprockets/compressors.rb +69 -7
- data/lib/sprockets/helpers/rails_helper.rb +6 -11
- data/lib/sprockets/railtie.rb +1 -0
- data/lib/sprockets/static_compiler.rb +0 -3
- metadata +57 -86
- checksums.yaml +0 -7
- data/lib/action_dispatch/middleware/closed_error.rb +0 -7
- data/lib/action_dispatch/routing/route.rb +0 -67
- data/lib/action_view/template/handler.rb +0 -49
@@ -2,50 +2,80 @@ module ActionDispatch
|
|
2
2
|
class RemoteIp
|
3
3
|
class IpSpoofAttackError < StandardError ; end
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
5
|
+
# IP addresses that are "trusted proxies" that can be stripped from
|
6
|
+
# the comma-delimited list in the X-Forwarded-For header. See also:
|
7
|
+
# http://en.wikipedia.org/wiki/Private_network#Private_IPv4_address_spaces
|
8
|
+
TRUSTED_PROXIES = %r{
|
9
|
+
^127\.0\.0\.1$ | # localhost
|
10
|
+
^(10 | # private IP 10.x.x.x
|
11
|
+
172\.(1[6-9]|2[0-9]|3[0-1]) | # private IP in the range 172.16.0.0 .. 172.31.255.255
|
12
|
+
192\.168 # private IP 192.168.x.x
|
13
|
+
)\.
|
14
|
+
}x
|
15
|
+
|
16
|
+
attr_reader :check_ip, :proxies
|
17
|
+
|
18
|
+
def initialize(app, check_ip_spoofing = true, custom_proxies = nil)
|
19
|
+
@app = app
|
20
|
+
@check_ip = check_ip_spoofing
|
21
|
+
if custom_proxies
|
22
|
+
custom_regexp = Regexp.new(custom_proxies)
|
23
|
+
@proxies = Regexp.union(TRUSTED_PROXIES, custom_regexp)
|
24
|
+
else
|
25
|
+
@proxies = TRUSTED_PROXIES
|
10
26
|
end
|
27
|
+
end
|
11
28
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
29
|
+
def call(env)
|
30
|
+
env["action_dispatch.remote_ip"] = GetIp.new(env, self)
|
31
|
+
@app.call(env)
|
32
|
+
end
|
33
|
+
|
34
|
+
class GetIp
|
35
|
+
def initialize(env, middleware)
|
36
|
+
@env = env
|
37
|
+
@middleware = middleware
|
38
|
+
@calculated_ip = false
|
17
39
|
end
|
18
40
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
41
|
+
# Determines originating IP address. REMOTE_ADDR is the standard
|
42
|
+
# but will be wrong if the user is behind a proxy. Proxies will set
|
43
|
+
# HTTP_CLIENT_IP and/or HTTP_X_FORWARDED_FOR, so we prioritize those.
|
44
|
+
# HTTP_X_FORWARDED_FOR may be a comma-delimited list in the case of
|
45
|
+
# multiple chained proxies. The last address which is not a known proxy
|
46
|
+
# will be the originating IP.
|
47
|
+
def calculate_ip
|
48
|
+
client_ip = @env['HTTP_CLIENT_IP']
|
49
|
+
forwarded_ips = ips_from('HTTP_X_FORWARDED_FOR')
|
50
|
+
remote_addrs = ips_from('REMOTE_ADDR')
|
51
|
+
|
52
|
+
check_ip = client_ip && @middleware.check_ip
|
53
|
+
if check_ip && !forwarded_ips.include?(client_ip)
|
54
|
+
# We don't know which came from the proxy, and which from the user
|
55
|
+
raise IpSpoofAttackError, "IP spoofing attack?!" \
|
56
|
+
"HTTP_CLIENT_IP=#{@env['HTTP_CLIENT_IP'].inspect}" \
|
57
|
+
"HTTP_X_FORWARDED_FOR=#{@env['HTTP_X_FORWARDED_FOR'].inspect}"
|
32
58
|
end
|
33
59
|
|
34
|
-
|
60
|
+
not_proxy = client_ip || forwarded_ips.last || remote_addrs.first
|
61
|
+
|
62
|
+
# Return first REMOTE_ADDR if there are no other options
|
63
|
+
not_proxy || ips_from('REMOTE_ADDR', :allow_proxies).first
|
35
64
|
end
|
36
|
-
end
|
37
65
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
@trusted_proxies = Regexp.new(regex, "i")
|
44
|
-
end
|
66
|
+
def to_s
|
67
|
+
return @ip if @calculated_ip
|
68
|
+
@calculated_ip = true
|
69
|
+
@ip = calculate_ip
|
70
|
+
end
|
45
71
|
|
46
|
-
|
47
|
-
|
48
|
-
|
72
|
+
protected
|
73
|
+
|
74
|
+
def ips_from(header, allow_proxies = false)
|
75
|
+
ips = @env[header] ? @env[header].strip.split(/[,\s]+/) : []
|
76
|
+
allow_proxies ? ips : ips.reject{|ip| ip =~ @middleware.proxies }
|
77
|
+
end
|
49
78
|
end
|
79
|
+
|
50
80
|
end
|
51
|
-
end
|
81
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'securerandom'
|
2
|
+
require 'active_support/core_ext/string/access'
|
3
|
+
require 'active_support/core_ext/object/blank'
|
4
|
+
|
5
|
+
module ActionDispatch
|
6
|
+
# Makes a unique request id available to the action_dispatch.request_id env variable (which is then accessible through
|
7
|
+
# ActionDispatch::Request#uuid) and sends the same id to the client via the X-Request-Id header.
|
8
|
+
#
|
9
|
+
# The unique request id is either based off the X-Request-Id header in the request, which would typically be generated
|
10
|
+
# by a firewall, load balancer, or the web server, or, if this header is not available, a random uuid. If the
|
11
|
+
# header is accepted from the outside world, we sanitize it to a max of 255 chars and alphanumeric and dashes only.
|
12
|
+
#
|
13
|
+
# The unique request id can be used to trace a request end-to-end and would typically end up being part of log files
|
14
|
+
# from multiple pieces of the stack.
|
15
|
+
class RequestId
|
16
|
+
def initialize(app)
|
17
|
+
@app = app
|
18
|
+
end
|
19
|
+
|
20
|
+
def call(env)
|
21
|
+
env["action_dispatch.request_id"] = external_request_id(env) || internal_request_id
|
22
|
+
status, headers, body = @app.call(env)
|
23
|
+
|
24
|
+
headers["X-Request-Id"] = env["action_dispatch.request_id"]
|
25
|
+
[ status, headers, body ]
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
def external_request_id(env)
|
30
|
+
if request_id = env["HTTP_X_REQUEST_ID"].presence
|
31
|
+
request_id.gsub(/[^\w\-]/, "").first(255)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def internal_request_id
|
36
|
+
SecureRandom.hex(16)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -25,8 +25,6 @@ module ActionDispatch
|
|
25
25
|
module Compatibility
|
26
26
|
def initialize(app, options = {})
|
27
27
|
options[:key] ||= '_session_id'
|
28
|
-
# FIXME Rack's secret is not being used
|
29
|
-
options[:secret] ||= SecureRandom.hex(30)
|
30
28
|
super
|
31
29
|
end
|
32
30
|
|
@@ -61,12 +59,12 @@ module ActionDispatch
|
|
61
59
|
# Note that the regexp does not allow $1 to end with a ':'
|
62
60
|
$1.constantize
|
63
61
|
rescue LoadError, NameError => const_error
|
64
|
-
raise ActionDispatch::Session::SessionRestoreError,
|
62
|
+
raise ActionDispatch::Session::SessionRestoreError,
|
63
|
+
"Session contains objects whose class definition isn't available.\n" +
|
64
|
+
"Remember to require the classes for all objects kept in the session.\n" +
|
65
|
+
"(Original exception: #{const_error.message} [#{const_error.class}])\n"
|
65
66
|
end
|
66
67
|
retry
|
67
|
-
elsif argument_error.message =~ %r{dump format error \(user class\)}
|
68
|
-
# Error unmarshalling object from session.
|
69
|
-
{}
|
70
68
|
else
|
71
69
|
raise
|
72
70
|
end
|
@@ -76,16 +74,6 @@ module ActionDispatch
|
|
76
74
|
class AbstractStore < Rack::Session::Abstract::ID
|
77
75
|
include Compatibility
|
78
76
|
include StaleSessionCheck
|
79
|
-
|
80
|
-
def destroy_session(env, sid, options)
|
81
|
-
ActiveSupport::Deprecation.warn "Implementing #destroy in session stores is deprecated. " <<
|
82
|
-
"Please implement destroy_session(env, session_id, options) instead."
|
83
|
-
destroy(env)
|
84
|
-
end
|
85
|
-
|
86
|
-
def destroy(env)
|
87
|
-
raise '#destroy needs to be implemented.'
|
88
|
-
end
|
89
77
|
end
|
90
78
|
end
|
91
79
|
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'action_dispatch/middleware/session/abstract_store'
|
2
|
+
require 'rack/session/memcache'
|
3
|
+
|
4
|
+
module ActionDispatch
|
5
|
+
module Session
|
6
|
+
# Session store that uses an ActiveSupport::Cache::Store to store the sessions. This store is most useful
|
7
|
+
# if you don't store critical data in your sessions and you don't need them to live for extended periods
|
8
|
+
# of time.
|
9
|
+
class CacheStore < AbstractStore
|
10
|
+
# Create a new store. The cache to use can be passed in the <tt>:cache</tt> option. If it is
|
11
|
+
# not specified, <tt>Rails.cache</tt> will be used.
|
12
|
+
def initialize(app, options = {})
|
13
|
+
@cache = options[:cache] || Rails.cache
|
14
|
+
options[:expire_after] ||= @cache.options[:expires_in]
|
15
|
+
super
|
16
|
+
end
|
17
|
+
|
18
|
+
# Get a session from the cache.
|
19
|
+
def get_session(env, sid)
|
20
|
+
sid ||= generate_sid
|
21
|
+
session = @cache.read(cache_key(sid))
|
22
|
+
session ||= {}
|
23
|
+
[sid, session]
|
24
|
+
end
|
25
|
+
|
26
|
+
# Set a session in the cache.
|
27
|
+
def set_session(env, sid, session, options)
|
28
|
+
key = cache_key(sid)
|
29
|
+
if session
|
30
|
+
@cache.write(key, session, :expires_in => options[:expire_after])
|
31
|
+
else
|
32
|
+
@cache.delete(key)
|
33
|
+
end
|
34
|
+
sid
|
35
|
+
end
|
36
|
+
|
37
|
+
# Remove a session from the cache.
|
38
|
+
def destroy_session(env, sid, options)
|
39
|
+
@cache.delete(cache_key(sid))
|
40
|
+
generate_sid
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
# Turn the session id into a cache key.
|
45
|
+
def cache_key(sid)
|
46
|
+
"_session_id:#{sid}"
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -1,171 +1,87 @@
|
|
1
|
-
require 'active_support/core_ext/exception'
|
2
|
-
require 'active_support/notifications'
|
3
1
|
require 'action_dispatch/http/request'
|
2
|
+
require 'action_dispatch/middleware/exception_wrapper'
|
3
|
+
require 'active_support/deprecation'
|
4
4
|
|
5
5
|
module ActionDispatch
|
6
|
-
# This middleware rescues any exception returned by the application
|
7
|
-
#
|
6
|
+
# This middleware rescues any exception returned by the application
|
7
|
+
# and calls an exceptions app that will wrap it in a format for the end user.
|
8
|
+
#
|
9
|
+
# The exceptions app should be passed as parameter on initialization
|
10
|
+
# of ShowExceptions. Everytime there is an exception, ShowExceptions will
|
11
|
+
# store the exception in env["action_dispatch.exception"], rewrite the
|
12
|
+
# PATH_INFO to the exception status code and call the rack app.
|
13
|
+
#
|
14
|
+
# If the application returns a "X-Cascade" pass response, this middleware
|
15
|
+
# will send an empty response as result with the correct status code.
|
16
|
+
# If any exception happens inside the exceptions app, this middleware
|
17
|
+
# catches the exceptions and returns a FAILSAFE_RESPONSE.
|
8
18
|
class ShowExceptions
|
9
|
-
RESCUES_TEMPLATE_PATH = File.join(File.dirname(__FILE__), 'templates')
|
10
|
-
|
11
|
-
cattr_accessor :rescue_responses
|
12
|
-
@@rescue_responses = Hash.new(:internal_server_error)
|
13
|
-
@@rescue_responses.update({
|
14
|
-
'ActionController::RoutingError' => :not_found,
|
15
|
-
'AbstractController::ActionNotFound' => :not_found,
|
16
|
-
'ActiveRecord::RecordNotFound' => :not_found,
|
17
|
-
'ActiveRecord::StaleObjectError' => :conflict,
|
18
|
-
'ActiveRecord::RecordInvalid' => :unprocessable_entity,
|
19
|
-
'ActiveRecord::RecordNotSaved' => :unprocessable_entity,
|
20
|
-
'ActionController::MethodNotAllowed' => :method_not_allowed,
|
21
|
-
'ActionController::NotImplemented' => :not_implemented,
|
22
|
-
'ActionController::InvalidAuthenticityToken' => :unprocessable_entity
|
23
|
-
})
|
24
|
-
|
25
|
-
cattr_accessor :rescue_templates
|
26
|
-
@@rescue_templates = Hash.new('diagnostics')
|
27
|
-
@@rescue_templates.update({
|
28
|
-
'ActionView::MissingTemplate' => 'missing_template',
|
29
|
-
'ActionController::RoutingError' => 'routing_error',
|
30
|
-
'AbstractController::ActionNotFound' => 'unknown_action',
|
31
|
-
'ActionView::Template::Error' => 'template_error'
|
32
|
-
})
|
33
|
-
|
34
19
|
FAILSAFE_RESPONSE = [500, {'Content-Type' => 'text/html'},
|
35
20
|
["<html><body><h1>500 Internal Server Error</h1>" <<
|
36
21
|
"If you are the administrator of this website, then please read this web " <<
|
37
22
|
"application's log file and/or the web server's log file to find out what " <<
|
38
23
|
"went wrong.</body></html>"]]
|
39
24
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
def call(env)
|
46
|
-
begin
|
47
|
-
status, headers, body = @app.call(env)
|
48
|
-
exception = nil
|
49
|
-
|
50
|
-
# Only this middleware cares about RoutingError. So, let's just raise
|
51
|
-
# it here.
|
52
|
-
if headers['X-Cascade'] == 'pass'
|
53
|
-
raise ActionController::RoutingError, "No route matches [#{env['REQUEST_METHOD']}] #{env['PATH_INFO'].inspect}"
|
54
|
-
end
|
55
|
-
rescue Exception => exception
|
56
|
-
raise exception if env['action_dispatch.show_exceptions'] == false
|
57
|
-
end
|
58
|
-
|
59
|
-
exception ? render_exception(env, exception) : [status, headers, body]
|
60
|
-
end
|
61
|
-
|
62
|
-
private
|
63
|
-
def render_exception(env, exception)
|
64
|
-
log_error(exception)
|
65
|
-
exception = original_exception(exception)
|
66
|
-
|
67
|
-
request = Request.new(env)
|
68
|
-
if @consider_all_requests_local || request.local?
|
69
|
-
rescue_action_locally(request, exception)
|
70
|
-
else
|
71
|
-
rescue_action_in_public(exception)
|
72
|
-
end
|
73
|
-
rescue Exception => failsafe_error
|
74
|
-
$stderr.puts "Error during failsafe response: #{failsafe_error}\n #{failsafe_error.backtrace * "\n "}"
|
75
|
-
FAILSAFE_RESPONSE
|
76
|
-
end
|
77
|
-
|
78
|
-
# Render detailed diagnostics for unhandled exceptions rescued from
|
79
|
-
# a controller action.
|
80
|
-
def rescue_action_locally(request, exception)
|
81
|
-
template = ActionView::Base.new([RESCUES_TEMPLATE_PATH],
|
82
|
-
:request => request,
|
83
|
-
:exception => exception,
|
84
|
-
:application_trace => application_trace(exception),
|
85
|
-
:framework_trace => framework_trace(exception),
|
86
|
-
:full_trace => full_trace(exception)
|
87
|
-
)
|
88
|
-
file = "rescues/#{@@rescue_templates[exception.class.name]}.erb"
|
89
|
-
body = template.render(:file => file, :layout => 'rescues/layout.erb')
|
90
|
-
render(status_code(exception), body)
|
91
|
-
end
|
92
|
-
|
93
|
-
# Attempts to render a static error page based on the
|
94
|
-
# <tt>status_code</tt> thrown, or just return headers if no such file
|
95
|
-
# exists. At first, it will try to render a localized static page.
|
96
|
-
# For example, if a 500 error is being handled Rails and locale is :da,
|
97
|
-
# it will first attempt to render the file at <tt>public/500.da.html</tt>
|
98
|
-
# then attempt to render <tt>public/500.html</tt>. If none of them exist,
|
99
|
-
# the body of the response will be left empty.
|
100
|
-
def rescue_action_in_public(exception)
|
101
|
-
status = status_code(exception)
|
102
|
-
locale_path = "#{public_path}/#{status}.#{I18n.locale}.html" if I18n.locale
|
103
|
-
path = "#{public_path}/#{status}.html"
|
104
|
-
|
105
|
-
if locale_path && File.exist?(locale_path)
|
106
|
-
render(status, File.read(locale_path))
|
107
|
-
elsif File.exist?(path)
|
108
|
-
render(status, File.read(path))
|
109
|
-
else
|
110
|
-
render(status, '')
|
111
|
-
end
|
25
|
+
class << self
|
26
|
+
def rescue_responses
|
27
|
+
ActiveSupport::Deprecation.warn "ActionDispatch::ShowExceptions.rescue_responses is deprecated. " \
|
28
|
+
"Please configure your exceptions using a railtie or in your application config instead."
|
29
|
+
ExceptionWrapper.rescue_responses
|
112
30
|
end
|
113
31
|
|
114
|
-
def
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
def render(status, body)
|
119
|
-
[status, {'Content-Type' => "text/html; charset=#{Response.default_charset}", 'Content-Length' => body.bytesize.to_s}, [body]]
|
32
|
+
def rescue_templates
|
33
|
+
ActiveSupport::Deprecation.warn "ActionDispatch::ShowExceptions.rescue_templates is deprecated. " \
|
34
|
+
"Please configure your exceptions using a railtie or in your application config instead."
|
35
|
+
ExceptionWrapper.rescue_templates
|
120
36
|
end
|
37
|
+
end
|
121
38
|
|
122
|
-
|
123
|
-
|
39
|
+
def initialize(app, exceptions_app = nil)
|
40
|
+
if [true, false].include?(exceptions_app)
|
41
|
+
ActiveSupport::Deprecation.warn "Passing consider_all_requests_local option to ActionDispatch::ShowExceptions middleware no longer works"
|
42
|
+
exceptions_app = nil
|
124
43
|
end
|
125
44
|
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
ActiveSupport::Deprecation.silence do
|
130
|
-
message = "\n#{exception.class} (#{exception.message}):\n"
|
131
|
-
message << exception.annoted_source_code.to_s if exception.respond_to?(:annoted_source_code)
|
132
|
-
message << " " << application_trace(exception).join("\n ")
|
133
|
-
logger.fatal("#{message}\n\n")
|
134
|
-
end
|
45
|
+
if exceptions_app.nil?
|
46
|
+
raise ArgumentError, "You need to pass an exceptions_app when initializing ActionDispatch::ShowExceptions. " \
|
47
|
+
"In case you want to render pages from a public path, you can use ActionDispatch::PublicExceptions.new('path/to/public')"
|
135
48
|
end
|
136
49
|
|
137
|
-
|
138
|
-
|
139
|
-
|
50
|
+
@app = app
|
51
|
+
@exceptions_app = exceptions_app
|
52
|
+
end
|
140
53
|
|
141
|
-
|
142
|
-
|
54
|
+
def call(env)
|
55
|
+
begin
|
56
|
+
response = @app.call(env)
|
57
|
+
rescue Exception => exception
|
58
|
+
raise exception if env['action_dispatch.show_exceptions'] == false
|
143
59
|
end
|
144
60
|
|
145
|
-
|
146
|
-
|
147
|
-
end
|
61
|
+
response || render_exception(env, exception)
|
62
|
+
end
|
148
63
|
|
149
|
-
|
150
|
-
defined?(Rails) && Rails.respond_to?(:backtrace_cleaner) ?
|
151
|
-
Rails.backtrace_cleaner.clean(exception.backtrace, *args) :
|
152
|
-
exception.backtrace
|
153
|
-
end
|
64
|
+
private
|
154
65
|
|
155
|
-
|
156
|
-
|
157
|
-
|
66
|
+
# Define this method because some plugins were monkey patching it.
|
67
|
+
# Remove this after 3.2 is out with the other deprecations in this class.
|
68
|
+
def status_code(*)
|
69
|
+
end
|
158
70
|
|
159
|
-
def
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
71
|
+
def render_exception(env, exception)
|
72
|
+
wrapper = ExceptionWrapper.new(env, exception)
|
73
|
+
status = wrapper.status_code
|
74
|
+
env["action_dispatch.exception"] = wrapper.exception
|
75
|
+
env["PATH_INFO"] = "/#{status}"
|
76
|
+
response = @exceptions_app.call(env)
|
77
|
+
response[1]['X-Cascade'] == 'pass' ? pass_response(status) : response
|
78
|
+
rescue Exception => failsafe_error
|
79
|
+
$stderr.puts "Error during failsafe response: #{failsafe_error}\n #{failsafe_error.backtrace * "\n "}"
|
80
|
+
FAILSAFE_RESPONSE
|
165
81
|
end
|
166
82
|
|
167
|
-
def
|
168
|
-
|
83
|
+
def pass_response(status)
|
84
|
+
[status, {"Content-Type" => "text/html; charset=#{Response.default_charset}", "Content-Length" => "0"}, []]
|
169
85
|
end
|
170
86
|
end
|
171
87
|
end
|