actionpack 5.2.3

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