actionpack 7.0.2.3 → 7.0.4

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 (45) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +86 -0
  3. data/lib/abstract_controller/base.rb +6 -5
  4. data/lib/abstract_controller/callbacks.rb +1 -1
  5. data/lib/abstract_controller/helpers.rb +1 -1
  6. data/lib/action_controller/api.rb +5 -5
  7. data/lib/action_controller/base.rb +5 -4
  8. data/lib/action_controller/form_builder.rb +2 -2
  9. data/lib/action_controller/metal/conditional_get.rb +1 -1
  10. data/lib/action_controller/metal/content_security_policy.rb +35 -1
  11. data/lib/action_controller/metal/helpers.rb +1 -1
  12. data/lib/action_controller/metal/http_authentication.rb +56 -29
  13. data/lib/action_controller/metal/live.rb +1 -0
  14. data/lib/action_controller/metal/permissions_policy.rb +18 -27
  15. data/lib/action_controller/metal/redirecting.rb +4 -3
  16. data/lib/action_controller/metal/renderers.rb +10 -11
  17. data/lib/action_controller/metal/rendering.rb +3 -3
  18. data/lib/action_controller/metal/request_forgery_protection.rb +4 -4
  19. data/lib/action_controller/metal/streaming.rb +5 -5
  20. data/lib/action_controller/metal/strong_parameters.rb +13 -4
  21. data/lib/action_controller/metal/url_for.rb +3 -3
  22. data/lib/action_controller/metal.rb +3 -3
  23. data/lib/action_controller/renderer.rb +1 -1
  24. data/lib/action_controller/test_case.rb +3 -3
  25. data/lib/action_controller.rb +1 -0
  26. data/lib/action_dispatch/http/content_security_policy.rb +71 -8
  27. data/lib/action_dispatch/http/mime_negotiation.rb +2 -2
  28. data/lib/action_dispatch/http/permissions_policy.rb +16 -0
  29. data/lib/action_dispatch/http/request.rb +2 -2
  30. data/lib/action_dispatch/http/response.rb +2 -3
  31. data/lib/action_dispatch/middleware/cookies.rb +6 -3
  32. data/lib/action_dispatch/middleware/flash.rb +8 -7
  33. data/lib/action_dispatch/middleware/request_id.rb +1 -1
  34. data/lib/action_dispatch/middleware/server_timing.rb +53 -10
  35. data/lib/action_dispatch/middleware/session/cookie_store.rb +9 -9
  36. data/lib/action_dispatch/routing/mapper.rb +5 -5
  37. data/lib/action_dispatch/routing/redirection.rb +5 -0
  38. data/lib/action_dispatch/routing/route_set.rb +3 -1
  39. data/lib/action_dispatch/routing/url_for.rb +3 -3
  40. data/lib/action_dispatch/routing.rb +3 -4
  41. data/lib/action_dispatch/testing/assertions/routing.rb +3 -2
  42. data/lib/action_dispatch/testing/test_response.rb +20 -2
  43. data/lib/action_pack/gem_version.rb +3 -3
  44. data/lib/action_pack/version.rb +1 -1
  45. metadata +12 -12
@@ -31,8 +31,7 @@ module ActionController
31
31
  class_attribute :_renderers, default: Set.new.freeze
32
32
  end
33
33
 
34
- # Used in <tt>ActionController::Base</tt>
35
- # and <tt>ActionController::API</tt> to include all
34
+ # Used in ActionController::Base and ActionController::API to include all
36
35
  # renderers by default.
37
36
  module All
38
37
  extend ActiveSupport::Concern
@@ -45,7 +44,7 @@ module ActionController
45
44
 
46
45
  # Adds a new renderer to call within controller actions.
47
46
  # A renderer is invoked by passing its name as an option to
48
- # <tt>AbstractController::Rendering#render</tt>. To create a renderer
47
+ # AbstractController::Rendering#render. To create a renderer
49
48
  # pass it a name and a block. The block takes two arguments, the first
50
49
  # is the value paired with its key and the second is the remaining
51
50
  # hash of options passed to +render+.
