actionpack 8.0.4 → 8.1.0.beta1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +241 -173
- data/lib/abstract_controller/asset_paths.rb +4 -2
- data/lib/abstract_controller/base.rb +10 -2
- data/lib/abstract_controller/caching.rb +6 -3
- data/lib/abstract_controller/helpers.rb +1 -1
- data/lib/abstract_controller/logger.rb +2 -1
- data/lib/action_controller/base.rb +1 -1
- data/lib/action_controller/caching.rb +1 -2
- data/lib/action_controller/form_builder.rb +1 -1
- data/lib/action_controller/log_subscriber.rb +7 -0
- data/lib/action_controller/metal/allow_browser.rb +1 -1
- data/lib/action_controller/metal/conditional_get.rb +25 -0
- data/lib/action_controller/metal/data_streaming.rb +1 -3
- data/lib/action_controller/metal/exceptions.rb +5 -0
- data/lib/action_controller/metal/flash.rb +1 -4
- data/lib/action_controller/metal/head.rb +3 -1
- data/lib/action_controller/metal/permissions_policy.rb +9 -0
- data/lib/action_controller/metal/rate_limiting.rb +22 -7
- data/lib/action_controller/metal/redirecting.rb +61 -5
- data/lib/action_controller/metal/renderers.rb +27 -6
- data/lib/action_controller/metal/rendering.rb +7 -1
- data/lib/action_controller/metal/request_forgery_protection.rb +18 -10
- data/lib/action_controller/metal/rescue.rb +9 -0
- data/lib/action_controller/railtie.rb +2 -6
- data/lib/action_dispatch/http/cache.rb +111 -1
- data/lib/action_dispatch/http/filter_parameters.rb +5 -3
- data/lib/action_dispatch/http/mime_types.rb +1 -0
- data/lib/action_dispatch/http/param_builder.rb +28 -27
- data/lib/action_dispatch/http/parameters.rb +3 -3
- data/lib/action_dispatch/http/permissions_policy.rb +4 -0
- data/lib/action_dispatch/http/query_parser.rb +12 -10
- data/lib/action_dispatch/http/request.rb +10 -5
- data/lib/action_dispatch/http/response.rb +16 -3
- data/lib/action_dispatch/http/url.rb +99 -3
- data/lib/action_dispatch/journey/gtg/simulator.rb +33 -12
- data/lib/action_dispatch/journey/gtg/transition_table.rb +33 -43
- data/lib/action_dispatch/journey/nodes/node.rb +2 -1
- data/lib/action_dispatch/journey/route.rb +45 -31
- data/lib/action_dispatch/journey/router/utils.rb +8 -14
- data/lib/action_dispatch/journey/router.rb +59 -81
- data/lib/action_dispatch/journey/routes.rb +7 -0
- data/lib/action_dispatch/journey/visitors.rb +55 -23
- data/lib/action_dispatch/journey/visualizer/fsm.js +4 -6
- data/lib/action_dispatch/middleware/cookies.rb +4 -2
- data/lib/action_dispatch/middleware/debug_exceptions.rb +7 -1
- data/lib/action_dispatch/middleware/debug_view.rb +11 -0
- data/lib/action_dispatch/middleware/exception_wrapper.rb +11 -5
- data/lib/action_dispatch/middleware/executor.rb +12 -2
- data/lib/action_dispatch/middleware/public_exceptions.rb +1 -5
- data/lib/action_dispatch/middleware/session/cache_store.rb +17 -0
- data/lib/action_dispatch/middleware/templates/rescues/_copy_button.html.erb +1 -0
- data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +3 -2
- data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +9 -5
- data/lib/action_dispatch/middleware/templates/rescues/blocked_host.html.erb +1 -0
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +1 -0
- data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +1 -0
- data/lib/action_dispatch/middleware/templates/rescues/layout.erb +50 -0
- data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +1 -0
- data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +1 -0
- data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +1 -0
- data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +1 -0
- data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +1 -0
- data/lib/action_dispatch/railtie.rb +10 -2
- data/lib/action_dispatch/routing/inspector.rb +4 -1
- data/lib/action_dispatch/routing/mapper.rb +323 -173
- data/lib/action_dispatch/routing/route_set.rb +2 -4
- data/lib/action_dispatch/routing/routes_proxy.rb +0 -1
- data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +2 -2
- data/lib/action_dispatch/testing/assertions/response.rb +14 -0
- data/lib/action_dispatch/testing/assertions/routing.rb +11 -3
- data/lib/action_dispatch/testing/integration.rb +4 -3
- data/lib/action_dispatch/testing/request_encoder.rb +9 -9
- data/lib/action_pack/gem_version.rb +3 -3
- metadata +11 -10
|
@@ -55,6 +55,17 @@ module ActionDispatch
|
|
|
55
55
|
end
|
|
56
56
|
end
|
|
57
57
|
|
|
58
|
+
def editor_url(location, line: nil)
|
|
59
|
+
if editor = ActiveSupport::Editor.current
|
|
60
|
+
line ||= location&.lineno
|
|
61
|
+
absolute_path = location&.absolute_path
|
|
62
|
+
|
|
63
|
+
if absolute_path && line && File.exist?(absolute_path)
|
|
64
|
+
editor.url_for(absolute_path, line)
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
58
69
|
def protect_against_forgery?
|
|
59
70
|
false
|
|
60
71
|
end
|
|
@@ -23,6 +23,7 @@ module ActionDispatch
|
|
|
23
23
|
"ActionDispatch::Http::Parameters::ParseError" => :bad_request,
|
|
24
24
|
"ActionController::BadRequest" => :bad_request,
|
|
25
25
|
"ActionController::ParameterMissing" => :bad_request,
|
|
26
|
+
"ActionController::TooManyRequests" => :too_many_requests,
|
|
26
27
|
"Rack::QueryParser::ParameterTypeError" => :bad_request,
|
|
27
28
|
"Rack::QueryParser::InvalidParameterError" => :bad_request
|
|
28
29
|
)
|
|
@@ -148,15 +149,20 @@ module ActionDispatch
|
|
|
148
149
|
application_trace_with_ids = []
|
|
149
150
|
framework_trace_with_ids = []
|
|
150
151
|
full_trace_with_ids = []
|
|
152
|
+
application_traces = application_trace.map(&:to_s)
|
|
151
153
|
|
|
154
|
+
full_trace = backtrace_cleaner&.clean_locations(backtrace, :all).presence || backtrace
|
|
152
155
|
full_trace.each_with_index do |trace, idx|
|
|
156
|
+
filtered_trace = backtrace_cleaner&.clean_frame(trace, :all) || trace
|
|
157
|
+
|
|
153
158
|
trace_with_id = {
|
|
154
159
|
exception_object_id: @exception.object_id,
|
|
155
160
|
id: idx,
|
|
156
|
-
trace: trace
|
|
161
|
+
trace: trace,
|
|
162
|
+
filtered_trace: filtered_trace,
|
|
157
163
|
}
|
|
158
164
|
|
|
159
|
-
if
|
|
165
|
+
if application_traces.include?(filtered_trace.to_s)
|
|
160
166
|
application_trace_with_ids << trace_with_id
|
|
161
167
|
else
|
|
162
168
|
framework_trace_with_ids << trace_with_id
|
|
@@ -197,7 +203,7 @@ module ActionDispatch
|
|
|
197
203
|
|
|
198
204
|
def source_extracts
|
|
199
205
|
backtrace.map do |trace|
|
|
200
|
-
extract_source(trace)
|
|
206
|
+
extract_source(trace).merge(trace: trace)
|
|
201
207
|
end
|
|
202
208
|
end
|
|
203
209
|
|
|
@@ -261,13 +267,13 @@ module ActionDispatch
|
|
|
261
267
|
end
|
|
262
268
|
|
|
263
269
|
(@exception.backtrace_locations || []).map do |loc|
|
|
264
|
-
if built_methods.key?(loc.
|
|
270
|
+
if built_methods.key?(loc.base_label)
|
|
265
271
|
thread_backtrace_location = if loc.respond_to?(:__getobj__)
|
|
266
272
|
loc.__getobj__
|
|
267
273
|
else
|
|
268
274
|
loc
|
|
269
275
|
end
|
|
270
|
-
SourceMapLocation.new(thread_backtrace_location, built_methods[loc.
|
|
276
|
+
SourceMapLocation.new(thread_backtrace_location, built_methods[loc.base_label])
|
|
271
277
|
else
|
|
272
278
|
loc
|
|
273
279
|
end
|
|
@@ -12,6 +12,10 @@ module ActionDispatch
|
|
|
12
12
|
|
|
13
13
|
def call(env)
|
|
14
14
|
state = @executor.run!(reset: true)
|
|
15
|
+
if response_finished = env["rack.response_finished"]
|
|
16
|
+
response_finished << proc { state.complete! }
|
|
17
|
+
end
|
|
18
|
+
|
|
15
19
|
begin
|
|
16
20
|
response = @app.call(env)
|
|
17
21
|
|
|
@@ -20,7 +24,11 @@ module ActionDispatch
|
|
|
20
24
|
@executor.error_reporter.report(error, handled: false, source: "application.action_dispatch")
|
|
21
25
|
end
|
|
22
26
|
|
|
23
|
-
|
|
27
|
+
unless response_finished
|
|
28
|
+
response << ::Rack::BodyProxy.new(response.pop) { state.complete! }
|
|
29
|
+
end
|
|
30
|
+
returned = true
|
|
31
|
+
response
|
|
24
32
|
rescue Exception => error
|
|
25
33
|
request = ActionDispatch::Request.new env
|
|
26
34
|
backtrace_cleaner = request.get_header("action_dispatch.backtrace_cleaner")
|
|
@@ -28,7 +36,9 @@ module ActionDispatch
|
|
|
28
36
|
@executor.error_reporter.report(wrapper.unwrapped_exception, handled: false, source: "application.action_dispatch")
|
|
29
37
|
raise
|
|
30
38
|
ensure
|
|
31
|
-
|
|
39
|
+
if !returned && !response_finished
|
|
40
|
+
state.complete!
|
|
41
|
+
end
|
|
32
42
|
end
|
|
33
43
|
end
|
|
34
44
|
end
|
|
@@ -25,11 +25,7 @@ module ActionDispatch
|
|
|
25
25
|
def call(env)
|
|
26
26
|
request = ActionDispatch::Request.new(env)
|
|
27
27
|
status = request.path_info[1..-1].to_i
|
|
28
|
-
|
|
29
|
-
content_type = request.formats.first
|
|
30
|
-
rescue ActionDispatch::Http::MimeNegotiation::InvalidType
|
|
31
|
-
content_type = Mime[:text]
|
|
32
|
-
end
|
|
28
|
+
content_type = request.formats.first
|
|
33
29
|
body = { status: status, error: Rack::Utils::HTTP_STATUS_CODES.fetch(status, Rack::Utils::HTTP_STATUS_CODES[500]) }
|
|
34
30
|
|
|
35
31
|
if env["action_dispatch.original_request_method"] == "HEAD"
|
|
@@ -18,11 +18,16 @@ module ActionDispatch
|
|
|
18
18
|
# * `expire_after` - The length of time a session will be stored before
|
|
19
19
|
# automatically expiring. By default, the `:expires_in` option of the cache
|
|
20
20
|
# is used.
|
|
21
|
+
# * `check_collisions` - Check if newly generated session ids aren't already in use.
|
|
22
|
+
# If for some reason 128 bits of randomness aren't considered secure enough to avoid
|
|
23
|
+
# collisions, this option can be enabled to ensure newly generated ids aren't in use.
|
|
24
|
+
# By default, it is set to `false` to avoid additional cache write operations.
|
|
21
25
|
#
|
|
22
26
|
class CacheStore < AbstractSecureStore
|
|
23
27
|
def initialize(app, options = {})
|
|
24
28
|
@cache = options[:cache] || Rails.cache
|
|
25
29
|
options[:expire_after] ||= @cache.options[:expires_in]
|
|
30
|
+
@check_collisions = options[:check_collisions] || false
|
|
26
31
|
super
|
|
27
32
|
end
|
|
28
33
|
|
|
@@ -61,6 +66,18 @@ module ActionDispatch
|
|
|
61
66
|
def get_session_with_fallback(sid)
|
|
62
67
|
@cache.read(cache_key(sid.private_id)) || @cache.read(cache_key(sid.public_id))
|
|
63
68
|
end
|
|
69
|
+
|
|
70
|
+
def generate_sid
|
|
71
|
+
if @check_collisions
|
|
72
|
+
loop do
|
|
73
|
+
sid = super
|
|
74
|
+
key = cache_key(sid.private_id)
|
|
75
|
+
break sid if @cache.write(key, {}, unless_exist: true, expires_in: default_options[:expire_after])
|
|
76
|
+
end
|
|
77
|
+
else
|
|
78
|
+
super
|
|
79
|
+
end
|
|
80
|
+
end
|
|
64
81
|
end
|
|
65
82
|
end
|
|
66
83
|
end
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<button onclick="copyAsText.bind(this)()">Copy as text</button>
|
|
@@ -11,8 +11,9 @@
|
|
|
11
11
|
<tr>
|
|
12
12
|
<td>
|
|
13
13
|
<pre class="line_numbers">
|
|
14
|
-
<% source_extract[:code].each_key do |
|
|
15
|
-
|
|
14
|
+
<% source_extract[:code].each_key do |line| %>
|
|
15
|
+
<% file_url = editor_url(source_extract[:trace], line: line) %>
|
|
16
|
+
<span><%= link_to_if file_url, line, file_url -%></span>
|
|
16
17
|
<% end %>
|
|
17
18
|
</pre>
|
|
18
19
|
</td>
|
|
@@ -13,13 +13,17 @@
|
|
|
13
13
|
<% end %>
|
|
14
14
|
|
|
15
15
|
<% traces.each do |name, trace| %>
|
|
16
|
-
<div id="<%= "#{name.gsub(/\s/, '-')}-#{error_index}" %>" style="display: <%= (name == trace_to_show) ? 'block' : 'none' %>;">
|
|
16
|
+
<div id="<%= "#{name.gsub(/\s/, '-')}-#{error_index}" %>" class="trace-container" style="display: <%= (name == trace_to_show) ? 'block' : 'none' %>;">
|
|
17
17
|
<code class="traces">
|
|
18
18
|
<% trace.each do |frame| %>
|
|
19
|
-
<
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
19
|
+
<div class="trace">
|
|
20
|
+
<% file_url = editor_url(frame[:trace]) %>
|
|
21
|
+
<%= file_url && link_to("✏️", file_url, class: "edit-icon") %>
|
|
22
|
+
<a class="trace-frames trace-frames-<%= error_index %>" data-exception-object-id="<%= frame[:exception_object_id] %>" data-frame-id="<%= frame[:id] %>" href="#">
|
|
23
|
+
<%= frame[:trace] %>
|
|
24
|
+
</a>
|
|
25
|
+
<br>
|
|
26
|
+
</div>
|
|
23
27
|
<% end %>
|
|
24
28
|
</code>
|
|
25
29
|
</div>
|
|
@@ -38,6 +38,22 @@
|
|
|
38
38
|
padding: 0.5em 1.5em;
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
+
header button {
|
|
42
|
+
appearance: none;
|
|
43
|
+
background-color: hsl(0 0% 0% / 0.2);
|
|
44
|
+
border: 0;
|
|
45
|
+
border-radius: 14px;
|
|
46
|
+
color: white;
|
|
47
|
+
float: right;
|
|
48
|
+
font-weight: 500;
|
|
49
|
+
height: 28px;
|
|
50
|
+
padding-inline: 14px;
|
|
51
|
+
margin: 0.35em 0;
|
|
52
|
+
}
|
|
53
|
+
header button:active {
|
|
54
|
+
background-color: hsl(0 0% 0% / 0.25);
|
|
55
|
+
}
|
|
56
|
+
|
|
41
57
|
h1 {
|
|
42
58
|
overflow-wrap: break-word;
|
|
43
59
|
margin: 0.2em 0;
|
|
@@ -54,6 +70,30 @@
|
|
|
54
70
|
font-size: 11px;
|
|
55
71
|
}
|
|
56
72
|
|
|
73
|
+
.trace-container {
|
|
74
|
+
margin-top: 10px;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
code.traces .trace {
|
|
78
|
+
display: flex;
|
|
79
|
+
align-items: center;
|
|
80
|
+
gap: 2px;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
.edit-icon {
|
|
84
|
+
width: 16px;
|
|
85
|
+
height: 16px;
|
|
86
|
+
display: flex;
|
|
87
|
+
font-size: 13px;
|
|
88
|
+
align-items: center;
|
|
89
|
+
justify-content: center;
|
|
90
|
+
text-decoration: none;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
.edit-icon:hover {
|
|
94
|
+
scale: 1.05;
|
|
95
|
+
}
|
|
96
|
+
|
|
57
97
|
.response-heading, .request-heading {
|
|
58
98
|
margin-top: 30px;
|
|
59
99
|
}
|
|
@@ -274,11 +314,21 @@
|
|
|
274
314
|
var toggleEnvDump = function() {
|
|
275
315
|
return toggle('env_dump');
|
|
276
316
|
}
|
|
317
|
+
var copyAsText = function() {
|
|
318
|
+
const text = document.getElementById("exception-message-for-copy").textContent;
|
|
319
|
+
|
|
320
|
+
navigator.clipboard.writeText(text).then(() => {
|
|
321
|
+
const beforeText = this.innerText;
|
|
322
|
+
this.innerText = "Copied!"
|
|
323
|
+
setTimeout(() => this.innerText = beforeText, 1000)
|
|
324
|
+
})
|
|
325
|
+
}
|
|
277
326
|
</script>
|
|
278
327
|
</head>
|
|
279
328
|
<body>
|
|
280
329
|
|
|
281
330
|
<%= yield %>
|
|
331
|
+
<script type="text/plain" id="exception-message-for-copy"><%= raw @exception_message_for_copy %></script>
|
|
282
332
|
|
|
283
333
|
</body>
|
|
284
334
|
</html>
|
|
@@ -55,8 +55,16 @@ module ActionDispatch
|
|
|
55
55
|
ActionDispatch::Http::URL.secure_protocol = app.config.force_ssl
|
|
56
56
|
ActionDispatch::Http::URL.tld_length = app.config.action_dispatch.tld_length
|
|
57
57
|
|
|
58
|
-
|
|
59
|
-
|
|
58
|
+
unless app.config.action_dispatch.domain_extractor.nil?
|
|
59
|
+
ActionDispatch::Http::URL.domain_extractor = app.config.action_dispatch.domain_extractor
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
unless app.config.action_dispatch.ignore_leading_brackets.nil?
|
|
63
|
+
ActionDispatch::ParamBuilder.ignore_leading_brackets = app.config.action_dispatch.ignore_leading_brackets
|
|
64
|
+
end
|
|
65
|
+
unless app.config.action_dispatch.strict_query_string_separator.nil?
|
|
66
|
+
ActionDispatch::QueryParser.strict_query_string_separator = app.config.action_dispatch.strict_query_string_separator
|
|
67
|
+
end
|
|
60
68
|
|
|
61
69
|
ActiveSupport.on_load(:action_dispatch_request) do
|
|
62
70
|
self.ignore_accept_header = app.config.action_dispatch.ignore_accept_header
|
|
@@ -89,7 +89,10 @@ module ActionDispatch
|
|
|
89
89
|
|
|
90
90
|
@engines.each do |name, engine_routes|
|
|
91
91
|
formatter.section_title "Routes for #{name}"
|
|
92
|
-
|
|
92
|
+
if engine_routes.any?
|
|
93
|
+
formatter.header engine_routes
|
|
94
|
+
formatter.section engine_routes
|
|
95
|
+
end
|
|
93
96
|
end
|
|
94
97
|
|
|
95
98
|
formatter.result
|