actionpack 4.2.11.1 → 6.1.3.2
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 +291 -489
- data/MIT-LICENSE +1 -1
- data/README.rdoc +9 -9
- data/lib/abstract_controller/asset_paths.rb +2 -0
- data/lib/abstract_controller/base.rb +81 -51
- data/lib/{action_controller → abstract_controller}/caching/fragments.rb +64 -17
- data/lib/abstract_controller/caching.rb +66 -0
- data/lib/abstract_controller/callbacks.rb +61 -33
- data/lib/abstract_controller/collector.rb +9 -13
- data/lib/abstract_controller/error.rb +6 -0
- data/lib/abstract_controller/helpers.rb +115 -99
- data/lib/abstract_controller/logger.rb +2 -0
- data/lib/abstract_controller/railties/routes_helpers.rb +21 -3
- data/lib/abstract_controller/rendering.rb +48 -47
- data/lib/abstract_controller/translation.rb +17 -8
- data/lib/abstract_controller/url_for.rb +2 -0
- data/lib/abstract_controller.rb +13 -5
- data/lib/action_controller/api/api_rendering.rb +16 -0
- data/lib/action_controller/api.rb +150 -0
- data/lib/action_controller/base.rb +29 -24
- data/lib/action_controller/caching.rb +12 -57
- data/lib/action_controller/form_builder.rb +50 -0
- data/lib/action_controller/log_subscriber.rb +17 -19
- data/lib/action_controller/metal/basic_implicit_render.rb +13 -0
- data/lib/action_controller/metal/conditional_get.rb +134 -46
- data/lib/action_controller/metal/content_security_policy.rb +51 -0
- data/lib/action_controller/metal/cookies.rb +6 -4
- data/lib/action_controller/metal/data_streaming.rb +30 -50
- data/lib/action_controller/metal/default_headers.rb +17 -0
- data/lib/action_controller/metal/etag_with_flash.rb +18 -0
- data/lib/action_controller/metal/etag_with_template_digest.rb +21 -16
- data/lib/action_controller/metal/exceptions.rb +63 -15
- data/lib/action_controller/metal/flash.rb +9 -8
- data/lib/action_controller/metal/head.rb +26 -21
- data/lib/action_controller/metal/helpers.rb +37 -18
- data/lib/action_controller/metal/http_authentication.rb +81 -73
- data/lib/action_controller/metal/implicit_render.rb +53 -9
- data/lib/action_controller/metal/instrumentation.rb +32 -35
- data/lib/action_controller/metal/live.rb +102 -120
- data/lib/action_controller/metal/logging.rb +20 -0
- data/lib/action_controller/metal/mime_responds.rb +49 -47
- data/lib/action_controller/metal/parameter_encoding.rb +82 -0
- data/lib/action_controller/metal/params_wrapper.rb +83 -66
- data/lib/action_controller/metal/permissions_policy.rb +46 -0
- data/lib/action_controller/metal/redirecting.rb +53 -32
- data/lib/action_controller/metal/renderers.rb +87 -44
- data/lib/action_controller/metal/rendering.rb +77 -50
- data/lib/action_controller/metal/request_forgery_protection.rb +267 -103
- data/lib/action_controller/metal/rescue.rb +10 -17
- data/lib/action_controller/metal/streaming.rb +12 -11
- data/lib/action_controller/metal/strong_parameters.rb +714 -186
- data/lib/action_controller/metal/testing.rb +2 -17
- data/lib/action_controller/metal/url_for.rb +19 -10
- data/lib/action_controller/metal.rb +104 -87
- data/lib/action_controller/railtie.rb +28 -10
- data/lib/action_controller/railties/helpers.rb +3 -1
- data/lib/action_controller/renderer.rb +141 -0
- data/lib/action_controller/template_assertions.rb +11 -0
- data/lib/action_controller/test_case.rb +296 -422
- data/lib/action_controller.rb +34 -23
- data/lib/action_dispatch/http/cache.rb +107 -56
- data/lib/action_dispatch/http/content_disposition.rb +45 -0
- data/lib/action_dispatch/http/content_security_policy.rb +286 -0
- data/lib/action_dispatch/http/filter_parameters.rb +32 -25
- data/lib/action_dispatch/http/filter_redirect.rb +10 -12
- data/lib/action_dispatch/http/headers.rb +55 -22
- data/lib/action_dispatch/http/mime_negotiation.rb +79 -51
- data/lib/action_dispatch/http/mime_type.rb +153 -121
- data/lib/action_dispatch/http/mime_types.rb +20 -6
- data/lib/action_dispatch/http/parameters.rb +90 -40
- data/lib/action_dispatch/http/permissions_policy.rb +173 -0
- data/lib/action_dispatch/http/rack_cache.rb +2 -0
- data/lib/action_dispatch/http/request.rb +226 -121
- data/lib/action_dispatch/http/response.rb +248 -113
- data/lib/action_dispatch/http/upload.rb +21 -7
- data/lib/action_dispatch/http/url.rb +182 -100
- data/lib/action_dispatch/journey/formatter.rb +90 -43
- data/lib/action_dispatch/journey/gtg/builder.rb +28 -41
- data/lib/action_dispatch/journey/gtg/simulator.rb +11 -16
- data/lib/action_dispatch/journey/gtg/transition_table.rb +23 -21
- data/lib/action_dispatch/journey/nfa/dot.rb +3 -14
- data/lib/action_dispatch/journey/nodes/node.rb +29 -15
- data/lib/action_dispatch/journey/parser.rb +17 -16
- data/lib/action_dispatch/journey/parser.y +4 -3
- data/lib/action_dispatch/journey/parser_extras.rb +12 -4
- data/lib/action_dispatch/journey/path/pattern.rb +58 -54
- data/lib/action_dispatch/journey/route.rb +100 -32
- data/lib/action_dispatch/journey/router/utils.rb +29 -18
- data/lib/action_dispatch/journey/router.rb +55 -51
- data/lib/action_dispatch/journey/routes.rb +17 -17
- data/lib/action_dispatch/journey/scanner.rb +26 -17
- data/lib/action_dispatch/journey/visitors.rb +98 -54
- data/lib/action_dispatch/journey.rb +5 -5
- data/lib/action_dispatch/middleware/actionable_exceptions.rb +46 -0
- data/lib/action_dispatch/middleware/callbacks.rb +3 -6
- data/lib/action_dispatch/middleware/cookies.rb +347 -217
- data/lib/action_dispatch/middleware/debug_exceptions.rb +135 -63
- data/lib/action_dispatch/middleware/debug_locks.rb +124 -0
- data/lib/action_dispatch/middleware/debug_view.rb +66 -0
- data/lib/action_dispatch/middleware/exception_wrapper.rb +115 -71
- data/lib/action_dispatch/middleware/executor.rb +21 -0
- data/lib/action_dispatch/middleware/flash.rb +78 -54
- data/lib/action_dispatch/middleware/host_authorization.rb +130 -0
- data/lib/action_dispatch/middleware/public_exceptions.rb +32 -27
- data/lib/action_dispatch/middleware/reloader.rb +5 -91
- data/lib/action_dispatch/middleware/remote_ip.rb +53 -45
- data/lib/action_dispatch/middleware/request_id.rb +17 -10
- data/lib/action_dispatch/middleware/session/abstract_store.rb +41 -26
- data/lib/action_dispatch/middleware/session/cache_store.rb +24 -14
- data/lib/action_dispatch/middleware/session/cookie_store.rb +74 -75
- data/lib/action_dispatch/middleware/session/mem_cache_store.rb +8 -2
- data/lib/action_dispatch/middleware/show_exceptions.rb +28 -23
- data/lib/action_dispatch/middleware/ssl.rb +118 -35
- data/lib/action_dispatch/middleware/stack.rb +82 -41
- data/lib/action_dispatch/middleware/static.rb +156 -89
- data/lib/action_dispatch/middleware/templates/rescues/_actions.html.erb +13 -0
- data/lib/action_dispatch/middleware/templates/rescues/_actions.text.erb +0 -0
- data/lib/action_dispatch/middleware/templates/rescues/_message_and_suggestions.html.erb +22 -0
- data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +4 -14
- data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.text.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/{_source.erb → _source.html.erb} +4 -2
- data/lib/action_dispatch/middleware/templates/rescues/_source.text.erb +8 -0
- data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +45 -35
- data/lib/action_dispatch/middleware/templates/rescues/blocked_host.html.erb +7 -0
- data/lib/action_dispatch/middleware/templates/rescues/blocked_host.text.erb +5 -0
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +23 -4
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +24 -0
- data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +15 -0
- data/lib/action_dispatch/middleware/templates/rescues/layout.erb +105 -8
- data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +19 -0
- data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.text.erb +3 -0
- data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +2 -2
- data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +3 -3
- data/lib/action_dispatch/middleware/templates/rescues/template_error.text.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +1 -1
- data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +4 -4
- data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +87 -64
- data/lib/action_dispatch/railtie.rb +27 -13
- data/lib/action_dispatch/request/session.rb +109 -61
- data/lib/action_dispatch/request/utils.rb +90 -23
- data/lib/action_dispatch/routing/endpoint.rb +9 -2
- data/lib/action_dispatch/routing/inspector.rb +141 -102
- data/lib/action_dispatch/routing/mapper.rb +811 -473
- data/lib/action_dispatch/routing/polymorphic_routes.rb +167 -143
- data/lib/action_dispatch/routing/redirection.rb +37 -27
- data/lib/action_dispatch/routing/route_set.rb +363 -331
- data/lib/action_dispatch/routing/routes_proxy.rb +32 -5
- data/lib/action_dispatch/routing/url_for.rb +66 -26
- data/lib/action_dispatch/routing.rb +36 -36
- data/lib/action_dispatch/system_test_case.rb +190 -0
- data/lib/action_dispatch/system_testing/browser.rb +86 -0
- data/lib/action_dispatch/system_testing/driver.rb +67 -0
- data/lib/action_dispatch/system_testing/server.rb +31 -0
- data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +138 -0
- data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +29 -0
- data/lib/action_dispatch/testing/assertion_response.rb +46 -0
- data/lib/action_dispatch/testing/assertions/response.rb +44 -22
- data/lib/action_dispatch/testing/assertions/routing.rb +47 -31
- data/lib/action_dispatch/testing/assertions.rb +6 -4
- data/lib/action_dispatch/testing/integration.rb +391 -220
- data/lib/action_dispatch/testing/request_encoder.rb +55 -0
- data/lib/action_dispatch/testing/test_process.rb +53 -22
- data/lib/action_dispatch/testing/test_request.rb +27 -34
- data/lib/action_dispatch/testing/test_response.rb +11 -11
- data/lib/action_dispatch.rb +35 -21
- data/lib/action_pack/gem_version.rb +6 -4
- data/lib/action_pack/version.rb +3 -1
- data/lib/action_pack.rb +4 -2
- metadata +78 -48
- data/lib/action_controller/metal/force_ssl.rb +0 -97
- data/lib/action_controller/metal/hide_actions.rb +0 -40
- data/lib/action_controller/metal/rack_delegation.rb +0 -32
- data/lib/action_controller/middleware.rb +0 -39
- data/lib/action_controller/model_naming.rb +0 -12
- data/lib/action_dispatch/http/parameter_filter.rb +0 -72
- data/lib/action_dispatch/journey/backwards.rb +0 -5
- data/lib/action_dispatch/journey/nfa/builder.rb +0 -76
- data/lib/action_dispatch/journey/nfa/simulator.rb +0 -47
- data/lib/action_dispatch/journey/nfa/transition_table.rb +0 -163
- data/lib/action_dispatch/journey/router/strexp.rb +0 -27
- data/lib/action_dispatch/middleware/params_parser.rb +0 -60
- data/lib/action_dispatch/testing/assertions/dom.rb +0 -3
- data/lib/action_dispatch/testing/assertions/selector.rb +0 -3
- data/lib/action_dispatch/testing/assertions/tag.rb +0 -3
@@ -1,8 +1,9 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_support/core_ext/module/attribute_accessors"
|
4
|
+
require "action_dispatch/http/filter_redirect"
|
5
|
+
require "action_dispatch/http/cache"
|
6
|
+
require "monitor"
|
6
7
|
|
7
8
|
module ActionDispatch # :nodoc:
|
8
9
|
# Represents an HTTP response generated by a controller action. Use it to
|
@@ -34,46 +35,74 @@ module ActionDispatch # :nodoc:
|
|
34
35
|
# end
|
35
36
|
# end
|
36
37
|
class Response
|
38
|
+
class Header < DelegateClass(Hash) # :nodoc:
|
39
|
+
def initialize(response, header)
|
40
|
+
@response = response
|
41
|
+
super(header)
|
42
|
+
end
|
43
|
+
|
44
|
+
def []=(k, v)
|
45
|
+
if @response.sending? || @response.sent?
|
46
|
+
raise ActionDispatch::IllegalStateError, "header already sent"
|
47
|
+
end
|
48
|
+
|
49
|
+
super
|
50
|
+
end
|
51
|
+
|
52
|
+
def merge(other)
|
53
|
+
self.class.new @response, __getobj__.merge(other)
|
54
|
+
end
|
55
|
+
|
56
|
+
def to_hash
|
57
|
+
__getobj__.dup
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
37
61
|
# The request that the response is responding to.
|
38
62
|
attr_accessor :request
|
39
63
|
|
40
64
|
# The HTTP status code.
|
41
65
|
attr_reader :status
|
42
66
|
|
43
|
-
|
44
|
-
|
45
|
-
# Get and set headers for this response.
|
46
|
-
attr_accessor :header
|
67
|
+
# Get headers for this response.
|
68
|
+
attr_reader :header
|
47
69
|
|
48
|
-
alias_method :headers=, :header=
|
49
70
|
alias_method :headers, :header
|
50
71
|
|
51
|
-
delegate :[], :[]=, :
|
52
|
-
delegate :each, :to => :@stream
|
72
|
+
delegate :[], :[]=, to: :@header
|
53
73
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
# the character set information will also be included in the content type
|
61
|
-
# information.
|
62
|
-
attr_reader :content_type
|
74
|
+
def each(&block)
|
75
|
+
sending!
|
76
|
+
x = @stream.each(&block)
|
77
|
+
sent!
|
78
|
+
x
|
79
|
+
end
|
63
80
|
|
64
|
-
|
65
|
-
|
66
|
-
|
81
|
+
CONTENT_TYPE = "Content-Type"
|
82
|
+
SET_COOKIE = "Set-Cookie"
|
83
|
+
LOCATION = "Location"
|
84
|
+
NO_CONTENT_CODES = [100, 101, 102, 103, 204, 205, 304]
|
85
|
+
|
86
|
+
cattr_accessor :default_charset, default: "utf-8"
|
87
|
+
cattr_accessor :default_headers
|
67
88
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
89
|
+
def self.return_only_media_type_on_content_type=(*)
|
90
|
+
ActiveSupport::Deprecation.warn(
|
91
|
+
".return_only_media_type_on_content_type= is dreprecated with no replacement and will be removed in 6.2."
|
92
|
+
)
|
93
|
+
end
|
72
94
|
|
73
|
-
|
74
|
-
|
95
|
+
def self.return_only_media_type_on_content_type
|
96
|
+
ActiveSupport::Deprecation.warn(
|
97
|
+
".return_only_media_type_on_content_type is dreprecated with no replacement and will be removed in 6.2."
|
98
|
+
)
|
99
|
+
end
|
75
100
|
|
76
101
|
include Rack::Response::Helpers
|
102
|
+
# Aliasing these off because AD::Http::Cache::Response defines them.
|
103
|
+
alias :_cache_control :cache_control
|
104
|
+
alias :_cache_control= :cache_control=
|
105
|
+
|
77
106
|
include ActionDispatch::Http::FilterRedirect
|
78
107
|
include ActionDispatch::Http::Cache::Response
|
79
108
|
include MonitorMixin
|
@@ -83,20 +112,33 @@ module ActionDispatch # :nodoc:
|
|
83
112
|
@response = response
|
84
113
|
@buf = buf
|
85
114
|
@closed = false
|
115
|
+
@str_body = nil
|
116
|
+
end
|
117
|
+
|
118
|
+
def body
|
119
|
+
@str_body ||= begin
|
120
|
+
buf = +""
|
121
|
+
each { |chunk| buf << chunk }
|
122
|
+
buf
|
123
|
+
end
|
86
124
|
end
|
87
125
|
|
88
126
|
def write(string)
|
89
127
|
raise IOError, "closed stream" if closed?
|
90
128
|
|
129
|
+
@str_body = nil
|
91
130
|
@response.commit!
|
92
131
|
@buf.push string
|
93
132
|
end
|
94
133
|
|
95
134
|
def each(&block)
|
96
|
-
@
|
97
|
-
|
98
|
-
|
99
|
-
|
135
|
+
if @str_body
|
136
|
+
return enum_for(:each) unless block_given?
|
137
|
+
|
138
|
+
yield @str_body
|
139
|
+
else
|
140
|
+
each_chunk(&block)
|
141
|
+
end
|
100
142
|
end
|
101
143
|
|
102
144
|
def abort
|
@@ -110,39 +152,47 @@ module ActionDispatch # :nodoc:
|
|
110
152
|
def closed?
|
111
153
|
@closed
|
112
154
|
end
|
155
|
+
|
156
|
+
private
|
157
|
+
def each_chunk(&block)
|
158
|
+
@buf.each(&block)
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
def self.create(status = 200, header = {}, body = [], default_headers: self.default_headers)
|
163
|
+
header = merge_default_headers(header, default_headers)
|
164
|
+
new status, header, body
|
165
|
+
end
|
166
|
+
|
167
|
+
def self.merge_default_headers(original, default)
|
168
|
+
default.respond_to?(:merge) ? default.merge(original) : original
|
113
169
|
end
|
114
170
|
|
115
171
|
# The underlying body, as a streamable object.
|
116
172
|
attr_reader :stream
|
117
173
|
|
118
|
-
def initialize(status = 200, header = {}, body = []
|
174
|
+
def initialize(status = 200, header = {}, body = [])
|
119
175
|
super()
|
120
176
|
|
121
|
-
|
122
|
-
header = merge_default_headers(header, default_headers)
|
177
|
+
@header = Header.new(self, header)
|
123
178
|
|
124
|
-
self.body, self.
|
179
|
+
self.body, self.status = body, status
|
125
180
|
|
126
|
-
@sending_file = false
|
127
|
-
@blank = false
|
128
181
|
@cv = new_cond
|
129
182
|
@committed = false
|
130
183
|
@sending = false
|
131
184
|
@sent = false
|
132
|
-
@content_type = nil
|
133
|
-
@charset = nil
|
134
|
-
|
135
|
-
if content_type = self[CONTENT_TYPE]
|
136
|
-
type, charset = content_type.split(/;\s*charset=/)
|
137
|
-
@content_type = Mime::Type.lookup(type)
|
138
|
-
@charset = charset || self.class.default_charset
|
139
|
-
end
|
140
185
|
|
141
186
|
prepare_cache_control!
|
142
187
|
|
143
188
|
yield self if block_given?
|
144
189
|
end
|
145
190
|
|
191
|
+
def has_header?(key); headers.key? key; end
|
192
|
+
def get_header(key); headers[key]; end
|
193
|
+
def set_header(key, v); headers[key] = v; end
|
194
|
+
def delete_header(key); headers.delete key; end
|
195
|
+
|
146
196
|
def await_commit
|
147
197
|
synchronize do
|
148
198
|
@cv.wait_until { @committed }
|
@@ -185,9 +235,58 @@ module ActionDispatch # :nodoc:
|
|
185
235
|
@status = Rack::Utils.status_code(status)
|
186
236
|
end
|
187
237
|
|
188
|
-
# Sets the HTTP content type.
|
238
|
+
# Sets the HTTP response's content MIME type. For example, in the controller
|
239
|
+
# you could write this:
|
240
|
+
#
|
241
|
+
# response.content_type = "text/plain"
|
242
|
+
#
|
243
|
+
# If a character set has been defined for this response (see charset=) then
|
244
|
+
# the character set information will also be included in the content type
|
245
|
+
# information.
|
189
246
|
def content_type=(content_type)
|
190
|
-
|
247
|
+
return unless content_type
|
248
|
+
new_header_info = parse_content_type(content_type.to_s)
|
249
|
+
prev_header_info = parsed_content_type_header
|
250
|
+
charset = new_header_info.charset || prev_header_info.charset
|
251
|
+
charset ||= self.class.default_charset unless prev_header_info.mime_type
|
252
|
+
set_content_type new_header_info.mime_type, charset
|
253
|
+
end
|
254
|
+
|
255
|
+
# Content type of response.
|
256
|
+
def content_type
|
257
|
+
super.presence
|
258
|
+
end
|
259
|
+
|
260
|
+
# Media type of response.
|
261
|
+
def media_type
|
262
|
+
parsed_content_type_header.mime_type
|
263
|
+
end
|
264
|
+
|
265
|
+
def sending_file=(v)
|
266
|
+
if true == v
|
267
|
+
self.charset = false
|
268
|
+
end
|
269
|
+
end
|
270
|
+
|
271
|
+
# Sets the HTTP character set. In case of +nil+ parameter
|
272
|
+
# it sets the charset to +default_charset+.
|
273
|
+
#
|
274
|
+
# response.charset = 'utf-16' # => 'utf-16'
|
275
|
+
# response.charset = nil # => 'utf-8'
|
276
|
+
def charset=(charset)
|
277
|
+
content_type = parsed_content_type_header.mime_type
|
278
|
+
if false == charset
|
279
|
+
set_content_type content_type, nil
|
280
|
+
else
|
281
|
+
set_content_type content_type, charset || self.class.default_charset
|
282
|
+
end
|
283
|
+
end
|
284
|
+
|
285
|
+
# The charset of the response. HTML wants to know the encoding of the
|
286
|
+
# content you're giving them, so we need to send that along.
|
287
|
+
def charset
|
288
|
+
header_info = parsed_content_type_header
|
289
|
+
header_info.charset || self.class.default_charset
|
191
290
|
end
|
192
291
|
|
193
292
|
# The response code of the request.
|
@@ -216,17 +315,15 @@ module ActionDispatch # :nodoc:
|
|
216
315
|
# Returns the content of the response as a string. This contains the contents
|
217
316
|
# of any calls to <tt>render</tt>.
|
218
317
|
def body
|
219
|
-
|
220
|
-
each { |part| strings << part.to_s }
|
221
|
-
strings.join
|
318
|
+
@stream.body
|
222
319
|
end
|
223
320
|
|
224
|
-
|
321
|
+
def write(string)
|
322
|
+
@stream.write string
|
323
|
+
end
|
225
324
|
|
226
325
|
# Allows you to manually set or override the response body.
|
227
326
|
def body=(body)
|
228
|
-
@blank = true if body == EMPTY
|
229
|
-
|
230
327
|
if body.respond_to?(:to_path)
|
231
328
|
@stream = body
|
232
329
|
else
|
@@ -236,31 +333,49 @@ module ActionDispatch # :nodoc:
|
|
236
333
|
end
|
237
334
|
end
|
238
335
|
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
336
|
+
# Avoid having to pass an open file handle as the response body.
|
337
|
+
# Rack::Sendfile will usually intercept the response and uses
|
338
|
+
# the path directly, so there is no reason to open the file.
|
339
|
+
class FileBody #:nodoc:
|
340
|
+
attr_reader :to_path
|
341
|
+
|
342
|
+
def initialize(path)
|
343
|
+
@to_path = path
|
344
|
+
end
|
244
345
|
|
245
|
-
|
246
|
-
|
346
|
+
def body
|
347
|
+
File.binread(to_path)
|
348
|
+
end
|
349
|
+
|
350
|
+
# Stream the file's contents if Rack::Sendfile isn't present.
|
351
|
+
def each
|
352
|
+
File.open(to_path, "rb") do |file|
|
353
|
+
while chunk = file.read(16384)
|
354
|
+
yield chunk
|
355
|
+
end
|
356
|
+
end
|
357
|
+
end
|
247
358
|
end
|
248
359
|
|
249
|
-
|
250
|
-
|
360
|
+
# Send the file stored at +path+ as the response body.
|
361
|
+
def send_file(path)
|
362
|
+
commit!
|
363
|
+
@stream = FileBody.new(path)
|
251
364
|
end
|
252
365
|
|
253
|
-
|
254
|
-
|
255
|
-
headers[LOCATION]
|
366
|
+
def reset_body!
|
367
|
+
@stream = build_buffer(self, [])
|
256
368
|
end
|
257
|
-
alias_method :redirect_url, :location
|
258
369
|
|
259
|
-
|
260
|
-
|
261
|
-
|
370
|
+
def body_parts
|
371
|
+
parts = []
|
372
|
+
@stream.each { |x| parts << x }
|
373
|
+
parts
|
262
374
|
end
|
263
375
|
|
376
|
+
# The location header we'll be responding with.
|
377
|
+
alias_method :redirect_url, :location
|
378
|
+
|
264
379
|
def close
|
265
380
|
stream.close if stream.respond_to?(:close)
|
266
381
|
end
|
@@ -277,37 +392,24 @@ module ActionDispatch # :nodoc:
|
|
277
392
|
end
|
278
393
|
|
279
394
|
# Turns the Response into a Rack-compatible array of the status, headers,
|
280
|
-
# and body. Allows
|
395
|
+
# and body. Allows explicit splatting:
|
281
396
|
#
|
282
397
|
# status, headers, body = *response
|
283
398
|
def to_a
|
399
|
+
commit!
|
284
400
|
rack_response @status, @header.to_hash
|
285
401
|
end
|
286
402
|
alias prepare! to_a
|
287
403
|
|
288
|
-
# Be super clear that a response object is not an Array. Defining this
|
289
|
-
# would make implicit splatting work, but it also makes adding responses
|
290
|
-
# as arrays work, and "flattening" responses, cascading to the rack body!
|
291
|
-
# Not sensible behavior.
|
292
|
-
def to_ary
|
293
|
-
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
294
|
-
`ActionDispatch::Response#to_ary` no longer performs implicit conversion
|
295
|
-
to an array. Please use `response.to_a` instead, or a splat like `status,
|
296
|
-
headers, body = *response`.
|
297
|
-
MSG
|
298
|
-
|
299
|
-
to_a
|
300
|
-
end
|
301
|
-
|
302
404
|
# Returns the response cookies, converted to a Hash of (name => value) pairs
|
303
405
|
#
|
304
406
|
# assert_equal 'AuthorOfNewPage', r.cookies['author']
|
305
407
|
def cookies
|
306
408
|
cookies = {}
|
307
|
-
if header =
|
409
|
+
if header = get_header(SET_COOKIE)
|
308
410
|
header = header.split("\n") if header.respond_to?(:to_str)
|
309
411
|
header.each do |cookie|
|
310
|
-
if pair = cookie.split(
|
412
|
+
if pair = cookie.split(";").first
|
311
413
|
key, value = pair.split("=").map { |v| Rack::Utils.unescape(v) }
|
312
414
|
cookies[key] = value
|
313
415
|
end
|
@@ -317,15 +419,53 @@ module ActionDispatch # :nodoc:
|
|
317
419
|
end
|
318
420
|
|
319
421
|
private
|
422
|
+
ContentTypeHeader = Struct.new :mime_type, :charset
|
423
|
+
NullContentTypeHeader = ContentTypeHeader.new nil, nil
|
424
|
+
|
425
|
+
CONTENT_TYPE_PARSER = /
|
426
|
+
\A
|
427
|
+
(?<mime_type>[^;\s]+\s*(?:;\s*(?:(?!charset)[^;\s])+)*)?
|
428
|
+
(?:;\s*charset=(?<quote>"?)(?<charset>[^;\s]+)\k<quote>)?
|
429
|
+
/x # :nodoc:
|
430
|
+
|
431
|
+
def parse_content_type(content_type)
|
432
|
+
if content_type && match = CONTENT_TYPE_PARSER.match(content_type)
|
433
|
+
ContentTypeHeader.new(match[:mime_type], match[:charset])
|
434
|
+
else
|
435
|
+
NullContentTypeHeader
|
436
|
+
end
|
437
|
+
end
|
438
|
+
|
439
|
+
# Small internal convenience method to get the parsed version of the current
|
440
|
+
# content type header.
|
441
|
+
def parsed_content_type_header
|
442
|
+
parse_content_type(get_header(CONTENT_TYPE))
|
443
|
+
end
|
444
|
+
|
445
|
+
def set_content_type(content_type, charset)
|
446
|
+
type = content_type || ""
|
447
|
+
type = "#{type}; charset=#{charset.to_s.downcase}" if charset
|
448
|
+
set_header CONTENT_TYPE, type
|
449
|
+
end
|
320
450
|
|
321
451
|
def before_committed
|
452
|
+
return if committed?
|
453
|
+
assign_default_content_type_and_charset!
|
454
|
+
merge_and_normalize_cache_control!(@cache_control)
|
455
|
+
handle_conditional_get!
|
456
|
+
handle_no_content!
|
322
457
|
end
|
323
458
|
|
324
459
|
def before_sending
|
325
|
-
|
460
|
+
# Normally we've already committed by now, but it's possible
|
461
|
+
# (e.g., if the controller action tries to read back its own
|
462
|
+
# response) to get here before that. In that case, we must force
|
463
|
+
# an "early" commit: we're about to freeze the headers, so this is
|
464
|
+
# our last chance.
|
465
|
+
commit! unless committed?
|
326
466
|
|
327
|
-
|
328
|
-
|
467
|
+
headers.freeze
|
468
|
+
request.commit_cookie_jar! unless committed?
|
329
469
|
end
|
330
470
|
|
331
471
|
def build_buffer(response, body)
|
@@ -336,20 +476,12 @@ module ActionDispatch # :nodoc:
|
|
336
476
|
body.respond_to?(:each) ? body : [body]
|
337
477
|
end
|
338
478
|
|
339
|
-
def assign_default_content_type_and_charset!
|
340
|
-
return if
|
341
|
-
|
342
|
-
@content_type ||= Mime::HTML
|
343
|
-
@charset ||= self.class.default_charset unless @charset == false
|
344
|
-
|
345
|
-
type = @content_type.to_s.dup
|
346
|
-
type << "; charset=#{@charset}" if append_charset?
|
479
|
+
def assign_default_content_type_and_charset!
|
480
|
+
return if media_type
|
347
481
|
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
def append_charset?
|
352
|
-
!@sending_file && @charset != false
|
482
|
+
ct = parsed_content_type_header
|
483
|
+
set_content_type(ct.mime_type || Mime[:html].to_s,
|
484
|
+
ct.charset || self.class.default_charset)
|
353
485
|
end
|
354
486
|
|
355
487
|
class RackBody
|
@@ -372,7 +504,7 @@ module ActionDispatch # :nodoc:
|
|
372
504
|
end
|
373
505
|
|
374
506
|
def respond_to?(method, include_private = false)
|
375
|
-
if method.
|
507
|
+
if method.to_sym == :to_path
|
376
508
|
@response.stream.respond_to?(method)
|
377
509
|
else
|
378
510
|
super
|
@@ -388,18 +520,21 @@ module ActionDispatch # :nodoc:
|
|
388
520
|
end
|
389
521
|
end
|
390
522
|
|
391
|
-
def
|
392
|
-
assign_default_content_type_and_charset!(header)
|
393
|
-
handle_conditional_get!
|
394
|
-
|
395
|
-
header[SET_COOKIE] = header[SET_COOKIE].join("\n") if header[SET_COOKIE].respond_to?(:join)
|
396
|
-
|
523
|
+
def handle_no_content!
|
397
524
|
if NO_CONTENT_CODES.include?(@status)
|
398
|
-
header.delete CONTENT_TYPE
|
525
|
+
@header.delete CONTENT_TYPE
|
526
|
+
@header.delete "Content-Length"
|
527
|
+
end
|
528
|
+
end
|
529
|
+
|
530
|
+
def rack_response(status, header)
|
531
|
+
if NO_CONTENT_CODES.include?(status)
|
399
532
|
[status, header, []]
|
400
533
|
else
|
401
534
|
[status, header, RackBody.new(self)]
|
402
535
|
end
|
403
536
|
end
|
404
537
|
end
|
538
|
+
|
539
|
+
ActiveSupport.run_load_hooks(:action_dispatch_response, Response)
|
405
540
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ActionDispatch
|
2
4
|
module Http
|
3
5
|
# Models uploaded files.
|
@@ -18,29 +20,32 @@ module ActionDispatch
|
|
18
20
|
# A +Tempfile+ object with the actual uploaded file. Note that some of
|
19
21
|
# its interface is available directly.
|
20
22
|
attr_accessor :tempfile
|
21
|
-
alias :to_io :tempfile
|
22
23
|
|
23
24
|
# A string with the headers of the multipart request.
|
24
25
|
attr_accessor :headers
|
25
26
|
|
26
27
|
def initialize(hash) # :nodoc:
|
27
|
-
@tempfile
|
28
|
-
raise(ArgumentError,
|
28
|
+
@tempfile = hash[:tempfile]
|
29
|
+
raise(ArgumentError, ":tempfile is required") unless @tempfile
|
30
|
+
|
31
|
+
if hash[:filename]
|
32
|
+
@original_filename = hash[:filename].dup
|
29
33
|
|
30
|
-
@original_filename = hash[:filename]
|
31
|
-
if @original_filename
|
32
34
|
begin
|
33
35
|
@original_filename.encode!(Encoding::UTF_8)
|
34
36
|
rescue EncodingError
|
35
37
|
@original_filename.force_encoding(Encoding::UTF_8)
|
36
38
|
end
|
39
|
+
else
|
40
|
+
@original_filename = nil
|
37
41
|
end
|
42
|
+
|
38
43
|
@content_type = hash[:type]
|
39
44
|
@headers = hash[:head]
|
40
45
|
end
|
41
46
|
|
42
47
|
# Shortcut for +tempfile.read+.
|
43
|
-
def read(length=nil, buffer=nil)
|
48
|
+
def read(length = nil, buffer = nil)
|
44
49
|
@tempfile.read(length, buffer)
|
45
50
|
end
|
46
51
|
|
@@ -50,7 +55,7 @@ module ActionDispatch
|
|
50
55
|
end
|
51
56
|
|
52
57
|
# Shortcut for +tempfile.close+.
|
53
|
-
def close(unlink_now=false)
|
58
|
+
def close(unlink_now = false)
|
54
59
|
@tempfile.close(unlink_now)
|
55
60
|
end
|
56
61
|
|
@@ -59,6 +64,11 @@ module ActionDispatch
|
|
59
64
|
@tempfile.path
|
60
65
|
end
|
61
66
|
|
67
|
+
# Shortcut for +tempfile.to_path+.
|
68
|
+
def to_path
|
69
|
+
@tempfile.to_path
|
70
|
+
end
|
71
|
+
|
62
72
|
# Shortcut for +tempfile.rewind+.
|
63
73
|
def rewind
|
64
74
|
@tempfile.rewind
|
@@ -73,6 +83,10 @@ module ActionDispatch
|
|
73
83
|
def eof?
|
74
84
|
@tempfile.eof?
|
75
85
|
end
|
86
|
+
|
87
|
+
def to_io
|
88
|
+
@tempfile.to_io
|
89
|
+
end
|
76
90
|
end
|
77
91
|
end
|
78
92
|
end
|