actionpack 6.0.0

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