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.

Files changed (187) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +291 -489
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +9 -9
  5. data/lib/abstract_controller/asset_paths.rb +2 -0
  6. data/lib/abstract_controller/base.rb +81 -51
  7. data/lib/{action_controller → abstract_controller}/caching/fragments.rb +64 -17
  8. data/lib/abstract_controller/caching.rb +66 -0
  9. data/lib/abstract_controller/callbacks.rb +61 -33
  10. data/lib/abstract_controller/collector.rb +9 -13
  11. data/lib/abstract_controller/error.rb +6 -0
  12. data/lib/abstract_controller/helpers.rb +115 -99
  13. data/lib/abstract_controller/logger.rb +2 -0
  14. data/lib/abstract_controller/railties/routes_helpers.rb +21 -3
  15. data/lib/abstract_controller/rendering.rb +48 -47
  16. data/lib/abstract_controller/translation.rb +17 -8
  17. data/lib/abstract_controller/url_for.rb +2 -0
  18. data/lib/abstract_controller.rb +13 -5
  19. data/lib/action_controller/api/api_rendering.rb +16 -0
  20. data/lib/action_controller/api.rb +150 -0
  21. data/lib/action_controller/base.rb +29 -24
  22. data/lib/action_controller/caching.rb +12 -57
  23. data/lib/action_controller/form_builder.rb +50 -0
  24. data/lib/action_controller/log_subscriber.rb +17 -19
  25. data/lib/action_controller/metal/basic_implicit_render.rb +13 -0
  26. data/lib/action_controller/metal/conditional_get.rb +134 -46
  27. data/lib/action_controller/metal/content_security_policy.rb +51 -0
  28. data/lib/action_controller/metal/cookies.rb +6 -4
  29. data/lib/action_controller/metal/data_streaming.rb +30 -50
  30. data/lib/action_controller/metal/default_headers.rb +17 -0
  31. data/lib/action_controller/metal/etag_with_flash.rb +18 -0
  32. data/lib/action_controller/metal/etag_with_template_digest.rb +21 -16
  33. data/lib/action_controller/metal/exceptions.rb +63 -15
  34. data/lib/action_controller/metal/flash.rb +9 -8
  35. data/lib/action_controller/metal/head.rb +26 -21
  36. data/lib/action_controller/metal/helpers.rb +37 -18
  37. data/lib/action_controller/metal/http_authentication.rb +81 -73
  38. data/lib/action_controller/metal/implicit_render.rb +53 -9
  39. data/lib/action_controller/metal/instrumentation.rb +32 -35
  40. data/lib/action_controller/metal/live.rb +102 -120
  41. data/lib/action_controller/metal/logging.rb +20 -0
  42. data/lib/action_controller/metal/mime_responds.rb +49 -47
  43. data/lib/action_controller/metal/parameter_encoding.rb +82 -0
  44. data/lib/action_controller/metal/params_wrapper.rb +83 -66
  45. data/lib/action_controller/metal/permissions_policy.rb +46 -0
  46. data/lib/action_controller/metal/redirecting.rb +53 -32
  47. data/lib/action_controller/metal/renderers.rb +87 -44
  48. data/lib/action_controller/metal/rendering.rb +77 -50
  49. data/lib/action_controller/metal/request_forgery_protection.rb +267 -103
  50. data/lib/action_controller/metal/rescue.rb +10 -17
  51. data/lib/action_controller/metal/streaming.rb +12 -11
  52. data/lib/action_controller/metal/strong_parameters.rb +714 -186
  53. data/lib/action_controller/metal/testing.rb +2 -17
  54. data/lib/action_controller/metal/url_for.rb +19 -10
  55. data/lib/action_controller/metal.rb +104 -87
  56. data/lib/action_controller/railtie.rb +28 -10
  57. data/lib/action_controller/railties/helpers.rb +3 -1
  58. data/lib/action_controller/renderer.rb +141 -0
  59. data/lib/action_controller/template_assertions.rb +11 -0
  60. data/lib/action_controller/test_case.rb +296 -422
  61. data/lib/action_controller.rb +34 -23
  62. data/lib/action_dispatch/http/cache.rb +107 -56
  63. data/lib/action_dispatch/http/content_disposition.rb +45 -0
  64. data/lib/action_dispatch/http/content_security_policy.rb +286 -0
  65. data/lib/action_dispatch/http/filter_parameters.rb +32 -25
  66. data/lib/action_dispatch/http/filter_redirect.rb +10 -12
  67. data/lib/action_dispatch/http/headers.rb +55 -22
  68. data/lib/action_dispatch/http/mime_negotiation.rb +79 -51
  69. data/lib/action_dispatch/http/mime_type.rb +153 -121
  70. data/lib/action_dispatch/http/mime_types.rb +20 -6
  71. data/lib/action_dispatch/http/parameters.rb +90 -40
  72. data/lib/action_dispatch/http/permissions_policy.rb +173 -0
  73. data/lib/action_dispatch/http/rack_cache.rb +2 -0
  74. data/lib/action_dispatch/http/request.rb +226 -121
  75. data/lib/action_dispatch/http/response.rb +248 -113
  76. data/lib/action_dispatch/http/upload.rb +21 -7
  77. data/lib/action_dispatch/http/url.rb +182 -100
  78. data/lib/action_dispatch/journey/formatter.rb +90 -43
  79. data/lib/action_dispatch/journey/gtg/builder.rb +28 -41
  80. data/lib/action_dispatch/journey/gtg/simulator.rb +11 -16
  81. data/lib/action_dispatch/journey/gtg/transition_table.rb +23 -21
  82. data/lib/action_dispatch/journey/nfa/dot.rb +3 -14
  83. data/lib/action_dispatch/journey/nodes/node.rb +29 -15
  84. data/lib/action_dispatch/journey/parser.rb +17 -16
  85. data/lib/action_dispatch/journey/parser.y +4 -3
  86. data/lib/action_dispatch/journey/parser_extras.rb +12 -4
  87. data/lib/action_dispatch/journey/path/pattern.rb +58 -54
  88. data/lib/action_dispatch/journey/route.rb +100 -32
  89. data/lib/action_dispatch/journey/router/utils.rb +29 -18
  90. data/lib/action_dispatch/journey/router.rb +55 -51
  91. data/lib/action_dispatch/journey/routes.rb +17 -17
  92. data/lib/action_dispatch/journey/scanner.rb +26 -17
  93. data/lib/action_dispatch/journey/visitors.rb +98 -54
  94. data/lib/action_dispatch/journey.rb +5 -5
  95. data/lib/action_dispatch/middleware/actionable_exceptions.rb +46 -0
  96. data/lib/action_dispatch/middleware/callbacks.rb +3 -6
  97. data/lib/action_dispatch/middleware/cookies.rb +347 -217
  98. data/lib/action_dispatch/middleware/debug_exceptions.rb +135 -63
  99. data/lib/action_dispatch/middleware/debug_locks.rb +124 -0
  100. data/lib/action_dispatch/middleware/debug_view.rb +66 -0
  101. data/lib/action_dispatch/middleware/exception_wrapper.rb +115 -71
  102. data/lib/action_dispatch/middleware/executor.rb +21 -0
  103. data/lib/action_dispatch/middleware/flash.rb +78 -54
  104. data/lib/action_dispatch/middleware/host_authorization.rb +130 -0
  105. data/lib/action_dispatch/middleware/public_exceptions.rb +32 -27
  106. data/lib/action_dispatch/middleware/reloader.rb +5 -91
  107. data/lib/action_dispatch/middleware/remote_ip.rb +53 -45
  108. data/lib/action_dispatch/middleware/request_id.rb +17 -10
  109. data/lib/action_dispatch/middleware/session/abstract_store.rb +41 -26
  110. data/lib/action_dispatch/middleware/session/cache_store.rb +24 -14
  111. data/lib/action_dispatch/middleware/session/cookie_store.rb +74 -75
  112. data/lib/action_dispatch/middleware/session/mem_cache_store.rb +8 -2
  113. data/lib/action_dispatch/middleware/show_exceptions.rb +28 -23
  114. data/lib/action_dispatch/middleware/ssl.rb +118 -35
  115. data/lib/action_dispatch/middleware/stack.rb +82 -41
  116. data/lib/action_dispatch/middleware/static.rb +156 -89
  117. data/lib/action_dispatch/middleware/templates/rescues/_actions.html.erb +13 -0
  118. data/lib/action_dispatch/middleware/templates/rescues/_actions.text.erb +0 -0
  119. data/lib/action_dispatch/middleware/templates/rescues/_message_and_suggestions.html.erb +22 -0
  120. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +4 -14
  121. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.text.erb +1 -1
  122. data/lib/action_dispatch/middleware/templates/rescues/{_source.erb → _source.html.erb} +4 -2
  123. data/lib/action_dispatch/middleware/templates/rescues/_source.text.erb +8 -0
  124. data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +45 -35
  125. data/lib/action_dispatch/middleware/templates/rescues/blocked_host.html.erb +7 -0
  126. data/lib/action_dispatch/middleware/templates/rescues/blocked_host.text.erb +5 -0
  127. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +23 -4
  128. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +1 -1
  129. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +24 -0
  130. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +15 -0
  131. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +105 -8
  132. data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +19 -0
  133. data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.text.erb +3 -0
  134. data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +2 -2
  135. data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +1 -1
  136. data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +3 -3
  137. data/lib/action_dispatch/middleware/templates/rescues/template_error.text.erb +1 -1
  138. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +1 -1
  139. data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +4 -4
  140. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +87 -64
  141. data/lib/action_dispatch/railtie.rb +27 -13
  142. data/lib/action_dispatch/request/session.rb +109 -61
  143. data/lib/action_dispatch/request/utils.rb +90 -23
  144. data/lib/action_dispatch/routing/endpoint.rb +9 -2
  145. data/lib/action_dispatch/routing/inspector.rb +141 -102
  146. data/lib/action_dispatch/routing/mapper.rb +811 -473
  147. data/lib/action_dispatch/routing/polymorphic_routes.rb +167 -143
  148. data/lib/action_dispatch/routing/redirection.rb +37 -27
  149. data/lib/action_dispatch/routing/route_set.rb +363 -331
  150. data/lib/action_dispatch/routing/routes_proxy.rb +32 -5
  151. data/lib/action_dispatch/routing/url_for.rb +66 -26
  152. data/lib/action_dispatch/routing.rb +36 -36
  153. data/lib/action_dispatch/system_test_case.rb +190 -0
  154. data/lib/action_dispatch/system_testing/browser.rb +86 -0
  155. data/lib/action_dispatch/system_testing/driver.rb +67 -0
  156. data/lib/action_dispatch/system_testing/server.rb +31 -0
  157. data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +138 -0
  158. data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +29 -0
  159. data/lib/action_dispatch/testing/assertion_response.rb +46 -0
  160. data/lib/action_dispatch/testing/assertions/response.rb +44 -22
  161. data/lib/action_dispatch/testing/assertions/routing.rb +47 -31
  162. data/lib/action_dispatch/testing/assertions.rb +6 -4
  163. data/lib/action_dispatch/testing/integration.rb +391 -220
  164. data/lib/action_dispatch/testing/request_encoder.rb +55 -0
  165. data/lib/action_dispatch/testing/test_process.rb +53 -22
  166. data/lib/action_dispatch/testing/test_request.rb +27 -34
  167. data/lib/action_dispatch/testing/test_response.rb +11 -11
  168. data/lib/action_dispatch.rb +35 -21
  169. data/lib/action_pack/gem_version.rb +6 -4
  170. data/lib/action_pack/version.rb +3 -1
  171. data/lib/action_pack.rb +4 -2
  172. metadata +78 -48
  173. data/lib/action_controller/metal/force_ssl.rb +0 -97
  174. data/lib/action_controller/metal/hide_actions.rb +0 -40
  175. data/lib/action_controller/metal/rack_delegation.rb +0 -32
  176. data/lib/action_controller/middleware.rb +0 -39
  177. data/lib/action_controller/model_naming.rb +0 -12
  178. data/lib/action_dispatch/http/parameter_filter.rb +0 -72
  179. data/lib/action_dispatch/journey/backwards.rb +0 -5
  180. data/lib/action_dispatch/journey/nfa/builder.rb +0 -76
  181. data/lib/action_dispatch/journey/nfa/simulator.rb +0 -47
  182. data/lib/action_dispatch/journey/nfa/transition_table.rb +0 -163
  183. data/lib/action_dispatch/journey/router/strexp.rb +0 -27
  184. data/lib/action_dispatch/middleware/params_parser.rb +0 -60
  185. data/lib/action_dispatch/testing/assertions/dom.rb +0 -3
  186. data/lib/action_dispatch/testing/assertions/selector.rb +0 -3
  187. data/lib/action_dispatch/testing/assertions/tag.rb +0 -3
