rack 2.2.8.1 → 3.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 rack might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +138 -89
- data/CONTRIBUTING.md +53 -47
- data/MIT-LICENSE +1 -1
- data/README.md +287 -0
- data/Rakefile +40 -7
- data/SPEC.rdoc +166 -125
- data/contrib/LICENSE.md +7 -0
- data/contrib/logo.webp +0 -0
- data/lib/rack/auth/abstract/handler.rb +3 -1
- data/lib/rack/auth/abstract/request.rb +3 -1
- data/lib/rack/auth/digest/md5.rb +1 -131
- data/lib/rack/auth/digest/nonce.rb +1 -54
- data/lib/rack/auth/digest/params.rb +1 -54
- data/lib/rack/auth/digest/request.rb +1 -43
- data/lib/rack/auth/digest.rb +256 -0
- data/lib/rack/body_proxy.rb +3 -1
- data/lib/rack/builder.rb +60 -42
- data/lib/rack/cascade.rb +2 -0
- data/lib/rack/chunked.rb +16 -13
- data/lib/rack/common_logger.rb +23 -18
- data/lib/rack/conditional_get.rb +18 -15
- data/lib/rack/constants.rb +62 -0
- data/lib/rack/content_length.rb +12 -16
- data/lib/rack/content_type.rb +8 -5
- data/lib/rack/deflater.rb +40 -26
- data/lib/rack/directory.rb +9 -3
- data/lib/rack/etag.rb +14 -23
- data/lib/rack/events.rb +4 -0
- data/lib/rack/file.rb +2 -0
- data/lib/rack/files.rb +15 -17
- data/lib/rack/head.rb +9 -8
- data/lib/rack/headers.rb +154 -0
- data/lib/rack/lint.rb +740 -649
- data/lib/rack/lock.rb +2 -5
- data/lib/rack/logger.rb +2 -0
- data/lib/rack/media_type.rb +4 -9
- data/lib/rack/method_override.rb +5 -1
- data/lib/rack/mime.rb +8 -0
- data/lib/rack/mock.rb +1 -271
- data/lib/rack/mock_request.rb +166 -0
- data/lib/rack/mock_response.rb +124 -0
- data/lib/rack/multipart/generator.rb +7 -5
- data/lib/rack/multipart/parser.rb +123 -79
- data/lib/rack/multipart/uploaded_file.rb +4 -0
- data/lib/rack/multipart.rb +20 -40
- data/lib/rack/null_logger.rb +9 -0
- data/lib/rack/query_parser.rb +76 -44
- data/lib/rack/recursive.rb +2 -0
- data/lib/rack/reloader.rb +0 -2
- data/lib/rack/request.rb +189 -91
- data/lib/rack/response.rb +131 -61
- data/lib/rack/rewindable_input.rb +24 -5
- data/lib/rack/runtime.rb +7 -6
- data/lib/rack/sendfile.rb +30 -25
- data/lib/rack/show_exceptions.rb +15 -2
- data/lib/rack/show_status.rb +17 -7
- data/lib/rack/static.rb +8 -8
- data/lib/rack/tempfile_reaper.rb +15 -4
- data/lib/rack/urlmap.rb +4 -2
- data/lib/rack/utils.rb +210 -199
- data/lib/rack/version.rb +9 -4
- data/lib/rack.rb +5 -76
- data/rack.gemspec +6 -6
- metadata +19 -31
- data/README.rdoc +0 -320
- data/bin/rackup +0 -5
- data/contrib/rack.png +0 -0
- data/contrib/rack.svg +0 -150
- data/contrib/rack_logo.svg +0 -164
- data/lib/rack/core_ext/regexp.rb +0 -14
- data/lib/rack/handler/cgi.rb +0 -59
- data/lib/rack/handler/fastcgi.rb +0 -100
- data/lib/rack/handler/lsws.rb +0 -61
- data/lib/rack/handler/scgi.rb +0 -71
- data/lib/rack/handler/thin.rb +0 -36
- data/lib/rack/handler/webrick.rb +0 -129
- data/lib/rack/handler.rb +0 -104
- data/lib/rack/lobster.rb +0 -70
- data/lib/rack/server.rb +0 -466
- data/lib/rack/session/abstract/id.rb +0 -523
- data/lib/rack/session/cookie.rb +0 -204
- data/lib/rack/session/memcache.rb +0 -10
- data/lib/rack/session/pool.rb +0 -85
data/lib/rack/query_parser.rb
CHANGED
@@ -2,9 +2,7 @@
|
|
2
2
|
|
3
3
|
module Rack
|
4
4
|
class QueryParser
|
5
|
-
|
6
|
-
|
7
|
-
DEFAULT_SEP = /[&;] */n
|
5
|
+
DEFAULT_SEP = /[&] */n
|
8
6
|
COMMON_SEP = { ";" => /[;] */n, ";," => /[;,] */n, "&" => /[&] */n }
|
9
7
|
|
10
8
|
# ParameterTypeError is the error that is raised when incoming structural
|
@@ -20,29 +18,35 @@ module Rack
|
|
20
18
|
# nested over the specified limit.
|
21
19
|
class ParamsTooDeepError < RangeError; end
|
22
20
|
|
23
|
-
def self.make_default(
|
24
|
-
|
21
|
+
def self.make_default(_key_space_limit=(not_deprecated = true; nil), param_depth_limit)
|
22
|
+
unless not_deprecated
|
23
|
+
warn("`first argument `key_space limit` is deprecated and no longer has an effect. Please call with only one argument, which will be required in a future version of Rack", uplevel: 1)
|
24
|
+
end
|
25
|
+
|
26
|
+
new Params, param_depth_limit
|
25
27
|
end
|
26
28
|
|
27
|
-
attr_reader :
|
29
|
+
attr_reader :param_depth_limit
|
30
|
+
|
31
|
+
def initialize(params_class, _key_space_limit=(not_deprecated = true; nil), param_depth_limit)
|
32
|
+
unless not_deprecated
|
33
|
+
warn("`second argument `key_space limit` is deprecated and no longer has an effect. Please call with only two arguments, which will be required in a future version of Rack", uplevel: 1)
|
34
|
+
end
|
28
35
|
|
29
|
-
def initialize(params_class, key_space_limit, param_depth_limit)
|
30
36
|
@params_class = params_class
|
31
|
-
@key_space_limit = key_space_limit
|
32
37
|
@param_depth_limit = param_depth_limit
|
33
38
|
end
|
34
39
|
|
35
40
|
# Stolen from Mongrel, with some small modifications:
|
36
|
-
# Parses a query string by breaking it up at the '&'
|
37
|
-
#
|
38
|
-
#
|
39
|
-
|
40
|
-
def parse_query(qs, d = nil, &unescaper)
|
41
|
+
# Parses a query string by breaking it up at the '&'. You can also use this
|
42
|
+
# to parse cookies by changing the characters used in the second parameter
|
43
|
+
# (which defaults to '&').
|
44
|
+
def parse_query(qs, separator = nil, &unescaper)
|
41
45
|
unescaper ||= method(:unescape)
|
42
46
|
|
43
47
|
params = make_params
|
44
48
|
|
45
|
-
(qs || '').split(
|
49
|
+
(qs || '').split(separator ? (COMMON_SEP[separator] || /[#{separator}] */n) : DEFAULT_SEP).each do |p|
|
46
50
|
next if p.empty?
|
47
51
|
k, v = p.split('=', 2).map!(&unescaper)
|
48
52
|
|
@@ -65,14 +69,14 @@ module Rack
|
|
65
69
|
# query strings with parameters of conflicting types, in this case a
|
66
70
|
# ParameterTypeError is raised. Users are encouraged to return a 400 in this
|
67
71
|
# case.
|
68
|
-
def parse_nested_query(qs,
|
72
|
+
def parse_nested_query(qs, separator = nil)
|
69
73
|
params = make_params
|
70
74
|
|
71
75
|
unless qs.nil? || qs.empty?
|
72
|
-
(qs || '').split(
|
76
|
+
(qs || '').split(separator ? (COMMON_SEP[separator] || /[#{separator}] */n) : DEFAULT_SEP).each do |p|
|
73
77
|
k, v = p.split('=', 2).map! { |s| unescape(s) }
|
74
78
|
|
75
|
-
|
79
|
+
_normalize_params(params, k, v, 0)
|
76
80
|
end
|
77
81
|
end
|
78
82
|
|
@@ -83,58 +87,89 @@ module Rack
|
|
83
87
|
|
84
88
|
# normalize_params recursively expands parameters into structural types. If
|
85
89
|
# the structural types represented by two different parameter names are in
|
86
|
-
# conflict, a ParameterTypeError is raised.
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
name
|
91
|
-
|
92
|
-
after = $' || ''
|
90
|
+
# conflict, a ParameterTypeError is raised. The depth argument is deprecated
|
91
|
+
# and should no longer be used, it is kept for backwards compatibility with
|
92
|
+
# earlier versions of rack.
|
93
|
+
def normalize_params(params, name, v, _depth=nil)
|
94
|
+
_normalize_params(params, name, v, 0)
|
95
|
+
end
|
93
96
|
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
+
private def _normalize_params(params, name, v, depth)
|
98
|
+
raise ParamsTooDeepError if depth >= param_depth_limit
|
99
|
+
|
100
|
+
if !name
|
101
|
+
# nil name, treat same as empty string (required by tests)
|
102
|
+
k = after = ''
|
103
|
+
elsif depth == 0
|
104
|
+
# Start of parsing, don't treat [] or [ at start of string specially
|
105
|
+
if start = name.index('[', 1)
|
106
|
+
# Start of parameter nesting, use part before brackets as key
|
107
|
+
k = name[0, start]
|
108
|
+
after = name[start, name.length]
|
97
109
|
else
|
98
|
-
|
110
|
+
# Plain parameter with no nesting
|
111
|
+
k = name
|
112
|
+
after = ''
|
99
113
|
end
|
114
|
+
elsif name.start_with?('[]')
|
115
|
+
# Array nesting
|
116
|
+
k = '[]'
|
117
|
+
after = name[2, name.length]
|
118
|
+
elsif name.start_with?('[') && (start = name.index(']', 1))
|
119
|
+
# Hash nesting, use the part inside brackets as the key
|
120
|
+
k = name[1, start-1]
|
121
|
+
after = name[start+1, name.length]
|
122
|
+
else
|
123
|
+
# Probably malformed input, nested but not starting with [
|
124
|
+
# treat full name as key for backwards compatibility.
|
125
|
+
k = name
|
126
|
+
after = ''
|
100
127
|
end
|
101
128
|
|
129
|
+
return if k.empty?
|
130
|
+
|
131
|
+
v ||= String.new
|
132
|
+
|
102
133
|
if after == ''
|
103
|
-
|
134
|
+
if k == '[]' && depth != 0
|
135
|
+
return [v]
|
136
|
+
else
|
137
|
+
params[k] = v
|
138
|
+
end
|
104
139
|
elsif after == "["
|
105
140
|
params[name] = v
|
106
141
|
elsif after == "[]"
|
107
142
|
params[k] ||= []
|
108
143
|
raise ParameterTypeError, "expected Array (got #{params[k].class.name}) for param `#{k}'" unless params[k].is_a?(Array)
|
109
144
|
params[k] << v
|
110
|
-
elsif after
|
111
|
-
|
145
|
+
elsif after.start_with?('[]')
|
146
|
+
# Recognize x[][y] (hash inside array) parameters
|
147
|
+
unless after[2] == '[' && after.end_with?(']') && (child_key = after[3, after.length-4]) && !child_key.empty? && !child_key.index('[') && !child_key.index(']')
|
148
|
+
# Handle other nested array parameters
|
149
|
+
child_key = after[2, after.length]
|
150
|
+
end
|
112
151
|
params[k] ||= []
|
113
152
|
raise ParameterTypeError, "expected Array (got #{params[k].class.name}) for param `#{k}'" unless params[k].is_a?(Array)
|
114
153
|
if params_hash_type?(params[k].last) && !params_hash_has_key?(params[k].last, child_key)
|
115
|
-
|
154
|
+
_normalize_params(params[k].last, child_key, v, depth + 1)
|
116
155
|
else
|
117
|
-
params[k] <<
|
156
|
+
params[k] << _normalize_params(make_params, child_key, v, depth + 1)
|
118
157
|
end
|
119
158
|
else
|
120
159
|
params[k] ||= make_params
|
121
160
|
raise ParameterTypeError, "expected Hash (got #{params[k].class.name}) for param `#{k}'" unless params_hash_type?(params[k])
|
122
|
-
params[k] =
|
161
|
+
params[k] = _normalize_params(params[k], after, v, depth + 1)
|
123
162
|
end
|
124
163
|
|
125
164
|
params
|
126
165
|
end
|
127
166
|
|
128
167
|
def make_params
|
129
|
-
@params_class.new
|
130
|
-
end
|
131
|
-
|
132
|
-
def new_space_limit(key_space_limit)
|
133
|
-
self.class.new @params_class, key_space_limit, param_depth_limit
|
168
|
+
@params_class.new
|
134
169
|
end
|
135
170
|
|
136
171
|
def new_depth_limit(param_depth_limit)
|
137
|
-
self.class.new @params_class,
|
172
|
+
self.class.new @params_class, param_depth_limit
|
138
173
|
end
|
139
174
|
|
140
175
|
private
|
@@ -160,8 +195,7 @@ module Rack
|
|
160
195
|
end
|
161
196
|
|
162
197
|
class Params
|
163
|
-
def initialize
|
164
|
-
@limit = limit
|
198
|
+
def initialize
|
165
199
|
@size = 0
|
166
200
|
@params = {}
|
167
201
|
end
|
@@ -171,8 +205,6 @@ module Rack
|
|
171
205
|
end
|
172
206
|
|
173
207
|
def []=(key, value)
|
174
|
-
@size += key.size if key && !@params.key?(key)
|
175
|
-
raise ParamsTooDeepError, 'exceeded available parameter key space' if @size > @limit
|
176
208
|
@params[key] = value
|
177
209
|
end
|
178
210
|
|
data/lib/rack/recursive.rb
CHANGED
data/lib/rack/reloader.rb
CHANGED
@@ -22,8 +22,6 @@ module Rack
|
|
22
22
|
# It is performing a check/reload cycle at the start of every request, but
|
23
23
|
# also respects a cool down time, during which nothing will be done.
|
24
24
|
class Reloader
|
25
|
-
(require_relative 'core_ext/regexp'; using ::Rack::RegexpExtensions) if RUBY_VERSION < '2.4'
|
26
|
-
|
27
25
|
def initialize(app, cooldown = 10, backend = Stat)
|
28
26
|
@app = app
|
29
27
|
@cooldown = cooldown
|
data/lib/rack/request.rb
CHANGED
@@ -1,5 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative 'constants'
|
4
|
+
require_relative 'utils'
|
5
|
+
require_relative 'media_type'
|
6
|
+
|
3
7
|
module Rack
|
4
8
|
# Rack::Request provides a convenient interface to a Rack
|
5
9
|
# environment. It is stateless, the environment +env+ passed to the
|
@@ -10,22 +14,54 @@ module Rack
|
|
10
14
|
# req.params["data"]
|
11
15
|
|
12
16
|
class Request
|
13
|
-
(require_relative 'core_ext/regexp'; using ::Rack::RegexpExtensions) if RUBY_VERSION < '2.4'
|
14
|
-
|
15
17
|
class << self
|
16
18
|
attr_accessor :ip_filter
|
17
|
-
end
|
18
19
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
20
|
+
# The priority when checking forwarded headers. The default
|
21
|
+
# is <tt>[:forwarded, :x_forwarded]</tt>, which means, check the
|
22
|
+
# +Forwarded+ header first, followed by the appropriate
|
23
|
+
# <tt>X-Forwarded-*</tt> header. You can revert the priority by
|
24
|
+
# reversing the priority, or remove checking of either
|
25
|
+
# or both headers by removing elements from the array.
|
26
|
+
#
|
27
|
+
# This should be set as appropriate in your environment
|
28
|
+
# based on what reverse proxies are in use. If you are not
|
29
|
+
# using reverse proxies, you should probably use an empty
|
30
|
+
# array.
|
31
|
+
attr_accessor :forwarded_priority
|
32
|
+
|
33
|
+
# The priority when checking either the <tt>X-Forwarded-Proto</tt>
|
34
|
+
# or <tt>X-Forwarded-Scheme</tt> header for the forwarded protocol.
|
35
|
+
# The default is <tt>[:proto, :scheme]</tt>, to try the
|
36
|
+
# <tt>X-Forwarded-Proto</tt> header before the
|
37
|
+
# <tt>X-Forwarded-Scheme</tt> header. Rack 2 had behavior
|
38
|
+
# similar to <tt>[:scheme, :proto]</tt>. You can remove either or
|
39
|
+
# both of the entries in array to ignore that respective header.
|
40
|
+
attr_accessor :x_forwarded_proto_priority
|
24
41
|
end
|
25
42
|
|
43
|
+
@forwarded_priority = [:forwarded, :x_forwarded]
|
44
|
+
@x_forwarded_proto_priority = [:proto, :scheme]
|
45
|
+
|
46
|
+
valid_ipv4_octet = /\.(25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])/
|
47
|
+
|
48
|
+
trusted_proxies = Regexp.union(
|
49
|
+
/\A127#{valid_ipv4_octet}{3}\z/, # localhost IPv4 range 127.x.x.x, per RFC-3330
|
50
|
+
/\A::1\z/, # localhost IPv6 ::1
|
51
|
+
/\Af[cd][0-9a-f]{2}(?::[0-9a-f]{0,4}){0,7}\z/i, # private IPv6 range fc00 .. fdff
|
52
|
+
/\A10#{valid_ipv4_octet}{3}\z/, # private IPv4 range 10.x.x.x
|
53
|
+
/\A172\.(1[6-9]|2[0-9]|3[01])#{valid_ipv4_octet}{2}\z/, # private IPv4 range 172.16.0.0 .. 172.31.255.255
|
54
|
+
/\A192\.168#{valid_ipv4_octet}{2}\z/, # private IPv4 range 192.168.x.x
|
55
|
+
/\Alocalhost\z|\Aunix(\z|:)/i, # localhost hostname, and unix domain sockets
|
56
|
+
)
|
57
|
+
|
58
|
+
self.ip_filter = lambda { |ip| trusted_proxies.match?(ip) }
|
59
|
+
|
60
|
+
ALLOWED_SCHEMES = %w(https http wss ws).freeze
|
61
|
+
|
26
62
|
def initialize(env)
|
63
|
+
@env = env
|
27
64
|
@params = nil
|
28
|
-
super(env)
|
29
65
|
end
|
30
66
|
|
31
67
|
def params
|
@@ -49,6 +85,8 @@ module Rack
|
|
49
85
|
|
50
86
|
def initialize(env)
|
51
87
|
@env = env
|
88
|
+
# This module is included at least in `ActionDispatch::Request`
|
89
|
+
# The call to `super()` allows additional mixed-in initializers are called
|
52
90
|
super()
|
53
91
|
end
|
54
92
|
|
@@ -135,6 +173,8 @@ module Rack
|
|
135
173
|
# The contents of the host/:authority header sent to the proxy.
|
136
174
|
HTTP_X_FORWARDED_HOST = 'HTTP_X_FORWARDED_HOST'
|
137
175
|
|
176
|
+
HTTP_FORWARDED = 'HTTP_FORWARDED'
|
177
|
+
|
138
178
|
# The value of the scheme sent to the proxy.
|
139
179
|
HTTP_X_FORWARDED_SCHEME = 'HTTP_X_FORWARDED_SCHEME'
|
140
180
|
|
@@ -144,7 +184,7 @@ module Rack
|
|
144
184
|
# The port used to connect to the proxy.
|
145
185
|
HTTP_X_FORWARDED_PORT = 'HTTP_X_FORWARDED_PORT'
|
146
186
|
|
147
|
-
# Another way for
|
187
|
+
# Another way for specifying https scheme was used.
|
148
188
|
HTTP_X_FORWARDED_SSL = 'HTTP_X_FORWARDED_SSL'
|
149
189
|
|
150
190
|
def body; get_header(RACK_INPUT) end
|
@@ -159,7 +199,6 @@ module Rack
|
|
159
199
|
def content_length; get_header('CONTENT_LENGTH') end
|
160
200
|
def logger; get_header(RACK_LOGGER) end
|
161
201
|
def user_agent; get_header('HTTP_USER_AGENT') end
|
162
|
-
def multithread?; get_header(RACK_MULTITHREAD) end
|
163
202
|
|
164
203
|
# the referer of the client
|
165
204
|
def referer; get_header('HTTP_REFERER') end
|
@@ -248,9 +287,7 @@ module Rack
|
|
248
287
|
end
|
249
288
|
|
250
289
|
def server_port
|
251
|
-
|
252
|
-
Integer(port)
|
253
|
-
end
|
290
|
+
get_header(SERVER_PORT)
|
254
291
|
end
|
255
292
|
|
256
293
|
def cookies
|
@@ -307,44 +344,67 @@ module Rack
|
|
307
344
|
|
308
345
|
def port
|
309
346
|
if authority = self.authority
|
310
|
-
_, _, port = split_authority(
|
311
|
-
|
312
|
-
if port
|
313
|
-
return port
|
314
|
-
end
|
347
|
+
_, _, port = split_authority(authority)
|
315
348
|
end
|
316
349
|
|
317
|
-
|
318
|
-
return forwarded_port.first
|
319
|
-
end
|
320
|
-
|
321
|
-
if scheme = self.scheme
|
322
|
-
if port = DEFAULT_PORTS[self.scheme]
|
323
|
-
return port
|
324
|
-
end
|
325
|
-
end
|
326
|
-
|
327
|
-
self.server_port
|
350
|
+
port || forwarded_port&.last || DEFAULT_PORTS[scheme] || server_port
|
328
351
|
end
|
329
352
|
|
330
353
|
def forwarded_for
|
331
|
-
|
332
|
-
|
333
|
-
|
354
|
+
forwarded_priority.each do |type|
|
355
|
+
case type
|
356
|
+
when :forwarded
|
357
|
+
if forwarded_for = get_http_forwarded(:for)
|
358
|
+
return(forwarded_for.map! do |authority|
|
359
|
+
split_authority(authority)[1]
|
360
|
+
end)
|
361
|
+
end
|
362
|
+
when :x_forwarded
|
363
|
+
if value = get_header(HTTP_X_FORWARDED_FOR)
|
364
|
+
return(split_header(value).map do |authority|
|
365
|
+
split_authority(wrap_ipv6(authority))[1]
|
366
|
+
end)
|
367
|
+
end
|
334
368
|
end
|
335
369
|
end
|
370
|
+
|
371
|
+
nil
|
336
372
|
end
|
337
373
|
|
338
374
|
def forwarded_port
|
339
|
-
|
340
|
-
|
375
|
+
forwarded_priority.each do |type|
|
376
|
+
case type
|
377
|
+
when :forwarded
|
378
|
+
if forwarded = get_http_forwarded(:for)
|
379
|
+
return(forwarded.map do |authority|
|
380
|
+
split_authority(authority)[2]
|
381
|
+
end.compact)
|
382
|
+
end
|
383
|
+
when :x_forwarded
|
384
|
+
if value = get_header(HTTP_X_FORWARDED_PORT)
|
385
|
+
return split_header(value).map(&:to_i)
|
386
|
+
end
|
387
|
+
end
|
341
388
|
end
|
389
|
+
|
390
|
+
nil
|
342
391
|
end
|
343
392
|
|
344
393
|
def forwarded_authority
|
345
|
-
|
346
|
-
|
394
|
+
forwarded_priority.each do |type|
|
395
|
+
case type
|
396
|
+
when :forwarded
|
397
|
+
if forwarded = get_http_forwarded(:host)
|
398
|
+
return forwarded.last
|
399
|
+
end
|
400
|
+
when :x_forwarded
|
401
|
+
if value = get_header(HTTP_X_FORWARDED_HOST)
|
402
|
+
return wrap_ipv6(split_header(value).last)
|
403
|
+
end
|
404
|
+
end
|
347
405
|
end
|
406
|
+
|
407
|
+
nil
|
348
408
|
end
|
349
409
|
|
350
410
|
def ssl?
|
@@ -356,17 +416,15 @@ module Rack
|
|
356
416
|
external_addresses = reject_trusted_ip_addresses(remote_addresses)
|
357
417
|
|
358
418
|
unless external_addresses.empty?
|
359
|
-
return external_addresses.
|
419
|
+
return external_addresses.last
|
360
420
|
end
|
361
421
|
|
362
|
-
if forwarded_for = self.forwarded_for
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
return reject_trusted_ip_addresses(forwarded_for).last || forwarded_for.first
|
369
|
-
end
|
422
|
+
if (forwarded_for = self.forwarded_for) && !forwarded_for.empty?
|
423
|
+
# The forwarded for addresses are ordered: client, proxy1, proxy2.
|
424
|
+
# So we reject all the trusted addresses (proxy*) and return the
|
425
|
+
# last client. Or if we trust everyone, we just return the first
|
426
|
+
# address.
|
427
|
+
return reject_trusted_ip_addresses(forwarded_for).last || forwarded_for.first
|
370
428
|
end
|
371
429
|
|
372
430
|
# If all the addresses are trusted, and we aren't forwarded, just return
|
@@ -402,13 +460,13 @@ module Rack
|
|
402
460
|
end
|
403
461
|
|
404
462
|
# Determine whether the request body contains form-data by checking
|
405
|
-
# the request
|
463
|
+
# the request content-type for one of the media-types:
|
406
464
|
# "application/x-www-form-urlencoded" or "multipart/form-data". The
|
407
465
|
# list of form-data media types can be modified through the
|
408
466
|
# +FORM_DATA_MEDIA_TYPES+ array.
|
409
467
|
#
|
410
468
|
# A request body is also assumed to contain form-data when no
|
411
|
-
#
|
469
|
+
# content-type header is provided and the request_method is POST.
|
412
470
|
def form_data?
|
413
471
|
type = media_type
|
414
472
|
meth = get_header(RACK_METHODOVERRIDE_ORIGINAL_METHOD) || get_header(REQUEST_METHOD)
|
@@ -427,7 +485,7 @@ module Rack
|
|
427
485
|
if get_header(RACK_REQUEST_QUERY_STRING) == query_string
|
428
486
|
get_header(RACK_REQUEST_QUERY_HASH)
|
429
487
|
else
|
430
|
-
query_hash = parse_query(query_string, '
|
488
|
+
query_hash = parse_query(query_string, '&')
|
431
489
|
set_header(RACK_REQUEST_QUERY_STRING, query_string)
|
432
490
|
set_header(RACK_REQUEST_QUERY_HASH, query_hash)
|
433
491
|
end
|
@@ -452,13 +510,12 @@ module Rack
|
|
452
510
|
|
453
511
|
set_header RACK_REQUEST_FORM_VARS, form_vars
|
454
512
|
set_header RACK_REQUEST_FORM_HASH, parse_query(form_vars, '&')
|
455
|
-
|
456
|
-
get_header(RACK_INPUT).rewind
|
457
513
|
end
|
458
514
|
set_header RACK_REQUEST_FORM_INPUT, get_header(RACK_INPUT)
|
459
515
|
get_header RACK_REQUEST_FORM_HASH
|
460
516
|
else
|
461
|
-
|
517
|
+
set_header RACK_REQUEST_FORM_INPUT, get_header(RACK_INPUT)
|
518
|
+
set_header(RACK_REQUEST_FORM_HASH, {})
|
462
519
|
end
|
463
520
|
end
|
464
521
|
|
@@ -530,9 +587,7 @@ module Rack
|
|
530
587
|
|
531
588
|
# shortcut for <tt>request.params[key]</tt>
|
532
589
|
def [](key)
|
533
|
-
|
534
|
-
warn("Request#[] is deprecated and will be removed in a future version of Rack. Please use request.params[] instead")
|
535
|
-
end
|
590
|
+
warn("Request#[] is deprecated and will be removed in a future version of Rack. Please use request.params[] instead", uplevel: 1)
|
536
591
|
|
537
592
|
params[key.to_s]
|
538
593
|
end
|
@@ -541,9 +596,7 @@ module Rack
|
|
541
596
|
#
|
542
597
|
# Note that modifications will not be persisted in the env. Use update_param or delete_param if you want to destructively modify params.
|
543
598
|
def []=(key, value)
|
544
|
-
|
545
|
-
warn("Request#[]= is deprecated and will be removed in a future version of Rack. Please use request.params[]= instead")
|
546
|
-
end
|
599
|
+
warn("Request#[]= is deprecated and will be removed in a future version of Rack. Please use request.params[]= instead", uplevel: 1)
|
547
600
|
|
548
601
|
params[key.to_s] = value
|
549
602
|
end
|
@@ -572,8 +625,8 @@ module Rack
|
|
572
625
|
end
|
573
626
|
|
574
627
|
def parse_http_accept_header(header)
|
575
|
-
header.to_s.split(
|
576
|
-
attribute, parameters = part.split(
|
628
|
+
header.to_s.split(/\s*,\s*/).map do |part|
|
629
|
+
attribute, parameters = part.split(/\s*;\s*/, 2)
|
577
630
|
quality = 1.0
|
578
631
|
if parameters and /\Aq=([\d.]+)/ =~ parameters
|
579
632
|
quality = $1.to_f
|
@@ -582,6 +635,11 @@ module Rack
|
|
582
635
|
end
|
583
636
|
end
|
584
637
|
|
638
|
+
# Get an array of values set in the RFC 7239 `Forwarded` request header.
|
639
|
+
def get_http_forwarded(token)
|
640
|
+
Utils.forwarded_values(get_header(HTTP_FORWARDED))&.[](token)
|
641
|
+
end
|
642
|
+
|
585
643
|
def query_parser
|
586
644
|
Utils.default_query_parser
|
587
645
|
end
|
@@ -598,58 +656,94 @@ module Rack
|
|
598
656
|
value ? value.strip.split(/[,\s]+/) : []
|
599
657
|
end
|
600
658
|
|
601
|
-
|
602
|
-
|
659
|
+
# ipv6 extracted from resolv stdlib, simplified
|
660
|
+
# to remove numbered match group creation.
|
661
|
+
ipv6 = Regexp.union(
|
662
|
+
/(?:[0-9A-Fa-f]{1,4}:){7}
|
663
|
+
[0-9A-Fa-f]{1,4}/x,
|
664
|
+
/(?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)? ::
|
665
|
+
(?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?/x,
|
666
|
+
/(?:[0-9A-Fa-f]{1,4}:){6,6}
|
667
|
+
\d+\.\d+\.\d+\.\d+/x,
|
668
|
+
/(?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)? ::
|
669
|
+
(?:[0-9A-Fa-f]{1,4}:)*
|
670
|
+
\d+\.\d+\.\d+\.\d+/x,
|
671
|
+
/[Ff][Ee]80
|
672
|
+
(?::[0-9A-Fa-f]{1,4}){7}
|
673
|
+
%[-0-9A-Za-z._~]+/x,
|
674
|
+
/[Ff][Ee]80:
|
675
|
+
(?:
|
676
|
+
(?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)? ::
|
677
|
+
(?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?
|
678
|
+
|
|
679
|
+
:(?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?
|
680
|
+
)?
|
681
|
+
:[0-9A-Fa-f]{1,4}%[-0-9A-Za-z._~]+/x)
|
682
|
+
|
683
|
+
AUTHORITY = /
|
684
|
+
\A
|
603
685
|
(?<host>
|
604
|
-
#
|
605
|
-
|
606
|
-
|
|
607
|
-
# An IPv4 address:
|
608
|
-
(?<ip4>[\d\.]+)
|
686
|
+
# Match IPv6 as a string of hex digits and colons in square brackets
|
687
|
+
\[(?<address>#{ipv6})\]
|
609
688
|
|
|
610
|
-
#
|
611
|
-
(?<
|
689
|
+
# Match any other printable string (except square brackets) as a hostname
|
690
|
+
(?<address>[[[:graph:]&&[^\[\]]]]*?)
|
612
691
|
)
|
613
|
-
# The optional port:
|
614
692
|
(:(?<port>\d+))?
|
615
|
-
|
693
|
+
\z
|
694
|
+
/x
|
616
695
|
|
617
696
|
private_constant :AUTHORITY
|
618
697
|
|
619
698
|
def split_authority(authority)
|
620
|
-
|
621
|
-
|
622
|
-
|
623
|
-
else
|
624
|
-
return match[:host], match[:host], match[:port]&.to_i
|
625
|
-
end
|
626
|
-
end
|
627
|
-
|
628
|
-
# Give up!
|
629
|
-
return authority, authority, nil
|
699
|
+
return [] if authority.nil?
|
700
|
+
return [] unless match = AUTHORITY.match(authority)
|
701
|
+
return match[:host], match[:address], match[:port]&.to_i
|
630
702
|
end
|
631
703
|
|
632
704
|
def reject_trusted_ip_addresses(ip_addresses)
|
633
705
|
ip_addresses.reject { |ip| trusted_proxy?(ip) }
|
634
706
|
end
|
635
707
|
|
708
|
+
FORWARDED_SCHEME_HEADERS = {
|
709
|
+
proto: HTTP_X_FORWARDED_PROTO,
|
710
|
+
scheme: HTTP_X_FORWARDED_SCHEME
|
711
|
+
}.freeze
|
712
|
+
private_constant :FORWARDED_SCHEME_HEADERS
|
636
713
|
def forwarded_scheme
|
637
|
-
|
638
|
-
|
714
|
+
forwarded_priority.each do |type|
|
715
|
+
case type
|
716
|
+
when :forwarded
|
717
|
+
if (forwarded_proto = get_http_forwarded(:proto)) &&
|
718
|
+
(scheme = allowed_scheme(forwarded_proto.last))
|
719
|
+
return scheme
|
720
|
+
end
|
721
|
+
when :x_forwarded
|
722
|
+
x_forwarded_proto_priority.each do |x_type|
|
723
|
+
if header = FORWARDED_SCHEME_HEADERS[x_type]
|
724
|
+
split_header(get_header(header)).reverse_each do |scheme|
|
725
|
+
if allowed_scheme(scheme)
|
726
|
+
return scheme
|
727
|
+
end
|
728
|
+
end
|
729
|
+
end
|
730
|
+
end
|
731
|
+
end
|
732
|
+
end
|
733
|
+
|
734
|
+
nil
|
639
735
|
end
|
640
736
|
|
641
737
|
def allowed_scheme(header)
|
642
738
|
header if ALLOWED_SCHEMES.include?(header)
|
643
739
|
end
|
644
740
|
|
645
|
-
def
|
646
|
-
|
647
|
-
|
648
|
-
|
649
|
-
|
650
|
-
|
651
|
-
end
|
652
|
-
end
|
741
|
+
def forwarded_priority
|
742
|
+
Request.forwarded_priority
|
743
|
+
end
|
744
|
+
|
745
|
+
def x_forwarded_proto_priority
|
746
|
+
Request.x_forwarded_proto_priority
|
653
747
|
end
|
654
748
|
end
|
655
749
|
|
@@ -657,3 +751,7 @@ module Rack
|
|
657
751
|
include Helpers
|
658
752
|
end
|
659
753
|
end
|
754
|
+
|
755
|
+
# :nocov:
|
756
|
+
require_relative 'multipart' unless defined?(Rack::Multipart)
|
757
|
+
# :nocov:
|