actionpack 4.2.8 → 5.2.4.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 (166) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +285 -444
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +6 -7
  5. data/lib/abstract_controller.rb +12 -5
  6. data/lib/abstract_controller/asset_paths.rb +2 -0
  7. data/lib/abstract_controller/base.rb +45 -49
  8. data/lib/abstract_controller/caching.rb +66 -0
  9. data/lib/{action_controller → abstract_controller}/caching/fragments.rb +78 -15
  10. data/lib/abstract_controller/callbacks.rb +47 -31
  11. data/lib/abstract_controller/collector.rb +8 -11
  12. data/lib/abstract_controller/error.rb +6 -0
  13. data/lib/abstract_controller/helpers.rb +25 -25
  14. data/lib/abstract_controller/logger.rb +2 -0
  15. data/lib/abstract_controller/railties/routes_helpers.rb +4 -2
  16. data/lib/abstract_controller/rendering.rb +42 -41
  17. data/lib/abstract_controller/translation.rb +10 -7
  18. data/lib/abstract_controller/url_for.rb +2 -0
  19. data/lib/action_controller.rb +29 -21
  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 +27 -19
  23. data/lib/action_controller/caching.rb +14 -57
  24. data/lib/action_controller/form_builder.rb +50 -0
  25. data/lib/action_controller/log_subscriber.rb +10 -15
  26. data/lib/action_controller/metal.rb +98 -83
  27. data/lib/action_controller/metal/basic_implicit_render.rb +13 -0
  28. data/lib/action_controller/metal/conditional_get.rb +118 -44
  29. data/lib/action_controller/metal/content_security_policy.rb +52 -0
  30. data/lib/action_controller/metal/cookies.rb +3 -3
  31. data/lib/action_controller/metal/data_streaming.rb +27 -46
  32. data/lib/action_controller/metal/etag_with_flash.rb +18 -0
  33. data/lib/action_controller/metal/etag_with_template_digest.rb +20 -13
  34. data/lib/action_controller/metal/exceptions.rb +8 -14
  35. data/lib/action_controller/metal/flash.rb +4 -3
  36. data/lib/action_controller/metal/force_ssl.rb +23 -21
  37. data/lib/action_controller/metal/head.rb +21 -19
  38. data/lib/action_controller/metal/helpers.rb +24 -14
  39. data/lib/action_controller/metal/http_authentication.rb +64 -57
  40. data/lib/action_controller/metal/implicit_render.rb +62 -8
  41. data/lib/action_controller/metal/instrumentation.rb +19 -21
  42. data/lib/action_controller/metal/live.rb +90 -106
  43. data/lib/action_controller/metal/mime_responds.rb +33 -46
  44. data/lib/action_controller/metal/parameter_encoding.rb +51 -0
  45. data/lib/action_controller/metal/params_wrapper.rb +61 -53
  46. data/lib/action_controller/metal/redirecting.rb +49 -28
  47. data/lib/action_controller/metal/renderers.rb +87 -44
  48. data/lib/action_controller/metal/rendering.rb +72 -50
  49. data/lib/action_controller/metal/request_forgery_protection.rb +203 -92
  50. data/lib/action_controller/metal/rescue.rb +9 -16
  51. data/lib/action_controller/metal/streaming.rb +12 -10
  52. data/lib/action_controller/metal/strong_parameters.rb +582 -165
  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/railtie.rb +28 -10
  56. data/lib/action_controller/railties/helpers.rb +2 -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 +280 -411
  60. data/lib/action_dispatch.rb +27 -19
  61. data/lib/action_dispatch/http/cache.rb +93 -47
  62. data/lib/action_dispatch/http/content_security_policy.rb +272 -0
  63. data/lib/action_dispatch/http/filter_parameters.rb +26 -20
  64. data/lib/action_dispatch/http/filter_redirect.rb +10 -11
  65. data/lib/action_dispatch/http/headers.rb +55 -22
  66. data/lib/action_dispatch/http/mime_negotiation.rb +60 -41
  67. data/lib/action_dispatch/http/mime_type.rb +134 -121
  68. data/lib/action_dispatch/http/mime_types.rb +20 -6
  69. data/lib/action_dispatch/http/parameter_filter.rb +25 -11
  70. data/lib/action_dispatch/http/parameters.rb +98 -39
  71. data/lib/action_dispatch/http/rack_cache.rb +2 -0
  72. data/lib/action_dispatch/http/request.rb +200 -118
  73. data/lib/action_dispatch/http/response.rb +225 -110
  74. data/lib/action_dispatch/http/upload.rb +12 -6
  75. data/lib/action_dispatch/http/url.rb +110 -28
  76. data/lib/action_dispatch/journey.rb +7 -5
  77. data/lib/action_dispatch/journey/formatter.rb +55 -32
  78. data/lib/action_dispatch/journey/gtg/builder.rb +7 -5
  79. data/lib/action_dispatch/journey/gtg/simulator.rb +3 -9
  80. data/lib/action_dispatch/journey/gtg/transition_table.rb +17 -16
  81. data/lib/action_dispatch/journey/nfa/builder.rb +5 -3
  82. data/lib/action_dispatch/journey/nfa/dot.rb +13 -13
  83. data/lib/action_dispatch/journey/nfa/simulator.rb +3 -1
  84. data/lib/action_dispatch/journey/nfa/transition_table.rb +5 -48
  85. data/lib/action_dispatch/journey/nodes/node.rb +18 -6
  86. data/lib/action_dispatch/journey/parser.rb +23 -22
  87. data/lib/action_dispatch/journey/parser.y +3 -2
  88. data/lib/action_dispatch/journey/parser_extras.rb +12 -4
  89. data/lib/action_dispatch/journey/path/pattern.rb +50 -44
  90. data/lib/action_dispatch/journey/route.rb +106 -28
  91. data/lib/action_dispatch/journey/router.rb +35 -23
  92. data/lib/action_dispatch/journey/router/utils.rb +20 -11
  93. data/lib/action_dispatch/journey/routes.rb +18 -16
  94. data/lib/action_dispatch/journey/scanner.rb +18 -15
  95. data/lib/action_dispatch/journey/visitors.rb +99 -52
  96. data/lib/action_dispatch/middleware/callbacks.rb +1 -2
  97. data/lib/action_dispatch/middleware/cookies.rb +304 -193
  98. data/lib/action_dispatch/middleware/debug_exceptions.rb +152 -57
  99. data/lib/action_dispatch/middleware/debug_locks.rb +124 -0
  100. data/lib/action_dispatch/middleware/exception_wrapper.rb +68 -69
  101. data/lib/action_dispatch/middleware/executor.rb +21 -0
  102. data/lib/action_dispatch/middleware/flash.rb +78 -54
  103. data/lib/action_dispatch/middleware/public_exceptions.rb +27 -25
  104. data/lib/action_dispatch/middleware/reloader.rb +5 -91
  105. data/lib/action_dispatch/middleware/remote_ip.rb +41 -31
  106. data/lib/action_dispatch/middleware/request_id.rb +17 -9
  107. data/lib/action_dispatch/middleware/session/abstract_store.rb +41 -25
  108. data/lib/action_dispatch/middleware/session/cache_store.rb +24 -14
  109. data/lib/action_dispatch/middleware/session/cookie_store.rb +72 -67
  110. data/lib/action_dispatch/middleware/session/mem_cache_store.rb +8 -2
  111. data/lib/action_dispatch/middleware/show_exceptions.rb +26 -22
  112. data/lib/action_dispatch/middleware/ssl.rb +114 -36
  113. data/lib/action_dispatch/middleware/stack.rb +31 -44
  114. data/lib/action_dispatch/middleware/static.rb +57 -50
  115. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +2 -14
  116. data/lib/action_dispatch/middleware/templates/rescues/{_source.erb → _source.html.erb} +0 -0
  117. data/lib/action_dispatch/middleware/templates/rescues/_source.text.erb +8 -0
  118. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +21 -0
  119. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +13 -0
  120. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +1 -0
  121. data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +1 -1
  122. data/lib/action_dispatch/middleware/templates/rescues/template_error.text.erb +1 -1
  123. data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +4 -4
  124. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +64 -64
  125. data/lib/action_dispatch/railtie.rb +19 -11
  126. data/lib/action_dispatch/request/session.rb +106 -59
  127. data/lib/action_dispatch/request/utils.rb +67 -24
  128. data/lib/action_dispatch/routing.rb +17 -18
  129. data/lib/action_dispatch/routing/endpoint.rb +9 -2
  130. data/lib/action_dispatch/routing/inspector.rb +58 -67
  131. data/lib/action_dispatch/routing/mapper.rb +734 -447
  132. data/lib/action_dispatch/routing/polymorphic_routes.rb +161 -139
  133. data/lib/action_dispatch/routing/redirection.rb +36 -26
  134. data/lib/action_dispatch/routing/route_set.rb +321 -291
  135. data/lib/action_dispatch/routing/routes_proxy.rb +32 -5
  136. data/lib/action_dispatch/routing/url_for.rb +65 -25
  137. data/lib/action_dispatch/system_test_case.rb +147 -0
  138. data/lib/action_dispatch/system_testing/browser.rb +49 -0
  139. data/lib/action_dispatch/system_testing/driver.rb +59 -0
  140. data/lib/action_dispatch/system_testing/server.rb +31 -0
  141. data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +96 -0
  142. data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +31 -0
  143. data/lib/action_dispatch/system_testing/test_helpers/undef_methods.rb +26 -0
  144. data/lib/action_dispatch/testing/assertion_response.rb +47 -0
  145. data/lib/action_dispatch/testing/assertions.rb +6 -4
  146. data/lib/action_dispatch/testing/assertions/response.rb +45 -20
  147. data/lib/action_dispatch/testing/assertions/routing.rb +30 -26
  148. data/lib/action_dispatch/testing/integration.rb +347 -209
  149. data/lib/action_dispatch/testing/request_encoder.rb +55 -0
  150. data/lib/action_dispatch/testing/test_process.rb +28 -22
  151. data/lib/action_dispatch/testing/test_request.rb +27 -34
  152. data/lib/action_dispatch/testing/test_response.rb +35 -7
  153. data/lib/action_pack.rb +4 -2
  154. data/lib/action_pack/gem_version.rb +5 -3
  155. data/lib/action_pack/version.rb +3 -1
  156. metadata +56 -39
  157. data/lib/action_controller/metal/hide_actions.rb +0 -40
  158. data/lib/action_controller/metal/rack_delegation.rb +0 -32
  159. data/lib/action_controller/middleware.rb +0 -39
  160. data/lib/action_controller/model_naming.rb +0 -12
  161. data/lib/action_dispatch/journey/backwards.rb +0 -5
  162. data/lib/action_dispatch/journey/router/strexp.rb +0 -27
  163. data/lib/action_dispatch/middleware/params_parser.rb +0 -60
  164. data/lib/action_dispatch/testing/assertions/dom.rb +0 -3
  165. data/lib/action_dispatch/testing/assertions/selector.rb +0 -3
  166. data/lib/action_dispatch/testing/assertions/tag.rb +0 -3
