actionpack 4.2.11.3 → 5.0.0.beta1
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 +379 -462
- data/MIT-LICENSE +1 -1
- data/README.rdoc +2 -3
- data/lib/abstract_controller.rb +0 -2
- data/lib/abstract_controller/base.rb +17 -32
- data/lib/abstract_controller/callbacks.rb +52 -19
- data/lib/abstract_controller/collector.rb +4 -9
- data/lib/abstract_controller/helpers.rb +2 -2
- data/lib/abstract_controller/railties/routes_helpers.rb +2 -2
- data/lib/abstract_controller/rendering.rb +27 -22
- data/lib/abstract_controller/translation.rb +8 -7
- data/lib/action_controller.rb +4 -3
- data/lib/action_controller/api.rb +146 -0
- data/lib/action_controller/base.rb +6 -10
- data/lib/action_controller/caching.rb +1 -3
- data/lib/action_controller/caching/fragments.rb +48 -3
- data/lib/action_controller/form_builder.rb +48 -0
- data/lib/action_controller/log_subscriber.rb +1 -10
- data/lib/action_controller/metal.rb +89 -62
- data/lib/action_controller/metal/basic_implicit_render.rb +11 -0
- data/lib/action_controller/metal/conditional_get.rb +65 -24
- data/lib/action_controller/metal/cookies.rb +0 -2
- data/lib/action_controller/metal/data_streaming.rb +2 -22
- data/lib/action_controller/metal/etag_with_template_digest.rb +1 -1
- data/lib/action_controller/metal/exceptions.rb +11 -6
- data/lib/action_controller/metal/force_ssl.rb +6 -6
- data/lib/action_controller/metal/head.rb +14 -7
- data/lib/action_controller/metal/helpers.rb +9 -5
- data/lib/action_controller/metal/http_authentication.rb +37 -38
- data/lib/action_controller/metal/implicit_render.rb +23 -6
- data/lib/action_controller/metal/instrumentation.rb +0 -1
- data/lib/action_controller/metal/live.rb +17 -55
- data/lib/action_controller/metal/mime_responds.rb +17 -37
- data/lib/action_controller/metal/params_wrapper.rb +8 -8
- data/lib/action_controller/metal/redirecting.rb +32 -9
- data/lib/action_controller/metal/renderers.rb +10 -8
- data/lib/action_controller/metal/rendering.rb +38 -6
- data/lib/action_controller/metal/request_forgery_protection.rb +67 -35
- data/lib/action_controller/metal/rescue.rb +2 -4
- data/lib/action_controller/metal/streaming.rb +4 -4
- data/lib/action_controller/metal/strong_parameters.rb +231 -78
- data/lib/action_controller/metal/testing.rb +1 -12
- data/lib/action_controller/metal/url_for.rb +12 -5
- data/lib/action_controller/renderer.rb +111 -0
- data/lib/action_controller/template_assertions.rb +9 -0
- data/lib/action_controller/test_case.rb +267 -363
- data/lib/action_dispatch.rb +2 -1
- data/lib/action_dispatch/http/cache.rb +23 -26
- data/lib/action_dispatch/http/filter_parameters.rb +6 -8
- data/lib/action_dispatch/http/filter_redirect.rb +7 -8
- data/lib/action_dispatch/http/headers.rb +28 -11
- data/lib/action_dispatch/http/mime_negotiation.rb +40 -26
- data/lib/action_dispatch/http/mime_type.rb +92 -61
- data/lib/action_dispatch/http/mime_types.rb +1 -4
- data/lib/action_dispatch/http/parameter_filter.rb +18 -8
- data/lib/action_dispatch/http/parameters.rb +45 -41
- data/lib/action_dispatch/http/request.rb +146 -82
- data/lib/action_dispatch/http/response.rb +180 -99
- data/lib/action_dispatch/http/url.rb +117 -8
- data/lib/action_dispatch/journey/formatter.rb +34 -28
- data/lib/action_dispatch/journey/gtg/transition_table.rb +1 -1
- data/lib/action_dispatch/journey/nfa/dot.rb +0 -2
- data/lib/action_dispatch/journey/nfa/transition_table.rb +1 -46
- data/lib/action_dispatch/journey/nodes/node.rb +14 -4
- data/lib/action_dispatch/journey/parser_extras.rb +4 -0
- data/lib/action_dispatch/journey/path/pattern.rb +37 -41
- data/lib/action_dispatch/journey/route.rb +71 -17
- data/lib/action_dispatch/journey/router.rb +5 -6
- data/lib/action_dispatch/journey/router/utils.rb +5 -5
- data/lib/action_dispatch/journey/routes.rb +14 -15
- data/lib/action_dispatch/journey/visitors.rb +86 -43
- data/lib/action_dispatch/middleware/cookies.rb +184 -135
- data/lib/action_dispatch/middleware/debug_exceptions.rb +115 -45
- data/lib/action_dispatch/middleware/exception_wrapper.rb +21 -20
- data/lib/action_dispatch/middleware/flash.rb +61 -45
- data/lib/action_dispatch/middleware/load_interlock.rb +21 -0
- data/lib/action_dispatch/middleware/params_parser.rb +30 -46
- data/lib/action_dispatch/middleware/public_exceptions.rb +2 -2
- data/lib/action_dispatch/middleware/reloader.rb +2 -4
- data/lib/action_dispatch/middleware/remote_ip.rb +29 -19
- data/lib/action_dispatch/middleware/request_id.rb +11 -6
- data/lib/action_dispatch/middleware/session/abstract_store.rb +23 -11
- data/lib/action_dispatch/middleware/session/cache_store.rb +9 -6
- data/lib/action_dispatch/middleware/session/cookie_store.rb +29 -23
- data/lib/action_dispatch/middleware/session/mem_cache_store.rb +4 -0
- data/lib/action_dispatch/middleware/show_exceptions.rb +11 -9
- data/lib/action_dispatch/middleware/ssl.rb +93 -36
- data/lib/action_dispatch/middleware/stack.rb +43 -48
- data/lib/action_dispatch/middleware/static.rb +52 -40
- data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +2 -14
- data/lib/action_dispatch/middleware/templates/rescues/{_source.erb → _source.html.erb} +0 -0
- data/lib/action_dispatch/middleware/templates/rescues/_source.text.erb +8 -0
- data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +1 -1
- data/lib/action_dispatch/middleware/templates/rescues/template_error.text.erb +1 -1
- data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +4 -4
- data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +59 -63
- data/lib/action_dispatch/railtie.rb +0 -2
- data/lib/action_dispatch/request/session.rb +66 -34
- data/lib/action_dispatch/request/utils.rb +51 -19
- data/lib/action_dispatch/routing.rb +3 -8
- data/lib/action_dispatch/routing/inspector.rb +6 -30
- data/lib/action_dispatch/routing/mapper.rb +447 -322
- data/lib/action_dispatch/routing/polymorphic_routes.rb +8 -14
- data/lib/action_dispatch/routing/redirection.rb +3 -3
- data/lib/action_dispatch/routing/route_set.rb +124 -227
- data/lib/action_dispatch/routing/url_for.rb +27 -10
- data/lib/action_dispatch/testing/assertions.rb +1 -1
- data/lib/action_dispatch/testing/assertions/response.rb +27 -9
- data/lib/action_dispatch/testing/assertions/routing.rb +9 -9
- data/lib/action_dispatch/testing/integration.rb +237 -76
- data/lib/action_dispatch/testing/test_process.rb +5 -5
- data/lib/action_dispatch/testing/test_request.rb +12 -21
- data/lib/action_dispatch/testing/test_response.rb +1 -4
- data/lib/action_pack.rb +1 -1
- data/lib/action_pack/gem_version.rb +4 -4
- metadata +26 -25
- 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/journey/router/strexp.rb +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
@@ -13,12 +13,14 @@ require 'action_dispatch/http/url'
|
|
13
13
|
require 'active_support/core_ext/array/conversions'
|
14
14
|
|
15
15
|
module ActionDispatch
|
16
|
-
class Request
|
16
|
+
class Request
|
17
|
+
include Rack::Request::Helpers
|
17
18
|
include ActionDispatch::Http::Cache::Request
|
18
19
|
include ActionDispatch::Http::MimeNegotiation
|
19
20
|
include ActionDispatch::Http::Parameters
|
20
21
|
include ActionDispatch::Http::FilterParameters
|
21
22
|
include ActionDispatch::Http::URL
|
23
|
+
include Rack::Request::Env
|
22
24
|
|
23
25
|
autoload :Session, 'action_dispatch/request/session'
|
24
26
|
autoload :Utils, 'action_dispatch/request/utils'
|
@@ -29,19 +31,28 @@ module ActionDispatch
|
|
29
31
|
PATH_TRANSLATED REMOTE_HOST
|
30
32
|
REMOTE_IDENT REMOTE_USER REMOTE_ADDR
|
31
33
|
SERVER_NAME SERVER_PROTOCOL
|
34
|
+
ORIGINAL_SCRIPT_NAME
|
32
35
|
|
33
36
|
HTTP_ACCEPT HTTP_ACCEPT_CHARSET HTTP_ACCEPT_ENCODING
|
34
37
|
HTTP_ACCEPT_LANGUAGE HTTP_CACHE_CONTROL HTTP_FROM
|
35
|
-
HTTP_NEGOTIATE HTTP_PRAGMA
|
38
|
+
HTTP_NEGOTIATE HTTP_PRAGMA HTTP_CLIENT_IP
|
39
|
+
HTTP_X_FORWARDED_FOR HTTP_ORIGIN HTTP_VERSION
|
40
|
+
HTTP_X_CSRF_TOKEN HTTP_X_REQUEST_ID HTTP_X_FORWARDED_HOST
|
41
|
+
SERVER_ADDR
|
42
|
+
].freeze
|
36
43
|
|
37
44
|
ENV_METHODS.each do |env|
|
38
45
|
class_eval <<-METHOD, __FILE__, __LINE__ + 1
|
39
46
|
def #{env.sub(/^HTTP_/n, '').downcase} # def accept_charset
|
40
|
-
|
47
|
+
get_header "#{env}".freeze # get_header "HTTP_ACCEPT_CHARSET".freeze
|
41
48
|
end # end
|
42
49
|
METHOD
|
43
50
|
end
|
44
51
|
|
52
|
+
def self.empty
|
53
|
+
new({})
|
54
|
+
end
|
55
|
+
|
45
56
|
def initialize(env)
|
46
57
|
super
|
47
58
|
@method = nil
|
@@ -50,7 +61,9 @@ module ActionDispatch
|
|
50
61
|
@original_fullpath = nil
|
51
62
|
@fullpath = nil
|
52
63
|
@ip = nil
|
53
|
-
|
64
|
+
end
|
65
|
+
|
66
|
+
def commit_cookie_jar! # :nodoc:
|
54
67
|
end
|
55
68
|
|
56
69
|
def check_path_parameters!
|
@@ -59,13 +72,32 @@ module ActionDispatch
|
|
59
72
|
path_parameters.each do |key, value|
|
60
73
|
next unless value.respond_to?(:valid_encoding?)
|
61
74
|
unless value.valid_encoding?
|
62
|
-
raise ActionController::BadRequest, "Invalid parameter: #{key} => #{value}"
|
75
|
+
raise ActionController::BadRequest, "Invalid parameter encoding: #{key} => #{value.inspect}"
|
63
76
|
end
|
64
77
|
end
|
65
78
|
end
|
66
79
|
|
80
|
+
PASS_NOT_FOUND = Class.new { # :nodoc:
|
81
|
+
def self.action(_); self; end
|
82
|
+
def self.call(_); [404, {'X-Cascade' => 'pass'}, []]; end
|
83
|
+
}
|
84
|
+
|
85
|
+
def controller_class
|
86
|
+
check_path_parameters!
|
87
|
+
params = path_parameters
|
88
|
+
|
89
|
+
if params.key?(:controller)
|
90
|
+
controller_param = params[:controller].underscore
|
91
|
+
params[:action] ||= 'index'
|
92
|
+
const_name = "#{controller_param.camelize}Controller"
|
93
|
+
ActiveSupport::Dependencies.constantize(const_name)
|
94
|
+
else
|
95
|
+
PASS_NOT_FOUND
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
67
99
|
def key?(key)
|
68
|
-
|
100
|
+
has_header? key
|
69
101
|
end
|
70
102
|
|
71
103
|
# List of HTTP request methods from the following RFCs:
|
@@ -102,15 +134,50 @@ module ActionDispatch
|
|
102
134
|
# the application should use), this \method returns the overridden
|
103
135
|
# value, not the original.
|
104
136
|
def request_method
|
105
|
-
@request_method ||= check_method(
|
137
|
+
@request_method ||= check_method(super)
|
138
|
+
end
|
139
|
+
|
140
|
+
def routes # :nodoc:
|
141
|
+
get_header("action_dispatch.routes".freeze)
|
142
|
+
end
|
143
|
+
|
144
|
+
def routes=(routes) # :nodoc:
|
145
|
+
set_header("action_dispatch.routes".freeze, routes)
|
146
|
+
end
|
147
|
+
|
148
|
+
def engine_script_name(_routes) # :nodoc:
|
149
|
+
get_header(_routes.env_key)
|
150
|
+
end
|
151
|
+
|
152
|
+
def engine_script_name=(name) # :nodoc:
|
153
|
+
set_header(routes.env_key, name.dup)
|
106
154
|
end
|
107
155
|
|
108
156
|
def request_method=(request_method) #:nodoc:
|
109
157
|
if check_method(request_method)
|
110
|
-
@request_method =
|
158
|
+
@request_method = set_header("REQUEST_METHOD", request_method)
|
111
159
|
end
|
112
160
|
end
|
113
161
|
|
162
|
+
def controller_instance # :nodoc:
|
163
|
+
get_header('action_controller.instance'.freeze)
|
164
|
+
end
|
165
|
+
|
166
|
+
def controller_instance=(controller) # :nodoc:
|
167
|
+
set_header('action_controller.instance'.freeze, controller)
|
168
|
+
end
|
169
|
+
|
170
|
+
def http_auth_salt
|
171
|
+
get_header "action_dispatch.http_auth_salt"
|
172
|
+
end
|
173
|
+
|
174
|
+
def show_exceptions? # :nodoc:
|
175
|
+
# We're treating `nil` as "unset", and we want the default setting to be
|
176
|
+
# `true`. This logic should be extracted to `env_config` and calculated
|
177
|
+
# once.
|
178
|
+
!(get_header('action_dispatch.show_exceptions'.freeze) == false)
|
179
|
+
end
|
180
|
+
|
114
181
|
# Returns a symbol form of the #request_method
|
115
182
|
def request_method_symbol
|
116
183
|
HTTP_METHOD_LOOKUP[request_method]
|
@@ -120,7 +187,7 @@ module ActionDispatch
|
|
120
187
|
# even if it was overridden by middleware. See #request_method for
|
121
188
|
# more information.
|
122
189
|
def method
|
123
|
-
@method ||= check_method(
|
190
|
+
@method ||= check_method(get_header("rack.methodoverride.original_method") || get_header('REQUEST_METHOD'))
|
124
191
|
end
|
125
192
|
|
126
193
|
# Returns a symbol form of the #method
|
@@ -128,47 +195,11 @@ module ActionDispatch
|
|
128
195
|
HTTP_METHOD_LOOKUP[method]
|
129
196
|
end
|
130
197
|
|
131
|
-
# Is this a GET (or HEAD) request?
|
132
|
-
# Equivalent to <tt>request.request_method_symbol == :get</tt>.
|
133
|
-
def get?
|
134
|
-
HTTP_METHOD_LOOKUP[request_method] == :get
|
135
|
-
end
|
136
|
-
|
137
|
-
# Is this a POST request?
|
138
|
-
# Equivalent to <tt>request.request_method_symbol == :post</tt>.
|
139
|
-
def post?
|
140
|
-
HTTP_METHOD_LOOKUP[request_method] == :post
|
141
|
-
end
|
142
|
-
|
143
|
-
# Is this a PATCH request?
|
144
|
-
# Equivalent to <tt>request.request_method == :patch</tt>.
|
145
|
-
def patch?
|
146
|
-
HTTP_METHOD_LOOKUP[request_method] == :patch
|
147
|
-
end
|
148
|
-
|
149
|
-
# Is this a PUT request?
|
150
|
-
# Equivalent to <tt>request.request_method_symbol == :put</tt>.
|
151
|
-
def put?
|
152
|
-
HTTP_METHOD_LOOKUP[request_method] == :put
|
153
|
-
end
|
154
|
-
|
155
|
-
# Is this a DELETE request?
|
156
|
-
# Equivalent to <tt>request.request_method_symbol == :delete</tt>.
|
157
|
-
def delete?
|
158
|
-
HTTP_METHOD_LOOKUP[request_method] == :delete
|
159
|
-
end
|
160
|
-
|
161
|
-
# Is this a HEAD request?
|
162
|
-
# Equivalent to <tt>request.request_method_symbol == :head</tt>.
|
163
|
-
def head?
|
164
|
-
HTTP_METHOD_LOOKUP[request_method] == :head
|
165
|
-
end
|
166
|
-
|
167
198
|
# Provides access to the request's HTTP headers, for example:
|
168
199
|
#
|
169
200
|
# request.headers["Content-Type"] # => "text/plain"
|
170
201
|
def headers
|
171
|
-
Http::Headers.new(
|
202
|
+
@headers ||= Http::Headers.new(self)
|
172
203
|
end
|
173
204
|
|
174
205
|
# Returns a +String+ with the last requested path including their params.
|
@@ -179,7 +210,7 @@ module ActionDispatch
|
|
179
210
|
# # get '/foo?bar'
|
180
211
|
# request.original_fullpath # => '/foo?bar'
|
181
212
|
def original_fullpath
|
182
|
-
@original_fullpath ||= (
|
213
|
+
@original_fullpath ||= (get_header("ORIGINAL_FULLPATH") || fullpath)
|
183
214
|
end
|
184
215
|
|
185
216
|
# Returns the +String+ full path including params of the last URL requested.
|
@@ -218,62 +249,84 @@ module ActionDispatch
|
|
218
249
|
# (case-insensitive), which may need to be manually added depending on the
|
219
250
|
# choice of JavaScript libraries and frameworks.
|
220
251
|
def xml_http_request?
|
221
|
-
|
252
|
+
get_header('HTTP_X_REQUESTED_WITH') =~ /XMLHttpRequest/i
|
222
253
|
end
|
223
254
|
alias :xhr? :xml_http_request?
|
224
255
|
|
256
|
+
# Returns the IP address of client as a +String+.
|
225
257
|
def ip
|
226
258
|
@ip ||= super
|
227
259
|
end
|
228
260
|
|
229
|
-
#
|
261
|
+
# Returns the IP address of client as a +String+,
|
262
|
+
# usually set by the RemoteIp middleware.
|
230
263
|
def remote_ip
|
231
|
-
@remote_ip ||= (
|
264
|
+
@remote_ip ||= (get_header("action_dispatch.remote_ip") || ip).to_s
|
232
265
|
end
|
233
266
|
|
267
|
+
def remote_ip=(remote_ip)
|
268
|
+
set_header "action_dispatch.remote_ip".freeze, remote_ip
|
269
|
+
end
|
270
|
+
|
271
|
+
ACTION_DISPATCH_REQUEST_ID = "action_dispatch.request_id".freeze # :nodoc:
|
272
|
+
|
234
273
|
# Returns the unique request id, which is based on either the X-Request-Id header that can
|
235
274
|
# be generated by a firewall, load balancer, or web server or by the RequestId middleware
|
236
275
|
# (which sets the action_dispatch.request_id environment variable).
|
237
276
|
#
|
238
277
|
# This unique ID is useful for tracing a request from end-to-end as part of logging or debugging.
|
239
278
|
# This relies on the rack variable set by the ActionDispatch::RequestId middleware.
|
240
|
-
def
|
241
|
-
|
279
|
+
def request_id
|
280
|
+
get_header ACTION_DISPATCH_REQUEST_ID
|
242
281
|
end
|
243
282
|
|
283
|
+
def request_id=(id) # :nodoc:
|
284
|
+
set_header ACTION_DISPATCH_REQUEST_ID, id
|
285
|
+
end
|
286
|
+
|
287
|
+
alias_method :uuid, :request_id
|
288
|
+
|
244
289
|
# Returns the lowercase name of the HTTP server software.
|
245
290
|
def server_software
|
246
|
-
(
|
291
|
+
(get_header('SERVER_SOFTWARE') && /^([a-zA-Z]+)/ =~ get_header('SERVER_SOFTWARE')) ? $1.downcase : nil
|
247
292
|
end
|
248
293
|
|
249
294
|
# Read the request \body. This is useful for web services that need to
|
250
295
|
# work with raw requests directly.
|
251
296
|
def raw_post
|
252
|
-
unless
|
297
|
+
unless has_header? 'RAW_POST_DATA'
|
253
298
|
raw_post_body = body
|
254
|
-
|
299
|
+
set_header('RAW_POST_DATA', raw_post_body.read(content_length))
|
255
300
|
raw_post_body.rewind if raw_post_body.respond_to?(:rewind)
|
256
301
|
end
|
257
|
-
|
302
|
+
get_header 'RAW_POST_DATA'
|
258
303
|
end
|
259
304
|
|
260
305
|
# The request body is an IO input stream. If the RAW_POST_DATA environment
|
261
306
|
# variable is already set, wrap it in a StringIO.
|
262
307
|
def body
|
263
|
-
if raw_post =
|
308
|
+
if raw_post = get_header('RAW_POST_DATA')
|
264
309
|
raw_post.force_encoding(Encoding::BINARY)
|
265
310
|
StringIO.new(raw_post)
|
266
311
|
else
|
267
|
-
|
312
|
+
body_stream
|
268
313
|
end
|
269
314
|
end
|
270
315
|
|
316
|
+
# Determine whether the request body contains form-data by checking
|
317
|
+
# the request Content-Type for one of the media-types:
|
318
|
+
# "application/x-www-form-urlencoded" or "multipart/form-data". The
|
319
|
+
# list of form-data media types can be modified through the
|
320
|
+
# +FORM_DATA_MEDIA_TYPES+ array.
|
321
|
+
#
|
322
|
+
# A request body is not assumed to contain form-data when no
|
323
|
+
# Content-Type header is provided and the request_method is POST.
|
271
324
|
def form_data?
|
272
|
-
FORM_DATA_MEDIA_TYPES.include?(
|
325
|
+
FORM_DATA_MEDIA_TYPES.include?(media_type)
|
273
326
|
end
|
274
327
|
|
275
328
|
def body_stream #:nodoc:
|
276
|
-
|
329
|
+
get_header('rack.input')
|
277
330
|
end
|
278
331
|
|
279
332
|
# TODO This should be broken apart into AD::Request::Session and probably
|
@@ -284,60 +337,71 @@ module ActionDispatch
|
|
284
337
|
else
|
285
338
|
self.session = {}
|
286
339
|
end
|
287
|
-
|
340
|
+
self.flash = nil
|
288
341
|
end
|
289
342
|
|
290
343
|
def session=(session) #:nodoc:
|
291
|
-
Session.set
|
344
|
+
Session.set self, session
|
292
345
|
end
|
293
346
|
|
294
347
|
def session_options=(options)
|
295
|
-
Session::Options.set
|
348
|
+
Session::Options.set self, options
|
296
349
|
end
|
297
350
|
|
298
351
|
# Override Rack's GET method to support indifferent access
|
299
352
|
def GET
|
300
|
-
|
353
|
+
fetch_header("action_dispatch.request.query_parameters") do |k|
|
354
|
+
rack_query_params = super || {}
|
355
|
+
# Check for non UTF-8 parameter values, which would cause errors later
|
356
|
+
Request::Utils.check_param_encoding(rack_query_params)
|
357
|
+
set_header k, Request::Utils.normalize_encode_params(rack_query_params)
|
358
|
+
end
|
301
359
|
rescue Rack::Utils::ParameterTypeError, Rack::Utils::InvalidParameterError => e
|
302
|
-
raise ActionController::BadRequest.new(
|
360
|
+
raise ActionController::BadRequest.new("Invalid query parameters: #{e.message}")
|
303
361
|
end
|
304
362
|
alias :query_parameters :GET
|
305
363
|
|
306
364
|
# Override Rack's POST method to support indifferent access
|
307
365
|
def POST
|
308
|
-
|
366
|
+
fetch_header("action_dispatch.request.request_parameters") do
|
367
|
+
pr = parse_formatted_parameters(params_parsers) do |params|
|
368
|
+
super || {}
|
369
|
+
end
|
370
|
+
self.request_parameters = Request::Utils.normalize_encode_params(pr)
|
371
|
+
end
|
372
|
+
rescue ParamsParser::ParseError # one of the parse strategies blew up
|
373
|
+
self.request_parameters = Request::Utils.normalize_encode_params(super || {})
|
374
|
+
raise
|
309
375
|
rescue Rack::Utils::ParameterTypeError, Rack::Utils::InvalidParameterError => e
|
310
|
-
raise ActionController::BadRequest.new(
|
376
|
+
raise ActionController::BadRequest.new("Invalid request parameters: #{e.message}")
|
311
377
|
end
|
312
378
|
alias :request_parameters :POST
|
313
379
|
|
314
380
|
# Returns the authorization header regardless of whether it was specified directly or through one of the
|
315
381
|
# proxy alternatives.
|
316
382
|
def authorization
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
383
|
+
get_header('HTTP_AUTHORIZATION') ||
|
384
|
+
get_header('X-HTTP_AUTHORIZATION') ||
|
385
|
+
get_header('X_HTTP_AUTHORIZATION') ||
|
386
|
+
get_header('REDIRECT_X_HTTP_AUTHORIZATION')
|
321
387
|
end
|
322
388
|
|
323
|
-
# True if the request came from localhost, 127.0.0.1.
|
389
|
+
# True if the request came from localhost, 127.0.0.1, or ::1.
|
324
390
|
def local?
|
325
391
|
LOCALHOST =~ remote_addr && LOCALHOST =~ remote_ip
|
326
392
|
end
|
327
393
|
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
)
|
394
|
+
def request_parameters=(params)
|
395
|
+
raise if params.nil?
|
396
|
+
set_header("action_dispatch.request.request_parameters".freeze, params)
|
397
|
+
end
|
333
398
|
|
334
|
-
|
399
|
+
def logger
|
400
|
+
get_header("action_dispatch.logger".freeze)
|
335
401
|
end
|
336
402
|
|
337
|
-
|
338
|
-
|
339
|
-
Utils.deep_munge(super)
|
340
|
-
end
|
403
|
+
def commit_flash
|
404
|
+
end
|
341
405
|
|
342
406
|
private
|
343
407
|
def check_method(name)
|
@@ -1,6 +1,4 @@
|
|
1
1
|
require 'active_support/core_ext/module/attribute_accessors'
|
2
|
-
require 'active_support/core_ext/string/filters'
|
3
|
-
require 'active_support/deprecation'
|
4
2
|
require 'action_dispatch/http/filter_redirect'
|
5
3
|
require 'monitor'
|
6
4
|
|
@@ -34,46 +32,56 @@ module ActionDispatch # :nodoc:
|
|
34
32
|
# end
|
35
33
|
# end
|
36
34
|
class Response
|
35
|
+
class Header < DelegateClass(Hash) # :nodoc:
|
36
|
+
def initialize(response, header)
|
37
|
+
@response = response
|
38
|
+
super(header)
|
39
|
+
end
|
40
|
+
|
41
|
+
def []=(k,v)
|
42
|
+
if @response.sending? || @response.sent?
|
43
|
+
raise ActionDispatch::IllegalStateError, 'header already sent'
|
44
|
+
end
|
45
|
+
|
46
|
+
super
|
47
|
+
end
|
48
|
+
|
49
|
+
def merge(other)
|
50
|
+
self.class.new @response, __getobj__.merge(other)
|
51
|
+
end
|
52
|
+
|
53
|
+
def to_hash
|
54
|
+
__getobj__.dup
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
37
58
|
# The request that the response is responding to.
|
38
59
|
attr_accessor :request
|
39
60
|
|
40
61
|
# The HTTP status code.
|
41
62
|
attr_reader :status
|
42
63
|
|
43
|
-
|
64
|
+
# Get headers for this response.
|
65
|
+
attr_reader :header
|
44
66
|
|
45
|
-
# Get and set headers for this response.
|
46
|
-
attr_accessor :header
|
47
|
-
|
48
|
-
alias_method :headers=, :header=
|
49
67
|
alias_method :headers, :header
|
50
68
|
|
51
69
|
delegate :[], :[]=, :to => :@header
|
52
70
|
delegate :each, :to => :@stream
|
53
71
|
|
54
|
-
# Sets the HTTP response's content MIME type. For example, in the controller
|
55
|
-
# you could write this:
|
56
|
-
#
|
57
|
-
# response.content_type = "text/plain"
|
58
|
-
#
|
59
|
-
# If a character set has been defined for this response (see charset=) then
|
60
|
-
# the character set information will also be included in the content type
|
61
|
-
# information.
|
62
|
-
attr_reader :content_type
|
63
|
-
|
64
|
-
# The charset of the response. HTML wants to know the encoding of the
|
65
|
-
# content you're giving them, so we need to send that along.
|
66
|
-
attr_accessor :charset
|
67
|
-
|
68
72
|
CONTENT_TYPE = "Content-Type".freeze
|
69
73
|
SET_COOKIE = "Set-Cookie".freeze
|
70
74
|
LOCATION = "Location".freeze
|
71
|
-
NO_CONTENT_CODES = [204, 304]
|
75
|
+
NO_CONTENT_CODES = [100, 101, 102, 204, 205, 304]
|
72
76
|
|
73
77
|
cattr_accessor(:default_charset) { "utf-8" }
|
74
78
|
cattr_accessor(:default_headers)
|
75
79
|
|
76
80
|
include Rack::Response::Helpers
|
81
|
+
# Aliasing these off because AD::Http::Cache::Response defines them
|
82
|
+
alias :_cache_control :cache_control
|
83
|
+
alias :_cache_control= :cache_control=
|
84
|
+
|
77
85
|
include ActionDispatch::Http::FilterRedirect
|
78
86
|
include ActionDispatch::Http::Cache::Response
|
79
87
|
include MonitorMixin
|
@@ -83,11 +91,21 @@ module ActionDispatch # :nodoc:
|
|
83
91
|
@response = response
|
84
92
|
@buf = buf
|
85
93
|
@closed = false
|
94
|
+
@str_body = nil
|
95
|
+
end
|
96
|
+
|
97
|
+
def body
|
98
|
+
@str_body ||= begin
|
99
|
+
buf = ''
|
100
|
+
each { |chunk| buf << chunk }
|
101
|
+
buf
|
102
|
+
end
|
86
103
|
end
|
87
104
|
|
88
105
|
def write(string)
|
89
106
|
raise IOError, "closed stream" if closed?
|
90
107
|
|
108
|
+
@str_body = nil
|
91
109
|
@response.commit!
|
92
110
|
@buf.push string
|
93
111
|
end
|
@@ -112,37 +130,40 @@ module ActionDispatch # :nodoc:
|
|
112
130
|
end
|
113
131
|
end
|
114
132
|
|
133
|
+
def self.create(status = 200, header = {}, body = [], default_headers: self.default_headers)
|
134
|
+
header = merge_default_headers(header, default_headers)
|
135
|
+
new status, header, body
|
136
|
+
end
|
137
|
+
|
138
|
+
def self.merge_default_headers(original, default)
|
139
|
+
default.respond_to?(:merge) ? default.merge(original) : original
|
140
|
+
end
|
141
|
+
|
115
142
|
# The underlying body, as a streamable object.
|
116
143
|
attr_reader :stream
|
117
144
|
|
118
|
-
def initialize(status = 200, header = {}, body = []
|
145
|
+
def initialize(status = 200, header = {}, body = [])
|
119
146
|
super()
|
120
147
|
|
121
|
-
|
122
|
-
header = merge_default_headers(header, default_headers)
|
148
|
+
@header = Header.new(self, header)
|
123
149
|
|
124
|
-
self.body, self.
|
150
|
+
self.body, self.status = body, status
|
125
151
|
|
126
|
-
@sending_file = false
|
127
|
-
@blank = false
|
128
152
|
@cv = new_cond
|
129
153
|
@committed = false
|
130
154
|
@sending = false
|
131
155
|
@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
156
|
|
141
157
|
prepare_cache_control!
|
142
158
|
|
143
159
|
yield self if block_given?
|
144
160
|
end
|
145
161
|
|
162
|
+
def has_header?(key); headers.key? key; end
|
163
|
+
def get_header(key); headers[key]; end
|
164
|
+
def set_header(key, v); headers[key] = v; end
|
165
|
+
def delete_header(key); headers.delete key; end
|
166
|
+
|
146
167
|
def await_commit
|
147
168
|
synchronize do
|
148
169
|
@cv.wait_until { @committed }
|
@@ -187,7 +208,49 @@ module ActionDispatch # :nodoc:
|
|
187
208
|
|
188
209
|
# Sets the HTTP content type.
|
189
210
|
def content_type=(content_type)
|
190
|
-
|
211
|
+
header_info = parse_content_type
|
212
|
+
set_content_type content_type.to_s, header_info.charset || self.class.default_charset
|
213
|
+
end
|
214
|
+
|
215
|
+
# Sets the HTTP response's content MIME type. For example, in the controller
|
216
|
+
# you could write this:
|
217
|
+
#
|
218
|
+
# response.content_type = "text/plain"
|
219
|
+
#
|
220
|
+
# If a character set has been defined for this response (see charset=) then
|
221
|
+
# the character set information will also be included in the content type
|
222
|
+
# information.
|
223
|
+
|
224
|
+
def content_type
|
225
|
+
parse_content_type.mime_type
|
226
|
+
end
|
227
|
+
|
228
|
+
def sending_file=(v)
|
229
|
+
if true == v
|
230
|
+
self.charset = false
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
234
|
+
# Sets the HTTP character set. In case of nil parameter
|
235
|
+
# it sets the charset to utf-8.
|
236
|
+
#
|
237
|
+
# response.charset = 'utf-16' # => 'utf-16'
|
238
|
+
# response.charset = nil # => 'utf-8'
|
239
|
+
def charset=(charset)
|
240
|
+
header_info = parse_content_type
|
241
|
+
if false == charset
|
242
|
+
set_header CONTENT_TYPE, header_info.mime_type
|
243
|
+
else
|
244
|
+
content_type = header_info.mime_type
|
245
|
+
set_content_type content_type, charset || self.class.default_charset
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
249
|
+
# The charset of the response. HTML wants to know the encoding of the
|
250
|
+
# content you're giving them, so we need to send that along.
|
251
|
+
def charset
|
252
|
+
header_info = parse_content_type
|
253
|
+
header_info.charset || self.class.default_charset
|
191
254
|
end
|
192
255
|
|
193
256
|
# The response code of the request.
|
@@ -216,17 +279,15 @@ module ActionDispatch # :nodoc:
|
|
216
279
|
# Returns the content of the response as a string. This contains the contents
|
217
280
|
# of any calls to <tt>render</tt>.
|
218
281
|
def body
|
219
|
-
|
220
|
-
each { |part| strings << part.to_s }
|
221
|
-
strings.join
|
282
|
+
@stream.body
|
222
283
|
end
|
223
284
|
|
224
|
-
|
285
|
+
def write(string)
|
286
|
+
@stream.write string
|
287
|
+
end
|
225
288
|
|
226
289
|
# Allows you to manually set or override the response body.
|
227
290
|
def body=(body)
|
228
|
-
@blank = true if body == EMPTY
|
229
|
-
|
230
291
|
if body.respond_to?(:to_path)
|
231
292
|
@stream = body
|
232
293
|
else
|
@@ -236,31 +297,49 @@ module ActionDispatch # :nodoc:
|
|
236
297
|
end
|
237
298
|
end
|
238
299
|
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
300
|
+
# Avoid having to pass an open file handle as the response body.
|
301
|
+
# Rack::Sendfile will usually intercept the response and uses
|
302
|
+
# the path directly, so there is no reason to open the file.
|
303
|
+
class FileBody #:nodoc:
|
304
|
+
attr_reader :to_path
|
305
|
+
|
306
|
+
def initialize(path)
|
307
|
+
@to_path = path
|
308
|
+
end
|
244
309
|
|
245
|
-
|
246
|
-
|
310
|
+
def body
|
311
|
+
File.binread(to_path)
|
312
|
+
end
|
313
|
+
|
314
|
+
# Stream the file's contents if Rack::Sendfile isn't present.
|
315
|
+
def each
|
316
|
+
File.open(to_path, 'rb') do |file|
|
317
|
+
while chunk = file.read(16384)
|
318
|
+
yield chunk
|
319
|
+
end
|
320
|
+
end
|
321
|
+
end
|
247
322
|
end
|
248
323
|
|
249
|
-
|
250
|
-
|
324
|
+
# Send the file stored at +path+ as the response body.
|
325
|
+
def send_file(path)
|
326
|
+
commit!
|
327
|
+
@stream = FileBody.new(path)
|
251
328
|
end
|
252
329
|
|
253
|
-
|
254
|
-
|
255
|
-
headers[LOCATION]
|
330
|
+
def reset_body!
|
331
|
+
@stream = build_buffer(self, [])
|
256
332
|
end
|
257
|
-
alias_method :redirect_url, :location
|
258
333
|
|
259
|
-
|
260
|
-
|
261
|
-
|
334
|
+
def body_parts
|
335
|
+
parts = []
|
336
|
+
@stream.each { |x| parts << x }
|
337
|
+
parts
|
262
338
|
end
|
263
339
|
|
340
|
+
# The location header we'll be responding with.
|
341
|
+
alias_method :redirect_url, :location
|
342
|
+
|
264
343
|
def close
|
265
344
|
stream.close if stream.respond_to?(:close)
|
266
345
|
end
|
@@ -277,34 +356,21 @@ module ActionDispatch # :nodoc:
|
|
277
356
|
end
|
278
357
|
|
279
358
|
# Turns the Response into a Rack-compatible array of the status, headers,
|
280
|
-
# and body. Allows
|
359
|
+
# and body. Allows explicit splatting:
|
281
360
|
#
|
282
361
|
# status, headers, body = *response
|
283
362
|
def to_a
|
363
|
+
commit!
|
284
364
|
rack_response @status, @header.to_hash
|
285
365
|
end
|
286
366
|
alias prepare! to_a
|
287
367
|
|
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
368
|
# Returns the response cookies, converted to a Hash of (name => value) pairs
|
303
369
|
#
|
304
370
|
# assert_equal 'AuthorOfNewPage', r.cookies['author']
|
305
371
|
def cookies
|
306
372
|
cookies = {}
|
307
|
-
if header =
|
373
|
+
if header = get_header(SET_COOKIE)
|
308
374
|
header = header.split("\n") if header.respond_to?(:to_str)
|
309
375
|
header.each do |cookie|
|
310
376
|
if pair = cookie.split(';').first
|
@@ -318,14 +384,36 @@ module ActionDispatch # :nodoc:
|
|
318
384
|
|
319
385
|
private
|
320
386
|
|
321
|
-
|
387
|
+
ContentTypeHeader = Struct.new :mime_type, :charset
|
388
|
+
NullContentTypeHeader = ContentTypeHeader.new nil, nil
|
389
|
+
|
390
|
+
def parse_content_type
|
391
|
+
content_type = get_header CONTENT_TYPE
|
392
|
+
if content_type
|
393
|
+
type, charset = content_type.split(/;\s*charset=/)
|
394
|
+
type = nil if type.empty?
|
395
|
+
ContentTypeHeader.new(type, charset)
|
396
|
+
else
|
397
|
+
NullContentTypeHeader
|
398
|
+
end
|
322
399
|
end
|
323
400
|
|
324
|
-
def
|
401
|
+
def set_content_type(content_type, charset)
|
402
|
+
type = (content_type || '').dup
|
403
|
+
type << "; charset=#{charset}" if charset
|
404
|
+
set_header CONTENT_TYPE, type
|
325
405
|
end
|
326
406
|
|
327
|
-
def
|
328
|
-
|
407
|
+
def before_committed
|
408
|
+
return if committed?
|
409
|
+
assign_default_content_type_and_charset!
|
410
|
+
handle_conditional_get!
|
411
|
+
handle_no_content!
|
412
|
+
end
|
413
|
+
|
414
|
+
def before_sending
|
415
|
+
headers.freeze
|
416
|
+
request.commit_cookie_jar! unless committed?
|
329
417
|
end
|
330
418
|
|
331
419
|
def build_buffer(response, body)
|
@@ -336,20 +424,12 @@ module ActionDispatch # :nodoc:
|
|
336
424
|
body.respond_to?(:each) ? body : [body]
|
337
425
|
end
|
338
426
|
|
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?
|
427
|
+
def assign_default_content_type_and_charset!
|
428
|
+
return if content_type
|
347
429
|
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
def append_charset?
|
352
|
-
!@sending_file && @charset != false
|
430
|
+
ct = parse_content_type
|
431
|
+
set_content_type(ct.mime_type || Mime[:html].to_s,
|
432
|
+
ct.charset || self.class.default_charset)
|
353
433
|
end
|
354
434
|
|
355
435
|
class RackBody
|
@@ -388,14 +468,15 @@ module ActionDispatch # :nodoc:
|
|
388
468
|
end
|
389
469
|
end
|
390
470
|
|
391
|
-
def
|
392
|
-
assign_default_content_type_and_charset!(header)
|
393
|
-
handle_conditional_get!
|
394
|
-
|
395
|
-
header[SET_COOKIE] = header[SET_COOKIE].join("\n") if header[SET_COOKIE].respond_to?(:join)
|
396
|
-
|
471
|
+
def handle_no_content!
|
397
472
|
if NO_CONTENT_CODES.include?(@status)
|
398
|
-
header.delete CONTENT_TYPE
|
473
|
+
@header.delete CONTENT_TYPE
|
474
|
+
@header.delete 'Content-Length'
|
475
|
+
end
|
476
|
+
end
|
477
|
+
|
478
|
+
def rack_response(status, header)
|
479
|
+
if NO_CONTENT_CODES.include?(status)
|
399
480
|
[status, header, []]
|
400
481
|
else
|
401
482
|
[status, header, RackBody.new(self)]
|