actionpack 7.1.3 → 7.2.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +82 -501
- data/lib/abstract_controller/asset_paths.rb +2 -0
- data/lib/abstract_controller/base.rb +102 -98
- data/lib/abstract_controller/caching/fragments.rb +50 -53
- data/lib/abstract_controller/caching.rb +2 -0
- data/lib/abstract_controller/callbacks.rb +66 -64
- data/lib/abstract_controller/collector.rb +6 -6
- data/lib/abstract_controller/deprecator.rb +2 -0
- data/lib/abstract_controller/error.rb +2 -0
- data/lib/abstract_controller/helpers.rb +70 -85
- data/lib/abstract_controller/logger.rb +2 -0
- data/lib/abstract_controller/railties/routes_helpers.rb +2 -0
- data/lib/abstract_controller/rendering.rb +13 -12
- data/lib/abstract_controller/translation.rb +15 -7
- data/lib/abstract_controller/url_for.rb +8 -6
- data/lib/abstract_controller.rb +2 -0
- data/lib/action_controller/api/api_rendering.rb +2 -0
- data/lib/action_controller/api.rb +74 -72
- data/lib/action_controller/base.rb +198 -126
- data/lib/action_controller/caching.rb +15 -12
- data/lib/action_controller/deprecator.rb +2 -0
- data/lib/action_controller/form_builder.rb +20 -17
- data/lib/action_controller/log_subscriber.rb +3 -1
- data/lib/action_controller/metal/allow_browser.rb +123 -0
- data/lib/action_controller/metal/basic_implicit_render.rb +2 -0
- data/lib/action_controller/metal/conditional_get.rb +188 -174
- data/lib/action_controller/metal/content_security_policy.rb +25 -24
- data/lib/action_controller/metal/cookies.rb +4 -2
- data/lib/action_controller/metal/data_streaming.rb +64 -55
- data/lib/action_controller/metal/default_headers.rb +5 -3
- data/lib/action_controller/metal/etag_with_flash.rb +3 -1
- data/lib/action_controller/metal/etag_with_template_digest.rb +17 -15
- data/lib/action_controller/metal/exceptions.rb +11 -9
- data/lib/action_controller/metal/flash.rb +12 -10
- data/lib/action_controller/metal/head.rb +12 -10
- data/lib/action_controller/metal/helpers.rb +63 -55
- data/lib/action_controller/metal/http_authentication.rb +210 -205
- data/lib/action_controller/metal/implicit_render.rb +17 -15
- data/lib/action_controller/metal/instrumentation.rb +15 -12
- data/lib/action_controller/metal/live.rb +113 -107
- data/lib/action_controller/metal/logging.rb +6 -4
- data/lib/action_controller/metal/mime_responds.rb +151 -142
- data/lib/action_controller/metal/parameter_encoding.rb +34 -32
- data/lib/action_controller/metal/params_wrapper.rb +57 -59
- data/lib/action_controller/metal/permissions_policy.rb +13 -12
- data/lib/action_controller/metal/rate_limiting.rb +62 -0
- data/lib/action_controller/metal/redirecting.rb +108 -82
- data/lib/action_controller/metal/renderers.rb +50 -49
- data/lib/action_controller/metal/rendering.rb +103 -75
- data/lib/action_controller/metal/request_forgery_protection.rb +162 -133
- data/lib/action_controller/metal/rescue.rb +11 -9
- data/lib/action_controller/metal/streaming.rb +138 -136
- data/lib/action_controller/metal/strong_parameters.rb +525 -480
- data/lib/action_controller/metal/testing.rb +2 -0
- data/lib/action_controller/metal/url_for.rb +17 -15
- data/lib/action_controller/metal.rb +86 -60
- data/lib/action_controller/railtie.rb +3 -0
- data/lib/action_controller/railties/helpers.rb +2 -0
- data/lib/action_controller/renderer.rb +42 -36
- data/lib/action_controller/template_assertions.rb +4 -2
- data/lib/action_controller/test_case.rb +146 -126
- data/lib/action_controller.rb +10 -3
- data/lib/action_dispatch/constants.rb +2 -0
- data/lib/action_dispatch/deprecator.rb +2 -0
- data/lib/action_dispatch/http/cache.rb +27 -26
- data/lib/action_dispatch/http/content_disposition.rb +2 -0
- data/lib/action_dispatch/http/content_security_policy.rb +44 -38
- data/lib/action_dispatch/http/filter_parameters.rb +18 -9
- data/lib/action_dispatch/http/filter_redirect.rb +22 -1
- data/lib/action_dispatch/http/headers.rb +22 -22
- data/lib/action_dispatch/http/mime_negotiation.rb +30 -41
- data/lib/action_dispatch/http/mime_type.rb +31 -24
- data/lib/action_dispatch/http/mime_types.rb +2 -0
- data/lib/action_dispatch/http/parameters.rb +11 -9
- data/lib/action_dispatch/http/permissions_policy.rb +20 -44
- data/lib/action_dispatch/http/rack_cache.rb +2 -0
- data/lib/action_dispatch/http/request.rb +94 -75
- data/lib/action_dispatch/http/response.rb +73 -61
- data/lib/action_dispatch/http/upload.rb +18 -16
- data/lib/action_dispatch/http/url.rb +75 -73
- data/lib/action_dispatch/journey/formatter.rb +13 -6
- data/lib/action_dispatch/journey/gtg/builder.rb +4 -3
- data/lib/action_dispatch/journey/gtg/simulator.rb +2 -0
- data/lib/action_dispatch/journey/gtg/transition_table.rb +10 -8
- data/lib/action_dispatch/journey/nfa/dot.rb +2 -0
- data/lib/action_dispatch/journey/nodes/node.rb +6 -5
- data/lib/action_dispatch/journey/parser.rb +4 -3
- data/lib/action_dispatch/journey/parser_extras.rb +2 -0
- data/lib/action_dispatch/journey/path/pattern.rb +4 -1
- data/lib/action_dispatch/journey/route.rb +9 -7
- data/lib/action_dispatch/journey/router/utils.rb +16 -15
- data/lib/action_dispatch/journey/router.rb +4 -2
- data/lib/action_dispatch/journey/routes.rb +4 -2
- data/lib/action_dispatch/journey/scanner.rb +4 -2
- data/lib/action_dispatch/journey/visitors.rb +2 -0
- data/lib/action_dispatch/journey.rb +2 -0
- data/lib/action_dispatch/log_subscriber.rb +2 -0
- data/lib/action_dispatch/middleware/actionable_exceptions.rb +2 -0
- data/lib/action_dispatch/middleware/assume_ssl.rb +8 -5
- data/lib/action_dispatch/middleware/callbacks.rb +3 -1
- data/lib/action_dispatch/middleware/cookies.rb +119 -104
- data/lib/action_dispatch/middleware/debug_exceptions.rb +13 -5
- data/lib/action_dispatch/middleware/debug_locks.rb +15 -13
- data/lib/action_dispatch/middleware/debug_view.rb +2 -0
- data/lib/action_dispatch/middleware/exception_wrapper.rb +6 -11
- data/lib/action_dispatch/middleware/executor.rb +8 -0
- data/lib/action_dispatch/middleware/flash.rb +63 -51
- data/lib/action_dispatch/middleware/host_authorization.rb +17 -15
- data/lib/action_dispatch/middleware/public_exceptions.rb +8 -6
- data/lib/action_dispatch/middleware/reloader.rb +5 -3
- data/lib/action_dispatch/middleware/remote_ip.rb +77 -72
- data/lib/action_dispatch/middleware/request_id.rb +14 -9
- data/lib/action_dispatch/middleware/server_timing.rb +4 -2
- data/lib/action_dispatch/middleware/session/abstract_store.rb +2 -0
- data/lib/action_dispatch/middleware/session/cache_store.rb +13 -8
- data/lib/action_dispatch/middleware/session/cookie_store.rb +27 -26
- data/lib/action_dispatch/middleware/session/mem_cache_store.rb +7 -3
- data/lib/action_dispatch/middleware/show_exceptions.rb +31 -21
- data/lib/action_dispatch/middleware/ssl.rb +43 -40
- data/lib/action_dispatch/middleware/stack.rb +11 -10
- data/lib/action_dispatch/middleware/static.rb +33 -31
- data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +1 -1
- data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +1 -1
- data/lib/action_dispatch/railtie.rb +2 -4
- data/lib/action_dispatch/request/session.rb +23 -21
- data/lib/action_dispatch/request/utils.rb +2 -0
- data/lib/action_dispatch/routing/endpoint.rb +2 -0
- data/lib/action_dispatch/routing/inspector.rb +5 -3
- data/lib/action_dispatch/routing/mapper.rb +671 -636
- data/lib/action_dispatch/routing/polymorphic_routes.rb +69 -62
- data/lib/action_dispatch/routing/redirection.rb +37 -32
- data/lib/action_dispatch/routing/route_set.rb +59 -45
- data/lib/action_dispatch/routing/routes_proxy.rb +6 -4
- data/lib/action_dispatch/routing/url_for.rb +130 -125
- data/lib/action_dispatch/routing.rb +150 -148
- data/lib/action_dispatch/system_test_case.rb +91 -81
- data/lib/action_dispatch/system_testing/browser.rb +10 -3
- data/lib/action_dispatch/system_testing/driver.rb +3 -1
- data/lib/action_dispatch/system_testing/server.rb +2 -0
- data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +32 -21
- data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +2 -0
- data/lib/action_dispatch/testing/assertion_response.rb +8 -6
- data/lib/action_dispatch/testing/assertions/response.rb +26 -23
- data/lib/action_dispatch/testing/assertions/routing.rb +153 -84
- data/lib/action_dispatch/testing/assertions.rb +2 -0
- data/lib/action_dispatch/testing/integration.rb +223 -222
- data/lib/action_dispatch/testing/request_encoder.rb +2 -0
- data/lib/action_dispatch/testing/test_helpers/page_dump_helper.rb +35 -0
- data/lib/action_dispatch/testing/test_process.rb +12 -8
- data/lib/action_dispatch/testing/test_request.rb +3 -1
- data/lib/action_dispatch/testing/test_response.rb +27 -26
- data/lib/action_dispatch.rb +22 -28
- data/lib/action_pack/gem_version.rb +6 -4
- data/lib/action_pack/version.rb +3 -1
- data/lib/action_pack.rb +17 -16
- metadata +39 -16
@@ -1,33 +1,35 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
# :markup: markdown
|
4
|
+
|
3
5
|
module ActionController
|
4
|
-
#
|
6
|
+
# # Action Controller Implicit Render
|
5
7
|
#
|
6
|
-
# Handles implicit rendering for a controller action that does not
|
7
|
-
#
|
8
|
+
# Handles implicit rendering for a controller action that does not explicitly
|
9
|
+
# respond with `render`, `respond_to`, `redirect`, or `head`.
|
8
10
|
#
|
9
|
-
# For API controllers, the implicit response is always
|
11
|
+
# For API controllers, the implicit response is always `204 No Content`.
|
10
12
|
#
|
11
|
-
# For all other controllers, we use these heuristics to decide whether to
|
12
|
-
#
|
13
|
-
#
|
13
|
+
# For all other controllers, we use these heuristics to decide whether to render
|
14
|
+
# a template, raise an error for a missing template, or respond with `204 No
|
15
|
+
# Content`:
|
14
16
|
#
|
15
|
-
# First, if we DO find a template, it's rendered. Template lookup accounts
|
16
|
-
#
|
17
|
-
#
|
17
|
+
# First, if we DO find a template, it's rendered. Template lookup accounts for
|
18
|
+
# the action name, locales, format, variant, template handlers, and more (see
|
19
|
+
# `render` for details).
|
18
20
|
#
|
19
21
|
# Second, if we DON'T find a template but the controller action does have
|
20
|
-
# templates for other formats, variants, etc., then we trust that you meant
|
21
|
-
#
|
22
|
+
# templates for other formats, variants, etc., then we trust that you meant to
|
23
|
+
# provide a template for this response, too, and we raise
|
22
24
|
# ActionController::UnknownFormat with an explanation.
|
23
25
|
#
|
24
26
|
# Third, if we DON'T find a template AND the request is a page load in a web
|
25
|
-
# browser (technically, a non-XHR GET request for an HTML response) where
|
26
|
-
#
|
27
|
+
# browser (technically, a non-XHR GET request for an HTML response) where you
|
28
|
+
# reasonably expect to have rendered a template, then we raise
|
27
29
|
# ActionController::MissingExactTemplate with an explanation.
|
28
30
|
#
|
29
31
|
# Finally, if we DON'T find a template AND the request isn't a browser page
|
30
|
-
# load, then we implicitly respond with
|
32
|
+
# load, then we implicitly respond with `204 No Content`.
|
31
33
|
module ImplicitRender
|
32
34
|
# :stopdoc:
|
33
35
|
include BasicImplicitRender
|
@@ -1,14 +1,17 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
# :markup: markdown
|
4
|
+
|
3
5
|
require "benchmark"
|
4
6
|
require "abstract_controller/logger"
|
5
7
|
|
6
8
|
module ActionController
|
7
|
-
#
|
9
|
+
# # Action Controller Instrumentation
|
8
10
|
#
|
9
|
-
# Adds instrumentation to several ends in ActionController::Base. It also
|
10
|
-
# some hooks related with process_action. This allows an ORM like
|
11
|
-
# and/or DataMapper to plug in ActionController and show related
|
11
|
+
# Adds instrumentation to several ends in ActionController::Base. It also
|
12
|
+
# provides some hooks related with process_action. This allows an ORM like
|
13
|
+
# Active Record and/or DataMapper to plug in ActionController and show related
|
14
|
+
# information.
|
12
15
|
#
|
13
16
|
# Check ActiveRecord::Railties::ControllerRuntime for an example.
|
14
17
|
module Instrumentation
|
@@ -91,23 +94,23 @@ module ActionController
|
|
91
94
|
# A hook which allows you to clean up any time, wrongly taken into account in
|
92
95
|
# views, like database querying time.
|
93
96
|
#
|
94
|
-
#
|
95
|
-
#
|
96
|
-
#
|
97
|
+
# def cleanup_view_runtime
|
98
|
+
# super - time_taken_in_something_expensive
|
99
|
+
# end
|
97
100
|
def cleanup_view_runtime # :doc:
|
98
101
|
yield
|
99
102
|
end
|
100
103
|
|
101
|
-
# Every time after an action is processed, this method is invoked
|
102
|
-
#
|
104
|
+
# Every time after an action is processed, this method is invoked with the
|
105
|
+
# payload, so you can add more information.
|
103
106
|
def append_info_to_payload(payload) # :doc:
|
104
107
|
payload[:view_runtime] = view_runtime
|
105
108
|
end
|
106
109
|
|
107
110
|
module ClassMethods
|
108
|
-
# A hook which allows other frameworks to log what happened during
|
109
|
-
#
|
110
|
-
#
|
111
|
+
# A hook which allows other frameworks to log what happened during controller
|
112
|
+
# process action. This method should return an array with the messages to be
|
113
|
+
# added.
|
111
114
|
def log_process_action(payload) # :nodoc:
|
112
115
|
messages, view_runtime = [], payload[:view_runtime]
|
113
116
|
messages << ("Views: %.1fms" % view_runtime.to_f) if view_runtime
|
@@ -1,56 +1,58 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
# :markup: markdown
|
4
|
+
|
3
5
|
require "action_dispatch/http/response"
|
4
6
|
require "delegate"
|
5
7
|
require "active_support/json"
|
6
8
|
|
7
9
|
module ActionController
|
8
|
-
#
|
10
|
+
# # Action Controller Live
|
9
11
|
#
|
10
|
-
# Mix this module into your controller, and all actions in that controller
|
11
|
-
#
|
12
|
+
# Mix this module into your controller, and all actions in that controller will
|
13
|
+
# be able to stream data to the client as it's written.
|
12
14
|
#
|
13
|
-
#
|
14
|
-
#
|
15
|
+
# class MyController < ActionController::Base
|
16
|
+
# include ActionController::Live
|
15
17
|
#
|
16
|
-
#
|
17
|
-
#
|
18
|
-
#
|
19
|
-
#
|
20
|
-
#
|
21
|
-
#
|
22
|
-
#
|
23
|
-
#
|
18
|
+
# def stream
|
19
|
+
# response.headers['Content-Type'] = 'text/event-stream'
|
20
|
+
# 100.times {
|
21
|
+
# response.stream.write "hello world\n"
|
22
|
+
# sleep 1
|
23
|
+
# }
|
24
|
+
# ensure
|
25
|
+
# response.stream.close
|
26
|
+
# end
|
24
27
|
# end
|
25
|
-
# end
|
26
28
|
#
|
27
|
-
# There are a few caveats with this module. You
|
28
|
-
# response has been committed (Response#committed? will return truthy).
|
29
|
-
# Calling
|
30
|
-
# object to be committed. Make sure all headers are set before calling write
|
31
|
-
#
|
29
|
+
# There are a few caveats with this module. You **cannot** write headers after
|
30
|
+
# the response has been committed (Response#committed? will return truthy).
|
31
|
+
# Calling `write` or `close` on the response stream will cause the response
|
32
|
+
# object to be committed. Make sure all headers are set before calling write or
|
33
|
+
# close on your stream.
|
32
34
|
#
|
33
|
-
# You
|
35
|
+
# You **must** call close on your stream when you're finished, otherwise the
|
34
36
|
# socket may be left open forever.
|
35
37
|
#
|
36
38
|
# The final caveat is that your actions are executed in a separate thread than
|
37
|
-
# the main thread. Make sure your actions are thread safe, and this shouldn't
|
38
|
-
#
|
39
|
+
# the main thread. Make sure your actions are thread safe, and this shouldn't be
|
40
|
+
# a problem (don't share state across threads, etc).
|
39
41
|
#
|
40
|
-
# Note that
|
42
|
+
# Note that Rails includes `Rack::ETag` by default, which will buffer your
|
41
43
|
# response. As a result, streaming responses may not work properly with Rack
|
42
|
-
# 2.2.x, and you may need to implement workarounds in your application.
|
43
|
-
#
|
44
|
-
#
|
44
|
+
# 2.2.x, and you may need to implement workarounds in your application. You can
|
45
|
+
# either set the `ETag` or `Last-Modified` response headers or remove
|
46
|
+
# `Rack::ETag` from the middleware stack to address this issue.
|
45
47
|
#
|
46
|
-
# Here's an example of how you can set the
|
48
|
+
# Here's an example of how you can set the `Last-Modified` header if your Rack
|
47
49
|
# version is 2.2.x:
|
48
50
|
#
|
49
|
-
#
|
50
|
-
#
|
51
|
-
#
|
52
|
-
#
|
53
|
-
#
|
51
|
+
# def stream
|
52
|
+
# response.headers["Content-Type"] = "text/event-stream"
|
53
|
+
# response.headers["Last-Modified"] = Time.now.httpdate # Add this line if your Rack version is 2.2.x
|
54
|
+
# ...
|
55
|
+
# end
|
54
56
|
module Live
|
55
57
|
extend ActiveSupport::Concern
|
56
58
|
|
@@ -66,44 +68,44 @@ module ActionController
|
|
66
68
|
end
|
67
69
|
end
|
68
70
|
|
69
|
-
#
|
71
|
+
# # Action Controller Live Server Sent Events
|
70
72
|
#
|
71
|
-
# This class provides the ability to write an SSE (Server Sent Event)
|
72
|
-
#
|
73
|
-
#
|
73
|
+
# This class provides the ability to write an SSE (Server Sent Event) to an IO
|
74
|
+
# stream. The class is initialized with a stream and can be used to either write
|
75
|
+
# a JSON string or an object which can be converted to JSON.
|
74
76
|
#
|
75
77
|
# Writing an object will convert it into standard SSE format with whatever
|
76
78
|
# options you have configured. You may choose to set the following options:
|
77
79
|
#
|
78
|
-
#
|
79
|
-
#
|
80
|
-
#
|
81
|
-
#
|
82
|
-
#
|
83
|
-
#
|
80
|
+
# 1) Event. If specified, an event with this name will be dispatched on
|
81
|
+
# the browser.
|
82
|
+
# 2) Retry. The reconnection time in milliseconds used when attempting
|
83
|
+
# to send the event.
|
84
|
+
# 3) Id. If the connection dies while sending an SSE to the browser, then
|
85
|
+
# the server will receive a +Last-Event-ID+ header with value equal to +id+.
|
84
86
|
#
|
85
|
-
# After setting an option in the constructor of the SSE object, all future
|
86
|
-
#
|
87
|
+
# After setting an option in the constructor of the SSE object, all future SSEs
|
88
|
+
# sent across the stream will use those options unless overridden.
|
87
89
|
#
|
88
90
|
# Example Usage:
|
89
91
|
#
|
90
|
-
#
|
91
|
-
#
|
92
|
+
# class MyController < ActionController::Base
|
93
|
+
# include ActionController::Live
|
92
94
|
#
|
93
|
-
#
|
94
|
-
#
|
95
|
-
#
|
96
|
-
#
|
97
|
-
#
|
98
|
-
#
|
99
|
-
#
|
100
|
-
#
|
101
|
-
#
|
95
|
+
# def index
|
96
|
+
# response.headers['Content-Type'] = 'text/event-stream'
|
97
|
+
# sse = SSE.new(response.stream, retry: 300, event: "event-name")
|
98
|
+
# sse.write({ name: 'John'})
|
99
|
+
# sse.write({ name: 'John'}, id: 10)
|
100
|
+
# sse.write({ name: 'John'}, id: 10, event: "other-event")
|
101
|
+
# sse.write({ name: 'John'}, id: 10, event: "other-event", retry: 500)
|
102
|
+
# ensure
|
103
|
+
# sse.close
|
104
|
+
# end
|
102
105
|
# end
|
103
|
-
# end
|
104
106
|
#
|
105
|
-
# Note: SSEs are not currently supported by IE. However, they are supported
|
106
|
-
#
|
107
|
+
# Note: SSEs are not currently supported by IE. However, they are supported by
|
108
|
+
# Chrome, Firefox, Opera, and Safari.
|
107
109
|
class SSE
|
108
110
|
PERMITTED_OPTIONS = %w( retry event id )
|
109
111
|
|
@@ -153,10 +155,9 @@ module ActionController
|
|
153
155
|
|
154
156
|
# Ignore that the client has disconnected.
|
155
157
|
#
|
156
|
-
# If this value is `true`, calling `write` after the client
|
157
|
-
#
|
158
|
-
#
|
159
|
-
# ClientDisconnected exception will be raised.
|
158
|
+
# If this value is `true`, calling `write` after the client disconnects will
|
159
|
+
# result in the written content being silently discarded. If this value is
|
160
|
+
# `false` (the default), a ClientDisconnected exception will be raised.
|
160
161
|
attr_accessor :ignore_disconnect
|
161
162
|
|
162
163
|
def initialize(response)
|
@@ -167,9 +168,10 @@ module ActionController
|
|
167
168
|
@ignore_disconnect = false
|
168
169
|
end
|
169
170
|
|
170
|
-
# ActionDispatch::Response delegates #to_ary to the internal
|
171
|
-
# defining #to_ary is an indicator that the
|
172
|
-
#
|
171
|
+
# ActionDispatch::Response delegates #to_ary to the internal
|
172
|
+
# ActionDispatch::Response::Buffer, defining #to_ary is an indicator that the
|
173
|
+
# response body can be buffered and/or cached by Rack middlewares, this is not
|
174
|
+
# the case for Live responses so we undefine it for this Buffer subclass.
|
173
175
|
undef_method :to_ary
|
174
176
|
|
175
177
|
def write(string)
|
@@ -184,21 +186,20 @@ module ActionController
|
|
184
186
|
@buf.clear
|
185
187
|
|
186
188
|
unless @ignore_disconnect
|
187
|
-
# Raise ClientDisconnected, which is a RuntimeError (not an
|
188
|
-
#
|
189
|
-
# the developer's control.
|
189
|
+
# Raise ClientDisconnected, which is a RuntimeError (not an IOError), because
|
190
|
+
# that's more appropriate for something beyond the developer's control.
|
190
191
|
raise ClientDisconnected, "client disconnected"
|
191
192
|
end
|
192
193
|
end
|
193
194
|
end
|
194
195
|
|
195
|
-
# Same as
|
196
|
+
# Same as `write` but automatically include a newline at the end of the string.
|
196
197
|
def writeln(string)
|
197
198
|
write string.end_with?("\n") ? string : "#{string}\n"
|
198
199
|
end
|
199
200
|
|
200
|
-
# Write a 'close' event to the buffer; the producer/writing thread
|
201
|
-
#
|
201
|
+
# Write a 'close' event to the buffer; the producer/writing thread uses this to
|
202
|
+
# notify us that it's finished supplying content.
|
202
203
|
#
|
203
204
|
# See also #abort.
|
204
205
|
def close
|
@@ -209,9 +210,8 @@ module ActionController
|
|
209
210
|
end
|
210
211
|
end
|
211
212
|
|
212
|
-
# Inform the producer/writing thread that the client has
|
213
|
-
#
|
214
|
-
# anything that's being written.
|
213
|
+
# Inform the producer/writing thread that the client has disconnected; the
|
214
|
+
# reading thread is no longer interested in anything that's being written.
|
215
215
|
#
|
216
216
|
# See also #close.
|
217
217
|
def abort
|
@@ -223,8 +223,8 @@ module ActionController
|
|
223
223
|
|
224
224
|
# Is the client still connected and waiting for content?
|
225
225
|
#
|
226
|
-
# The result of calling `write` when this is `false` is determined
|
227
|
-
#
|
226
|
+
# The result of calling `write` when this is `false` is determined by
|
227
|
+
# `ignore_disconnect`.
|
228
228
|
def connected?
|
229
229
|
!@aborted
|
230
230
|
end
|
@@ -275,15 +275,15 @@ module ActionController
|
|
275
275
|
locals = t1.keys.map { |key| [key, t1[key]] }
|
276
276
|
|
277
277
|
error = nil
|
278
|
-
# This processes the action in a child thread. It lets us return the
|
279
|
-
#
|
280
|
-
#
|
278
|
+
# This processes the action in a child thread. It lets us return the response
|
279
|
+
# code and headers back up the Rack stack, and still process the body in
|
280
|
+
# parallel with sending data to the client.
|
281
281
|
new_controller_thread {
|
282
282
|
ActiveSupport::Dependencies.interlock.running do
|
283
283
|
t2 = Thread.current
|
284
284
|
|
285
|
-
# Since we're processing the view in a different thread, copy the
|
286
|
-
#
|
285
|
+
# Since we're processing the view in a different thread, copy the thread locals
|
286
|
+
# from the main thread to the child thread. :'(
|
287
287
|
locals.each { |k, v| t2[k] = v }
|
288
288
|
ActiveSupport::IsolatedExecutionState.share_with(t1)
|
289
289
|
|
@@ -321,46 +321,52 @@ module ActionController
|
|
321
321
|
response.close if response
|
322
322
|
end
|
323
323
|
|
324
|
-
# Sends a stream to the browser, which is helpful when you're generating exports
|
325
|
-
# don't want the entire file buffered in memory
|
324
|
+
# Sends a stream to the browser, which is helpful when you're generating exports
|
325
|
+
# or other running data where you don't want the entire file buffered in memory
|
326
|
+
# first. Similar to send_data, but where the data is generated live.
|
326
327
|
#
|
327
328
|
# Options:
|
328
|
-
# *
|
329
|
-
# *
|
330
|
-
#
|
331
|
-
#
|
332
|
-
#
|
333
|
-
#
|
334
|
-
#
|
329
|
+
# * `:filename` - suggests a filename for the browser to use.
|
330
|
+
# * `:type` - specifies an HTTP content type. You can specify either a string
|
331
|
+
# or a symbol for a registered type with `Mime::Type.register`, for example
|
332
|
+
# :json. If omitted, type will be inferred from the file extension specified
|
333
|
+
# in `:filename`. If no content type is registered for the extension, the
|
334
|
+
# default type 'application/octet-stream' will be used.
|
335
|
+
# * `:disposition` - specifies whether the file will be shown inline or
|
336
|
+
# downloaded. Valid values are 'inline' and 'attachment' (default).
|
337
|
+
#
|
335
338
|
#
|
336
339
|
# Example of generating a csv export:
|
337
340
|
#
|
338
|
-
#
|
339
|
-
#
|
341
|
+
# send_stream(filename: "subscribers.csv") do |stream|
|
342
|
+
# stream.write "email_address,updated_at\n"
|
340
343
|
#
|
341
|
-
#
|
342
|
-
#
|
343
|
-
#
|
344
|
-
#
|
344
|
+
# @subscribers.find_each do |subscriber|
|
345
|
+
# stream.write "#{subscriber.email_address},#{subscriber.updated_at}\n"
|
346
|
+
# end
|
347
|
+
# end
|
345
348
|
def send_stream(filename:, disposition: "attachment", type: nil)
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
349
|
+
payload = { filename: filename, disposition: disposition, type: type }
|
350
|
+
ActiveSupport::Notifications.instrument("send_stream.action_controller", payload) do
|
351
|
+
response.headers["Content-Type"] =
|
352
|
+
(type.is_a?(Symbol) ? Mime[type].to_s : type) ||
|
353
|
+
Mime::Type.lookup_by_extension(File.extname(filename).downcase.delete("."))&.to_s ||
|
354
|
+
"application/octet-stream"
|
350
355
|
|
351
|
-
|
352
|
-
|
356
|
+
response.headers["Content-Disposition"] =
|
357
|
+
ActionDispatch::Http::ContentDisposition.format(disposition: disposition, filename: filename)
|
353
358
|
|
354
|
-
|
359
|
+
yield response.stream
|
360
|
+
end
|
355
361
|
ensure
|
356
362
|
response.stream.close
|
357
363
|
end
|
358
364
|
|
359
365
|
private
|
360
|
-
# Spawn a new thread to serve up the controller in. This is to get
|
361
|
-
#
|
362
|
-
#
|
363
|
-
#
|
366
|
+
# Spawn a new thread to serve up the controller in. This is to get around the
|
367
|
+
# fact that Rack isn't based around IOs and we need to use a thread to stream
|
368
|
+
# data from the response bodies. Nobody should call this method except in Rails
|
369
|
+
# internals. Seriously!
|
364
370
|
def new_controller_thread # :nodoc:
|
365
371
|
Thread.new {
|
366
372
|
t2 = Thread.current
|
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
# :markup: markdown
|
4
|
+
|
3
5
|
module ActionController
|
4
6
|
module Logging
|
5
7
|
extend ActiveSupport::Concern
|
@@ -7,10 +9,10 @@ module ActionController
|
|
7
9
|
module ClassMethods
|
8
10
|
# Set a different log level per request.
|
9
11
|
#
|
10
|
-
#
|
11
|
-
#
|
12
|
-
#
|
13
|
-
#
|
12
|
+
# # Use the debug log level if a particular cookie is set.
|
13
|
+
# class ApplicationController < ActionController::Base
|
14
|
+
# log_at :debug, if: -> { cookies[:debug] }
|
15
|
+
# end
|
14
16
|
#
|
15
17
|
def log_at(level, **options)
|
16
18
|
around_action ->(_, action) { logger.log_at(level, &action) }, **options
|