omg-actionpack 8.0.0.alpha1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|