omg-actionpack 8.0.0.alpha1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (187) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +129 -0
  3. data/MIT-LICENSE +21 -0
  4. data/README.rdoc +57 -0
  5. data/lib/abstract_controller/asset_paths.rb +14 -0
  6. data/lib/abstract_controller/base.rb +299 -0
  7. data/lib/abstract_controller/caching/fragments.rb +149 -0
  8. data/lib/abstract_controller/caching.rb +68 -0
  9. data/lib/abstract_controller/callbacks.rb +265 -0
  10. data/lib/abstract_controller/collector.rb +44 -0
  11. data/lib/abstract_controller/deprecator.rb +9 -0
  12. data/lib/abstract_controller/error.rb +8 -0
  13. data/lib/abstract_controller/helpers.rb +243 -0
  14. data/lib/abstract_controller/logger.rb +16 -0
  15. data/lib/abstract_controller/railties/routes_helpers.rb +25 -0
  16. data/lib/abstract_controller/rendering.rb +126 -0
  17. data/lib/abstract_controller/translation.rb +42 -0
  18. data/lib/abstract_controller/url_for.rb +37 -0
  19. data/lib/abstract_controller.rb +36 -0
  20. data/lib/action_controller/api/api_rendering.rb +18 -0
  21. data/lib/action_controller/api.rb +155 -0
  22. data/lib/action_controller/base.rb +332 -0
  23. data/lib/action_controller/caching.rb +49 -0
  24. data/lib/action_controller/deprecator.rb +9 -0
  25. data/lib/action_controller/form_builder.rb +55 -0
  26. data/lib/action_controller/log_subscriber.rb +96 -0
  27. data/lib/action_controller/metal/allow_browser.rb +123 -0
  28. data/lib/action_controller/metal/basic_implicit_render.rb +17 -0
  29. data/lib/action_controller/metal/conditional_get.rb +341 -0
  30. data/lib/action_controller/metal/content_security_policy.rb +86 -0
  31. data/lib/action_controller/metal/cookies.rb +20 -0
  32. data/lib/action_controller/metal/data_streaming.rb +154 -0
  33. data/lib/action_controller/metal/default_headers.rb +21 -0
  34. data/lib/action_controller/metal/etag_with_flash.rb +22 -0
  35. data/lib/action_controller/metal/etag_with_template_digest.rb +59 -0
  36. data/lib/action_controller/metal/exceptions.rb +106 -0
  37. data/lib/action_controller/metal/flash.rb +67 -0
  38. data/lib/action_controller/metal/head.rb +67 -0
  39. data/lib/action_controller/metal/helpers.rb +129 -0
  40. data/lib/action_controller/metal/http_authentication.rb +565 -0
  41. data/lib/action_controller/metal/implicit_render.rb +67 -0
  42. data/lib/action_controller/metal/instrumentation.rb +120 -0
  43. data/lib/action_controller/metal/live.rb +398 -0
  44. data/lib/action_controller/metal/logging.rb +22 -0
  45. data/lib/action_controller/metal/mime_responds.rb +337 -0
  46. data/lib/action_controller/metal/parameter_encoding.rb +84 -0
  47. data/lib/action_controller/metal/params_wrapper.rb +312 -0
  48. data/lib/action_controller/metal/permissions_policy.rb +38 -0
  49. data/lib/action_controller/metal/rate_limiting.rb +62 -0
  50. data/lib/action_controller/metal/redirecting.rb +251 -0
  51. data/lib/action_controller/metal/renderers.rb +181 -0
  52. data/lib/action_controller/metal/rendering.rb +260 -0
  53. data/lib/action_controller/metal/request_forgery_protection.rb +667 -0
  54. data/lib/action_controller/metal/rescue.rb +33 -0
  55. data/lib/action_controller/metal/streaming.rb +183 -0
  56. data/lib/action_controller/metal/strong_parameters.rb +1546 -0
  57. data/lib/action_controller/metal/testing.rb +25 -0
  58. data/lib/action_controller/metal/url_for.rb +65 -0
  59. data/lib/action_controller/metal.rb +339 -0
  60. data/lib/action_controller/railtie.rb +149 -0
  61. data/lib/action_controller/railties/helpers.rb +26 -0
  62. data/lib/action_controller/renderer.rb +161 -0
  63. data/lib/action_controller/template_assertions.rb +13 -0
  64. data/lib/action_controller/test_case.rb +691 -0
  65. data/lib/action_controller.rb +80 -0
  66. data/lib/action_dispatch/constants.rb +34 -0
  67. data/lib/action_dispatch/deprecator.rb +9 -0
  68. data/lib/action_dispatch/http/cache.rb +249 -0
  69. data/lib/action_dispatch/http/content_disposition.rb +47 -0
  70. data/lib/action_dispatch/http/content_security_policy.rb +365 -0
  71. data/lib/action_dispatch/http/filter_parameters.rb +80 -0
  72. data/lib/action_dispatch/http/filter_redirect.rb +50 -0
  73. data/lib/action_dispatch/http/headers.rb +134 -0
  74. data/lib/action_dispatch/http/mime_negotiation.rb +187 -0
  75. data/lib/action_dispatch/http/mime_type.rb +389 -0
  76. data/lib/action_dispatch/http/mime_types.rb +54 -0
  77. data/lib/action_dispatch/http/parameters.rb +119 -0
  78. data/lib/action_dispatch/http/permissions_policy.rb +189 -0
  79. data/lib/action_dispatch/http/rack_cache.rb +67 -0
  80. data/lib/action_dispatch/http/request.rb +498 -0
  81. data/lib/action_dispatch/http/response.rb +556 -0
  82. data/lib/action_dispatch/http/upload.rb +107 -0
  83. data/lib/action_dispatch/http/url.rb +344 -0
  84. data/lib/action_dispatch/journey/formatter.rb +226 -0
  85. data/lib/action_dispatch/journey/gtg/builder.rb +149 -0
  86. data/lib/action_dispatch/journey/gtg/simulator.rb +50 -0
  87. data/lib/action_dispatch/journey/gtg/transition_table.rb +217 -0
  88. data/lib/action_dispatch/journey/nfa/dot.rb +27 -0
  89. data/lib/action_dispatch/journey/nodes/node.rb +208 -0
  90. data/lib/action_dispatch/journey/parser.rb +103 -0
  91. data/lib/action_dispatch/journey/path/pattern.rb +209 -0
  92. data/lib/action_dispatch/journey/route.rb +189 -0
  93. data/lib/action_dispatch/journey/router/utils.rb +105 -0
  94. data/lib/action_dispatch/journey/router.rb +151 -0
  95. data/lib/action_dispatch/journey/routes.rb +82 -0
  96. data/lib/action_dispatch/journey/scanner.rb +70 -0
  97. data/lib/action_dispatch/journey/visitors.rb +267 -0
  98. data/lib/action_dispatch/journey/visualizer/fsm.css +30 -0
  99. data/lib/action_dispatch/journey/visualizer/fsm.js +159 -0
  100. data/lib/action_dispatch/journey/visualizer/index.html.erb +52 -0
  101. data/lib/action_dispatch/journey.rb +7 -0
  102. data/lib/action_dispatch/log_subscriber.rb +25 -0
  103. data/lib/action_dispatch/middleware/actionable_exceptions.rb +46 -0
  104. data/lib/action_dispatch/middleware/assume_ssl.rb +27 -0
  105. data/lib/action_dispatch/middleware/callbacks.rb +38 -0
  106. data/lib/action_dispatch/middleware/cookies.rb +719 -0
  107. data/lib/action_dispatch/middleware/debug_exceptions.rb +206 -0
  108. data/lib/action_dispatch/middleware/debug_locks.rb +129 -0
  109. data/lib/action_dispatch/middleware/debug_view.rb +73 -0
  110. data/lib/action_dispatch/middleware/exception_wrapper.rb +350 -0
  111. data/lib/action_dispatch/middleware/executor.rb +32 -0
  112. data/lib/action_dispatch/middleware/flash.rb +318 -0
  113. data/lib/action_dispatch/middleware/host_authorization.rb +171 -0
  114. data/lib/action_dispatch/middleware/public_exceptions.rb +64 -0
  115. data/lib/action_dispatch/middleware/reloader.rb +16 -0
  116. data/lib/action_dispatch/middleware/remote_ip.rb +199 -0
  117. data/lib/action_dispatch/middleware/request_id.rb +50 -0
  118. data/lib/action_dispatch/middleware/server_timing.rb +78 -0
  119. data/lib/action_dispatch/middleware/session/abstract_store.rb +112 -0
  120. data/lib/action_dispatch/middleware/session/cache_store.rb +66 -0
  121. data/lib/action_dispatch/middleware/session/cookie_store.rb +129 -0
  122. data/lib/action_dispatch/middleware/session/mem_cache_store.rb +34 -0
  123. data/lib/action_dispatch/middleware/show_exceptions.rb +88 -0
  124. data/lib/action_dispatch/middleware/ssl.rb +180 -0
  125. data/lib/action_dispatch/middleware/stack.rb +194 -0
  126. data/lib/action_dispatch/middleware/static.rb +192 -0
  127. data/lib/action_dispatch/middleware/templates/rescues/_actions.html.erb +13 -0
  128. data/lib/action_dispatch/middleware/templates/rescues/_actions.text.erb +0 -0
  129. data/lib/action_dispatch/middleware/templates/rescues/_message_and_suggestions.html.erb +22 -0
  130. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +17 -0
  131. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.text.erb +23 -0
  132. data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +36 -0
  133. data/lib/action_dispatch/middleware/templates/rescues/_source.text.erb +8 -0
  134. data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +62 -0
  135. data/lib/action_dispatch/middleware/templates/rescues/_trace.text.erb +9 -0
  136. data/lib/action_dispatch/middleware/templates/rescues/blocked_host.html.erb +12 -0
  137. data/lib/action_dispatch/middleware/templates/rescues/blocked_host.text.erb +9 -0
  138. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +35 -0
  139. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +9 -0
  140. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +24 -0
  141. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +16 -0
  142. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +284 -0
  143. data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +23 -0
  144. data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.text.erb +3 -0
  145. data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +11 -0
  146. data/lib/action_dispatch/middleware/templates/rescues/missing_template.text.erb +3 -0
  147. data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +32 -0
  148. data/lib/action_dispatch/middleware/templates/rescues/routing_error.text.erb +11 -0
  149. data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +20 -0
  150. data/lib/action_dispatch/middleware/templates/rescues/template_error.text.erb +7 -0
  151. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +6 -0
  152. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.text.erb +3 -0
  153. data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +19 -0
  154. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +232 -0
  155. data/lib/action_dispatch/railtie.rb +77 -0
  156. data/lib/action_dispatch/request/session.rb +283 -0
  157. data/lib/action_dispatch/request/utils.rb +109 -0
  158. data/lib/action_dispatch/routing/endpoint.rb +19 -0
  159. data/lib/action_dispatch/routing/inspector.rb +323 -0
  160. data/lib/action_dispatch/routing/mapper.rb +2372 -0
  161. data/lib/action_dispatch/routing/polymorphic_routes.rb +363 -0
  162. data/lib/action_dispatch/routing/redirection.rb +218 -0
  163. data/lib/action_dispatch/routing/route_set.rb +958 -0
  164. data/lib/action_dispatch/routing/routes_proxy.rb +66 -0
  165. data/lib/action_dispatch/routing/url_for.rb +244 -0
  166. data/lib/action_dispatch/routing.rb +262 -0
  167. data/lib/action_dispatch/system_test_case.rb +206 -0
  168. data/lib/action_dispatch/system_testing/browser.rb +75 -0
  169. data/lib/action_dispatch/system_testing/driver.rb +85 -0
  170. data/lib/action_dispatch/system_testing/server.rb +33 -0
  171. data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +164 -0
  172. data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +23 -0
  173. data/lib/action_dispatch/testing/assertion_response.rb +48 -0
  174. data/lib/action_dispatch/testing/assertions/response.rb +114 -0
  175. data/lib/action_dispatch/testing/assertions/routing.rb +343 -0
  176. data/lib/action_dispatch/testing/assertions.rb +25 -0
  177. data/lib/action_dispatch/testing/integration.rb +694 -0
  178. data/lib/action_dispatch/testing/request_encoder.rb +60 -0
  179. data/lib/action_dispatch/testing/test_helpers/page_dump_helper.rb +35 -0
  180. data/lib/action_dispatch/testing/test_process.rb +57 -0
  181. data/lib/action_dispatch/testing/test_request.rb +73 -0
  182. data/lib/action_dispatch/testing/test_response.rb +58 -0
  183. data/lib/action_dispatch.rb +147 -0
  184. data/lib/action_pack/gem_version.rb +19 -0
  185. data/lib/action_pack/version.rb +12 -0
  186. data/lib/action_pack.rb +27 -0
  187. metadata +375 -0
