actionpack 7.0.8.4 → 7.1.3.4
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 +358 -362
- data/MIT-LICENSE +1 -1
- data/README.rdoc +2 -2
- data/lib/abstract_controller/base.rb +20 -11
- 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 -20
- 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 +17 -8
- 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 +4 -2
- data/lib/action_controller/metal/permissions_policy.rb +1 -1
- data/lib/action_controller/metal/redirecting.rb +7 -7
- 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 +139 -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 +132 -52
- 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 +11 -5
- data/lib/action_dispatch/http/headers.rb +2 -0
- data/lib/action_dispatch/http/mime_negotiation.rb +22 -22
- 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 +38 -16
- 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 +80 -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 +9 -8
- 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 +186 -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 +35 -24
- 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 +10 -15
- data/lib/action_dispatch/routing/url_for.rb +5 -1
- data/lib/action_dispatch/routing.rb +7 -7
- data/lib/action_dispatch/system_test_case.rb +3 -3
- data/lib/action_dispatch/system_testing/browser.rb +20 -19
- 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/assertion_response.rb +1 -1
- 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 +3 -3
- data/lib/action_pack/version.rb +1 -1
- data/lib/action_pack.rb +1 -1
- metadata +62 -26
@@ -11,6 +11,7 @@ module Mime
|
|
11
11
|
def initialize
|
12
12
|
@mimes = []
|
13
13
|
@symbols = []
|
14
|
+
@symbols_set = Set.new
|
14
15
|
end
|
15
16
|
|
16
17
|
def each(&block)
|
@@ -19,17 +20,25 @@ module Mime
|
|
19
20
|
|
20
21
|
def <<(type)
|
21
22
|
@mimes << type
|
22
|
-
|
23
|
+
sym_type = type.to_sym
|
24
|
+
@symbols << sym_type
|
25
|
+
@symbols_set << sym_type
|
23
26
|
end
|
24
27
|
|
25
28
|
def delete_if
|
26
29
|
@mimes.delete_if do |x|
|
27
30
|
if yield x
|
28
|
-
|
31
|
+
sym_type = x.to_sym
|
32
|
+
@symbols.delete(sym_type)
|
33
|
+
@symbols_set.delete(sym_type)
|
29
34
|
true
|
30
35
|
end
|
31
36
|
end
|
32
37
|
end
|
38
|
+
|
39
|
+
def valid_symbols?(symbols) # :nodoc
|
40
|
+
symbols.all? { |s| @symbols_set.include?(s) }
|
41
|
+
end
|
33
42
|
end
|
34
43
|
|
35
44
|
SET = Mimes.new
|
@@ -42,6 +51,14 @@ module Mime
|
|
42
51
|
Type.lookup_by_extension(type)
|
43
52
|
end
|
44
53
|
|
54
|
+
def symbols
|
55
|
+
SET.symbols
|
56
|
+
end
|
57
|
+
|
58
|
+
def valid_symbols?(symbols) # :nodoc:
|
59
|
+
SET.valid_symbols?(symbols)
|
60
|
+
end
|
61
|
+
|
45
62
|
def fetch(type, &block)
|
46
63
|
return type if type.is_a?(Type)
|
47
64
|
EXTENSION_LOOKUP.fetch(type.to_s, &block)
|
@@ -135,14 +152,18 @@ module Mime
|
|
135
152
|
|
136
153
|
class << self
|
137
154
|
TRAILING_STAR_REGEXP = /^(text|application)\/\*/
|
138
|
-
|
155
|
+
# all media-type parameters need to be before the q-parameter
|
156
|
+
# https://www.rfc-editor.org/rfc/rfc7231#section-5.3.2
|
157
|
+
PARAMETER_SEPARATOR_REGEXP = /;\s*q="?/
|
158
|
+
ACCEPT_HEADER_REGEXP = /[^,\s"](?:[^,"]|"[^"]*")*/
|
139
159
|
|
140
160
|
def register_callback(&block)
|
141
161
|
@register_callbacks << block
|
142
162
|
end
|
143
163
|
|
144
164
|
def lookup(string)
|
145
|
-
|
165
|
+
# fallback to the media-type without parameters if it was not found
|
166
|
+
LOOKUP[string] || LOOKUP[string.split(";", 2)[0]&.rstrip] || Type.new(string)
|
146
167
|
end
|
147
168
|
|
148
169
|
def lookup_by_extension(extension)
|
@@ -171,12 +192,14 @@ module Mime
|
|
171
192
|
|
172
193
|
def parse(accept_header)
|
173
194
|
if !accept_header.include?(",")
|
174
|
-
|
175
|
-
|
176
|
-
|
195
|
+
if (index = accept_header.index(PARAMETER_SEPARATOR_REGEXP))
|
196
|
+
accept_header = accept_header[0, index].strip
|
197
|
+
end
|
198
|
+
return [] if accept_header.blank?
|
199
|
+
parse_trailing_star(accept_header) || Array(Mime::Type.lookup(accept_header))
|
177
200
|
else
|
178
201
|
list, index = [], 0
|
179
|
-
accept_header.
|
202
|
+
accept_header.scan(ACCEPT_HEADER_REGEXP).each do |header|
|
180
203
|
params, q = header.split(PARAMETER_SEPARATOR_REGEXP)
|
181
204
|
|
182
205
|
next unless params
|
@@ -199,10 +222,10 @@ module Mime
|
|
199
222
|
end
|
200
223
|
|
201
224
|
# For an input of <tt>'text'</tt>, returns <tt>[Mime[:json], Mime[:xml], Mime[:ics],
|
202
|
-
# Mime[:html], Mime[:css], Mime[:csv], Mime[:js], Mime[:yaml], Mime[:text]</tt>.
|
225
|
+
# Mime[:html], Mime[:css], Mime[:csv], Mime[:js], Mime[:yaml], Mime[:text]]</tt>.
|
203
226
|
#
|
204
227
|
# For an input of <tt>'application'</tt>, returns <tt>[Mime[:html], Mime[:js],
|
205
|
-
# Mime[:xml], Mime[:yaml], Mime[:atom], Mime[:json], Mime[:rss], Mime[:url_encoded_form]</tt>.
|
228
|
+
# Mime[:xml], Mime[:yaml], Mime[:atom], Mime[:json], Mime[:rss], Mime[:url_encoded_form]]</tt>.
|
206
229
|
def parse_data_with_trailing_star(type)
|
207
230
|
Mime::SET.select { |m| m.match?(type) }
|
208
231
|
end
|
@@ -225,7 +248,7 @@ module Mime
|
|
225
248
|
attr_reader :hash
|
226
249
|
|
227
250
|
MIME_NAME = "[a-zA-Z0-9][a-zA-Z0-9#{Regexp.escape('!#$&-^_.+')}]{0,126}"
|
228
|
-
MIME_PARAMETER_VALUE = "
|
251
|
+
MIME_PARAMETER_VALUE = "(?:#{MIME_NAME}|\"[^\"\r\\\\]*\")"
|
229
252
|
MIME_PARAMETER = "\s*;\s*#{MIME_NAME}(?:=#{MIME_PARAMETER_VALUE})?"
|
230
253
|
MIME_REGEXP = /\A(?:\*\/\*|#{MIME_NAME}\/(?:\*|#{MIME_NAME})(?>#{MIME_PARAMETER})*\s*)\z/
|
231
254
|
|
@@ -291,7 +314,7 @@ module Mime
|
|
291
314
|
end
|
292
315
|
|
293
316
|
def html?
|
294
|
-
(symbol == :html) ||
|
317
|
+
(symbol == :html) || @string.include?("html")
|
295
318
|
end
|
296
319
|
|
297
320
|
def all?; false; end
|
@@ -18,6 +18,7 @@ Mime::Type.register "image/gif", :gif, [], %w(gif)
|
|
18
18
|
Mime::Type.register "image/bmp", :bmp, [], %w(bmp)
|
19
19
|
Mime::Type.register "image/tiff", :tiff, [], %w(tif tiff)
|
20
20
|
Mime::Type.register "image/svg+xml", :svg
|
21
|
+
Mime::Type.register "image/webp", :webp, [], %w(webp)
|
21
22
|
|
22
23
|
Mime::Type.register "video/mpeg", :mpeg, [], %w(mpg mpeg mpe)
|
23
24
|
|
@@ -43,7 +44,8 @@ Mime::Type.register "application/x-www-form-urlencoded", :url_encoded_form
|
|
43
44
|
|
44
45
|
# https://www.ietf.org/rfc/rfc4627.txt
|
45
46
|
# http://www.json.org/JSONRequest.html
|
46
|
-
|
47
|
+
# https://www.ietf.org/rfc/rfc7807.txt
|
48
|
+
Mime::Type.register "application/json", :json, %w( text/x-json application/jsonrequest application/problem+json )
|
47
49
|
|
48
50
|
Mime::Type.register "application/pdf", :pdf, [], %w(pdf)
|
49
51
|
Mime::Type.register "application/zip", :zip, [], %w(zip)
|
@@ -76,7 +76,7 @@ module ActionDispatch
|
|
76
76
|
end
|
77
77
|
|
78
78
|
# Returns a hash with the \parameters used to form the \path of the request.
|
79
|
-
# Returned hash keys are
|
79
|
+
# Returned hash keys are symbols:
|
80
80
|
#
|
81
81
|
# { action: "my_action", controller: "my_controller" }
|
82
82
|
def path_parameters
|
@@ -3,6 +3,8 @@
|
|
3
3
|
require "active_support/core_ext/object/deep_dup"
|
4
4
|
|
5
5
|
module ActionDispatch # :nodoc:
|
6
|
+
# = Action Dispatch \PermissionsPolicy
|
7
|
+
#
|
6
8
|
# Configures the HTTP
|
7
9
|
# {Feature-Policy}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Feature-Policy]
|
8
10
|
# response header to specify which browser features the current document and
|
@@ -19,32 +21,30 @@ module ActionDispatch # :nodoc:
|
|
19
21
|
# policy.payment :self, "https://secure.example.com"
|
20
22
|
# end
|
21
23
|
#
|
24
|
+
# The Feature-Policy header has been renamed to Permissions-Policy.
|
25
|
+
# The Permissions-Policy requires a different implementation and isn't
|
26
|
+
# yet supported by all browsers. To avoid having to rename this
|
27
|
+
# middleware in the future we use the new name for the middleware but
|
28
|
+
# keep the old header name and implementation for now.
|
22
29
|
class PermissionsPolicy
|
23
30
|
class Middleware
|
24
|
-
CONTENT_TYPE = "Content-Type"
|
25
|
-
# The Feature-Policy header has been renamed to Permissions-Policy.
|
26
|
-
# The Permissions-Policy requires a different implementation and isn't
|
27
|
-
# yet supported by all browsers. To avoid having to rename this
|
28
|
-
# middleware in the future we use the new name for the middleware but
|
29
|
-
# keep the old header name and implementation for now.
|
30
|
-
POLICY = "Feature-Policy"
|
31
|
-
|
32
31
|
def initialize(app)
|
33
32
|
@app = app
|
34
33
|
end
|
35
34
|
|
36
35
|
def call(env)
|
37
|
-
request = ActionDispatch::Request.new(env)
|
38
36
|
_, headers, _ = response = @app.call(env)
|
39
37
|
|
40
38
|
return response if policy_present?(headers)
|
41
39
|
|
40
|
+
request = ActionDispatch::Request.new(env)
|
41
|
+
|
42
42
|
if policy = request.permissions_policy
|
43
|
-
headers[
|
43
|
+
headers[ActionDispatch::Constants::FEATURE_POLICY] = policy.build(request.controller_instance)
|
44
44
|
end
|
45
45
|
|
46
46
|
if policy_empty?(policy)
|
47
|
-
headers.delete(
|
47
|
+
headers.delete(ActionDispatch::Constants::FEATURE_POLICY)
|
48
48
|
end
|
49
49
|
|
50
50
|
response
|
@@ -52,7 +52,7 @@ module ActionDispatch # :nodoc:
|
|
52
52
|
|
53
53
|
private
|
54
54
|
def policy_present?(headers)
|
55
|
-
headers[
|
55
|
+
headers[ActionDispatch::Constants::FEATURE_POLICY]
|
56
56
|
end
|
57
57
|
|
58
58
|
def policy_empty?(policy)
|
@@ -78,7 +78,7 @@ module ActionDispatch # :nodoc:
|
|
78
78
|
}.freeze
|
79
79
|
|
80
80
|
# List of available permissions can be found at
|
81
|
-
# https://github.com/w3c/webappsec-permissions-policy/blob/
|
81
|
+
# https://github.com/w3c/webappsec-permissions-policy/blob/main/features.md#policy-controlled-features
|
82
82
|
DIRECTIVES = {
|
83
83
|
accelerometer: "accelerometer",
|
84
84
|
ambient_light_sensor: "ambient-light-sensor",
|
@@ -88,15 +88,18 @@ module ActionDispatch # :nodoc:
|
|
88
88
|
fullscreen: "fullscreen",
|
89
89
|
geolocation: "geolocation",
|
90
90
|
gyroscope: "gyroscope",
|
91
|
+
hid: "hid",
|
92
|
+
idle_detection: "idle_detection",
|
91
93
|
magnetometer: "magnetometer",
|
92
94
|
microphone: "microphone",
|
93
95
|
midi: "midi",
|
94
96
|
payment: "payment",
|
95
97
|
picture_in_picture: "picture-in-picture",
|
96
|
-
|
98
|
+
screen_wake_lock: "screen-wake-lock",
|
99
|
+
serial: "serial",
|
100
|
+
sync_xhr: "sync-xhr",
|
97
101
|
usb: "usb",
|
98
|
-
|
99
|
-
vr: "vr",
|
102
|
+
web_share: "web-share",
|
100
103
|
}.freeze
|
101
104
|
|
102
105
|
private_constant :MAPPINGS, :DIRECTIVES
|
@@ -122,6 +125,25 @@ module ActionDispatch # :nodoc:
|
|
122
125
|
end
|
123
126
|
end
|
124
127
|
|
128
|
+
%w[speaker vibrate vr].each do |directive|
|
129
|
+
define_method(directive) do |*sources|
|
130
|
+
ActionDispatch.deprecator.warn(<<~MSG)
|
131
|
+
The `#{directive}` permissions policy directive is deprecated
|
132
|
+
and will be removed in Rails 7.2.
|
133
|
+
|
134
|
+
There is no browser support for this directive, and no plan
|
135
|
+
for browser support in the future. You can just remove this
|
136
|
+
directive from your application.
|
137
|
+
MSG
|
138
|
+
|
139
|
+
if sources.first
|
140
|
+
@directives[directive] = apply_mappings(sources)
|
141
|
+
else
|
142
|
+
@directives.delete(directive)
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
125
147
|
def build(context = nil)
|
126
148
|
build_directives(context).compact.join("; ")
|
127
149
|
end
|
@@ -72,7 +72,7 @@ module ActionDispatch
|
|
72
72
|
|
73
73
|
PASS_NOT_FOUND = Class.new { # :nodoc:
|
74
74
|
def self.action(_); self; end
|
75
|
-
def self.call(_); [404, {
|
75
|
+
def self.call(_); [404, { Constants::X_CASCADE => "pass" }, []]; end
|
76
76
|
def self.action_encoding_template(action); false; end
|
77
77
|
}
|
78
78
|
|
@@ -145,6 +145,18 @@ module ActionDispatch
|
|
145
145
|
@request_method ||= check_method(super)
|
146
146
|
end
|
147
147
|
|
148
|
+
# Returns the URI pattern of the matched route for the request,
|
149
|
+
# using the same format as `bin/rails routes`:
|
150
|
+
#
|
151
|
+
# request.route_uri_pattern # => "/:controller(/:action(/:id))(.:format)"
|
152
|
+
def route_uri_pattern
|
153
|
+
get_header("action_dispatch.route_uri_pattern")
|
154
|
+
end
|
155
|
+
|
156
|
+
def route_uri_pattern=(pattern) # :nodoc:
|
157
|
+
set_header("action_dispatch.route_uri_pattern", pattern)
|
158
|
+
end
|
159
|
+
|
148
160
|
def routes # :nodoc:
|
149
161
|
get_header("action_dispatch.routes")
|
150
162
|
end
|
@@ -179,13 +191,6 @@ module ActionDispatch
|
|
179
191
|
get_header "action_dispatch.http_auth_salt"
|
180
192
|
end
|
181
193
|
|
182
|
-
def show_exceptions? # :nodoc:
|
183
|
-
# We're treating `nil` as "unset", and we want the default setting to be
|
184
|
-
# `true`. This logic should be extracted to `env_config` and calculated
|
185
|
-
# once.
|
186
|
-
!(get_header("action_dispatch.show_exceptions") == false)
|
187
|
-
end
|
188
|
-
|
189
194
|
# Returns a symbol form of the #request_method.
|
190
195
|
def request_method_symbol
|
191
196
|
HTTP_METHOD_LOOKUP[request_method]
|
@@ -194,9 +199,20 @@ module ActionDispatch
|
|
194
199
|
# Returns the original value of the environment's REQUEST_METHOD,
|
195
200
|
# even if it was overridden by middleware. See #request_method for
|
196
201
|
# more information.
|
197
|
-
|
198
|
-
|
202
|
+
#
|
203
|
+
# For debugging purposes, when called with arguments this method will
|
204
|
+
# fall back to Object#method
|
205
|
+
def method(*args)
|
206
|
+
if args.empty?
|
207
|
+
@method ||= check_method(
|
208
|
+
get_header("rack.methodoverride.original_method") ||
|
209
|
+
get_header("REQUEST_METHOD")
|
210
|
+
)
|
211
|
+
else
|
212
|
+
super
|
213
|
+
end
|
199
214
|
end
|
215
|
+
ruby2_keywords(:method)
|
200
216
|
|
201
217
|
# Returns a symbol form of the #method.
|
202
218
|
def method_symbol
|
@@ -267,6 +283,7 @@ module ActionDispatch
|
|
267
283
|
|
268
284
|
# Returns the content length of the request as an integer.
|
269
285
|
def content_length
|
286
|
+
return raw_post.bytesize if headers.key?("Transfer-Encoding")
|
270
287
|
super.to_i
|
271
288
|
end
|
272
289
|
|
@@ -321,9 +338,8 @@ module ActionDispatch
|
|
321
338
|
# work with raw requests directly.
|
322
339
|
def raw_post
|
323
340
|
unless has_header? "RAW_POST_DATA"
|
324
|
-
|
325
|
-
|
326
|
-
raw_post_body.rewind if raw_post_body.respond_to?(:rewind)
|
341
|
+
set_header("RAW_POST_DATA", read_body_stream)
|
342
|
+
body_stream.rewind if body_stream.respond_to?(:rewind)
|
327
343
|
end
|
328
344
|
get_header "RAW_POST_DATA"
|
329
345
|
end
|
@@ -357,6 +373,7 @@ module ActionDispatch
|
|
357
373
|
|
358
374
|
def reset_session
|
359
375
|
session.destroy
|
376
|
+
reset_csrf_token
|
360
377
|
end
|
361
378
|
|
362
379
|
def session=(session) # :nodoc:
|
@@ -428,15 +445,32 @@ module ActionDispatch
|
|
428
445
|
"#<#{self.class.name} #{method} #{original_url.dump} for #{remote_ip}>"
|
429
446
|
end
|
430
447
|
|
448
|
+
def reset_csrf_token
|
449
|
+
controller_instance.reset_csrf_token(self) if controller_instance.respond_to?(:reset_csrf_token)
|
450
|
+
end
|
451
|
+
|
452
|
+
def commit_csrf_token
|
453
|
+
controller_instance.commit_csrf_token(self) if controller_instance.respond_to?(:commit_csrf_token)
|
454
|
+
end
|
455
|
+
|
431
456
|
private
|
432
457
|
def check_method(name)
|
433
|
-
|
458
|
+
if name
|
459
|
+
HTTP_METHOD_LOOKUP[name] || raise(ActionController::UnknownHttpMethod, "#{name}, accepted HTTP methods are #{HTTP_METHODS[0...-1].join(', ')}, and #{HTTP_METHODS[-1]}")
|
460
|
+
end
|
461
|
+
|
434
462
|
name
|
435
463
|
end
|
436
464
|
|
437
465
|
def default_session
|
438
466
|
Session.disabled(self)
|
439
467
|
end
|
468
|
+
|
469
|
+
def read_body_stream
|
470
|
+
body_stream.rewind if body_stream.respond_to?(:rewind)
|
471
|
+
return body_stream.read if headers.key?("Transfer-Encoding") # Read body stream until EOF if "Transfer-Encoding" is present
|
472
|
+
body_stream.read(content_length)
|
473
|
+
end
|
440
474
|
end
|
441
475
|
end
|
442
476
|
|
@@ -6,23 +6,23 @@ require "action_dispatch/http/cache"
|
|
6
6
|
require "monitor"
|
7
7
|
|
8
8
|
module ActionDispatch # :nodoc:
|
9
|
+
# = Action Dispatch \Response
|
10
|
+
#
|
9
11
|
# Represents an HTTP response generated by a controller action. Use it to
|
10
12
|
# retrieve the current state of the response, or customize the response. It can
|
11
13
|
# either represent a real HTTP response (i.e. one that is meant to be sent
|
12
14
|
# back to the web browser) or a TestResponse (i.e. one that is generated
|
13
15
|
# from integration tests).
|
14
16
|
#
|
15
|
-
# \Response
|
16
|
-
#
|
17
|
-
# methods
|
18
|
-
#
|
19
|
-
# ActionControllerBase#headers instead of Response#headers.
|
17
|
+
# The \Response object for the current request is exposed on controllers as
|
18
|
+
# ActionController::Metal#response. ActionController::Metal also provides a
|
19
|
+
# few additional methods that delegate to attributes of the \Response such as
|
20
|
+
# ActionController::Metal#headers.
|
20
21
|
#
|
21
|
-
#
|
22
|
-
# more detail
|
23
|
-
#
|
24
|
-
#
|
25
|
-
# objects of type TestResponse (which are of course also of type \Response).
|
22
|
+
# Integration tests will likely also want to inspect responses in
|
23
|
+
# more detail. Methods such as Integration::RequestHelpers#get
|
24
|
+
# and Integration::RequestHelpers#post return instances of
|
25
|
+
# TestResponse (which inherits from \Response) for this purpose.
|
26
26
|
#
|
27
27
|
# For example, the following demo integration test prints the body of the
|
28
28
|
# controller response to the console:
|
@@ -34,41 +34,43 @@ module ActionDispatch # :nodoc:
|
|
34
34
|
# end
|
35
35
|
# end
|
36
36
|
class Response
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
raise ActionDispatch::IllegalStateError, "header already sent"
|
46
|
-
end
|
47
|
-
|
48
|
-
super
|
49
|
-
end
|
50
|
-
|
51
|
-
def merge(other)
|
52
|
-
self.class.new @response, __getobj__.merge(other)
|
53
|
-
end
|
54
|
-
|
55
|
-
def to_hash
|
56
|
-
__getobj__.dup
|
57
|
-
end
|
37
|
+
begin
|
38
|
+
# For `Rack::Headers` (Rack 3+):
|
39
|
+
require "rack/headers"
|
40
|
+
Headers = ::Rack::Headers
|
41
|
+
rescue LoadError
|
42
|
+
# For `Rack::Utils::HeaderHash`:
|
43
|
+
require "rack/utils"
|
44
|
+
Headers = ::Rack::Utils::HeaderHash
|
58
45
|
end
|
59
46
|
|
47
|
+
# To be deprecated:
|
48
|
+
Header = Headers
|
49
|
+
|
60
50
|
# The request that the response is responding to.
|
61
51
|
attr_accessor :request
|
62
52
|
|
63
53
|
# The HTTP status code.
|
64
54
|
attr_reader :status
|
65
55
|
|
66
|
-
#
|
67
|
-
|
56
|
+
# The headers for the response.
|
57
|
+
#
|
58
|
+
# header["Content-Type"] # => "text/plain"
|
59
|
+
# header["Content-Type"] = "application/json"
|
60
|
+
# header["Content-Type"] # => "application/json"
|
61
|
+
#
|
62
|
+
# Also aliased as +headers+.
|
63
|
+
#
|
64
|
+
# headers["Content-Type"] # => "text/plain"
|
65
|
+
# headers["Content-Type"] = "application/json"
|
66
|
+
# headers["Content-Type"] # => "application/json"
|
67
|
+
#
|
68
|
+
# Also aliased as +header+ for compatibility.
|
69
|
+
attr_reader :headers
|
68
70
|
|
69
|
-
alias_method :
|
71
|
+
alias_method :header, :headers
|
70
72
|
|
71
|
-
delegate :[], :[]=, to: :@
|
73
|
+
delegate :[], :[]=, to: :@headers
|
72
74
|
|
73
75
|
def each(&block)
|
74
76
|
sending!
|
@@ -79,7 +81,6 @@ module ActionDispatch # :nodoc:
|
|
79
81
|
|
80
82
|
CONTENT_TYPE = "Content-Type"
|
81
83
|
SET_COOKIE = "Set-Cookie"
|
82
|
-
LOCATION = "Location"
|
83
84
|
NO_CONTENT_CODES = [100, 101, 102, 103, 204, 205, 304]
|
84
85
|
|
85
86
|
cattr_accessor :default_charset, default: "utf-8"
|
@@ -102,6 +103,12 @@ module ActionDispatch # :nodoc:
|
|
102
103
|
@str_body = nil
|
103
104
|
end
|
104
105
|
|
106
|
+
def to_ary
|
107
|
+
@buf.respond_to?(:to_ary) ?
|
108
|
+
@buf.to_ary :
|
109
|
+
@buf.each
|
110
|
+
end
|
111
|
+
|
105
112
|
def body
|
106
113
|
@str_body ||= begin
|
107
114
|
buf = +""
|
@@ -117,6 +124,7 @@ module ActionDispatch # :nodoc:
|
|
117
124
|
@response.commit!
|
118
125
|
@buf.push string
|
119
126
|
end
|
127
|
+
alias_method :<<, :write
|
120
128
|
|
121
129
|
def each(&block)
|
122
130
|
if @str_body
|
@@ -146,9 +154,9 @@ module ActionDispatch # :nodoc:
|
|
146
154
|
end
|
147
155
|
end
|
148
156
|
|
149
|
-
def self.create(status = 200,
|
150
|
-
|
151
|
-
new status,
|
157
|
+
def self.create(status = 200, headers = {}, body = [], default_headers: self.default_headers)
|
158
|
+
headers = merge_default_headers(headers, default_headers)
|
159
|
+
new status, headers, body
|
152
160
|
end
|
153
161
|
|
154
162
|
def self.merge_default_headers(original, default)
|
@@ -158,10 +166,14 @@ module ActionDispatch # :nodoc:
|
|
158
166
|
# The underlying body, as a streamable object.
|
159
167
|
attr_reader :stream
|
160
168
|
|
161
|
-
def initialize(status = 200,
|
169
|
+
def initialize(status = 200, headers = nil, body = [])
|
162
170
|
super()
|
163
171
|
|
164
|
-
@
|
172
|
+
@headers = Headers.new
|
173
|
+
|
174
|
+
headers&.each do |key, value|
|
175
|
+
@headers[key] = value
|
176
|
+
end
|
165
177
|
|
166
178
|
self.body, self.status = body, status
|
167
179
|
|
@@ -175,10 +187,10 @@ module ActionDispatch # :nodoc:
|
|
175
187
|
yield self if block_given?
|
176
188
|
end
|
177
189
|
|
178
|
-
def has_header?(key); headers.key? key; end
|
179
|
-
def get_header(key); headers[key]; end
|
180
|
-
def set_header(key, v); headers[key] = v; end
|
181
|
-
def delete_header(key); headers.delete key; end
|
190
|
+
def has_header?(key); @headers.key? key; end
|
191
|
+
def get_header(key); @headers[key]; end
|
192
|
+
def set_header(key, v); @headers[key] = v; end
|
193
|
+
def delete_header(key); @headers.delete key; end
|
182
194
|
|
183
195
|
def await_commit
|
184
196
|
synchronize do
|
@@ -281,7 +293,7 @@ module ActionDispatch # :nodoc:
|
|
281
293
|
@status
|
282
294
|
end
|
283
295
|
|
284
|
-
# Returns a string to ensure compatibility with
|
296
|
+
# Returns a string to ensure compatibility with +Net::HTTPResponse+.
|
285
297
|
def code
|
286
298
|
@status.to_s
|
287
299
|
end
|
@@ -384,7 +396,7 @@ module ActionDispatch # :nodoc:
|
|
384
396
|
# status, headers, body = *response
|
385
397
|
def to_a
|
386
398
|
commit!
|
387
|
-
rack_response @status, @
|
399
|
+
rack_response @status, @headers.to_hash
|
388
400
|
end
|
389
401
|
alias prepare! to_a
|
390
402
|
|
@@ -451,8 +463,7 @@ module ActionDispatch # :nodoc:
|
|
451
463
|
# our last chance.
|
452
464
|
commit! unless committed?
|
453
465
|
|
454
|
-
|
455
|
-
request.commit_cookie_jar! unless committed?
|
466
|
+
@request.commit_cookie_jar! unless committed?
|
456
467
|
end
|
457
468
|
|
458
469
|
def build_buffer(response, body)
|
@@ -476,10 +487,6 @@ module ActionDispatch # :nodoc:
|
|
476
487
|
@response = response
|
477
488
|
end
|
478
489
|
|
479
|
-
def each(*args, &block)
|
480
|
-
@response.each(*args, &block)
|
481
|
-
end
|
482
|
-
|
483
490
|
def close
|
484
491
|
# Rack "close" maps to Response#abort, and *not* Response#close
|
485
492
|
# (which is used when the controller's finished writing)
|
@@ -490,14 +497,28 @@ module ActionDispatch # :nodoc:
|
|
490
497
|
@response.body
|
491
498
|
end
|
492
499
|
|
500
|
+
BODY_METHODS = { to_ary: true, each: true, call: true, to_path: true }
|
501
|
+
|
493
502
|
def respond_to?(method, include_private = false)
|
494
|
-
if method
|
503
|
+
if BODY_METHODS.key?(method)
|
495
504
|
@response.stream.respond_to?(method)
|
496
505
|
else
|
497
506
|
super
|
498
507
|
end
|
499
508
|
end
|
500
509
|
|
510
|
+
def to_ary
|
511
|
+
@response.stream.to_ary
|
512
|
+
end
|
513
|
+
|
514
|
+
def each(*args, &block)
|
515
|
+
@response.each(*args, &block)
|
516
|
+
end
|
517
|
+
|
518
|
+
def call(*arguments, &block)
|
519
|
+
@response.stream.call(*arguments, &block)
|
520
|
+
end
|
521
|
+
|
501
522
|
def to_path
|
502
523
|
@response.stream.to_path
|
503
524
|
end
|
@@ -505,16 +526,16 @@ module ActionDispatch # :nodoc:
|
|
505
526
|
|
506
527
|
def handle_no_content!
|
507
528
|
if NO_CONTENT_CODES.include?(@status)
|
508
|
-
@
|
509
|
-
@
|
529
|
+
@headers.delete CONTENT_TYPE
|
530
|
+
@headers.delete "Content-Length"
|
510
531
|
end
|
511
532
|
end
|
512
533
|
|
513
|
-
def rack_response(status,
|
534
|
+
def rack_response(status, headers)
|
514
535
|
if NO_CONTENT_CODES.include?(status)
|
515
|
-
[status,
|
536
|
+
[status, headers, []]
|
516
537
|
else
|
517
|
-
[status,
|
538
|
+
[status, headers, RackBody.new(self)]
|
518
539
|
end
|
519
540
|
end
|
520
541
|
end
|