@@ -96,18 +95,18 @@ module ActionController
96
95
  # Adds, by name, a renderer or renderers to the +_renderers+ available
97
96
  # to call within controller actions.
98
97
  #
99
- # It is useful when rendering from an <tt>ActionController::Metal</tt> controller or
98
+ # It is useful when rendering from an ActionController::Metal controller or
100
99
  # otherwise to add an available renderer proc to a specific controller.
101
100
  #
102
- # Both <tt>ActionController::Base</tt> and <tt>ActionController::API</tt>
103
- # include <tt>ActionController::Renderers::All</tt>, making all renderers
101
+ # Both ActionController::Base and ActionController::API
102
+ # include ActionController::Renderers::All, making all renderers
104
103
  # available in the controller. See <tt>Renderers::RENDERERS</tt> and <tt>Renderers.add</tt>.
105
104
  #
106
- # Since <tt>ActionController::Metal</tt> controllers cannot render, the controller
107
- # must include <tt>AbstractController::Rendering</tt>, <tt>ActionController::Rendering</tt>,
108
- # and <tt>ActionController::Renderers</tt>, and have at least one renderer.
105
+ # Since ActionController::Metal controllers cannot render, the controller
106
+ # must include AbstractController::Rendering, ActionController::Rendering,
107
+ # and ActionController::Renderers, and have at least one renderer.
109
108
  #
110
- # Rather than including <tt>ActionController::Renderers::All</tt> and including all renderers,
109
+ # Rather than including ActionController::Renderers::All and including all renderers,
111
110
  # you may specify which renderers to include by passing the renderer name or names to
112
111
  # +use_renderers+. For example, a controller that includes only the <tt>:json</tt> renderer
113
112
  # (+_render_with_renderer_json+) might look like:
@@ -133,7 +132,7 @@ module ActionController
133
132
  alias use_renderer use_renderers
134
133
  end
135
134
 
136
- # Called by +render+ in <tt>AbstractController::Rendering</tt>
135
+ # Called by +render+ in AbstractController::Rendering
137
136
  # which sets the return value as the +response_body+.
138
137
  #
139
138
  # If no renderer is found, +super+ returns control to
@@ -30,7 +30,7 @@ module ActionController
30
30
  super
31
31
  end
32
32
 
33
- # Overwrite render_to_string because body can now be set to a Rack body.
33
+ # Override render_to_string because body can now be set to a Rack body.
34
34
  def render_to_string(*)
35
35
  result = super
36
36
  if result.respond_to?(:each)
@@ -78,8 +78,8 @@ module ActionController
78
78
  end
79
79
 
80
80
  def _set_vary_header
81
- if self.headers["Vary"].blank? && request.should_apply_vary_header?
82
- self.headers["Vary"] = "Accept"
81
+ if response.headers["Vary"].blank? && request.should_apply_vary_header?
82
+ response.headers["Vary"] = "Accept"
83
83
  end
84
84
  end
85
85
 
@@ -32,7 +32,7 @@ module ActionController # :nodoc:
32
32
  # response may be extracted. To prevent this, only XmlHttpRequest (known as XHR or
33
33
  # Ajax) requests are allowed to make requests for JavaScript responses.
34
34
  #
35
- # Subclasses of <tt>ActionController::Base</tt> are protected by default with the
35
+ # Subclasses of ActionController::Base are protected by default with the
36
36
  # <tt>:exception</tt> strategy, which raises an
37
37
  # <tt>ActionController::InvalidAuthenticityToken</tt> error on unverified requests.
38
38
  #
@@ -124,8 +124,8 @@ module ActionController # :nodoc:
124
124
  #
125
125
  # Valid Options:
126
126
  #
127
- # * <tt>:only/:except</tt> - Only apply forgery protection to a subset of actions. For example <tt>only: [ :create, :create_all ]</tt>.
128
- # * <tt>:if/:unless</tt> - Turn off the forgery protection entirely depending on the passed Proc or method reference.
127
+ # * <tt>:only</tt> / <tt>:except</tt> - Only apply forgery protection to a subset of actions. For example <tt>only: [ :create, :create_all ]</tt>.
128
+ # * <tt>:if</tt> / <tt>:unless</tt> - Turn off the forgery protection entirely depending on the passed Proc or method reference.
129
129
  # * <tt>:prepend</tt> - By default, the verification of the authentication token will be added at the position of the
