actionpack 4.1.16 → 4.2.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 +4 -4
- data/CHANGELOG.md +163 -690
- data/README.rdoc +7 -2
- data/lib/abstract_controller/base.rb +16 -6
- data/lib/abstract_controller/callbacks.rb +28 -51
- data/lib/abstract_controller/helpers.rb +0 -3
- data/lib/abstract_controller/railties/routes_helpers.rb +3 -3
- data/lib/abstract_controller/rendering.rb +1 -7
- data/lib/abstract_controller/url_for.rb +1 -1
- data/lib/action_controller.rb +1 -0
- data/lib/action_controller/base.rb +2 -1
- data/lib/action_controller/caching.rb +1 -1
- data/lib/action_controller/caching/fragments.rb +7 -1
- data/lib/action_controller/log_subscriber.rb +26 -25
- data/lib/action_controller/metal.rb +11 -7
- data/lib/action_controller/metal/conditional_get.rb +31 -6
- data/lib/action_controller/metal/etag_with_template_digest.rb +50 -0
- data/lib/action_controller/metal/force_ssl.rb +1 -1
- data/lib/action_controller/metal/head.rb +2 -0
- data/lib/action_controller/metal/http_authentication.rb +3 -15
- data/lib/action_controller/metal/instrumentation.rb +4 -7
- data/lib/action_controller/metal/live.rb +57 -6
- data/lib/action_controller/metal/mime_responds.rb +17 -227
- data/lib/action_controller/metal/redirecting.rb +14 -8
- data/lib/action_controller/metal/renderers.rb +19 -3
- data/lib/action_controller/metal/rendering.rb +2 -6
- data/lib/action_controller/metal/request_forgery_protection.rb +75 -7
- data/lib/action_controller/metal/streaming.rb +1 -1
- data/lib/action_controller/metal/strong_parameters.rb +111 -11
- data/lib/action_controller/metal/url_for.rb +11 -12
- data/lib/action_controller/model_naming.rb +1 -1
- data/lib/action_controller/railtie.rb +4 -0
- data/lib/action_controller/test_case.rb +87 -75
- data/lib/action_dispatch/http/cache.rb +1 -1
- data/lib/action_dispatch/http/filter_parameters.rb +2 -2
- data/lib/action_dispatch/http/headers.rb +43 -9
- data/lib/action_dispatch/http/mime_negotiation.rb +10 -4
- data/lib/action_dispatch/http/mime_type.rb +2 -16
- data/lib/action_dispatch/http/parameter_filter.rb +1 -1
- data/lib/action_dispatch/http/parameters.rb +11 -26
- data/lib/action_dispatch/http/request.rb +30 -10
- data/lib/action_dispatch/http/response.rb +52 -17
- data/lib/action_dispatch/http/upload.rb +3 -8
- data/lib/action_dispatch/http/url.rb +87 -70
- data/lib/action_dispatch/journey/formatter.rb +18 -17
- data/lib/action_dispatch/journey/gtg/builder.rb +3 -3
- data/lib/action_dispatch/journey/gtg/simulator.rb +10 -7
- data/lib/action_dispatch/journey/gtg/transition_table.rb +18 -26
- data/lib/action_dispatch/journey/nfa/dot.rb +2 -2
- data/lib/action_dispatch/journey/nfa/simulator.rb +1 -1
- data/lib/action_dispatch/journey/nfa/transition_table.rb +5 -5
- data/lib/action_dispatch/journey/nodes/node.rb +4 -0
- data/lib/action_dispatch/journey/parser.rb +52 -60
- data/lib/action_dispatch/journey/parser.y +11 -10
- data/lib/action_dispatch/journey/path/pattern.rb +16 -19
- data/lib/action_dispatch/journey/route.rb +3 -18
- data/lib/action_dispatch/journey/router.rb +34 -65
- data/lib/action_dispatch/journey/router/strexp.rb +9 -6
- data/lib/action_dispatch/journey/routes.rb +0 -4
- data/lib/action_dispatch/journey/visitors.rb +81 -92
- data/lib/action_dispatch/journey/visualizer/index.html.erb +2 -2
- data/lib/action_dispatch/middleware/cookies.rb +27 -31
- data/lib/action_dispatch/middleware/debug_exceptions.rb +32 -3
- data/lib/action_dispatch/middleware/exception_wrapper.rb +19 -17
- data/lib/action_dispatch/middleware/flash.rb +7 -4
- data/lib/action_dispatch/middleware/public_exceptions.rb +13 -8
- data/lib/action_dispatch/middleware/remote_ip.rb +3 -3
- data/lib/action_dispatch/middleware/request_id.rb +1 -1
- data/lib/action_dispatch/middleware/session/cookie_store.rb +1 -1
- data/lib/action_dispatch/middleware/show_exceptions.rb +1 -0
- data/lib/action_dispatch/middleware/static.rb +22 -23
- data/lib/action_dispatch/middleware/templates/rescues/_source.erb +22 -18
- data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +36 -8
- data/lib/action_dispatch/middleware/templates/rescues/_trace.text.erb +2 -8
- data/lib/action_dispatch/middleware/templates/rescues/{diagnostics.erb → diagnostics.html.erb} +0 -0
- data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +9 -0
- data/lib/action_dispatch/middleware/templates/rescues/layout.erb +6 -0
- data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +1 -24
- data/lib/action_dispatch/middleware/templates/rescues/template_error.text.erb +0 -1
- data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +119 -63
- data/lib/action_dispatch/routing/endpoint.rb +10 -0
- data/lib/action_dispatch/routing/inspector.rb +4 -11
- data/lib/action_dispatch/routing/mapper.rb +399 -278
- data/lib/action_dispatch/routing/polymorphic_routes.rb +190 -78
- data/lib/action_dispatch/routing/redirection.rb +10 -12
- data/lib/action_dispatch/routing/route_set.rb +224 -177
- data/lib/action_dispatch/routing/url_for.rb +9 -4
- data/lib/action_dispatch/testing/assertions.rb +11 -7
- data/lib/action_dispatch/testing/assertions/dom.rb +2 -26
- data/lib/action_dispatch/testing/assertions/response.rb +2 -7
- data/lib/action_dispatch/testing/assertions/routing.rb +9 -9
- data/lib/action_dispatch/testing/assertions/selector.rb +2 -429
- data/lib/action_dispatch/testing/assertions/tag.rb +2 -134
- data/lib/action_dispatch/testing/integration.rb +15 -18
- data/lib/action_dispatch/testing/test_request.rb +1 -1
- data/lib/action_dispatch/testing/test_response.rb +5 -1
- data/lib/action_pack/gem_version.rb +3 -3
- metadata +57 -15
- data/lib/action_controller/metal/responder.rb +0 -297
@@ -54,8 +54,14 @@ module ActionDispatch
|
|
54
54
|
end
|
55
55
|
|
56
56
|
def formats
|
57
|
-
@env["action_dispatch.request.formats"] ||=
|
58
|
-
|
57
|
+
@env["action_dispatch.request.formats"] ||= begin
|
58
|
+
params_readable = begin
|
59
|
+
parameters[:format]
|
60
|
+
rescue ActionController::BadRequest
|
61
|
+
false
|
62
|
+
end
|
63
|
+
|
64
|
+
if params_readable
|
59
65
|
Array(Mime[parameters[:format]])
|
60
66
|
elsif use_accept_header && valid_accept_header
|
61
67
|
accepts
|
@@ -64,13 +70,13 @@ module ActionDispatch
|
|
64
70
|
else
|
65
71
|
[Mime::HTML]
|
66
72
|
end
|
73
|
+
end
|
67
74
|
end
|
68
|
-
|
69
75
|
# Sets the \variant for template.
|
70
76
|
def variant=(variant)
|
71
77
|
if variant.is_a?(Symbol)
|
72
78
|
@variant = [variant]
|
73
|
-
elsif variant.
|
79
|
+
elsif variant.is_a?(Array) && variant.any? && variant.all?{ |v| v.is_a?(Symbol) }
|
74
80
|
@variant = variant
|
75
81
|
else
|
76
82
|
raise ArgumentError, "request.variant must be set to a Symbol or an Array of Symbols, not a #{variant.class}. " \
|
@@ -23,7 +23,7 @@ module Mime
|
|
23
23
|
|
24
24
|
SET = Mimes.new
|
25
25
|
EXTENSION_LOOKUP = {}
|
26
|
-
LOOKUP = {}
|
26
|
+
LOOKUP = Hash.new { |h, k| h[k] = Type.new(k) unless k.blank? }
|
27
27
|
|
28
28
|
class << self
|
29
29
|
def [](type)
|
@@ -146,7 +146,7 @@ module Mime
|
|
146
146
|
end
|
147
147
|
|
148
148
|
def lookup(string)
|
149
|
-
LOOKUP[string]
|
149
|
+
LOOKUP[string]
|
150
150
|
end
|
151
151
|
|
152
152
|
def lookup_by_extension(extension)
|
@@ -225,12 +225,9 @@ module Mime
|
|
225
225
|
end
|
226
226
|
end
|
227
227
|
|
228
|
-
attr_reader :hash
|
229
|
-
|
230
228
|
def initialize(string, symbol = nil, synonyms = [])
|
231
229
|
@symbol, @synonyms = symbol, synonyms
|
232
230
|
@string = string
|
233
|
-
@hash = [@string, @synonyms, @symbol].hash
|
234
231
|
end
|
235
232
|
|
236
233
|
def to_s
|
@@ -264,13 +261,6 @@ module Mime
|
|
264
261
|
end
|
265
262
|
end
|
266
263
|
|
267
|
-
def eql?(other)
|
268
|
-
super || (self.class == other.class &&
|
269
|
-
@string == other.string &&
|
270
|
-
@synonyms == other.synonyms &&
|
271
|
-
@symbol == other.symbol)
|
272
|
-
end
|
273
|
-
|
274
264
|
def =~(mime_type)
|
275
265
|
return false if mime_type.blank?
|
276
266
|
regexp = Regexp.new(Regexp.quote(mime_type.to_s))
|
@@ -284,10 +274,6 @@ module Mime
|
|
284
274
|
end
|
285
275
|
|
286
276
|
|
287
|
-
protected
|
288
|
-
|
289
|
-
attr_reader :string, :synonyms
|
290
|
-
|
291
277
|
private
|
292
278
|
|
293
279
|
def to_ary; end
|
@@ -56,7 +56,7 @@ module ActionDispatch
|
|
56
56
|
elsif value.is_a?(Array)
|
57
57
|
value = value.map { |v| v.is_a?(Hash) ? call(v) : v }
|
58
58
|
elsif blocks.any?
|
59
|
-
key = key.dup
|
59
|
+
key = key.dup
|
60
60
|
value = value.dup if value.duplicable?
|
61
61
|
blocks.each { |b| b.call(key, value) }
|
62
62
|
end
|
@@ -1,13 +1,11 @@
|
|
1
1
|
require 'active_support/core_ext/hash/keys'
|
2
2
|
require 'active_support/core_ext/hash/indifferent_access'
|
3
|
+
require 'active_support/deprecation'
|
3
4
|
|
4
5
|
module ActionDispatch
|
5
6
|
module Http
|
6
7
|
module Parameters
|
7
|
-
|
8
|
-
super
|
9
|
-
@symbolized_path_params = nil
|
10
|
-
end
|
8
|
+
PARAMETERS_KEY = 'action_dispatch.request.path_parameters'
|
11
9
|
|
12
10
|
# Returns both GET and POST \parameters in a single hash.
|
13
11
|
def parameters
|
@@ -18,55 +16,42 @@ module ActionDispatch
|
|
18
16
|
query_parameters.dup
|
19
17
|
end
|
20
18
|
params.merge!(path_parameters)
|
21
|
-
params.with_indifferent_access
|
22
19
|
end
|
23
20
|
end
|
24
21
|
alias :params :parameters
|
25
22
|
|
26
23
|
def path_parameters=(parameters) #:nodoc:
|
27
|
-
@
|
28
|
-
@env
|
29
|
-
@env["action_dispatch.request.path_parameters"] = parameters
|
24
|
+
@env.delete('action_dispatch.request.parameters')
|
25
|
+
@env[PARAMETERS_KEY] = parameters
|
30
26
|
end
|
31
27
|
|
32
|
-
# The same as <tt>path_parameters</tt> with explicitly symbolized keys.
|
33
28
|
def symbolized_path_parameters
|
34
|
-
|
29
|
+
ActiveSupport::Deprecation.warn(
|
30
|
+
"`symbolized_path_parameters` is deprecated. Please use `path_parameters`"
|
31
|
+
)
|
32
|
+
path_parameters
|
35
33
|
end
|
36
34
|
|
37
35
|
# Returns a hash with the \parameters used to form the \path of the request.
|
38
36
|
# Returned hash keys are strings:
|
39
37
|
#
|
40
38
|
# {'action' => 'my_action', 'controller' => 'my_controller'}
|
41
|
-
#
|
42
|
-
# See <tt>symbolized_path_parameters</tt> for symbolized keys.
|
43
39
|
def path_parameters
|
44
|
-
@env[
|
45
|
-
end
|
46
|
-
|
47
|
-
def reset_parameters #:nodoc:
|
48
|
-
@env.delete("action_dispatch.request.parameters")
|
40
|
+
@env[PARAMETERS_KEY] ||= {}
|
49
41
|
end
|
50
42
|
|
51
43
|
private
|
52
44
|
|
53
|
-
# Convert nested Hash to HashWithIndifferentAccess
|
54
|
-
# and UTF-8 encode both keys and values in nested Hash.
|
45
|
+
# Convert nested Hash to HashWithIndifferentAccess.
|
55
46
|
#
|
56
|
-
# TODO: Validate that the characters are UTF-8. If they aren't,
|
57
|
-
# you'll get a weird error down the road, but our form handling
|
58
|
-
# should really prevent that from happening
|
59
47
|
def normalize_encode_params(params)
|
60
48
|
case params
|
61
|
-
when String
|
62
|
-
params.force_encoding(Encoding::UTF_8).encode!
|
63
49
|
when Hash
|
64
50
|
if params.has_key?(:tempfile)
|
65
51
|
UploadedFile.new(params)
|
66
52
|
else
|
67
53
|
params.each_with_object({}) do |(key, val), new_hash|
|
68
|
-
|
69
|
-
new_hash[new_key] = if val.is_a?(Array)
|
54
|
+
new_hash[key] = if val.is_a?(Array)
|
70
55
|
val.map! { |el| normalize_encode_params(el) }
|
71
56
|
else
|
72
57
|
normalize_encode_params(val)
|
@@ -23,7 +23,7 @@ module ActionDispatch
|
|
23
23
|
autoload :Session, 'action_dispatch/request/session'
|
24
24
|
autoload :Utils, 'action_dispatch/request/utils'
|
25
25
|
|
26
|
-
LOCALHOST = Regexp.union [/^127
|
26
|
+
LOCALHOST = Regexp.union [/^127\.\d{1,3}\.\d{1,3}\.\d{1,3}$/, /^::1$/, /^0:0:0:0:0:0:0:1(%.*)?$/]
|
27
27
|
|
28
28
|
ENV_METHODS = %w[ AUTH_TYPE GATEWAY_INTERFACE
|
29
29
|
PATH_TRANSLATED REMOTE_HOST
|
@@ -53,6 +53,17 @@ module ActionDispatch
|
|
53
53
|
@uuid = nil
|
54
54
|
end
|
55
55
|
|
56
|
+
def check_path_parameters!
|
57
|
+
# If any of the path parameters has an invalid encoding then
|
58
|
+
# raise since it's likely to trigger errors further on.
|
59
|
+
path_parameters.each do |key, value|
|
60
|
+
next unless value.respond_to?(:valid_encoding?)
|
61
|
+
unless value.valid_encoding?
|
62
|
+
raise ActionController::BadRequest, "Invalid parameter: #{key} => #{value}"
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
56
67
|
def key?(key)
|
57
68
|
@env.key?(key)
|
58
69
|
end
|
@@ -64,6 +75,7 @@ module ActionDispatch
|
|
64
75
|
# Ordered Collections Protocol (WebDAV) (http://www.ietf.org/rfc/rfc3648.txt)
|
65
76
|
# Web Distributed Authoring and Versioning (WebDAV) Access Control Protocol (http://www.ietf.org/rfc/rfc3744.txt)
|
66
77
|
# Web Distributed Authoring and Versioning (WebDAV) SEARCH (http://www.ietf.org/rfc/rfc5323.txt)
|
78
|
+
# Calendar Extensions to WebDAV (http://www.ietf.org/rfc/rfc4791.txt)
|
67
79
|
# PATCH Method for HTTP (http://www.ietf.org/rfc/rfc5789.txt)
|
68
80
|
RFC2616 = %w(OPTIONS GET HEAD POST PUT DELETE TRACE CONNECT)
|
69
81
|
RFC2518 = %w(PROPFIND PROPPATCH MKCOL COPY MOVE LOCK UNLOCK)
|
@@ -71,9 +83,10 @@ module ActionDispatch
|
|
71
83
|
RFC3648 = %w(ORDERPATCH)
|
72
84
|
RFC3744 = %w(ACL)
|
73
85
|
RFC5323 = %w(SEARCH)
|
86
|
+
RFC4791 = %w(MKCALENDAR)
|
74
87
|
RFC5789 = %w(PATCH)
|
75
88
|
|
76
|
-
HTTP_METHODS = RFC2616 + RFC2518 + RFC3253 + RFC3648 + RFC3744 + RFC5323 + RFC5789
|
89
|
+
HTTP_METHODS = RFC2616 + RFC2518 + RFC3253 + RFC3648 + RFC3744 + RFC5323 + RFC4791 + RFC5789
|
77
90
|
|
78
91
|
HTTP_METHOD_LOOKUP = {}
|
79
92
|
|
@@ -152,6 +165,13 @@ module ActionDispatch
|
|
152
165
|
Http::Headers.new(@env)
|
153
166
|
end
|
154
167
|
|
168
|
+
# Returns a +String+ with the last requested path including their params.
|
169
|
+
#
|
170
|
+
# # get '/foo'
|
171
|
+
# request.original_fullpath # => '/foo'
|
172
|
+
#
|
173
|
+
# # get '/foo?bar'
|
174
|
+
# request.original_fullpath # => '/foo?bar'
|
155
175
|
def original_fullpath
|
156
176
|
@original_fullpath ||= (env["ORIGINAL_FULLPATH"] || fullpath)
|
157
177
|
end
|
@@ -189,8 +209,8 @@ module ActionDispatch
|
|
189
209
|
end
|
190
210
|
|
191
211
|
# Returns true if the "X-Requested-With" header contains "XMLHttpRequest"
|
192
|
-
# (case-insensitive)
|
193
|
-
#
|
212
|
+
# (case-insensitive), which may need to be manually added depending on the
|
213
|
+
# choice of JavaScript libraries and frameworks.
|
194
214
|
def xml_http_request?
|
195
215
|
@env['HTTP_X_REQUESTED_WITH'] =~ /XMLHttpRequest/i
|
196
216
|
end
|
@@ -205,7 +225,7 @@ module ActionDispatch
|
|
205
225
|
@remote_ip ||= (@env["action_dispatch.remote_ip"] || ip).to_s
|
206
226
|
end
|
207
227
|
|
208
|
-
# Returns the unique request id, which is based
|
228
|
+
# Returns the unique request id, which is based on either the X-Request-Id header that can
|
209
229
|
# be generated by a firewall, load balancer, or web server or by the RequestId middleware
|
210
230
|
# (which sets the action_dispatch.request_id environment variable).
|
211
231
|
#
|
@@ -271,16 +291,16 @@ module ActionDispatch
|
|
271
291
|
|
272
292
|
# Override Rack's GET method to support indifferent access
|
273
293
|
def GET
|
274
|
-
@env["action_dispatch.request.query_parameters"] ||= Utils.deep_munge(
|
275
|
-
rescue TypeError => e
|
294
|
+
@env["action_dispatch.request.query_parameters"] ||= Utils.deep_munge(normalize_encode_params(super || {}))
|
295
|
+
rescue TypeError, Rack::Utils::InvalidParameterError => e
|
276
296
|
raise ActionController::BadRequest.new(:query, e)
|
277
297
|
end
|
278
298
|
alias :query_parameters :GET
|
279
299
|
|
280
300
|
# Override Rack's POST method to support indifferent access
|
281
301
|
def POST
|
282
|
-
@env["action_dispatch.request.request_parameters"] ||= Utils.deep_munge(
|
283
|
-
rescue TypeError => e
|
302
|
+
@env["action_dispatch.request.request_parameters"] ||= Utils.deep_munge(normalize_encode_params(super || {}))
|
303
|
+
rescue TypeError, Rack::Utils::InvalidParameterError => e
|
284
304
|
raise ActionController::BadRequest.new(:request, e)
|
285
305
|
end
|
286
306
|
alias :request_parameters :POST
|
@@ -315,7 +335,7 @@ module ActionDispatch
|
|
315
335
|
|
316
336
|
private
|
317
337
|
def check_method(name)
|
318
|
-
HTTP_METHOD_LOOKUP[name] || raise(ActionController::UnknownHttpMethod, "#{name}, accepted HTTP methods are #{HTTP_METHODS
|
338
|
+
HTTP_METHOD_LOOKUP[name] || raise(ActionController::UnknownHttpMethod, "#{name}, accepted HTTP methods are #{HTTP_METHODS.to_sentence(:locale => :en)}")
|
319
339
|
name
|
320
340
|
end
|
321
341
|
end
|
@@ -97,6 +97,9 @@ module ActionDispatch # :nodoc:
|
|
97
97
|
x
|
98
98
|
end
|
99
99
|
|
100
|
+
def abort
|
101
|
+
end
|
102
|
+
|
100
103
|
def close
|
101
104
|
@response.commit!
|
102
105
|
@closed = true
|
@@ -110,11 +113,10 @@ module ActionDispatch # :nodoc:
|
|
110
113
|
# The underlying body, as a streamable object.
|
111
114
|
attr_reader :stream
|
112
115
|
|
113
|
-
def initialize(status = 200, header = {}, body = []
|
116
|
+
def initialize(status = 200, header = {}, body = [])
|
114
117
|
super()
|
115
118
|
|
116
|
-
|
117
|
-
header = merge_default_headers(header, default_headers)
|
119
|
+
header = merge_default_headers(header, self.class.default_headers)
|
118
120
|
|
119
121
|
self.body, self.header, self.status = body, header, status
|
120
122
|
|
@@ -208,18 +210,6 @@ module ActionDispatch # :nodoc:
|
|
208
210
|
end
|
209
211
|
alias_method :status_message, :message
|
210
212
|
|
211
|
-
def respond_to?(method, include_private = false)
|
212
|
-
if method.to_s == 'to_path'
|
213
|
-
stream.respond_to?(method)
|
214
|
-
else
|
215
|
-
super
|
216
|
-
end
|
217
|
-
end
|
218
|
-
|
219
|
-
def to_path
|
220
|
-
stream.to_path
|
221
|
-
end
|
222
|
-
|
223
213
|
# Returns the content of the response as a string. This contains the contents
|
224
214
|
# of any calls to <tt>render</tt>.
|
225
215
|
def body
|
@@ -272,6 +262,17 @@ module ActionDispatch # :nodoc:
|
|
272
262
|
stream.close if stream.respond_to?(:close)
|
273
263
|
end
|
274
264
|
|
265
|
+
def abort
|
266
|
+
if stream.respond_to?(:abort)
|
267
|
+
stream.abort
|
268
|
+
elsif stream.respond_to?(:close)
|
269
|
+
# `stream.close` should really be reserved for a close from the
|
270
|
+
# other direction, but we must fall back to it for
|
271
|
+
# compatibility.
|
272
|
+
stream.close
|
273
|
+
end
|
274
|
+
end
|
275
|
+
|
275
276
|
# Turns the Response into a Rack-compatible array of the status, headers,
|
276
277
|
# and body.
|
277
278
|
def to_a
|
@@ -309,7 +310,9 @@ module ActionDispatch # :nodoc:
|
|
309
310
|
end
|
310
311
|
|
311
312
|
def merge_default_headers(original, default)
|
312
|
-
default.respond_to?(:merge)
|
313
|
+
return original unless default.respond_to?(:merge)
|
314
|
+
|
315
|
+
default.merge(original)
|
313
316
|
end
|
314
317
|
|
315
318
|
def build_buffer(response, body)
|
@@ -336,6 +339,38 @@ module ActionDispatch # :nodoc:
|
|
336
339
|
!@sending_file && @charset != false
|
337
340
|
end
|
338
341
|
|
342
|
+
class RackBody
|
343
|
+
def initialize(response)
|
344
|
+
@response = response
|
345
|
+
end
|
346
|
+
|
347
|
+
def each(*args, &block)
|
348
|
+
@response.each(*args, &block)
|
349
|
+
end
|
350
|
+
|
351
|
+
def close
|
352
|
+
# Rack "close" maps to Response#abort, and *not* Response#close
|
353
|
+
# (which is used when the controller's finished writing)
|
354
|
+
@response.abort
|
355
|
+
end
|
356
|
+
|
357
|
+
def body
|
358
|
+
@response.body
|
359
|
+
end
|
360
|
+
|
361
|
+
def respond_to?(method, include_private = false)
|
362
|
+
if method.to_s == 'to_path'
|
363
|
+
@response.stream.respond_to?(method)
|
364
|
+
else
|
365
|
+
super
|
366
|
+
end
|
367
|
+
end
|
368
|
+
|
369
|
+
def to_path
|
370
|
+
@response.stream.to_path
|
371
|
+
end
|
372
|
+
end
|
373
|
+
|
339
374
|
def rack_response(status, header)
|
340
375
|
assign_default_content_type_and_charset!(header)
|
341
376
|
handle_conditional_get!
|
@@ -346,7 +381,7 @@ module ActionDispatch # :nodoc:
|
|
346
381
|
header.delete CONTENT_TYPE
|
347
382
|
[status, header, []]
|
348
383
|
else
|
349
|
-
[status, header,
|
384
|
+
[status, header, RackBody.new(self)]
|
350
385
|
end
|
351
386
|
end
|
352
387
|
end
|
@@ -18,6 +18,7 @@ module ActionDispatch
|
|
18
18
|
# A +Tempfile+ object with the actual uploaded file. Note that some of
|
19
19
|
# its interface is available directly.
|
20
20
|
attr_accessor :tempfile
|
21
|
+
alias :to_io :tempfile
|
21
22
|
|
22
23
|
# A string with the headers of the multipart request.
|
23
24
|
attr_accessor :headers
|
@@ -26,7 +27,8 @@ module ActionDispatch
|
|
26
27
|
@tempfile = hash[:tempfile]
|
27
28
|
raise(ArgumentError, ':tempfile is required') unless @tempfile
|
28
29
|
|
29
|
-
@original_filename =
|
30
|
+
@original_filename = hash[:filename]
|
31
|
+
@original_filename &&= @original_filename.encode "UTF-8"
|
30
32
|
@content_type = hash[:type]
|
31
33
|
@headers = hash[:head]
|
32
34
|
end
|
@@ -65,13 +67,6 @@ module ActionDispatch
|
|
65
67
|
def eof?
|
66
68
|
@tempfile.eof?
|
67
69
|
end
|
68
|
-
|
69
|
-
private
|
70
|
-
|
71
|
-
def encode_filename(filename)
|
72
|
-
# Encode the filename in the utf8 encoding, unless it is nil
|
73
|
-
filename.force_encoding(Encoding::UTF_8).encode! if filename
|
74
|
-
end
|
75
70
|
end
|
76
71
|
end
|
77
72
|
end
|
@@ -5,51 +5,81 @@ module ActionDispatch
|
|
5
5
|
module Http
|
6
6
|
module URL
|
7
7
|
IP_HOST_REGEXP = /\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/
|
8
|
-
HOST_REGEXP = /(
|
8
|
+
HOST_REGEXP = /(^[^:]+:\/\/)?([^:]+)(?::(\d+$))?/
|
9
9
|
PROTOCOL_REGEXP = /^([^:]+)(:)?(\/\/)?$/
|
10
10
|
|
11
11
|
mattr_accessor :tld_length
|
12
12
|
self.tld_length = 1
|
13
13
|
|
14
14
|
class << self
|
15
|
-
def extract_domain(host, tld_length
|
16
|
-
host
|
15
|
+
def extract_domain(host, tld_length)
|
16
|
+
extract_domain_from(host, tld_length) if named_host?(host)
|
17
17
|
end
|
18
18
|
|
19
|
-
def extract_subdomains(host, tld_length
|
19
|
+
def extract_subdomains(host, tld_length)
|
20
20
|
if named_host?(host)
|
21
|
-
|
22
|
-
parts[0..-(tld_length + 2)]
|
21
|
+
extract_subdomains_from(host, tld_length)
|
23
22
|
else
|
24
23
|
[]
|
25
24
|
end
|
26
25
|
end
|
27
26
|
|
28
|
-
def extract_subdomain(host, tld_length
|
27
|
+
def extract_subdomain(host, tld_length)
|
29
28
|
extract_subdomains(host, tld_length).join('.')
|
30
29
|
end
|
31
30
|
|
32
|
-
def url_for(options
|
33
|
-
|
34
|
-
|
35
|
-
|
31
|
+
def url_for(options)
|
32
|
+
if options[:only_path]
|
33
|
+
path_for options
|
34
|
+
else
|
35
|
+
full_url_for options
|
36
|
+
end
|
37
|
+
end
|
36
38
|
|
37
|
-
|
39
|
+
def full_url_for(options)
|
40
|
+
host = options[:host]
|
41
|
+
protocol = options[:protocol]
|
42
|
+
port = options[:port]
|
38
43
|
|
39
|
-
|
40
|
-
|
44
|
+
unless host
|
45
|
+
raise ArgumentError, 'Missing host to link to! Please provide the :host parameter, set default_url_options[:host], or set :only_path to true'
|
46
|
+
end
|
41
47
|
|
42
|
-
|
48
|
+
build_host_url(host, port, protocol, options, path_for(options))
|
49
|
+
end
|
43
50
|
|
44
|
-
|
51
|
+
def path_for(options)
|
52
|
+
path = options[:script_name].to_s.chomp("/")
|
53
|
+
path << options[:path] if options.key?(:path)
|
45
54
|
|
46
|
-
|
47
|
-
|
48
|
-
|
55
|
+
add_trailing_slash(path) if options[:trailing_slash]
|
56
|
+
add_params(path, options[:params]) if options.key?(:params)
|
57
|
+
add_anchor(path, options[:anchor]) if options.key?(:anchor)
|
58
|
+
|
59
|
+
path
|
49
60
|
end
|
50
61
|
|
51
62
|
private
|
52
63
|
|
64
|
+
def add_params(path, params)
|
65
|
+
params = { params: params } unless params.is_a?(Hash)
|
66
|
+
params.reject! { |_,v| v.to_param.nil? }
|
67
|
+
path << "?#{params.to_query}" unless params.empty?
|
68
|
+
end
|
69
|
+
|
70
|
+
def add_anchor(path, anchor)
|
71
|
+
path << "##{Journey::Router::Utils.escape_fragment(anchor.to_param.to_s)}"
|
72
|
+
end
|
73
|
+
|
74
|
+
def extract_domain_from(host, tld_length)
|
75
|
+
host.split('.').last(1 + tld_length).join('.')
|
76
|
+
end
|
77
|
+
|
78
|
+
def extract_subdomains_from(host, tld_length)
|
79
|
+
parts = host.split('.')
|
80
|
+
parts[0..-(tld_length + 2)]
|
81
|
+
end
|
82
|
+
|
53
83
|
def add_trailing_slash(path)
|
54
84
|
# includes querysting
|
55
85
|
if path.include?('?')
|
@@ -58,54 +88,38 @@ module ActionDispatch
|
|
58
88
|
elsif !path.include?(".")
|
59
89
|
path.sub!(/[^\/]\z|\A\z/, '\&/')
|
60
90
|
end
|
61
|
-
|
62
|
-
path
|
63
91
|
end
|
64
92
|
|
65
|
-
def build_host_url(options)
|
66
|
-
if
|
67
|
-
|
93
|
+
def build_host_url(host, port, protocol, options, path)
|
94
|
+
if match = host.match(HOST_REGEXP)
|
95
|
+
protocol ||= match[1] unless protocol == false
|
96
|
+
host = match[2]
|
97
|
+
port = match[3] unless options.key? :port
|
68
98
|
end
|
69
99
|
|
70
|
-
|
100
|
+
protocol = normalize_protocol protocol
|
101
|
+
host = normalize_host(host, options)
|
71
102
|
|
72
|
-
|
73
|
-
if match = options[:host].match(HOST_REGEXP)
|
74
|
-
options[:protocol] ||= match[1] unless options[:protocol] == false
|
75
|
-
options[:host] = match[2]
|
76
|
-
options[:port] = match[3] unless options.key?(:port)
|
77
|
-
end
|
103
|
+
result = protocol.dup
|
78
104
|
|
79
|
-
|
80
|
-
options[:
|
81
|
-
options[:port] = normalize_port(options)
|
82
|
-
|
83
|
-
result << options[:protocol]
|
84
|
-
result << rewrite_authentication(options)
|
85
|
-
result << options[:host]
|
86
|
-
result << ":#{options[:port]}" if options[:port]
|
105
|
+
if options[:user] && options[:password]
|
106
|
+
result << "#{Rack::Utils.escape(options[:user])}:#{Rack::Utils.escape(options[:password])}@"
|
87
107
|
end
|
88
|
-
result
|
89
|
-
end
|
90
108
|
|
91
|
-
|
92
|
-
|
93
|
-
|
109
|
+
result << host
|
110
|
+
normalize_port(port, protocol) { |normalized_port|
|
111
|
+
result << ":#{normalized_port}"
|
112
|
+
}
|
94
113
|
|
95
|
-
|
96
|
-
(options[:subdomain] == true || !options.key?(:subdomain)) && options[:domain].nil?
|
114
|
+
result.concat path
|
97
115
|
end
|
98
116
|
|
99
|
-
def
|
100
|
-
|
101
|
-
"#{Rack::Utils.escape(options[:user])}:#{Rack::Utils.escape(options[:password])}@"
|
102
|
-
else
|
103
|
-
""
|
104
|
-
end
|
117
|
+
def named_host?(host)
|
118
|
+
IP_HOST_REGEXP !~ host
|
105
119
|
end
|
106
120
|
|
107
|
-
def normalize_protocol(
|
108
|
-
case
|
121
|
+
def normalize_protocol(protocol)
|
122
|
+
case protocol
|
109
123
|
when nil
|
110
124
|
"http://"
|
111
125
|
when false, "//"
|
@@ -113,36 +127,39 @@ module ActionDispatch
|
|
113
127
|
when PROTOCOL_REGEXP
|
114
128
|
"#{$1}://"
|
115
129
|
else
|
116
|
-
raise ArgumentError, "Invalid :protocol option: #{
|
130
|
+
raise ArgumentError, "Invalid :protocol option: #{protocol.inspect}"
|
117
131
|
end
|
118
132
|
end
|
119
133
|
|
120
|
-
def normalize_host(options)
|
121
|
-
return
|
134
|
+
def normalize_host(_host, options)
|
135
|
+
return _host unless named_host?(_host)
|
122
136
|
|
123
137
|
tld_length = options[:tld_length] || @@tld_length
|
138
|
+
subdomain = options.fetch :subdomain, true
|
139
|
+
domain = options[:domain]
|
124
140
|
|
125
141
|
host = ""
|
126
|
-
if
|
127
|
-
|
128
|
-
|
129
|
-
host <<
|
142
|
+
if subdomain == true
|
143
|
+
return _host if domain.nil?
|
144
|
+
|
145
|
+
host << extract_subdomains_from(_host, tld_length).join('.')
|
146
|
+
elsif subdomain
|
147
|
+
host << subdomain.to_param
|
130
148
|
end
|
131
149
|
host << "." unless host.empty?
|
132
|
-
host << (
|
150
|
+
host << (domain || extract_domain_from(_host, tld_length))
|
133
151
|
host
|
134
152
|
end
|
135
153
|
|
136
|
-
def normalize_port(
|
137
|
-
return
|
154
|
+
def normalize_port(port, protocol)
|
155
|
+
return unless port
|
138
156
|
|
139
|
-
case
|
140
|
-
when "//"
|
141
|
-
options[:port]
|
157
|
+
case protocol
|
158
|
+
when "//" then yield port
|
142
159
|
when "https://"
|
143
|
-
|
160
|
+
yield port unless port.to_i == 443
|
144
161
|
else
|
145
|
-
|
162
|
+
yield port unless port.to_i == 80
|
146
163
|
end
|
147
164
|
end
|
148
165
|
end
|
@@ -165,7 +182,7 @@ module ActionDispatch
|
|
165
182
|
|
166
183
|
# Returns the \host for this request, such as "example.com".
|
167
184
|
def raw_host_with_port
|
168
|
-
if forwarded = env["HTTP_X_FORWARDED_HOST"]
|
185
|
+
if forwarded = env["HTTP_X_FORWARDED_HOST"]
|
169
186
|
forwarded.split(/,\s?/).last
|
170
187
|
else
|
171
188
|
env['HTTP_HOST'] || "#{env['SERVER_NAME'] || env['SERVER_ADDR']}:#{env['SERVER_PORT']}"
|