@@ -1,19 +1,73 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActionController
4
+ # Handles implicit rendering for a controller action that does not
5
+ # explicitly respond with +render+, +respond_to+, +redirect+, or +head+.
6
+ #
7
+ # For API controllers, the implicit response is always <tt>204 No Content</tt>.
8
+ #
9
+ # For all other controllers, we use these heuristics to decide whether to
10
+ # render a template, raise an error for a missing template, or respond with
11
+ # <tt>204 No Content</tt>:
12
+ #
13
+ # First, if we DO find a template, it's rendered. Template lookup accounts
14
+ # for the action name, locales, format, variant, template handlers, and more
15
+ # (see +render+ for details).
16
+ #
17
+ # Second, if we DON'T find a template but the controller action does have
18
+ # templates for other formats, variants, etc., then we trust that you meant
19
+ # to provide a template for this response, too, and we raise
20
+ # <tt>ActionController::UnknownFormat</tt> with an explanation.
21
+ #
22
+ # Third, if we DON'T find a template AND the request is a page load in a web
23
+ # browser (technically, a non-XHR GET request for an HTML response) where
24
+ # you reasonably expect to have rendered a template, then we raise
25
+ # <tt>ActionView::UnknownFormat</tt> with an explanation.
26
+ #
27
+ # Finally, if we DON'T find a template AND the request isn't a browser page
28
+ # load, then we implicitly respond with <tt>204 No Content</tt>.
2
29
  module ImplicitRender