130
130
  # protect_from_forgery call in your application. This means any callbacks added before are run first. This is useful
131
131
  # when you want your forgery protection to depend on other callbacks, like authentication methods (Oauth vs Cookie auth).
@@ -168,7 +168,7 @@ module ActionController # :nodoc:
168
168
  #
169
169
  # See +skip_before_action+ for allowed options.
170
170
  def skip_forgery_protection(options = {})
171
- skip_before_action :verify_authenticity_token, options
171
+ skip_before_action :verify_authenticity_token, options.reverse_merge(raise: false)
172
172
  end
173
173
 
174
174
  private
@@ -24,7 +24,7 @@ module ActionController # :nodoc:
24
24
  # Ruby implementation).
25
25
  #
26
26
  # Streaming can be added to a given template easily, all you need to do is
27
- # to pass the :stream option.
27
+ # to pass the +:stream+ option.
28
28
  #
29
29
  # class PostsController
30
30
  # def index
@@ -59,8 +59,8 @@ module ActionController # :nodoc:
59
59
  # render stream: true
60
60
  # end
61
61
  #
62
- # Notice that :stream only works with templates. Rendering :json
63
- # or :xml with :stream won't work.
62
+ # Notice that +:stream+ only works with templates. Rendering +:json+
63
+ # or +:xml+ with +:stream+ won't work.
64
64
  #
65
65
  # == Communication between layout and template
66
66
  #
@@ -72,7 +72,7 @@ module ActionController # :nodoc:
72
72
  # variables set in the template to be used in the layout, they won't
73
73
  # work once you move to streaming. The proper way to communicate
74
74
  # between layout and template, regardless of whether you use streaming
75
- # or not, is by using +content_for+, +provide+ and +yield+.
75
+ # or not, is by using +content_for+, +provide+, and +yield+.
76
76
  #
77
77
  # Take a simple example where the layout expects the template to tell
78
78
  # which title to use:
@@ -132,7 +132,7 @@ module ActionController # :nodoc:
132
132
  # That said, when streaming, you need to properly check your templates
133
133
  # and choose when to use +provide+ and +content_for+.
134
134
  #
135
- # == Headers, cookies, session and flash
135
+ # == Headers, cookies, session, and flash
136
136
  #
137
137
  # When streaming, the HTTP headers are sent to the client right before
138
138
  # it renders the first line. This means that, modifying headers, cookies,
@@ -236,7 +236,7 @@ module ActionController
236
236
  # By default, never raise an UnpermittedParameters exception if these
237
237
  # params are present. The default includes both 'controller' and 'action'
238
238
  # because they are added by Rails and should be of no concern. One way
239
- # to change these is to specify `always_permitted_parameters` in your
239
+ # to change these is to specify +always_permitted_parameters+ in your
240
240
  # config. For instance:
241
241
  #
242
242
  # config.action_controller.always_permitted_parameters = %w( controller action format )
@@ -279,10 +279,15 @@ module ActionController
279
279
  @parameters == other
280
280
  end
281
281
  end
282
- alias eql? ==
282
+
283
+ def eql?(other)
284
+ self.class == other.class &&
285
+ permitted? == other.permitted? &&
286
+ parameters.eql?(other.parameters)
287
+ end
283
288
 
284
289
  def hash
285
- [@parameters.hash, @permitted].hash
290
+ [self.class, @parameters, @permitted].hash
286
291
  end
287
292
 
288
293
  # Returns a safe <tt>ActiveSupport::HashWithIndifferentAccess</tt>
@@ -778,7 +783,7 @@ module ActionController
778
783
 
779
784
  # Deletes a key-value pair from +Parameters+ and returns the value. If
780
785
  # +key+ is not found, returns +nil+ (or, with optional code block, yields
781
- # +key+ and returns the result). Cf. +#extract!+, which returns the
786
+ # +key+ and returns the result). Cf. #extract!, which returns the
782
787
  # corresponding +ActionController::Parameters+ object.