@@ -0,0 +1,556 @@
1
+ # frozen_string_literal: true
2
+
3
+ # :markup: markdown
4
+
5
+ require "active_support/core_ext/module/attribute_accessors"
6
+ require "action_dispatch/http/filter_redirect"
7
+ require "action_dispatch/http/cache"
8
+ require "monitor"
9
+
10
+ module ActionDispatch # :nodoc:
11
+ # # Action Dispatch Response
12
+ #
13
+ # Represents an HTTP response generated by a controller action. Use it to
14
+ # retrieve the current state of the response, or customize the response. It can
15
+ # either represent a real HTTP response (i.e. one that is meant to be sent back
16
+ # to the web browser) or a TestResponse (i.e. one that is generated from
17
+ # integration tests).
18
+ #
19
+ # The Response object for the current request is exposed on controllers as
20
+ # ActionController::Metal#response. ActionController::Metal also provides a few
21
+ # additional methods that delegate to attributes of the Response such as
22
+ # ActionController::Metal#headers.
23
+ #
24
+ # Integration tests will likely also want to inspect responses in more detail.
25
+ # Methods such as Integration::RequestHelpers#get and
26
+ # Integration::RequestHelpers#post return instances of TestResponse (which
27
+ # inherits from Response) for this purpose.
28
+ #
29
+ # For example, the following demo integration test prints the body of the
30
+ # controller response to the console:
31
+ #
32
+ # class DemoControllerTest < ActionDispatch::IntegrationTest
33
+ # def test_print_root_path_to_console
34
+ # get('/')
35
+ # puts response.body
36
+ # end
37
+ # end
38
+ class Response
39
+ begin
40
+ # For `Rack::Headers` (Rack 3+):
41
+ require "rack/headers"
42
+ Headers = ::Rack::Headers
43
+ rescue LoadError
44
+ # For `Rack::Utils::HeaderHash`:
45
+ require "rack/utils"
46
+ Headers = ::Rack::Utils::HeaderHash
47
+ end
48
+
49
+ # To be deprecated:
50
+ Header = Headers
51
+
52
+ # The request that the response is responding to.
53
+ attr_accessor :request
54
+
55
+ # The HTTP status code.
56
+ attr_reader :status
57
+
58
+ # The headers for the response.
59
+ #
60
+ # header["Content-Type"] # => "text/plain"
61
+ # header["Content-Type"] = "application/json"
62
+ # header["Content-Type"] # => "application/json"
63
+ #
64
+ # Also aliased as `headers`.
65
+ #
66
+ # headers["Content-Type"] # => "text/plain"
67
+ # headers["Content-Type"] = "application/json"
68
+ # headers["Content-Type"] # => "application/json"
69
+ #
70
+ # Also aliased as `header` for compatibility.
71
+ attr_reader :headers
72
+
73
+ alias_method :header, :headers
74
+
75
+ delegate :[], :[]=, to: :@headers
76
+
77
+ def each(&block)
78
+ sending!
79
+ x = @stream.each(&block)
80
+ sent!
81
+ x
82
+ end
83
+
84
+ CONTENT_TYPE = "Content-Type"
85
+ SET_COOKIE = "Set-Cookie"
86
+ NO_CONTENT_CODES = [100, 101, 102, 103, 204, 205, 304]
87
+
88
+ cattr_accessor :default_charset, default: "utf-8"
89
+ cattr_accessor :default_headers
90
+
91
+ include Rack::Response::Helpers
92
+ # Aliasing these off because AD::Http::Cache::Response defines them.
93
+ alias :_cache_control :cache_control
94
+ alias :_cache_control= :cache_control=
95
+
96
+ include ActionDispatch::Http::FilterRedirect
97
+ include ActionDispatch::Http::Cache::Response
98
+ include MonitorMixin
99
+
100
+ class Buffer # :nodoc:
101
+ def initialize(response, buf)
102
+ @response = response
103
+ @buf = buf
104
+ @closed = false
105
+ @str_body = nil
106
+ end
107
+
108
+ def to_ary
109
+ @buf.respond_to?(:to_ary) ?
110
+ @buf.to_ary :
111
+ @buf.each
112
+ end
113
+
114
+ def body
115
+ @str_body ||= begin
116
+ buf = +""
117
+ each { |chunk| buf << chunk }
118
+ buf
119
+ end
120
+ end
121
+
122
+ def write(string)
123
+ raise IOError, "closed stream" if closed?
124
+
125
+ @str_body = nil
126
+ @response.commit!
127
+ @buf.push string
128
+ end
129
+ alias_method :<<, :write
130
+
131
+ def each(&block)
132
+ if @str_body
133
+ return enum_for(:each) unless block_given?
134
+
135
+ yield @str_body
136
+ else
137
+ each_chunk(&block)
138
+ end
139
+ end
140
+
141
+ def abort
142
+ end
143
+
144
+ def close
145
+ @response.commit!
146
+ @closed = true
147
+ end
148
+
149
+ def closed?
150
+ @closed
151
+ end
152
+
153
+ private
154
+ def each_chunk(&block)
155
+ @buf.each(&block)
156
+ end
157
+ end
158
+
159
+ def self.create(status = 200, headers = {}, body = [], default_headers: self.default_headers)
160
+ headers = merge_default_headers(headers, default_headers)
161
+ new status, headers, body
162
+ end
163
+
164
+ def self.merge_default_headers(original, default)
165
+ default.respond_to?(:merge) ? default.merge(original) : original
166
+ end
167
+
168
+ # The underlying body, as a streamable object.
169
+ attr_reader :stream
170
+
171
+ def initialize(status = 200, headers = nil, body = [])
172
+ super()
173
+
174
+ @headers = Headers.new
175
+
176
+ headers&.each do |key, value|
177
+ @headers[key] = value
178
+ end
179
+
180
+ self.body, self.status = body, status
181
+
182
+ @cv = new_cond
183
+ @committed = false
184
+ @sending = false
185
+ @sent = false
186
+
187
+ prepare_cache_control!
188
+
189
+ yield self if block_given?
190
+ end
191
+
192
+ def has_header?(key); @headers.key? key; end
193
+ def get_header(key); @headers[key]; end
194
+ def set_header(key, v); @headers[key] = v; end
195
+ def delete_header(key); @headers.delete key; end
196
+
197
+ def await_commit
198
+ synchronize do
199
+ @cv.wait_until { @committed }
200
+ end
201
+ end
202
+
203
+ def await_sent
204
+ synchronize { @cv.wait_until { @sent } }
205
+ end
206
+
207
+ def commit!
208
+ synchronize do
209
+ before_committed
210
+ @committed = true
211
+ @cv.broadcast
212
+ end
213
+ end
214
+
215
+ def sending!
216
+ synchronize do
217
+ before_sending
218
+ @sending = true
219
+ @cv.broadcast
220
+ end
221
+ end
222
+
223
+ def sent!
224
+ synchronize do
225
+ @sent = true
226
+ @cv.broadcast
227
+ end
228
+ end
229
+
230
+ def sending?; synchronize { @sending }; end
231
+ def committed?; synchronize { @committed }; end
232
+ def sent?; synchronize { @sent }; end
233
+
234
+ ##
235
+ # :method: location
236
+ #
237
+ # Location of the response.
238
+
239
+ ##
240
+ # :method: location=
241
+ #
242
+ # :call-seq: location=(location)
243
+ #
244
+ # Sets the location of the response
245
+
246
+ # Sets the HTTP status code.
247
+ def status=(status)
248
+ @status = Rack::Utils.status_code(status)
249
+ end
250
+
251
+ # Sets the HTTP response's content MIME type. For example, in the controller you
252
+ # could write this:
253
+ #
254
+ # response.content_type = "text/plain"
255
+ #
256
+ # If a character set has been defined for this response (see #charset=) then the
257
+ # character set information will also be included in the content type
258
+ # information.
259
+ def content_type=(content_type)
260
+ return unless content_type
261
+ new_header_info = parse_content_type(content_type.to_s)
262
+ prev_header_info = parsed_content_type_header
263
+ charset = new_header_info.charset || prev_header_info.charset
264
+ charset ||= self.class.default_charset unless prev_header_info.mime_type
265
+ set_content_type new_header_info.mime_type, charset
266
+ end
267
+
268
+ # Content type of response.
269
+ def content_type
270
+ super.presence
271
+ end
272
+
273
+ # Media type of response.
274
+ def media_type
275
+ parsed_content_type_header.mime_type
276
+ end
277
+
278
+ def sending_file=(v)
279
+ if true == v
280
+ self.charset = false
281
+ end
282
+ end
283
+
284
+ # Sets the HTTP character set. In case of `nil` parameter it sets the charset to
285
+ # `default_charset`.
286
+ #
287
+ # response.charset = 'utf-16' # => 'utf-16'
288
+ # response.charset = nil # => 'utf-8'
289
+ def charset=(charset)
290
+ content_type = parsed_content_type_header.mime_type
291
+ if false == charset
292
+ set_content_type content_type, nil
293
+ else
294
+ set_content_type content_type, charset || self.class.default_charset
295
+ end
296
+ end
297
+
298
+ # The charset of the response. HTML wants to know the encoding of the content
299
+ # you're giving them, so we need to send that along.
300
+ def charset
301
+ header_info = parsed_content_type_header
302
+ header_info.charset || self.class.default_charset
303
+ end
304
+
305
+ # The response code of the request.
306
+ def response_code
307
+ @status
308
+ end
309
+
310
+ # Returns a string to ensure compatibility with `Net::HTTPResponse`.
311
+ def code
312
+ @status.to_s
313
+ end
314
+
315
+ # Returns the corresponding message for the current HTTP status code:
316
+ #
317
+ # response.status = 200
318
+ # response.message # => "OK"
319
+ #
320
+ # response.status = 404
321
+ # response.message # => "Not Found"
322
+ #
323
+ def message
324
+ Rack::Utils::HTTP_STATUS_CODES[@status]
325
+ end
326
+ alias_method :status_message, :message
327
+
328
+ # Returns the content of the response as a string. This contains the contents of
329
+ # any calls to `render`.
330
+ def body
331
+ @stream.body
332
+ end
333
+
334
+ def write(string)
335
+ @stream.write string
336
+ end
337
+
338
+ # Allows you to manually set or override the response body.
339
+ def body=(body)
340
+ if body.respond_to?(:to_path)
341
+ @stream = body
342
+ else
343
+ synchronize do
344
+ @stream = build_buffer self, munge_body_object(body)
345
+ end
346
+ end
347
+ end
348
+
349
+ # Avoid having to pass an open file handle as the response body. Rack::Sendfile
350
+ # will usually intercept the response and uses the path directly, so there is no
351
+ # reason to open the file.
352
+ class FileBody # :nodoc:
353
+ attr_reader :to_path
354
+
355
+ def initialize(path)
356
+ @to_path = path
357
+ end
358
+
359
+ def body
360
+ File.binread(to_path)
361
+ end
362
+
363
+ # Stream the file's contents if Rack::Sendfile isn't present.
364
+ def each
365
+ File.open(to_path, "rb") do |file|
366
+ while chunk = file.read(16384)
367
+ yield chunk
368
+ end
369
+ end
370
+ end
371
+ end
372
+
373
+ # Send the file stored at `path` as the response body.
374
+ def send_file(path)
375
+ commit!
376
+ @stream = FileBody.new(path)
377
+ end
378
+
379
+ def reset_body!
380
+ @stream = build_buffer(self, [])
381
+ end
382
+
383
+ def body_parts
384
+ parts = []
385
+ @stream.each { |x| parts << x }
386
+ parts
387
+ end
388
+
389
+ # The location header we'll be responding with.
390
+ alias_method :redirect_url, :location
391
+
392
+ def close
393
+ stream.close if stream.respond_to?(:close)
394
+ end
395
+
396
+ def abort
397
+ if stream.respond_to?(:abort)
398
+ stream.abort
399
+ elsif stream.respond_to?(:close)
400
+ # `stream.close` should really be reserved for a close from the other direction,
401
+ # but we must fall back to it for compatibility.
402
+ stream.close
403
+ end
404
+ end
405
+
406
+ # Turns the Response into a Rack-compatible array of the status, headers, and
407
+ # body. Allows explicit splatting:
408
+ #
409
+ # status, headers, body = *response
410
+ def to_a
411
+ commit!
412
+ rack_response @status, @headers.to_hash
413
+ end
414
+ alias prepare! to_a
415
+
416
+ # Returns the response cookies, converted to a Hash of (name => value) pairs
417
+ #
418
+ # assert_equal 'AuthorOfNewPage', r.cookies['author']
419
+ def cookies
420
+ cookies = {}
421
+ if header = get_header(SET_COOKIE)
422
+ header = header.split("\n") if header.respond_to?(:to_str)
423
+ header.each do |cookie|
424
+ if pair = cookie.split(";").first
425
+ key, value = pair.split("=").map { |v| Rack::Utils.unescape(v) }
426
+ cookies[key] = value
427
+ end
428
+ end
429
+ end
430
+ cookies
431
+ end
432
+
433
+ private
434
+ ContentTypeHeader = Struct.new :mime_type, :charset
435
+ NullContentTypeHeader = ContentTypeHeader.new nil, nil
436
+
437
+ CONTENT_TYPE_PARSER = /
438
+ \A
439
+ (?<mime_type>[^;\s]+\s*(?:;\s*(?:(?!charset)[^;\s])+)*)?
440
+ (?:;\s*charset=(?<quote>"?)(?<charset>[^;\s]+)\k<quote>)?
441
+ /x # :nodoc:
442
+
443
+ def parse_content_type(content_type)
444
+ if content_type && match = CONTENT_TYPE_PARSER.match(content_type)
445
+ ContentTypeHeader.new(match[:mime_type], match[:charset])
446
+ else
447
+ NullContentTypeHeader
448
+ end
449
+ end
450
+
451
+ # Small internal convenience method to get the parsed version of the current
452
+ # content type header.
453
+ def parsed_content_type_header
454
+ parse_content_type(get_header(CONTENT_TYPE))
455
+ end
456
+
457
+ def set_content_type(content_type, charset)
458
+ type = content_type || ""
459
+ type = "#{type}; charset=#{charset.to_s.downcase}" if charset
460
+ set_header CONTENT_TYPE, type
461
+ end
462
+
463
+ def before_committed
464
+ return if committed?
465
+ assign_default_content_type_and_charset!
466
+ merge_and_normalize_cache_control!(@cache_control)
467
+ handle_conditional_get!
468
+ handle_no_content!
469
+ end
470
+
471
+ def before_sending
472
+ # Normally we've already committed by now, but it's possible (e.g., if the
473
+ # controller action tries to read back its own response) to get here before
474
+ # that. In that case, we must force an "early" commit: we're about to freeze the
475
+ # headers, so this is our last chance.
476
+ commit! unless committed?
477
+
478
+ @request.commit_cookie_jar! unless committed?
479
+ end
480
+
481
+ def build_buffer(response, body)
482
+ Buffer.new response, body
483
+ end
484
+
485
+ def munge_body_object(body)
486
+ body.respond_to?(:each) ? body : [body]
487
+ end
488
+
489
+ def assign_default_content_type_and_charset!
490
+ return if media_type
491
+
492
+ ct = parsed_content_type_header
493
+ set_content_type(ct.mime_type || Mime[:html].to_s,
494
+ ct.charset || self.class.default_charset)
495
+ end
496
+
497
+ class RackBody
498
+ def initialize(response)
499
+ @response = response
500
+ end
501
+
502
+ def close
503
+ # Rack "close" maps to Response#abort, and **not** Response#close (which is used
504
+ # when the controller's finished writing)
505
+ @response.abort
506
+ end
507
+
508
+ def body
509
+ @response.body
510
+ end
511
+
512
+ BODY_METHODS = { to_ary: true, each: true, call: true, to_path: true }
513
+
514
+ def respond_to?(method, include_private = false)
515
+ if BODY_METHODS.key?(method)
516
+ @response.stream.respond_to?(method)
517
+ else
518
+ super
519
+ end
520
+ end
521
+
522
+ def to_ary
523
+ @response.stream.to_ary
524
+ end
525
+
526
+ def each(*args, &block)
527
+ @response.each(*args, &block)
528
+ end
529
+
530
+ def call(*arguments, &block)
531
+ @response.stream.call(*arguments, &block)
532
+ end
533
+
534
+ def to_path
535
+ @response.stream.to_path
536
+ end
537
+ end
538
+
539
+ def handle_no_content!
540
+ if NO_CONTENT_CODES.include?(@status)
541
+ @headers.delete CONTENT_TYPE
542
+ @headers.delete "Content-Length"
543
+ end
544
+ end
545
+
546
+ def rack_response(status, headers)
547
+ if NO_CONTENT_CODES.include?(status)
548
+ [status, headers, []]
549
+ else
550
+ [status, headers, RackBody.new(self)]
551
+ end
552
+ end
553
+ end
554
+
555
+ ActiveSupport.run_load_hooks(:action_dispatch_response, Response)
556
+ end
@@ -0,0 +1,107 @@
1
+ # frozen_string_literal: true
2
+
3
+ # :markup: markdown
4
+
5
+ module ActionDispatch
6
+ module Http
7
+ # # Action Dispatch HTTP UploadedFile
8
+ #
9
+ # Models uploaded files.
10
+ #
11
+ # The actual file is accessible via the `tempfile` accessor, though some of its
12
+ # interface is available directly for convenience.
13
+ #
14
+ # Uploaded files are temporary files whose lifespan is one request. When the
15
+ # object is finalized Ruby unlinks the file, so there is no need to clean them
16
+ # with a separate maintenance task.
17
+ class UploadedFile
18
+ # The basename of the file in the client.
19
+ attr_accessor :original_filename
20
+
21
+ # A string with the MIME type of the file.
22
+ attr_accessor :content_type
23
+
24
+ # A `Tempfile` object with the actual uploaded file. Note that some of its
25
+ # interface is available directly.
26
+ attr_accessor :tempfile
27
+
28
+ # A string with the headers of the multipart request.
29
+ attr_accessor :headers
30
+
31
+ def initialize(hash) # :nodoc:
32
+ @tempfile = hash[:tempfile]
33
+ raise(ArgumentError, ":tempfile is required") unless @tempfile
34
+
35
+ @content_type = hash[:type]
36
+
37
+ if hash[:filename]
38
+ @original_filename = hash[:filename].dup
39
+
40
+ begin
41
+ @original_filename.encode!(Encoding::UTF_8)
42
+ rescue EncodingError
43
+ @original_filename.force_encoding(Encoding::UTF_8)
44
+ end
45
+ else
46
+ @original_filename = nil
47
+ end
48
+
49
+ if hash[:head]
50
+ @headers = hash[:head].dup
51
+
52
+ begin
53
+ @headers.encode!(Encoding::UTF_8)
54
+ rescue EncodingError
55
+ @headers.force_encoding(Encoding::UTF_8)
56
+ end
57
+ else
58
+ @headers = nil
59
+ end
60
+ end
61
+
62
+ # Shortcut for `tempfile.read`.
63
+ def read(length = nil, buffer = nil)
64
+ @tempfile.read(length, buffer)
65
+ end
66
+
67
+ # Shortcut for `tempfile.open`.
68
+ def open
69
+ @tempfile.open
70
+ end
71
+
72
+ # Shortcut for `tempfile.close`.
73
+ def close(unlink_now = false)
74
+ @tempfile.close(unlink_now)
75
+ end
76
+
77
+ # Shortcut for `tempfile.path`.
78
+ def path
79
+ @tempfile.path
80
+ end
81
+
82
+ # Shortcut for `tempfile.to_path`.
83
+ def to_path
84
+ @tempfile.to_path
85
+ end
86
+
87
+ # Shortcut for `tempfile.rewind`.
88
+ def rewind
89
+ @tempfile.rewind
90
+ end
91
+
92
+ # Shortcut for `tempfile.size`.
93
+ def size
94
+ @tempfile.size
95
+ end
96
+
97
+ # Shortcut for `tempfile.eof?`.
98
+ def eof?
99
+ @tempfile.eof?
100
+ end
101
+
102
+ def to_io
103
+ @tempfile.to_io
104
+ end
105
+ end
106
+ end
107
+ end