3
- def send_action(method, *args)
4
- ret = super
5
- default_render unless performed?
6
- ret
7
- end
30
+ # :stopdoc:
31
+ include BasicImplicitRender
8
32
 
9
33
  def default_render(*args)
10
- render(*args)
34
+ if template_exists?(action_name.to_s, _prefixes, variants: request.variant)
35
+ render(*args)
36
+ elsif any_templates?(action_name.to_s, _prefixes)
37
+ message = "#{self.class.name}\##{action_name} is missing a template " \
38
+ "for this request format and variant.\n" \
39
+ "\nrequest.formats: #{request.formats.map(&:to_s).inspect}" \
40
+ "\nrequest.variant: #{request.variant.inspect}"
41
+
42
+ raise ActionController::UnknownFormat, message
43
+ elsif interactive_browser_request?
44
+ message = "#{self.class.name}\##{action_name} is missing a template " \
45
+ "for this request format and variant.\n\n" \
46
+ "request.formats: #{request.formats.map(&:to_s).inspect}\n" \
47
+ "request.variant: #{request.variant.inspect}\n\n" \
48
+ "NOTE! For XHR/Ajax or API requests, this action would normally " \
49
+ "respond with 204 No Content: an empty white screen. Since you're " \
50
+ "loading it in a web browser, we assume that you expected to " \
51
+ "actually render a template, not nothing, so we're showing an " \
52
+ "error to be extra-clear. If you expect 204 No Content, carry on. " \
53
+ "That's what you'll get from an XHR or API request. Give it a shot."
54
+
55
+ raise ActionController::UnknownFormat, message
56
+ else
57
+ logger.info "No template found for #{self.class.name}\##{action_name}, rendering head :no_content" if logger
58
+ super
59
+ end
11
60
  end