783
788
  def delete(key, &block)
784
789
  convert_value_to_parameters(@parameters.delete(key, &block))
@@ -908,6 +913,10 @@ module ActionController
908
913
  end
909
914
  end
910
915
 
916
+ def encode_with(coder) # :nodoc:
917
+ coder.map = { "parameters" => @parameters, "permitted" => @permitted }
918
+ end
919
+
911
920
  # Returns duplicate of object including all parameters.
912
921
  def deep_dup
913
922
  self.class.new(@parameters.deep_dup, @logging_context).tap do |duplicate|
@@ -4,11 +4,11 @@ module ActionController
4
4
  # Includes +url_for+ into the host class. The class has to provide a +RouteSet+ by implementing
5
5
  # the <tt>_routes</tt> method. Otherwise, an exception will be raised.
6
6
  #
7
- # In addition to <tt>AbstractController::UrlFor</tt>, this module accesses the HTTP layer to define
7
+ # In addition to AbstractController::UrlFor, this module accesses the HTTP layer to define
8
8
  # URL options like the +host+. In order to do so, this module requires the host class
9
9
  # to implement +env+ which needs to be Rack-compatible and +request+
10
- # which is either an instance of +ActionDispatch::Request+ or an object
11
- # that responds to the +host+, +optional_port+, +protocol+ and
10
+ # which is either an instance of ActionDispatch::Request or an object
11
+ # that responds to the +host+, +optional_port+, +protocol+, and
12
12
  # +symbolized_path_parameter+ methods.
13
13
  #
14
14
  # class RootUrl
@@ -60,7 +60,7 @@ module ActionController
60
60
 
61
61
  # <tt>ActionController::Metal</tt> is the simplest possible controller, providing a
62
62
  # valid Rack interface without the additional niceties provided by
63
- # <tt>ActionController::Base</tt>.
63
+ # ActionController::Base.
64
64
  #
65
65
  # A sample metal controller might look like this:
66
66
  #
@@ -111,7 +111,7 @@ module ActionController
111
111
  #
112
112
  # == Other Helpers
113
113
  #
114
- # You can refer to the modules included in <tt>ActionController::Base</tt> to see
114
+ # You can refer to the modules included in ActionController::Base to see
115
115
  # other features you can bring into your metal controller.
116
116
  #
117
117
  class Metal < AbstractController::Base
@@ -137,7 +137,7 @@ module ActionController
137
137
  false
138
138
  end
139
139
 
140
- # Delegates to the class' <tt>controller_name</tt>.
140
+ # Delegates to the class's ::controller_name.
141
141
  def controller_name
142
142
  self.class.controller_name
143
143
  end
@@ -71,7 +71,7 @@ module ActionController
71
71
  # Render templates with any options from ActionController::Base#render_to_string.
72
72
  #
73
73
  # The primary options are:
74
- # * <tt>:partial</tt> - See <tt>ActionView::PartialRenderer</tt> for details.
74
+ # * <tt>:partial</tt> - See ActionView::PartialRenderer for details.
75
75
  # * <tt>:file</tt> - Renders an explicit template file. Add <tt>:locals</tt> to pass in, if so desired.
76
76
  # It shouldn’t be used directly with unsanitized user input due to lack of validation.
77
77
  # * <tt>:inline</tt> - Renders an ERB template string.
@@ -241,7 +241,7 @@ module ActionController
241
241
  # == Basic example
242
242
  #
243
243
  # Functional tests are written as follows:
244
- # 1. First, one uses the +get+, +post+, +patch+, +put+, +delete+ or +head+ method to simulate
244
+ # 1. First, one uses the +get+, +post+, +patch+, +put+, +delete+, or +head+ method to simulate
245
245
  # an HTTP request.
246
246
  # 2. Then, one asserts whether the current state is as expected. "State" can be anything:
247
247
  # the controller's HTTP response, the database contents, etc.
@@ -391,7 +391,7 @@ module ActionController
391
391
  #
392
392
  # You can also simulate POST, PATCH, PUT, DELETE, and HEAD requests with