@@ -1,8 +1,9 @@
1
- require 'active_support/core_ext/module/attribute_accessors'
2
- require 'active_support/core_ext/string/filters'
3
- require 'active_support/deprecation'
4
- require 'action_dispatch/http/filter_redirect'
5
- require 'monitor'
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
- attr_writer :sending_file
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 :[], :[]=, :to => :@header
52
- delegate :each, :to => :@stream
72
+ delegate :[], :[]=, to: :@header
53
73
 
54
- # Sets the HTTP response's content MIME type. For example, in the controller
55
- # you could write this:
56
- #
57
- # response.content_type = "text/plain"
58
- #
59
- # If a character set has been defined for this response (see charset=) then
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
- # The charset of the response. HTML wants to know the encoding of the
65
- # content you're giving them, so we need to send that along.
66
- attr_accessor :charset
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
- CONTENT_TYPE = "Content-Type".freeze
69
- SET_COOKIE = "Set-Cookie".freeze
70
- LOCATION = "Location".freeze
71
- NO_CONTENT_CODES = [204, 304]
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
- cattr_accessor(:default_charset) { "utf-8" }
74
- cattr_accessor(:default_headers)
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
- @response.sending!
97
- x = @buf.each(&block)
98
- @response.sent!
99
- x
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 = [], options = {})
174
+ def initialize(status = 200, header = {}, body = [])
119
175
  super()