12
61
 
13
62
  def method_for_action(action_name)
14
63
  super || if template_exists?(action_name.to_s, _prefixes)
15
- "default_render"
16
- end
64
+ "default_render"
65
+ end
17
66
  end
67
+
68
+ private
69
+ def interactive_browser_request?
70
+ request.get? && request.format == Mime[:html] && !request.xhr?
71
+ end
18
72
  end
19
73
  end
@@ -1,9 +1,11 @@
1
- require 'benchmark'
2
- require 'abstract_controller/logger'
1
+ # frozen_string_literal: true
2
+
3
+ require "benchmark"
4
+ require "abstract_controller/logger"
3
5
 
4
6
  module ActionController
5
7
  # Adds instrumentation to several ends in ActionController::Base. It also provides
6
- # some hooks related with process_action, this allows an ORM like Active Record
8
+ # some hooks related with process_action. This allows an ORM like Active Record
7
9
  # and/or DataMapper to plug in ActionController and show related information.
8
10
  #
9
11
  # Check ActiveRecord::Railties::ControllerRuntime for an example.
@@ -11,18 +13,18 @@ module ActionController
11
13
  extend ActiveSupport::Concern
12
14
 
13
15
  include AbstractController::Logger
14
- include ActionController::RackDelegation
15
16
 
16
17
  attr_internal :view_runtime
17
18
 
18
19
  def process_action(*args)