393
393
  # +post+, +patch+, +put+, +delete+, and +head+.
394
- # Example sending parameters, session and setting a flash message:
394
+ # Example sending parameters, session, and setting a flash message:
395
395
  #
396
396
  # get :show,
397
397
  # params: { id: 7 },
@@ -461,7 +461,7 @@ module ActionController
461
461
  # session: { user_id: 1 },
462
462
  # flash: { notice: 'This is flash message' }
463
463
  #
464
- # To simulate +GET+, +POST+, +PATCH+, +PUT+, +DELETE+ and +HEAD+ requests
464
+ # To simulate +GET+, +POST+, +PATCH+, +PUT+, +DELETE+, and +HEAD+ requests
465
465
  # prefer using #get, #post, #patch, #put, #delete and #head methods
466
466
  # respectively which will make tests more expressive.
467
467
  #
@@ -3,6 +3,7 @@
3
3
  require "abstract_controller"
4
4
  require "action_dispatch"
5
5
  require "action_controller/metal/strong_parameters"
6
+ require "action_controller/metal/exceptions"
6
7
 
7
8
  module ActionController
8
9
  extend ActiveSupport::Autoload
@@ -1,8 +1,26 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "active_support/core_ext/object/deep_dup"
4
+ require "active_support/core_ext/array/wrap"
4
5
 
5
6
  module ActionDispatch # :nodoc:
7
+ # Configures the HTTP
8
+ # {Content-Security-Policy}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy]
9
+ # response header to help protect against XSS and injection attacks.
10
+ #
11
+ # Example global policy:
12
+ #
13
+ # Rails.application.config.content_security_policy do |policy|
14
+ # policy.default_src :self, :https
15
+ # policy.font_src :self, :https, :data
16
+ # policy.img_src :self, :https, :data
17
+ # policy.object_src :none
18
+ # policy.script_src :self, :https
19
+ # policy.style_src :self, :https
20
+ #
21
+ # # Specify URI for violation reports
22
+ # policy.report_uri "/csp-violation-report-endpoint"
23
+ # end
6
24
  class ContentSecurityPolicy
7
25
  class Middleware
8
26
  CONTENT_TYPE = "Content-Type"
@@ -17,7 +35,6 @@ module ActionDispatch # :nodoc:
17
35
  request = ActionDispatch::Request.new env
18
36
  _, headers, _ = response = @app.call(env)
19
37
 
20
- return response unless html_response?(headers)
21
38
  return response if policy_present?(headers)
22
39
 
23
40
  if policy = request.content_security_policy
@@ -31,12 +48,6 @@ module ActionDispatch # :nodoc:
31
48
  end
32
49
 
33
50
  private
34
- def html_response?(headers)
35
- if content_type = headers[CONTENT_TYPE]
36
- /html/.match?(content_type)
37
- end
38
- end
39
-
40
51
  def header_name(request)
41
52
  if request.content_security_policy_report_only
42
53
  POLICY_REPORT_ONLY
@@ -174,6 +185,15 @@ module ActionDispatch # :nodoc:
174
185
  end
175
186
  end
176
187
 
188
+ # Specify whether to prevent the user agent from loading any assets over
189
+ # HTTP when the page uses HTTPS:
190
+ #
191
+ # policy.block_all_mixed_content
192
+ #
193
+ # Pass +false+ to allow it again:
194
+ #
195
+ # policy.block_all_mixed_content false
196
+ #
177
197
  def block_all_mixed_content(enabled = true)
178
198
  if enabled
179
199
  @directives["block-all-mixed-content"] = true
@@ -182,6 +202,14 @@ module ActionDispatch # :nodoc:
182
202
  end
183
203
  end
184
204
 
205
+ # Restricts the set of plugins that can be embedded:
206
+ #
207
+ # policy.plugin_types "application/x-shockwave-flash"
208
+ #
209
+ # Leave empty to allow all plugins:
210
+ #
211
+ # policy.plugin_types
212
+ #
185
213
  def plugin_types(*types)
186
214
  if types.first
187
215
  @directives["plugin-types"] = types
@@ -190,10 +218,24 @@ module ActionDispatch # :nodoc:
190
218
  end
