actionpack 7.0.8.6 → 7.1.0.beta1
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +318 -423
- data/MIT-LICENSE +1 -1
- data/README.rdoc +2 -2
- data/lib/abstract_controller/base.rb +19 -10
- data/lib/abstract_controller/caching/fragments.rb +2 -0
- data/lib/abstract_controller/callbacks.rb +31 -6
- data/lib/abstract_controller/deprecator.rb +7 -0
- data/lib/abstract_controller/helpers.rb +61 -18
- data/lib/abstract_controller/railties/routes_helpers.rb +1 -16
- data/lib/abstract_controller/rendering.rb +3 -3
- data/lib/abstract_controller/translation.rb +1 -27
- data/lib/abstract_controller/url_for.rb +2 -0
- data/lib/abstract_controller.rb +6 -0
- data/lib/action_controller/api.rb +5 -3
- data/lib/action_controller/base.rb +3 -17
- data/lib/action_controller/caching.rb +2 -0
- data/lib/action_controller/deprecator.rb +7 -0
- data/lib/action_controller/form_builder.rb +2 -0
- data/lib/action_controller/log_subscriber.rb +16 -4
- data/lib/action_controller/metal/content_security_policy.rb +1 -1
- data/lib/action_controller/metal/data_streaming.rb +2 -0
- data/lib/action_controller/metal/default_headers.rb +2 -0
- data/lib/action_controller/metal/etag_with_flash.rb +2 -0
- data/lib/action_controller/metal/etag_with_template_digest.rb +2 -0
- data/lib/action_controller/metal/exceptions.rb +8 -0
- data/lib/action_controller/metal/head.rb +8 -6
- data/lib/action_controller/metal/helpers.rb +3 -14
- data/lib/action_controller/metal/http_authentication.rb +10 -4
- data/lib/action_controller/metal/implicit_render.rb +5 -3
- data/lib/action_controller/metal/instrumentation.rb +8 -1
- data/lib/action_controller/metal/live.rb +24 -0
- data/lib/action_controller/metal/mime_responds.rb +2 -2
- data/lib/action_controller/metal/params_wrapper.rb +3 -1
- data/lib/action_controller/metal/permissions_policy.rb +1 -1
- data/lib/action_controller/metal/redirecting.rb +6 -6
- data/lib/action_controller/metal/renderers.rb +2 -2
- data/lib/action_controller/metal/rendering.rb +0 -7
- data/lib/action_controller/metal/request_forgery_protection.rb +138 -50
- data/lib/action_controller/metal/rescue.rb +2 -0
- data/lib/action_controller/metal/streaming.rb +70 -30
- data/lib/action_controller/metal/strong_parameters.rb +89 -50
- data/lib/action_controller/metal/url_for.rb +7 -0
- data/lib/action_controller/metal.rb +79 -21
- data/lib/action_controller/railtie.rb +22 -9
- data/lib/action_controller/renderer.rb +98 -65
- data/lib/action_controller/test_case.rb +15 -5
- data/lib/action_controller.rb +8 -1
- data/lib/action_dispatch/constants.rb +32 -0
- data/lib/action_dispatch/deprecator.rb +7 -0
- data/lib/action_dispatch/http/cache.rb +1 -3
- data/lib/action_dispatch/http/content_security_policy.rb +9 -8
- data/lib/action_dispatch/http/filter_parameters.rb +15 -14
- data/lib/action_dispatch/http/headers.rb +2 -0
- data/lib/action_dispatch/http/mime_negotiation.rb +21 -21
- data/lib/action_dispatch/http/mime_type.rb +35 -12
- data/lib/action_dispatch/http/mime_types.rb +3 -1
- data/lib/action_dispatch/http/parameters.rb +1 -1
- data/lib/action_dispatch/http/permissions_policy.rb +44 -15
- data/lib/action_dispatch/http/rack_cache.rb +2 -0
- data/lib/action_dispatch/http/request.rb +48 -14
- data/lib/action_dispatch/http/response.rb +78 -59
- data/lib/action_dispatch/http/upload.rb +2 -0
- data/lib/action_dispatch/journey/formatter.rb +8 -2
- data/lib/action_dispatch/journey/path/pattern.rb +14 -14
- data/lib/action_dispatch/journey/route.rb +3 -2
- data/lib/action_dispatch/journey/router.rb +5 -4
- data/lib/action_dispatch/journey/routes.rb +2 -2
- data/lib/action_dispatch/log_subscriber.rb +23 -0
- data/lib/action_dispatch/middleware/actionable_exceptions.rb +5 -6
- data/lib/action_dispatch/middleware/assume_ssl.rb +24 -0
- data/lib/action_dispatch/middleware/callbacks.rb +2 -0
- data/lib/action_dispatch/middleware/cookies.rb +81 -98
- data/lib/action_dispatch/middleware/debug_exceptions.rb +26 -25
- data/lib/action_dispatch/middleware/debug_locks.rb +4 -1
- data/lib/action_dispatch/middleware/debug_view.rb +7 -2
- data/lib/action_dispatch/middleware/exception_wrapper.rb +181 -27
- data/lib/action_dispatch/middleware/executor.rb +1 -1
- data/lib/action_dispatch/middleware/flash.rb +7 -0
- data/lib/action_dispatch/middleware/host_authorization.rb +6 -3
- data/lib/action_dispatch/middleware/public_exceptions.rb +5 -3
- data/lib/action_dispatch/middleware/reloader.rb +7 -5
- data/lib/action_dispatch/middleware/remote_ip.rb +17 -16
- data/lib/action_dispatch/middleware/request_id.rb +2 -0
- data/lib/action_dispatch/middleware/server_timing.rb +4 -4
- data/lib/action_dispatch/middleware/session/abstract_store.rb +5 -0
- data/lib/action_dispatch/middleware/session/cache_store.rb +2 -0
- data/lib/action_dispatch/middleware/session/cookie_store.rb +11 -5
- data/lib/action_dispatch/middleware/session/mem_cache_store.rb +3 -1
- data/lib/action_dispatch/middleware/show_exceptions.rb +19 -15
- data/lib/action_dispatch/middleware/ssl.rb +18 -6
- data/lib/action_dispatch/middleware/stack.rb +7 -2
- data/lib/action_dispatch/middleware/static.rb +12 -8
- data/lib/action_dispatch/middleware/templates/rescues/_actions.html.erb +2 -2
- data/lib/action_dispatch/middleware/templates/rescues/_message_and_suggestions.html.erb +4 -4
- data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +8 -1
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +7 -7
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +2 -2
- data/lib/action_dispatch/middleware/templates/rescues/layout.erb +17 -0
- data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +16 -12
- data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +3 -3
- data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +4 -4
- data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/unknown_action.text.erb +1 -1
- data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +3 -0
- data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +46 -37
- data/lib/action_dispatch/railtie.rb +14 -4
- data/lib/action_dispatch/request/session.rb +16 -6
- data/lib/action_dispatch/request/utils.rb +8 -3
- data/lib/action_dispatch/routing/inspector.rb +54 -6
- data/lib/action_dispatch/routing/mapper.rb +26 -14
- data/lib/action_dispatch/routing/polymorphic_routes.rb +2 -0
- data/lib/action_dispatch/routing/redirection.rb +15 -6
- data/lib/action_dispatch/routing/route_set.rb +52 -22
- data/lib/action_dispatch/routing/routes_proxy.rb +1 -1
- data/lib/action_dispatch/routing/url_for.rb +5 -1
- data/lib/action_dispatch/routing.rb +4 -4
- data/lib/action_dispatch/system_test_case.rb +3 -3
- data/lib/action_dispatch/system_testing/browser.rb +5 -6
- data/lib/action_dispatch/system_testing/driver.rb +13 -21
- data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +27 -16
- data/lib/action_dispatch/testing/assertions/response.rb +13 -6
- data/lib/action_dispatch/testing/assertions/routing.rb +67 -28
- data/lib/action_dispatch/testing/assertions.rb +3 -1
- data/lib/action_dispatch/testing/integration.rb +27 -17
- data/lib/action_dispatch/testing/request_encoder.rb +4 -1
- data/lib/action_dispatch/testing/test_process.rb +4 -3
- data/lib/action_dispatch/testing/test_request.rb +1 -1
- data/lib/action_dispatch/testing/test_response.rb +23 -9
- data/lib/action_dispatch.rb +37 -4
- data/lib/action_pack/gem_version.rb +4 -4
- data/lib/action_pack/version.rb +1 -1
- data/lib/action_pack.rb +1 -1
- metadata +55 -33
@@ -70,7 +70,7 @@ module ActionDispatch
|
|
70
70
|
end
|
71
71
|
|
72
72
|
def cookies_same_site_protection
|
73
|
-
get_header(Cookies::COOKIES_SAME_SITE_PROTECTION)
|
73
|
+
get_header(Cookies::COOKIES_SAME_SITE_PROTECTION)&.call(self)
|
74
74
|
end
|
75
75
|
|
76
76
|
def cookies_digest
|
@@ -92,7 +92,7 @@ module ActionDispatch
|
|
92
92
|
include RequestCookieMethods
|
93
93
|
end
|
94
94
|
|
95
|
-
# Read and write data to cookies through ActionController::
|
95
|
+
# Read and write data to cookies through ActionController::Cookies#cookies.
|
96
96
|
#
|
97
97
|
# When reading cookie data, the data is read from the HTTP request header, Cookie.
|
98
98
|
# When writing cookie data, the data is sent out in the HTTP response header, +Set-Cookie+.
|
@@ -160,13 +160,18 @@ module ActionDispatch
|
|
160
160
|
# to <tt>:all</tt>. To support multiple domains, provide an array, and
|
161
161
|
# the first domain matching <tt>request.host</tt> will be used. Make
|
162
162
|
# sure to specify the <tt>:domain</tt> option with <tt>:all</tt> or
|
163
|
-
# <tt>Array</tt> again when deleting cookies.
|
163
|
+
# <tt>Array</tt> again when deleting cookies. For more flexibility you
|
164
|
+
# can set the domain on a per-request basis by specifying <tt>:domain</tt>
|
165
|
+
# with a proc.
|
164
166
|
#
|
165
167
|
# domain: nil # Does not set cookie domain. (default)
|
166
168
|
# domain: :all # Allow the cookie for the top most level
|
167
169
|
# # domain and subdomains.
|
168
170
|
# domain: %w(.example.com .example.org) # Allow the cookie
|
169
171
|
# # for concrete domain names.
|
172
|
+
# domain: proc { Tenant.current.cookie_domain } # Set cookie domain dynamically
|
173
|
+
# domain: proc { |req| ".sub.#{req.host}" } # Set cookie domain dynamically based on request
|
174
|
+
#
|
170
175
|
#
|
171
176
|
# * <tt>:tld_length</tt> - When using <tt>:domain => :all</tt>, this option can be used to explicitly
|
172
177
|
# set the TLD length when using a short (<= 3 character) domain that is being interpreted as part of a TLD.
|
@@ -178,7 +183,8 @@ module ActionDispatch
|
|
178
183
|
# only HTTP. Defaults to +false+.
|
179
184
|
# * <tt>:same_site</tt> - The value of the +SameSite+ cookie attribute, which
|
180
185
|
# determines how this cookie should be restricted in cross-site contexts.
|
181
|
-
# Possible values are +:none+, +:lax+, and +:strict+. Defaults to
|
186
|
+
# Possible values are +nil+, +:none+, +:lax+, and +:strict+. Defaults to
|
187
|
+
# +:lax+.
|
182
188
|
class Cookies
|
183
189
|
HTTP_HEADER = "Set-Cookie"
|
184
190
|
GENERATOR_KEY = "action_dispatch.key_generator"
|
@@ -376,6 +382,8 @@ module ActionDispatch
|
|
376
382
|
# Removes the cookie on the client machine by setting the value to an empty string
|
377
383
|
# and the expiration date in the past. Like <tt>[]=</tt>, you can pass in
|
378
384
|
# an options hash to delete cookies with extra data such as a <tt>:path</tt>.
|
385
|
+
#
|
386
|
+
# Returns the value of the cookie, or +nil+ if the cookie does not exist.
|
379
387
|
def delete(name, options = {})
|
380
388
|
return unless @cookies.has_key? name.to_s
|
381
389
|
|
@@ -401,9 +409,15 @@ module ActionDispatch
|
|
401
409
|
@cookies.each_key { |k| delete(k, options) }
|
402
410
|
end
|
403
411
|
|
404
|
-
def write(
|
405
|
-
|
406
|
-
|
412
|
+
def write(response)
|
413
|
+
@set_cookies.each do |name, value|
|
414
|
+
if write_cookie?(value)
|
415
|
+
response.set_cookie(name, value)
|
416
|
+
end
|
417
|
+
end
|
418
|
+
|
419
|
+
@delete_cookies.each do |name, value|
|
420
|
+
response.delete_cookie(name, value)
|
407
421
|
end
|
408
422
|
end
|
409
423
|
|
@@ -414,19 +428,6 @@ module ActionDispatch
|
|
414
428
|
::Rack::Utils.escape(string)
|
415
429
|
end
|
416
430
|
|
417
|
-
def make_set_cookie_header(header)
|
418
|
-
header = @set_cookies.inject(header) { |m, (k, v)|
|
419
|
-
if write_cookie?(v)
|
420
|
-
::Rack::Utils.add_cookie_to_header(m, k, v)
|
421
|
-
else
|
422
|
-
m
|
423
|
-
end
|
424
|
-
}
|
425
|
-
@delete_cookies.inject(header) { |m, (k, v)|
|
426
|
-
::Rack::Utils.add_remove_cookie_to_header(m, k, v)
|
427
|
-
}
|
428
|
-
end
|
429
|
-
|
430
431
|
def write_cookie?(cookie)
|
431
432
|
request.ssl? || !cookie[:secure] || always_write_cookie || request.host.end_with?(".onion")
|
432
433
|
end
|
@@ -438,8 +439,9 @@ module ActionDispatch
|
|
438
439
|
|
439
440
|
options[:path] ||= "/"
|
440
441
|
|
441
|
-
|
442
|
-
|
442
|
+
unless options.key?(:same_site)
|
443
|
+
options[:same_site] = request.cookies_same_site_protection
|
444
|
+
end
|
443
445
|
|
444
446
|
if options[:domain] == :all || options[:domain] == "all"
|
445
447
|
cookie_domain = ""
|
@@ -470,7 +472,7 @@ module ActionDispatch
|
|
470
472
|
end
|
471
473
|
|
472
474
|
options[:domain] = if cookie_domain.present?
|
473
|
-
|
475
|
+
cookie_domain
|
474
476
|
end
|
475
477
|
elsif options[:domain].is_a? Array
|
476
478
|
# If host matches one of the supplied domains.
|
@@ -478,6 +480,8 @@ module ActionDispatch
|
|
478
480
|
domain = domain.delete_prefix(".")
|
479
481
|
request.host == domain || request.host.end_with?(".#{domain}")
|
480
482
|
end
|
483
|
+
elsif options[:domain].respond_to?(:call)
|
484
|
+
options[:domain] = options[:domain].call(request)
|
481
485
|
end
|
482
486
|
end
|
483
487
|
end
|
@@ -541,75 +545,57 @@ module ActionDispatch
|
|
541
545
|
end
|
542
546
|
end
|
543
547
|
|
544
|
-
class MarshalWithJsonFallback # :nodoc:
|
545
|
-
def self.load(value)
|
546
|
-
Marshal.load(value)
|
547
|
-
rescue TypeError => e
|
548
|
-
ActiveSupport::JSON.decode(value) rescue raise e
|
549
|
-
end
|
550
|
-
|
551
|
-
def self.dump(value)
|
552
|
-
Marshal.dump(value)
|
553
|
-
end
|
554
|
-
end
|
555
|
-
|
556
|
-
class JsonSerializer # :nodoc:
|
557
|
-
def self.load(value)
|
558
|
-
ActiveSupport::JSON.decode(value)
|
559
|
-
end
|
560
|
-
|
561
|
-
def self.dump(value)
|
562
|
-
ActiveSupport::JSON.encode(value)
|
563
|
-
end
|
564
|
-
end
|
565
|
-
|
566
548
|
module SerializedCookieJars # :nodoc:
|
567
|
-
MARSHAL_SIGNATURE = "\x04\x08"
|
568
549
|
SERIALIZER = ActiveSupport::MessageEncryptor::NullSerializer
|
569
550
|
|
570
551
|
protected
|
571
|
-
def
|
572
|
-
request.
|
552
|
+
def digest
|
553
|
+
request.cookies_digest || "SHA1"
|
573
554
|
end
|
574
555
|
|
575
|
-
|
576
|
-
|
556
|
+
private
|
557
|
+
def serializer
|
558
|
+
@serializer ||=
|
559
|
+
case request.cookies_serializer
|
560
|
+
when nil
|
561
|
+
ActiveSupport::Messages::SerializerWithFallback[:marshal]
|
562
|
+
when :hybrid
|
563
|
+
ActiveSupport::Messages::SerializerWithFallback[:json_allow_marshal]
|
564
|
+
when Symbol
|
565
|
+
ActiveSupport::Messages::SerializerWithFallback[request.cookies_serializer]
|
566
|
+
else
|
567
|
+
request.cookies_serializer
|
568
|
+
end
|
577
569
|
end
|
578
570
|
|
579
|
-
def
|
580
|
-
|
581
|
-
|
571
|
+
def reserialize?(dumped)
|
572
|
+
serializer.is_a?(ActiveSupport::Messages::SerializerWithFallback) &&
|
573
|
+
serializer != ActiveSupport::Messages::SerializerWithFallback[:marshal] &&
|
574
|
+
!serializer.dumped?(dumped)
|
575
|
+
end
|
582
576
|
|
583
|
-
|
584
|
-
|
585
|
-
|
586
|
-
|
587
|
-
|
588
|
-
|
589
|
-
when rotate
|
590
|
-
serializer.load(value).tap do |v|
|
591
|
-
self[name] = { value: v }
|
592
|
-
end
|
593
|
-
else
|
594
|
-
serializer.load(value)
|
577
|
+
def parse(name, dumped, force_reserialize: false, **)
|
578
|
+
if dumped
|
579
|
+
begin
|
580
|
+
value = serializer.load(dumped)
|
581
|
+
rescue StandardError
|
582
|
+
return
|
595
583
|
end
|
584
|
+
|
585
|
+
self[name] = { value: value } if force_reserialize || reserialize?(dumped)
|
586
|
+
|
587
|
+
value
|
596
588
|
end
|
597
589
|
end
|
598
590
|
|
599
|
-
def
|
600
|
-
|
601
|
-
case serializer
|
602
|
-
when :marshal
|
603
|
-
MarshalWithJsonFallback
|
604
|
-
when :json, :hybrid
|
605
|
-
JsonSerializer
|
606
|
-
else
|
607
|
-
serializer
|
608
|
-
end
|
591
|
+
def commit(name, options)
|
592
|
+
options[:value] = serializer.dump(options[:value])
|
609
593
|
end
|
610
594
|
|
611
|
-
def
|
612
|
-
|
595
|
+
def check_for_overflow!(name, options)
|
596
|
+
if options[:value].bytesize > MAX_COOKIE_SIZE
|
597
|
+
raise CookieOverflow, "#{name} cookie overflowed with size #{options[:value].bytesize} bytes"
|
598
|
+
end
|
613
599
|
end
|
614
600
|
end
|
615
601
|
|
@@ -630,15 +616,15 @@ module ActionDispatch
|
|
630
616
|
|
631
617
|
private
|
632
618
|
def parse(name, signed_message, purpose: nil)
|
633
|
-
|
634
|
-
|
635
|
-
|
619
|
+
rotated = false
|
620
|
+
data = @verifier.verified(signed_message, purpose: purpose, on_rotation: -> { rotated = true })
|
621
|
+
super(name, data, force_reserialize: rotated)
|
636
622
|
end
|
637
623
|
|
638
624
|
def commit(name, options)
|
639
|
-
|
640
|
-
|
641
|
-
|
625
|
+
super
|
626
|
+
options[:value] = @verifier.generate(options[:value], **cookie_metadata(name, options))
|
627
|
+
check_for_overflow!(name, options)
|
642
628
|
end
|
643
629
|
end
|
644
630
|
|
@@ -680,17 +666,17 @@ module ActionDispatch
|
|
680
666
|
|
681
667
|
private
|
682
668
|
def parse(name, encrypted_message, purpose: nil)
|
683
|
-
|
684
|
-
|
685
|
-
|
686
|
-
rescue ActiveSupport::MessageEncryptor::InvalidMessage, ActiveSupport::MessageVerifier::InvalidSignature
|
669
|
+
rotated = false
|
670
|
+
data = @encryptor.decrypt_and_verify(encrypted_message, purpose: purpose, on_rotation: -> { rotated = true })
|
671
|
+
super(name, data, force_reserialize: rotated)
|
672
|
+
rescue ActiveSupport::MessageEncryptor::InvalidMessage, ActiveSupport::MessageVerifier::InvalidSignature
|
687
673
|
nil
|
688
674
|
end
|
689
675
|
|
690
676
|
def commit(name, options)
|
691
|
-
|
692
|
-
|
693
|
-
|
677
|
+
super
|
678
|
+
options[:value] = @encryptor.encrypt_and_sign(options[:value], **cookie_metadata(name, options))
|
679
|
+
check_for_overflow!(name, options)
|
694
680
|
end
|
695
681
|
end
|
696
682
|
|
@@ -699,21 +685,18 @@ module ActionDispatch
|
|
699
685
|
end
|
700
686
|
|
701
687
|
def call(env)
|
702
|
-
request = ActionDispatch::Request.new
|
703
|
-
|
704
|
-
status, headers, body = @app.call(env)
|
688
|
+
request = ActionDispatch::Request.new(env)
|
689
|
+
response = @app.call(env)
|
705
690
|
|
706
691
|
if request.have_cookie_jar?
|
707
692
|
cookie_jar = request.cookie_jar
|
708
693
|
unless cookie_jar.committed?
|
709
|
-
|
710
|
-
|
711
|
-
headers[HTTP_HEADER] = headers[HTTP_HEADER].join("\n")
|
712
|
-
end
|
694
|
+
response = Rack::Response[*response]
|
695
|
+
cookie_jar.write(response)
|
713
696
|
end
|
714
697
|
end
|
715
698
|
|
716
|
-
|
699
|
+
response.to_a
|
717
700
|
end
|
718
701
|
end
|
719
702
|
end
|
@@ -6,6 +6,8 @@ require "action_dispatch/routing/inspector"
|
|
6
6
|
require "action_view"
|
7
7
|
|
8
8
|
module ActionDispatch
|
9
|
+
# = Action Dispatch \DebugExceptions
|
10
|
+
#
|
9
11
|
# This middleware is responsible for logging exceptions and
|
10
12
|
# showing a debugging page in case the request is local.
|
11
13
|
class DebugExceptions
|
@@ -24,26 +26,26 @@ module ActionDispatch
|
|
24
26
|
end
|
25
27
|
|
26
28
|
def call(env)
|
27
|
-
request = ActionDispatch::Request.new env
|
28
29
|
_, headers, body = response = @app.call(env)
|
29
30
|
|
30
|
-
if headers[
|
31
|
+
if headers[Constants::X_CASCADE] == "pass"
|
31
32
|
body.close if body.respond_to?(:close)
|
32
33
|
raise ActionController::RoutingError, "No route matches [#{env['REQUEST_METHOD']}] #{env['PATH_INFO'].inspect}"
|
33
34
|
end
|
34
35
|
|
35
36
|
response
|
36
37
|
rescue Exception => exception
|
37
|
-
|
38
|
-
|
39
|
-
|
38
|
+
request = ActionDispatch::Request.new env
|
39
|
+
backtrace_cleaner = request.get_header("action_dispatch.backtrace_cleaner")
|
40
|
+
wrapper = ExceptionWrapper.new(backtrace_cleaner, exception)
|
41
|
+
|
42
|
+
invoke_interceptors(request, exception, wrapper)
|
43
|
+
raise exception unless wrapper.show?(request)
|
44
|
+
render_exception(request, exception, wrapper)
|
40
45
|
end
|
41
46
|
|
42
47
|
private
|
43
|
-
def invoke_interceptors(request, exception)
|
44
|
-
backtrace_cleaner = request.get_header("action_dispatch.backtrace_cleaner")
|
45
|
-
wrapper = ExceptionWrapper.new(backtrace_cleaner, exception)
|
46
|
-
|
48
|
+
def invoke_interceptors(request, exception, wrapper)
|
47
49
|
@interceptors.each do |interceptor|
|
48
50
|
interceptor.call(request, exception)
|
49
51
|
rescue Exception
|
@@ -51,9 +53,7 @@ module ActionDispatch
|
|
51
53
|
end
|
52
54
|
end
|
53
55
|
|
54
|
-
def render_exception(request, exception)
|
55
|
-
backtrace_cleaner = request.get_header("action_dispatch.backtrace_cleaner")
|
56
|
-
wrapper = ExceptionWrapper.new(backtrace_cleaner, exception)
|
56
|
+
def render_exception(request, exception, wrapper)
|
57
57
|
log_error(request, wrapper)
|
58
58
|
|
59
59
|
if request.get_header("action_dispatch.show_detailed_exceptions")
|
@@ -94,7 +94,7 @@ module ActionDispatch
|
|
94
94
|
wrapper.status_code,
|
95
95
|
Rack::Utils::HTTP_STATUS_CODES[500]
|
96
96
|
),
|
97
|
-
exception: wrapper.
|
97
|
+
exception: wrapper.exception_inspect,
|
98
98
|
traces: wrapper.traces
|
99
99
|
}
|
100
100
|
|
@@ -115,19 +115,19 @@ module ActionDispatch
|
|
115
115
|
DebugView.new(
|
116
116
|
request: request,
|
117
117
|
exception_wrapper: wrapper,
|
118
|
+
# Everything should use the wrapper, but we need to pass
|
119
|
+
# `exception` for legacy code.
|
118
120
|
exception: wrapper.exception,
|
119
121
|
traces: wrapper.traces,
|
120
122
|
show_source_idx: wrapper.source_to_show_id,
|
121
123
|
trace_to_show: wrapper.trace_to_show,
|
122
|
-
routes_inspector: routes_inspector(wrapper
|
124
|
+
routes_inspector: routes_inspector(wrapper),
|
123
125
|
source_extracts: wrapper.source_extracts,
|
124
|
-
line_number: wrapper.line_number,
|
125
|
-
file: wrapper.file
|
126
126
|
)
|
127
127
|
end
|
128
128
|
|
129
129
|
def render(status, body, format)
|
130
|
-
[status, {
|
130
|
+
[status, { Rack::CONTENT_TYPE => "#{format}; charset=#{Response.default_charset}", Rack::CONTENT_LENGTH => body.bytesize.to_s }, [body]]
|
131
131
|
end
|
132
132
|
|
133
133
|
def log_error(request, wrapper)
|
@@ -136,26 +136,27 @@ module ActionDispatch
|
|
136
136
|
return unless logger
|
137
137
|
return if !log_rescued_responses?(request) && wrapper.rescue_response?
|
138
138
|
|
139
|
-
exception = wrapper.exception
|
140
139
|
trace = wrapper.exception_trace
|
141
140
|
|
142
141
|
message = []
|
143
142
|
message << " "
|
144
|
-
message << "#{
|
145
|
-
message.concat(
|
143
|
+
message << "#{wrapper.exception_class_name} (#{wrapper.message}):"
|
144
|
+
message.concat(wrapper.annotated_source_code)
|
146
145
|
message << " "
|
147
146
|
message.concat(trace)
|
148
147
|
|
149
|
-
log_array(logger, message)
|
148
|
+
log_array(logger, message, request)
|
150
149
|
end
|
151
150
|
|
152
|
-
def log_array(logger, lines)
|
151
|
+
def log_array(logger, lines, request)
|
153
152
|
return if lines.empty?
|
154
153
|
|
154
|
+
level = request.get_header("action_dispatch.debug_exception_log_level")
|
155
|
+
|
155
156
|
if logger.formatter && logger.formatter.respond_to?(:tags_text)
|
156
|
-
logger.
|
157
|
+
logger.add(level, lines.join("\n#{logger.formatter.tags_text}"))
|
157
158
|
else
|
158
|
-
logger.
|
159
|
+
logger.add(level, lines.join("\n"))
|
159
160
|
end
|
160
161
|
end
|
161
162
|
|
@@ -168,7 +169,7 @@ module ActionDispatch
|
|
168
169
|
end
|
169
170
|
|
170
171
|
def routes_inspector(exception)
|
171
|
-
if @routes_app.respond_to?(:routes) && (exception.
|
172
|
+
if @routes_app.respond_to?(:routes) && (exception.routing_error? || exception.template_error?)
|
172
173
|
ActionDispatch::Routing::RoutesInspector.new(@routes_app.routes.routes)
|
173
174
|
end
|
174
175
|
end
|
@@ -1,6 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module ActionDispatch
|
4
|
+
# = Action Dispatch \DebugLocks
|
5
|
+
#
|
4
6
|
# This middleware can be used to diagnose deadlocks in the autoload interlock.
|
5
7
|
#
|
6
8
|
# To use it, insert it near the top of the middleware stack, using
|
@@ -97,7 +99,8 @@ module ActionDispatch
|
|
97
99
|
msg << "\n#{info[:backtrace].join("\n")}\n" if info[:backtrace]
|
98
100
|
end.join("\n\n---\n\n\n")
|
99
101
|
|
100
|
-
[200, {
|
102
|
+
[200, { Rack::CONTENT_TYPE => "text/plain; charset=#{ActionDispatch::Response.default_charset}",
|
103
|
+
Rack::CONTENT_LENGTH => str.size.to_s }, [str]]
|
101
104
|
end
|
102
105
|
|
103
106
|
def blocked_by?(victim, blocker, all_threads)
|
@@ -7,18 +7,23 @@ require "action_view/base"
|
|
7
7
|
|
8
8
|
module ActionDispatch
|
9
9
|
class DebugView < ActionView::Base # :nodoc:
|
10
|
-
|
10
|
+
RESCUES_TEMPLATE_PATHS = [File.expand_path("templates", __dir__)]
|
11
11
|
|
12
12
|
def initialize(assigns)
|
13
|
-
paths =
|
13
|
+
paths = RESCUES_TEMPLATE_PATHS.dup
|
14
14
|
lookup_context = ActionView::LookupContext.new(paths)
|
15
15
|
super(lookup_context, assigns, nil)
|
16
|
+
@exception_wrapper = assigns[:exception_wrapper]
|
16
17
|
end
|
17
18
|
|
18
19
|
def compiled_method_container
|
19
20
|
self.class
|
20
21
|
end
|
21
22
|
|
23
|
+
def error_highlight_available?
|
24
|
+
@exception_wrapper.error_highlight_available?
|
25
|
+
end
|
26
|
+
|
22
27
|
def debug_params(params)
|
23
28
|
clean_params = params.clone
|
24
29
|
clean_params.delete("action")
|