19
20
  raw_payload = {
20
- :controller => self.class.name,
21
- :action => self.action_name,
22
- :params => request.filtered_parameters,
23
- :format => request.format.try(:ref),
24
- :method => request.request_method,
25
- :path => (request.fullpath rescue "unknown")
21
+ controller: self.class.name,
22
+ action: action_name,
23
+ params: request.filtered_parameters,
24
+ headers: request.headers,
25
+ format: request.format.ref,
26
+ method: request.request_method,
27
+ path: request.fullpath
26
28
  }
27
29
 
28
30
  ActiveSupport::Notifications.instrument("start_processing.action_controller", raw_payload.dup)
@@ -46,9 +48,9 @@ module ActionController
46
48
  render_output
47
49
  end
48
50
 
49
- def send_file(path, options={})
51
+ def send_file(path, options = {})
50
52
  ActiveSupport::Notifications.instrument("send_file.action_controller",
51
- options.merge(:path => path)) do
53
+ options.merge(path: path)) do
52
54
  super
53
55
  end
54
56
  end
@@ -72,25 +74,22 @@ module ActionController
72
74
 
73
75
  # A hook invoked every time a before callback is halted.
74
76
  def halted_callback_hook(filter)
75
- ActiveSupport::Notifications.instrument("halted_callback.action_controller", :filter => filter)
77
+ ActiveSupport::Notifications.instrument("halted_callback.action_controller", filter: filter)
76
78
  end
77
79
 
78
- # A hook which allows you to clean up any time taken into account in
79
- # views wrongly, like database querying time.
80
+ # A hook which allows you to clean up any time, wrongly taken into account in
81
+ # views, like database querying time.
80
82
  #
81
83
  # def cleanup_view_runtime
82
84
  # super - time_taken_in_something_expensive
83
85
  # end
84
- #
85
- # :api: plugin
86
- def cleanup_view_runtime #:nodoc:
86
+ def cleanup_view_runtime # :doc:
87
87
  yield
88
88
  end
89
89
 
90
90
  # Every time after an action is processed, this method is invoked
91
91
  # with the payload, so you can add more information.
92
- # :api: plugin
93
- def append_info_to_payload(payload) #:nodoc:
92
+ def append_info_to_payload(payload) # :doc:
94
93
  payload[:view_runtime] = view_runtime
95
94
  end
96
95
 
@@ -98,7 +97,6 @@ module ActionController
98
97
  # A hook which allows other frameworks to log what happened during
99
98
  # controller process action. This method should return an array
100
99
  # with the messages to be added.
101
- # :api: plugin
102
100
  def log_process_action(payload) #:nodoc:
103
101
  messages, view_runtime = [], payload[:view_runtime]
104
102
  messages << ("Views: %.1fms" % view_runtime.to_f) if view_runtime
@@ -1,9 +1,11 @@
1
- require 'action_dispatch/http/response'
2
- require 'delegate'
3
- require 'active_support/json'
1
+ # frozen_string_literal: true
2
+
3
+ require "action_dispatch/http/response"
4
+ require "delegate"
5
+ require "active_support/json"
4
6
 
5
7
  module ActionController
6
- # Mix this module in to your controller, and all actions in that controller
8
+ # Mix this module into your controller, and all actions in that controller
7
9
  # will be able to stream data to the client as it's written.
8
10
  #
9
11
  # class MyController < ActionController::Base
@@ -20,7 +22,7 @@ module ActionController
20
22
  # end
21
23
  # end
22
24
  #
23
- # There are a few caveats with this use. You *cannot* write headers after the
25
+ # There are a few caveats with this module. You *cannot* write headers after the
24
26
  # response has been committed (Response#committed? will return truthy).
25
27
  # Calling +write+ or +close+ on the response stream will cause the response
26
28
  # object to be committed. Make sure all headers are set before calling write
@@ -33,6 +35,20 @@ module ActionController
33
35
  # the main thread. Make sure your actions are thread safe, and this shouldn't
34
36
  # be a problem (don't share state across threads, etc).
35
37
  module Live