191
219
  end
192
220
 
221
+ # Enable the {report-uri}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/report-uri]
222
+ # directive. Violation reports will be sent to the specified URI:
223
+ #
224
+ # policy.report_uri "/csp-violation-report-endpoint"
225
+ #
193
226
  def report_uri(uri)
194
227
  @directives["report-uri"] = [uri]
195
228
  end
196
229
 
230
+ # Specify asset types for which {Subresource Integrity}[https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity]
231
+ # is required:
232
+ #
233
+ # policy.require_sri_for :script, :style
234
+ #
235
+ # Leave empty to not require Subresource Integrity:
236
+ #
237
+ # policy.require_sri_for
238
+ #
197
239
  def require_sri_for(*types)
198
240
  if types.first
199
241
  @directives["require-sri-for"] = types
@@ -202,6 +244,19 @@ module ActionDispatch # :nodoc:
202
244
  end
203
245
  end
204
246
 
247
+ # Specify whether a {sandbox}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/sandbox]
248
+ # should be enabled for the requested resource:
249
+ #
250
+ # policy.sandbox
251
+ #
252
+ # Values can be passed as arguments:
253
+ #
254
+ # policy.sandbox "allow-scripts", "allow-modals"
255
+ #
256
+ # Pass +false+ to disable the sandbox:
257
+ #
258
+ # policy.sandbox false
259
+ #
205
260
  def sandbox(*values)
206
261
  if values.empty?
207
262
  @directives["sandbox"] = true
@@ -212,6 +267,14 @@ module ActionDispatch # :nodoc:
212
267
  end
213
268
  end
214
269
 
270
+ # Specify whether user agents should treat any assets over HTTP as HTTPS:
271
+ #
272
+ # policy.upgrade_insecure_requests
273
+ #
274
+ # Pass +false+ to disable it:
275
+ #
276
+ # policy.upgrade_insecure_requests false
277
+ #
215
278
  def upgrade_insecure_requests(enabled = true)
216
279
  if enabled
217
280
  @directives["upgrade-insecure-requests"] = true
@@ -276,7 +339,7 @@ module ActionDispatch # :nodoc:
276
339
  raise RuntimeError, "Missing context for the dynamic content security policy source: #{source.inspect}"
277
340
  else
278
341
  resolved = context.instance_exec(&source)
279
- resolved.is_a?(Symbol) ? apply_mapping(resolved) : resolved
342
+ apply_mappings(Array.wrap(resolved))
280
343
  end
281
344
  else
282
345
  raise RuntimeError, "Unexpected content security policy source: #{source.inspect}"
@@ -132,8 +132,8 @@ module ActionDispatch
132
132
  # Sets the \formats by string extensions. This differs from #format= by allowing you
133
133
  # to set multiple, ordered formats, which is useful when you want to have a fallback.
134
134
  #
135
- # In this example, the :iphone format will be used if it's available, otherwise it'll fallback
136
- # to the :html format.
135
+ # In this example, the +:iphone+ format will be used if it's available, otherwise it'll fallback
136
+ # to the +:html+ format.
137
137
  #
138
138
  # class ApplicationController < ActionController::Base
139
139
  # before_action :adjust_format_for_iphone_with_html_fallback
@@ -3,6 +3,22 @@
3
3
  require "active_support/core_ext/object/deep_dup"
4
4
 
5
5
  module ActionDispatch # :nodoc:
6
+ # Configures the HTTP
7
+ # {Feature-Policy}[https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Feature-Policy]
8
+ # response header to specify which browser features the current document and
9
+ # its iframes can use.
10
+ #
11
+ # Example global policy:
12
+ #
13
+ # Rails.application.config.permissions_policy do |policy|
14
+ # policy.camera :none
15
+ # policy.gyroscope :none
16
+ # policy.microphone :none
17
+ # policy.usb :none
18
+ # policy.fullscreen :self
19
+ # policy.payment :self, "https://secure.example.com"
20
+ # end
21
+ #
6
22
  class PermissionsPolicy
7
23
  class Middleware
8
24
  CONTENT_TYPE = "Content-Type"
