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