38
+ extend ActiveSupport::Concern
39
+
40
+ module ClassMethods
41
+ def make_response!(request)
42
+ if request.get_header("HTTP_VERSION") == "HTTP/1.0"
43
+ super
44
+ else
45
+ Live::Response.new.tap do |res|
46
+ res.request = request
47
+ end
48
+ end
49
+ end
50
+ end
51
+
36
52
  # This class provides the ability to write an SSE (Server Sent Event)
37
53
  # to an IO stream. The class is initialized with a stream and can be used
38
54
  # to either write a JSON string or an object which can be converted to JSON.
@@ -70,7 +86,6 @@ module ActionController
70
86
  # Note: SSEs are not currently supported by IE. However, they are supported
71
87
  # by Chrome, Firefox, Opera, and Safari.
72
88
  class SSE
73
-
74
89
  WHITELISTED_OPTIONS = %w( retry event id )
75
90
 
76
91
  def initialize(stream, options = {})
@@ -102,7 +117,7 @@ module ActionController
102
117
  end
103
118
  end
104
119
 
105
- message = json.gsub(/\n/, "\ndata: ")
120
+ message = json.gsub("\n".freeze, "\ndata: ".freeze)
106
121
  @stream.write "data: #{message}\n\n"
107
122
  end
108
123
  end
@@ -131,8 +146,8 @@ module ActionController
131
146
 
132
147
  def write(string)
133
148
  unless @response.committed?
134
- @response.headers["Cache-Control"] = "no-cache"
135
- @response.headers.delete "Content-Length"
149
+ @response.set_header "Cache-Control", "no-cache"
150
+ @response.delete_header "Content-Length"
136
151
  end
137
152
 
138
153
  super
@@ -149,14 +164,6 @@ module ActionController
149
164
  end
150
165
  end
151
166
 
152
- def each
153
- @response.sending!
154
- while str = @buf.pop
155
- yield str
156
- end
157
- @response.sent!
158
- end
159
-
160
167
  # Write a 'close' event to the buffer; the producer/writing thread
161
168
  # uses this to notify us that it's finished supplying content.
162
169
  #
@@ -189,12 +196,6 @@ module ActionController
189
196
  !@aborted
190
197
  end
191
198
 
192
- def await_close
193
- synchronize do
194
- @cv.wait_until { @closed }
195
- end
196
- end
197
-
198
199
  def on_error(&block)
199
200
  @error_callback = block
200
201
  end
@@ -202,60 +203,36 @@ module ActionController
202
203
  def call_on_error
203
204
  @error_callback.call
204
205
  end
205
- end
206
206
 
207
- class Response < ActionDispatch::Response #:nodoc: all
208
- class Header < DelegateClass(Hash) # :nodoc:
209
- def initialize(response, header)
210
- @response = response
211
- super(header)
212
- end
207
+ private
213
208
 
214
- def []=(k,v)
215
- if @response.committed?
216
- raise ActionDispatch::IllegalStateError, 'header already sent'
209
+ def each_chunk(&block)
210
+ loop do
211
+ str = nil
212
+ ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
213
+ str = @buf.pop
214
+ end
215
+ break unless str
216
+ yield str
217
217
  end
218
-
219
- super
220
- end
221
-
222
- def merge(other)
223
- self.class.new @response, __getobj__.merge(other)
224
218
  end
219
+ end
225
220
 
226
- def to_hash
227
- __getobj__.dup
228
- end
229
- end
230
-
221
+ class Response < ActionDispatch::Response #:nodoc: all
231
222
  private
232
223
 
233
- def before_committed
234
- super
235
- jar = request.cookie_jar
236
- # The response can be committed multiple times
237
- jar.write self unless committed?
238
- end
239
-
240
- def before_sending
241
- super
242
- request.cookie_jar.commit!
243
- headers.freeze
244
- end
245
-
246
- def build_buffer(response, body)
247
- buf = Live::Buffer.new response
248
- body.each { |part| buf.write part }
249
- buf
250
- end
251
-
252
- def merge_default_headers(original, default)
253
- Header.new self, super
254
- end
224
+ def before_committed
225
+ super
226
+ jar = request.cookie_jar
227
+ # The response can be committed multiple times
228
+ jar.write self unless committed?
229
+ end
255
230
 