@@ -298,8 +298,8 @@ module ActionDispatch
298
298
  ACTION_DISPATCH_REQUEST_ID = "action_dispatch.request_id" # :nodoc:
299
299
 
300
300
  # Returns the unique request id, which is based on either the X-Request-Id header that can
301
- # be generated by a firewall, load balancer, or web server or by the RequestId middleware
302
- # (which sets the action_dispatch.request_id environment variable).
301
+ # be generated by a firewall, load balancer, or web server, or by the RequestId middleware
302
+ # (which sets the +action_dispatch.request_id+ environment variable).
303
303
  #
304
304
  # This unique ID is useful for tracing a request from end-to-end as part of logging or debugging.
305
305
  # This relies on the Rack variable set by the ActionDispatch::RequestId middleware.
@@ -21,9 +21,8 @@ module ActionDispatch # :nodoc:
21
21
  # Nevertheless, integration tests may want to inspect controller responses in
22
22
  # more detail, and that's when \Response can be useful for application
23
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).
24
+ # Integration::RequestHelpers#get and Integration::RequestHelpers#post return
25
+ # objects of type TestResponse (which are of course also of type \Response).
27
26
  #
28
27
  # For example, the following demo integration test prints the body of the
29
28
  # controller response to the console:
@@ -92,7 +92,7 @@ module ActionDispatch
92
92
  include RequestCookieMethods
93
93
  end
94
94
 
95
- # Read and write data to cookies through ActionController#cookies.
95
+ # Read and write data to cookies through ActionController::Base#cookies.
96
96
  #
97
97
  # When reading cookie data, the data is read from the HTTP request header, Cookie.
98
98
  # When writing cookie data, the data is sent out in the HTTP response header, Set-Cookie.
@@ -139,7 +139,7 @@ module ActionDispatch
139
139
  #
140
140
  # cookies.delete :user_name
141
141
  #
142
- # Please note that if you specify a :domain when setting a cookie, you must also specify the domain when deleting the cookie:
142
+ # Please note that if you specify a +:domain+ when setting a cookie, you must also specify the domain when deleting the cookie:
143
143
  #
144
144
  # cookies[:name] = {
145
145
  # value: 'a yummy cookie',
@@ -176,6 +176,9 @@ module ActionDispatch
176
176
  # Default is +false+.
177
177
  # * <tt>:httponly</tt> - Whether this cookie is accessible via scripting or
178
178
  # only HTTP. Defaults to +false+.
179
+ # * <tt>:same_site</tt> - The value of the +SameSite+ cookie attribute, which
180
+ # determines how this cookie should be restricted in cross-site contexts.
181
+ # Possible values are +:none+, +:lax+, and +:strict+. Defaults to +:lax+.
179
182
  class Cookies
180
183
  HTTP_HEADER = "Set-Cookie"
181
184
  GENERATOR_KEY = "action_dispatch.key_generator"
@@ -199,7 +202,7 @@ module ActionDispatch
199
202
  # Raised when storing more than 4K of session data.
200
203
  CookieOverflow = Class.new StandardError
201
204
 
202
- # Include in a cookie jar to allow chaining, e.g. cookies.permanent.signed.
205
+ # Include in a cookie jar to allow chaining, e.g. +cookies.permanent.signed+.
203
206
  module ChainedCookieJars
204
207
  # Returns a jar that'll automatically set the assigned cookies to have an expiration date 20 years from now. Example:
205
208
  #
@@ -20,10 +20,11 @@ module ActionDispatch
20
20
  # end
21
21
  # end
22
22
  #
23
- # show.html.erb
24
- # <% if flash[:notice] %>
25
- # <div class="notice"><%= flash[:notice] %></div>
26
- # <% end %>
23
+ # Then in +show.html.erb+:
24
+ #
25
+ # <% if flash[:notice] %>
26
+ # <div class="notice"><%= flash[:notice] %></div>
27
+ # <% end %>
27
28
  #
28
29
  # Since the +notice+ and +alert+ keys are a common idiom, convenience accessors are available:
29
30
  #
@@ -41,9 +42,9 @@ module ActionDispatch
41
42
  KEY = "action_dispatch.request.flash_hash"
