omg-actionpack 8.0.0.alpha1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CHANGELOG.md +129 -0
- data/MIT-LICENSE +21 -0
- data/README.rdoc +57 -0
- data/lib/abstract_controller/asset_paths.rb +14 -0
- data/lib/abstract_controller/base.rb +299 -0
- data/lib/abstract_controller/caching/fragments.rb +149 -0
- data/lib/abstract_controller/caching.rb +68 -0
- data/lib/abstract_controller/callbacks.rb +265 -0
- data/lib/abstract_controller/collector.rb +44 -0
- data/lib/abstract_controller/deprecator.rb +9 -0
- data/lib/abstract_controller/error.rb +8 -0
- data/lib/abstract_controller/helpers.rb +243 -0
- data/lib/abstract_controller/logger.rb +16 -0
- data/lib/abstract_controller/railties/routes_helpers.rb +25 -0
- data/lib/abstract_controller/rendering.rb +126 -0
- data/lib/abstract_controller/translation.rb +42 -0
- data/lib/abstract_controller/url_for.rb +37 -0
- data/lib/abstract_controller.rb +36 -0
- data/lib/action_controller/api/api_rendering.rb +18 -0
- data/lib/action_controller/api.rb +155 -0
- data/lib/action_controller/base.rb +332 -0
- data/lib/action_controller/caching.rb +49 -0
- data/lib/action_controller/deprecator.rb +9 -0
- data/lib/action_controller/form_builder.rb +55 -0
- data/lib/action_controller/log_subscriber.rb +96 -0
- data/lib/action_controller/metal/allow_browser.rb +123 -0
- data/lib/action_controller/metal/basic_implicit_render.rb +17 -0
- data/lib/action_controller/metal/conditional_get.rb +341 -0
- data/lib/action_controller/metal/content_security_policy.rb +86 -0
- data/lib/action_controller/metal/cookies.rb +20 -0
- data/lib/action_controller/metal/data_streaming.rb +154 -0
- 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 +59 -0
- data/lib/action_controller/metal/exceptions.rb +106 -0
- data/lib/action_controller/metal/flash.rb +67 -0
- data/lib/action_controller/metal/head.rb +67 -0
- data/lib/action_controller/metal/helpers.rb +129 -0
- data/lib/action_controller/metal/http_authentication.rb +565 -0
- data/lib/action_controller/metal/implicit_render.rb +67 -0
- data/lib/action_controller/metal/instrumentation.rb +120 -0
- data/lib/action_controller/metal/live.rb +398 -0
- data/lib/action_controller/metal/logging.rb +22 -0
- data/lib/action_controller/metal/mime_responds.rb +337 -0
- data/lib/action_controller/metal/parameter_encoding.rb +84 -0
- data/lib/action_controller/metal/params_wrapper.rb +312 -0
- 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 +251 -0
- data/lib/action_controller/metal/renderers.rb +181 -0
- data/lib/action_controller/metal/rendering.rb +260 -0
- data/lib/action_controller/metal/request_forgery_protection.rb +667 -0
- data/lib/action_controller/metal/rescue.rb +33 -0
- data/lib/action_controller/metal/streaming.rb +183 -0
- data/lib/action_controller/metal/strong_parameters.rb +1546 -0
- data/lib/action_controller/metal/testing.rb +25 -0
- data/lib/action_controller/metal/url_for.rb +65 -0
- data/lib/action_controller/metal.rb +339 -0
- data/lib/action_controller/railtie.rb +149 -0
- data/lib/action_controller/railties/helpers.rb +26 -0
- data/lib/action_controller/renderer.rb +161 -0
- data/lib/action_controller/template_assertions.rb +13 -0
- data/lib/action_controller/test_case.rb +691 -0
- data/lib/action_controller.rb +80 -0
- data/lib/action_dispatch/constants.rb +34 -0
- data/lib/action_dispatch/deprecator.rb +9 -0
- data/lib/action_dispatch/http/cache.rb +249 -0
- data/lib/action_dispatch/http/content_disposition.rb +47 -0
- data/lib/action_dispatch/http/content_security_policy.rb +365 -0
- data/lib/action_dispatch/http/filter_parameters.rb +80 -0
- data/lib/action_dispatch/http/filter_redirect.rb +50 -0
- data/lib/action_dispatch/http/headers.rb +134 -0
- data/lib/action_dispatch/http/mime_negotiation.rb +187 -0
- data/lib/action_dispatch/http/mime_type.rb +389 -0
- data/lib/action_dispatch/http/mime_types.rb +54 -0
- data/lib/action_dispatch/http/parameters.rb +119 -0
- data/lib/action_dispatch/http/permissions_policy.rb +189 -0
- data/lib/action_dispatch/http/rack_cache.rb +67 -0
- data/lib/action_dispatch/http/request.rb +498 -0
- data/lib/action_dispatch/http/response.rb +556 -0
- data/lib/action_dispatch/http/upload.rb +107 -0
- data/lib/action_dispatch/http/url.rb +344 -0
- data/lib/action_dispatch/journey/formatter.rb +226 -0
- data/lib/action_dispatch/journey/gtg/builder.rb +149 -0
- data/lib/action_dispatch/journey/gtg/simulator.rb +50 -0
- data/lib/action_dispatch/journey/gtg/transition_table.rb +217 -0
- data/lib/action_dispatch/journey/nfa/dot.rb +27 -0
- data/lib/action_dispatch/journey/nodes/node.rb +208 -0
- data/lib/action_dispatch/journey/parser.rb +103 -0
- data/lib/action_dispatch/journey/path/pattern.rb +209 -0
- data/lib/action_dispatch/journey/route.rb +189 -0
- data/lib/action_dispatch/journey/router/utils.rb +105 -0
- data/lib/action_dispatch/journey/router.rb +151 -0
- data/lib/action_dispatch/journey/routes.rb +82 -0
- data/lib/action_dispatch/journey/scanner.rb +70 -0
- data/lib/action_dispatch/journey/visitors.rb +267 -0
- data/lib/action_dispatch/journey/visualizer/fsm.css +30 -0
- data/lib/action_dispatch/journey/visualizer/fsm.js +159 -0
- data/lib/action_dispatch/journey/visualizer/index.html.erb +52 -0
- data/lib/action_dispatch/journey.rb +7 -0
- 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 +38 -0
- data/lib/action_dispatch/middleware/cookies.rb +719 -0
- data/lib/action_dispatch/middleware/debug_exceptions.rb +206 -0
- 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 +350 -0
- data/lib/action_dispatch/middleware/executor.rb +32 -0
- data/lib/action_dispatch/middleware/flash.rb +318 -0
- data/lib/action_dispatch/middleware/host_authorization.rb +171 -0
- data/lib/action_dispatch/middleware/public_exceptions.rb +64 -0
- data/lib/action_dispatch/middleware/reloader.rb +16 -0
- data/lib/action_dispatch/middleware/remote_ip.rb +199 -0
- data/lib/action_dispatch/middleware/request_id.rb +50 -0
- data/lib/action_dispatch/middleware/server_timing.rb +78 -0
- data/lib/action_dispatch/middleware/session/abstract_store.rb +112 -0
- data/lib/action_dispatch/middleware/session/cache_store.rb +66 -0
- data/lib/action_dispatch/middleware/session/cookie_store.rb +129 -0
- data/lib/action_dispatch/middleware/session/mem_cache_store.rb +34 -0
- data/lib/action_dispatch/middleware/show_exceptions.rb +88 -0
- data/lib/action_dispatch/middleware/ssl.rb +180 -0
- data/lib/action_dispatch/middleware/stack.rb +194 -0
- data/lib/action_dispatch/middleware/static.rb +192 -0
- 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 +17 -0
- data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.text.erb +23 -0
- 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 +62 -0
- data/lib/action_dispatch/middleware/templates/rescues/_trace.text.erb +9 -0
- 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 +35 -0
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +9 -0
- 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 +284 -0
- 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 +11 -0
- data/lib/action_dispatch/middleware/templates/rescues/missing_template.text.erb +3 -0
- data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +32 -0
- data/lib/action_dispatch/middleware/templates/rescues/routing_error.text.erb +11 -0
- data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +20 -0
- data/lib/action_dispatch/middleware/templates/rescues/template_error.text.erb +7 -0
- data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +6 -0
- data/lib/action_dispatch/middleware/templates/rescues/unknown_action.text.erb +3 -0
- data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +19 -0
- data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +232 -0
- data/lib/action_dispatch/railtie.rb +77 -0
- data/lib/action_dispatch/request/session.rb +283 -0
- data/lib/action_dispatch/request/utils.rb +109 -0
- data/lib/action_dispatch/routing/endpoint.rb +19 -0
- data/lib/action_dispatch/routing/inspector.rb +323 -0
- data/lib/action_dispatch/routing/mapper.rb +2372 -0
- data/lib/action_dispatch/routing/polymorphic_routes.rb +363 -0
- data/lib/action_dispatch/routing/redirection.rb +218 -0
- data/lib/action_dispatch/routing/route_set.rb +958 -0
- data/lib/action_dispatch/routing/routes_proxy.rb +66 -0
- data/lib/action_dispatch/routing/url_for.rb +244 -0
- data/lib/action_dispatch/routing.rb +262 -0
- data/lib/action_dispatch/system_test_case.rb +206 -0
- data/lib/action_dispatch/system_testing/browser.rb +75 -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 +114 -0
- data/lib/action_dispatch/testing/assertions/routing.rb +343 -0
- data/lib/action_dispatch/testing/assertions.rb +25 -0
- data/lib/action_dispatch/testing/integration.rb +694 -0
- 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 +57 -0
- data/lib/action_dispatch/testing/test_request.rb +73 -0
- data/lib/action_dispatch/testing/test_response.rb +58 -0
- data/lib/action_dispatch.rb +147 -0
- data/lib/action_pack/gem_version.rb +19 -0
- data/lib/action_pack/version.rb +12 -0
- data/lib/action_pack.rb +27 -0
- metadata +375 -0
@@ -0,0 +1,556 @@
|
|
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"
|
9
|
+
|
10
|
+
module ActionDispatch # :nodoc:
|
11
|
+
# # Action Dispatch Response
|
12
|
+
#
|
13
|
+
# Represents an HTTP response generated by a controller action. Use it to
|
14
|
+
# retrieve the current state of the response, or customize the response. It can
|
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).
|
18
|
+
#
|
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.
|
23
|
+
#
|
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.
|
28
|
+
#
|
29
|
+
# For example, the following demo integration test prints the body of the
|
30
|
+
# controller response to the console:
|
31
|
+
#
|
32
|
+
# class DemoControllerTest < ActionDispatch::IntegrationTest
|
33
|
+
# def test_print_root_path_to_console
|
34
|
+
# get('/')
|
35
|
+
# puts response.body
|
36
|
+
# end
|
37
|
+
# end
|
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
|
+
|
52
|
+
# The request that the response is responding to.
|
53
|
+
attr_accessor :request
|
54
|
+
|
55
|
+
# The HTTP status code.
|
56
|
+
attr_reader :status
|
57
|
+
|
58
|
+
# The headers for the response.
|
59
|
+
#
|
60
|
+
# header["Content-Type"] # => "text/plain"
|
61
|
+
# header["Content-Type"] = "application/json"
|
62
|
+
# header["Content-Type"] # => "application/json"
|
63
|
+
#
|
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
|
74
|
+
|
75
|
+
delegate :[], :[]=, to: :@headers
|
76
|
+
|
77
|
+
def each(&block)
|
78
|
+
sending!
|
79
|
+
x = @stream.each(&block)
|
80
|
+
sent!
|
81
|
+
x
|
82
|
+
end
|
83
|
+
|
84
|
+
CONTENT_TYPE = "Content-Type"
|
85
|
+
SET_COOKIE = "Set-Cookie"
|
86
|
+
NO_CONTENT_CODES = [100, 101, 102, 103, 204, 205, 304]
|
87
|
+
|
88
|
+
cattr_accessor :default_charset, default: "utf-8"
|
89
|
+
cattr_accessor :default_headers
|
90
|
+
|
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
|
+
|
96
|
+
include ActionDispatch::Http::FilterRedirect
|
97
|
+
include ActionDispatch::Http::Cache::Response
|
98
|
+
include MonitorMixin
|
99
|
+
|
100
|
+
class Buffer # :nodoc:
|
101
|
+
def initialize(response, buf)
|
102
|
+
@response = response
|
103
|
+
@buf = buf
|
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
|
120
|
+
end
|
121
|
+
|
122
|
+
def write(string)
|
123
|
+
raise IOError, "closed stream" if closed?
|
124
|
+
|
125
|
+
@str_body = nil
|
126
|
+
@response.commit!
|
127
|
+
@buf.push string
|
128
|
+
end
|
129
|
+
alias_method :<<, :write
|
130
|
+
|
131
|
+
def each(&block)
|
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
|
139
|
+
end
|
140
|
+
|
141
|
+
def abort
|
142
|
+
end
|
143
|
+
|
144
|
+
def close
|
145
|
+
@response.commit!
|
146
|
+
@closed = true
|
147
|
+
end
|
148
|
+
|
149
|
+
def closed?
|
150
|
+
@closed
|
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
|
166
|
+
end
|
167
|
+
|
168
|
+
# The underlying body, as a streamable object.
|
169
|
+
attr_reader :stream
|
170
|
+
|
171
|
+
def initialize(status = 200, headers = nil, body = [])
|
172
|
+
super()
|
173
|
+
|
174
|
+
@headers = Headers.new
|
175
|
+
|
176
|
+
headers&.each do |key, value|
|
177
|
+
@headers[key] = value
|
178
|
+
end
|
179
|
+
|
180
|
+
self.body, self.status = body, status
|
181
|
+
|
182
|
+
@cv = new_cond
|
183
|
+
@committed = false
|
184
|
+
@sending = false
|
185
|
+
@sent = false
|
186
|
+
|
187
|
+
prepare_cache_control!
|
188
|
+
|
189
|
+
yield self if block_given?
|
190
|
+
end
|
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
|
+
|
197
|
+
def await_commit
|
198
|
+
synchronize do
|
199
|
+
@cv.wait_until { @committed }
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
def await_sent
|
204
|
+
synchronize { @cv.wait_until { @sent } }
|
205
|
+
end
|
206
|
+
|
207
|
+
def commit!
|
208
|
+
synchronize do
|
209
|
+
before_committed
|
210
|
+
@committed = true
|
211
|
+
@cv.broadcast
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
def sending!
|
216
|
+
synchronize do
|
217
|
+
before_sending
|
218
|
+
@sending = true
|
219
|
+
@cv.broadcast
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
def sent!
|
224
|
+
synchronize do
|
225
|
+
@sent = true
|
226
|
+
@cv.broadcast
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
def sending?; synchronize { @sending }; end
|
231
|
+
def committed?; synchronize { @committed }; end
|
232
|
+
def sent?; synchronize { @sent }; end
|
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
|
+
|
246
|
+
# Sets the HTTP status code.
|
247
|
+
def status=(status)
|
248
|
+
@status = Rack::Utils.status_code(status)
|
249
|
+
end
|
250
|
+
|
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.
|
259
|
+
def content_type=(content_type)
|
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
|
303
|
+
end
|
304
|
+
|
305
|
+
# The response code of the request.
|
306
|
+
def response_code
|
307
|
+
@status
|
308
|
+
end
|
309
|
+
|
310
|
+
# Returns a string to ensure compatibility with `Net::HTTPResponse`.
|
311
|
+
def code
|
312
|
+
@status.to_s
|
313
|
+
end
|
314
|
+
|
315
|
+
# Returns the corresponding message for the current HTTP status code:
|
316
|
+
#
|
317
|
+
# response.status = 200
|
318
|
+
# response.message # => "OK"
|
319
|
+
#
|
320
|
+
# response.status = 404
|
321
|
+
# response.message # => "Not Found"
|
322
|
+
#
|
323
|
+
def message
|
324
|
+
Rack::Utils::HTTP_STATUS_CODES[@status]
|
325
|
+
end
|
326
|
+
alias_method :status_message, :message
|
327
|
+
|
328
|
+
# Returns the content of the response as a string. This contains the contents of
|
329
|
+
# any calls to `render`.
|
330
|
+
def body
|
331
|
+
@stream.body
|
332
|
+
end
|
333
|
+
|
334
|
+
def write(string)
|
335
|
+
@stream.write string
|
336
|
+
end
|
337
|
+
|
338
|
+
# Allows you to manually set or override the response body.
|
339
|
+
def body=(body)
|
340
|
+
if body.respond_to?(:to_path)
|
341
|
+
@stream = body
|
342
|
+
else
|
343
|
+
synchronize do
|
344
|
+
@stream = build_buffer self, munge_body_object(body)
|
345
|
+
end
|
346
|
+
end
|
347
|
+
end
|
348
|
+
|
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
|
358
|
+
|
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
|
371
|
+
end
|
372
|
+
|
373
|
+
# Send the file stored at `path` as the response body.
|
374
|
+
def send_file(path)
|
375
|
+
commit!
|
376
|
+
@stream = FileBody.new(path)
|
377
|
+
end
|
378
|
+
|
379
|
+
def reset_body!
|
380
|
+
@stream = build_buffer(self, [])
|
381
|
+
end
|
382
|
+
|
383
|
+
def body_parts
|
384
|
+
parts = []
|
385
|
+
@stream.each { |x| parts << x }
|
386
|
+
parts
|
387
|
+
end
|
388
|
+
|
389
|
+
# The location header we'll be responding with.
|
390
|
+
alias_method :redirect_url, :location
|
391
|
+
|
392
|
+
def close
|
393
|
+
stream.close if stream.respond_to?(:close)
|
394
|
+
end
|
395
|
+
|
396
|
+
def abort
|
397
|
+
if stream.respond_to?(:abort)
|
398
|
+
stream.abort
|
399
|
+
elsif stream.respond_to?(:close)
|
400
|
+
# `stream.close` should really be reserved for a close from the other direction,
|
401
|
+
# but we must fall back to it for compatibility.
|
402
|
+
stream.close
|
403
|
+
end
|
404
|
+
end
|
405
|
+
|
406
|
+
# Turns the Response into a Rack-compatible array of the status, headers, and
|
407
|
+
# body. Allows explicit splatting:
|
408
|
+
#
|
409
|
+
# status, headers, body = *response
|
410
|
+
def to_a
|
411
|
+
commit!
|
412
|
+
rack_response @status, @headers.to_hash
|
413
|
+
end
|
414
|
+
alias prepare! to_a
|
415
|
+
|
416
|
+
# Returns the response cookies, converted to a Hash of (name => value) pairs
|
417
|
+
#
|
418
|
+
# assert_equal 'AuthorOfNewPage', r.cookies['author']
|
419
|
+
def cookies
|
420
|
+
cookies = {}
|
421
|
+
if header = get_header(SET_COOKIE)
|
422
|
+
header = header.split("\n") if header.respond_to?(:to_str)
|
423
|
+
header.each do |cookie|
|
424
|
+
if pair = cookie.split(";").first
|
425
|
+
key, value = pair.split("=").map { |v| Rack::Utils.unescape(v) }
|
426
|
+
cookies[key] = value
|
427
|
+
end
|
428
|
+
end
|
429
|
+
end
|
430
|
+
cookies
|
431
|
+
end
|
432
|
+
|
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
|
462
|
+
|
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!
|
469
|
+
end
|
470
|
+
|
471
|
+
def before_sending
|
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?
|
477
|
+
|
478
|
+
@request.commit_cookie_jar! unless committed?
|
479
|
+
end
|
480
|
+
|
481
|
+
def build_buffer(response, body)
|
482
|
+
Buffer.new response, body
|
483
|
+
end
|
484
|
+
|
485
|
+
def munge_body_object(body)
|
486
|
+
body.respond_to?(:each) ? body : [body]
|
487
|
+
end
|
488
|
+
|
489
|
+
def assign_default_content_type_and_charset!
|
490
|
+
return if media_type
|
491
|
+
|
492
|
+
ct = parsed_content_type_header
|
493
|
+
set_content_type(ct.mime_type || Mime[:html].to_s,
|
494
|
+
ct.charset || self.class.default_charset)
|
495
|
+
end
|
496
|
+
|
497
|
+
class RackBody
|
498
|
+
def initialize(response)
|
499
|
+
@response = response
|
500
|
+
end
|
501
|
+
|
502
|
+
def close
|
503
|
+
# Rack "close" maps to Response#abort, and **not** Response#close (which is used
|
504
|
+
# when the controller's finished writing)
|
505
|
+
@response.abort
|
506
|
+
end
|
507
|
+
|
508
|
+
def body
|
509
|
+
@response.body
|
510
|
+
end
|
511
|
+
|
512
|
+
BODY_METHODS = { to_ary: true, each: true, call: true, to_path: true }
|
513
|
+
|
514
|
+
def respond_to?(method, include_private = false)
|
515
|
+
if BODY_METHODS.key?(method)
|
516
|
+
@response.stream.respond_to?(method)
|
517
|
+
else
|
518
|
+
super
|
519
|
+
end
|
520
|
+
end
|
521
|
+
|
522
|
+
def to_ary
|
523
|
+
@response.stream.to_ary
|
524
|
+
end
|
525
|
+
|
526
|
+
def each(*args, &block)
|
527
|
+
@response.each(*args, &block)
|
528
|
+
end
|
529
|
+
|
530
|
+
def call(*arguments, &block)
|
531
|
+
@response.stream.call(*arguments, &block)
|
532
|
+
end
|
533
|
+
|
534
|
+
def to_path
|
535
|
+
@response.stream.to_path
|
536
|
+
end
|
537
|
+
end
|
538
|
+
|
539
|
+
def handle_no_content!
|
540
|
+
if NO_CONTENT_CODES.include?(@status)
|
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, []]
|
549
|
+
else
|
550
|
+
[status, headers, RackBody.new(self)]
|
551
|
+
end
|
552
|
+
end
|
553
|
+
end
|
554
|
+
|
555
|
+
ActiveSupport.run_load_hooks(:action_dispatch_response, Response)
|
556
|
+
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# :markup: markdown
|
4
|
+
|
5
|
+
module ActionDispatch
|
6
|
+
module Http
|
7
|
+
# # Action Dispatch HTTP UploadedFile
|
8
|
+
#
|
9
|
+
# Models uploaded files.
|
10
|
+
#
|
11
|
+
# The actual file is accessible via the `tempfile` accessor, though some of its
|
12
|
+
# interface is available directly for convenience.
|
13
|
+
#
|
14
|
+
# Uploaded files are temporary files whose lifespan is one request. When the
|
15
|
+
# object is finalized Ruby unlinks the file, so there is no need to clean them
|
16
|
+
# with a separate maintenance task.
|
17
|
+
class UploadedFile
|
18
|
+
# The basename of the file in the client.
|
19
|
+
attr_accessor :original_filename
|
20
|
+
|
21
|
+
# A string with the MIME type of the file.
|
22
|
+
attr_accessor :content_type
|
23
|
+
|
24
|
+
# A `Tempfile` object with the actual uploaded file. Note that some of its
|
25
|
+
# interface is available directly.
|
26
|
+
attr_accessor :tempfile
|
27
|
+
|
28
|
+
# A string with the headers of the multipart request.
|
29
|
+
attr_accessor :headers
|
30
|
+
|
31
|
+
def initialize(hash) # :nodoc:
|
32
|
+
@tempfile = hash[:tempfile]
|
33
|
+
raise(ArgumentError, ":tempfile is required") unless @tempfile
|
34
|
+
|
35
|
+
@content_type = hash[:type]
|
36
|
+
|
37
|
+
if hash[:filename]
|
38
|
+
@original_filename = hash[:filename].dup
|
39
|
+
|
40
|
+
begin
|
41
|
+
@original_filename.encode!(Encoding::UTF_8)
|
42
|
+
rescue EncodingError
|
43
|
+
@original_filename.force_encoding(Encoding::UTF_8)
|
44
|
+
end
|
45
|
+
else
|
46
|
+
@original_filename = nil
|
47
|
+
end
|
48
|
+
|
49
|
+
if hash[:head]
|
50
|
+
@headers = hash[:head].dup
|
51
|
+
|
52
|
+
begin
|
53
|
+
@headers.encode!(Encoding::UTF_8)
|
54
|
+
rescue EncodingError
|
55
|
+
@headers.force_encoding(Encoding::UTF_8)
|
56
|
+
end
|
57
|
+
else
|
58
|
+
@headers = nil
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# Shortcut for `tempfile.read`.
|
63
|
+
def read(length = nil, buffer = nil)
|
64
|
+
@tempfile.read(length, buffer)
|
65
|
+
end
|
66
|
+
|
67
|
+
# Shortcut for `tempfile.open`.
|
68
|
+
def open
|
69
|
+
@tempfile.open
|
70
|
+
end
|
71
|
+
|
72
|
+
# Shortcut for `tempfile.close`.
|
73
|
+
def close(unlink_now = false)
|
74
|
+
@tempfile.close(unlink_now)
|
75
|
+
end
|
76
|
+
|
77
|
+
# Shortcut for `tempfile.path`.
|
78
|
+
def path
|
79
|
+
@tempfile.path
|
80
|
+
end
|
81
|
+
|
82
|
+
# Shortcut for `tempfile.to_path`.
|
83
|
+
def to_path
|
84
|
+
@tempfile.to_path
|
85
|
+
end
|
86
|
+
|
87
|
+
# Shortcut for `tempfile.rewind`.
|
88
|
+
def rewind
|
89
|
+
@tempfile.rewind
|
90
|
+
end
|
91
|
+
|
92
|
+
# Shortcut for `tempfile.size`.
|
93
|
+
def size
|
94
|
+
@tempfile.size
|
95
|
+
end
|
96
|
+
|
97
|
+
# Shortcut for `tempfile.eof?`.
|
98
|
+
def eof?
|
99
|
+
@tempfile.eof?
|
100
|
+
end
|
101
|
+
|
102
|
+
def to_io
|
103
|
+
@tempfile.to_io
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|