256
- def handle_conditional_get!
257
- super unless committed?
258
- end
231
+ def build_buffer(response, body)
232
+ buf = Live::Buffer.new response
233
+ body.each { |part| buf.write part }
234
+ buf
235
+ end
259
236
  end
260
237
 
261
238
  def process(name)
@@ -264,47 +241,63 @@ module ActionController
264
241
 
265
242
  error = nil
266
243
  # This processes the action in a child thread. It lets us return the
267
- # response code and headers back up the rack stack, and still process
268
- # the body in parallel with sending data to the client
269
- Thread.new {
270
- t2 = Thread.current
271
- t2.abort_on_exception = true
272
-
273
- # Since we're processing the view in a different thread, copy the
274
- # thread locals from the main thread to the child thread. :'(
275
- locals.each { |k,v| t2[k] = v }
276
-
277
- begin
278
- super(name)
279
- rescue => e
280
- if @_response.committed?
281
- begin
282
- @_response.stream.write(ActionView::Base.streaming_completion_on_exception) if request.format == :html
283
- @_response.stream.call_on_error
284
- rescue => exception
285
- log_error(exception)
286
- ensure
287
- log_error(e)
288
- @_response.stream.close
244
+ # response code and headers back up the Rack stack, and still process
245
+ # the body in parallel with sending data to the client.
246
+ new_controller_thread {
247
+ ActiveSupport::Dependencies.interlock.running do
248
+ t2 = Thread.current
249
+
250
+ # Since we're processing the view in a different thread, copy the
251
+ # thread locals from the main thread to the child thread. :'(
252
+ locals.each { |k, v| t2[k] = v }
253
+
254
+ begin
255
+ super(name)
256
+ rescue => e
257
+ if @_response.committed?
258
+ begin
259
+ @_response.stream.write(ActionView::Base.streaming_completion_on_exception) if request.format == :html
260
+ @_response.stream.call_on_error
261
+ rescue => exception
262
+ log_error(exception)
263
+ ensure
264
+ log_error(e)
265
+ @_response.stream.close
266
+ end
267
+ else
268
+ error = e
289
269
  end
290
- else
291
- error = e
270
+ ensure
271
+ @_response.commit!
292
272
  end
293
- ensure
294
- @_response.commit!
295
273
  end
296
274
  }
297
275
 
298
- @_response.await_commit
276
+ ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
277
+ @_response.await_commit
278
+ end
279
+
299
280
  raise error if error
300
281
  end
301
282
 
283
+ # Spawn a new thread to serve up the controller in. This is to get
284
+ # around the fact that Rack isn't based around IOs and we need to use
285
+ # a thread to stream data from the response bodies. Nobody should call
286
+ # this method except in Rails internals. Seriously!
287
+ def new_controller_thread # :nodoc:
288
+ Thread.new {
289
+ t2 = Thread.current
290
+ t2.abort_on_exception = true
291
+ yield
292
+ }
293
+ end
294
+
302
295
  def log_error(exception)
303
296
  logger = ActionController::Base.logger
304
297
  return unless logger
305
298
 
306
299
  logger.fatal do
307
- message = "\n#{exception.class} (#{exception.message}):\n"
300
+ message = "\n#{exception.class} (#{exception.message}):\n".dup
308
301
  message << exception.annoted_source_code.to_s if exception.respond_to?(:annoted_source_code)
309
302
  message << " " << exception.backtrace.join("\n ")
310
303
  "#{message}\n\n"
@@ -315,14 +308,5 @@ module ActionController
315
308
  super
316
309
  response.close if response
317
310
  end
318
-
319
- def set_response!(request)
320
- if request.env["HTTP_VERSION"] == "HTTP/1.0"
321
- super
322
- else
323
- @_response = Live::Response.new
324
- @_response.request = request
325
- end
326
- end
327
311
  end
328
312
  end