42
43
 
43
44
  module RequestMethods
44
- # Access the contents of the flash. Use <tt>flash["notice"]</tt> to
45
- # read a notice you put there or <tt>flash["notice"] = "hello"</tt>
46
- # to put a new one.
45
+ # Access the contents of the flash. Returns a ActionDispatch::Flash::FlashHash.
46
+ #
47
+ # See ActionDispatch::Flash for example usage.
47
48
  def flash
48
49
  flash = flash_hash
49
50
  return flash if flash
@@ -5,7 +5,7 @@ require "active_support/core_ext/string/access"
5
5
 
6
6
  module ActionDispatch
7
7
  # Makes a unique request id available to the +action_dispatch.request_id+ env variable (which is then accessible
8
- # through <tt>ActionDispatch::Request#request_id</tt> or the alias <tt>ActionDispatch::Request#uuid</tt>) and sends
8
+ # through ActionDispatch::Request#request_id or the alias ActionDispatch::Request#uuid) and sends
9
9
  # the same id to the client via the X-Request-Id header.
10
10
  #
11
11
  # The unique request id is either based on the X-Request-Id header in the request, which would typically be generated
@@ -6,28 +6,71 @@ module ActionDispatch
6
6
  class ServerTiming
7
7
  SERVER_TIMING_HEADER = "Server-Timing"
8
8
 
9
+ class Subscriber # :nodoc:
10
+ include Singleton
11
+ KEY = :action_dispatch_server_timing_events
12
+
13
+ def initialize
14
+ @mutex = Mutex.new
15
+ end
16
+
17
+ def call(event)
18
+ if events = ActiveSupport::IsolatedExecutionState[KEY]
19
+ events << event
20
+ end
21
+ end
22
+
23
+ def collect_events
24
+ events = []
25
+ ActiveSupport::IsolatedExecutionState[KEY] = events
26
+ yield
27
+ events
28
+ ensure
29
+ ActiveSupport::IsolatedExecutionState.delete(KEY)
30
+ end
31
+
32
+ def ensure_subscribed
33
+ @mutex.synchronize do
34
+ # Subscribe to all events, except those beginning with "!"
35
+ # Ideally we would be more selective of what is being measured
36
+ @subscriber ||= ActiveSupport::Notifications.subscribe(/\A[^!]/, self)
37
+ end
38
+ end
39
+
40
+ def unsubscribe
41
+ @mutex.synchronize do
42
+ ActiveSupport::Notifications.unsubscribe @subscriber
43
+ @subscriber = nil
44
+ end
45
+ end
46
+ end
47
+
48
+ def self.unsubscribe # :nodoc:
49
+ Subscriber.instance.unsubscribe
50
+ end
51
+
9
52
  def initialize(app)
10
53
  @app = app
54
+ @subscriber = Subscriber.instance
55
+ @subscriber.ensure_subscribed
11
56
  end
12
57
 
13
58
  def call(env)
14
- events = []
15
- subscriber = ActiveSupport::Notifications.subscribe(/.*/) do |*args|
16
- events << ActiveSupport::Notifications::Event.new(*args)
59
+ response = nil
60
+ events = @subscriber.collect_events do
61
+ response = @app.call(env)
17
62
  end
18
63
 
19
- status, headers, body = begin
20
- @app.call(env)
21
- ensure
22
- ActiveSupport::Notifications.unsubscribe(subscriber)
23
- end
64
+ headers = response[1]
24
65
 
25
66
  header_info = events.group_by(&:name).map do |event_name, events_collection|
26
- "#{event_name};dur=#{events_collection.sum(&:duration)}"
67
+ "%s;dur=%.2f" % [event_name, events_collection.sum(&:duration)]
27
68
  end
69
+
70
+ header_info.prepend(headers[SERVER_TIMING_HEADER]) if headers[SERVER_TIMING_HEADER].present?
28
71
  headers[SERVER_TIMING_HEADER] = header_info.join(", ")
29
72
 
30
- [ status, headers, body ]
73
+ response
31
74
  end
32
75
  end
33
76
  end