120
176
 
121
- default_headers = options.fetch(:default_headers, self.class.default_headers)
122
- header = merge_default_headers(header, default_headers)
177
+ @header = Header.new(self, header)
123
178
 
124
- self.body, self.header, self.status = body, header, status
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
- @content_type = content_type.to_s
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
- strings = []
220
- each { |part| strings << part.to_s }
221
- strings.join
318
+ @stream.body
222
319
  end
223
320
 
224
- EMPTY = " "
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
- def body_parts
240
- parts = []
241
- @stream.each { |x| parts << x }
242
- parts
243
- end
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
- def set_cookie(key, value)
246
- ::Rack::Utils.set_cookie_header!(header, key, value)
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
- def delete_cookie(key, value={})
250
- ::Rack::Utils.delete_cookie_header!(header, key, value)
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
- # The location header we'll be responding with.
254
- def location
255
- headers[LOCATION]
366
+ def reset_body!
367
+ @stream = build_buffer(self, [])
256
368
  end
257
- alias_method :redirect_url, :location
258
369
 
259
- # Sets the location header we'll be responding with.
260
- def location=(url)
261
- headers[LOCATION] = url
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 explict splatting:
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 = self[SET_COOKIE]
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(';').first
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
- end
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
- def merge_default_headers(original, default)
328
- default.respond_to?(:merge) ? default.merge(original) : original
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!(headers)
340
- return if headers[CONTENT_TYPE].present?
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
- headers[CONTENT_TYPE] = type
349
- end
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.to_s == 'to_path'
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 rack_response(status, header)
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 = hash[:tempfile]
28
- raise(ArgumentError, ':tempfile is required') unless @tempfile
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