rack 2.0.9.3 → 2.2.1
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 +681 -0
- data/CONTRIBUTING.md +136 -0
- data/{COPYING → MIT-LICENSE} +4 -2
- data/README.rdoc +152 -162
- data/Rakefile +37 -23
- data/{SPEC → SPEC.rdoc} +35 -10
- data/bin/rackup +1 -0
- data/example/lobster.ru +2 -0
- data/example/protectedlobster.rb +3 -1
- data/example/protectedlobster.ru +2 -0
- data/lib/rack/auth/abstract/handler.rb +3 -1
- data/lib/rack/auth/abstract/request.rb +1 -1
- data/lib/rack/auth/basic.rb +7 -4
- data/lib/rack/auth/digest/md5.rb +13 -11
- data/lib/rack/auth/digest/nonce.rb +6 -3
- data/lib/rack/auth/digest/params.rb +4 -2
- data/lib/rack/auth/digest/request.rb +5 -3
- data/lib/rack/body_proxy.rb +15 -14
- data/lib/rack/builder.rb +116 -23
- data/lib/rack/cascade.rb +28 -12
- data/lib/rack/chunked.rb +68 -20
- data/lib/rack/common_logger.rb +33 -28
- data/lib/rack/conditional_get.rb +20 -16
- data/lib/rack/config.rb +2 -0
- data/lib/rack/content_length.rb +8 -7
- data/lib/rack/content_type.rb +5 -4
- data/lib/rack/core_ext/regexp.rb +14 -0
- data/lib/rack/deflater.rb +59 -34
- data/lib/rack/directory.rb +84 -64
- data/lib/rack/etag.rb +5 -4
- data/lib/rack/events.rb +19 -20
- data/lib/rack/file.rb +4 -173
- data/lib/rack/files.rb +218 -0
- data/lib/rack/handler/cgi.rb +2 -3
- data/lib/rack/handler/fastcgi.rb +4 -4
- data/lib/rack/handler/lsws.rb +3 -3
- data/lib/rack/handler/scgi.rb +9 -8
- data/lib/rack/handler/thin.rb +17 -11
- data/lib/rack/handler/webrick.rb +15 -6
- data/lib/rack/handler.rb +7 -2
- data/lib/rack/head.rb +1 -1
- data/lib/rack/lint.rb +72 -26
- data/lib/rack/lobster.rb +10 -10
- data/lib/rack/lock.rb +2 -1
- data/lib/rack/logger.rb +2 -0
- data/lib/rack/media_type.rb +10 -5
- data/lib/rack/method_override.rb +4 -2
- data/lib/rack/mime.rb +9 -1
- data/lib/rack/mock.rb +97 -20
- data/lib/rack/multipart/generator.rb +17 -13
- data/lib/rack/multipart/parser.rb +58 -73
- data/lib/rack/multipart/uploaded_file.rb +15 -7
- data/lib/rack/multipart.rb +7 -4
- data/lib/rack/null_logger.rb +2 -0
- data/lib/rack/query_parser.rb +53 -28
- data/lib/rack/recursive.rb +7 -5
- data/lib/rack/reloader.rb +8 -4
- data/lib/rack/request.rb +220 -61
- data/lib/rack/response.rb +127 -44
- data/lib/rack/rewindable_input.rb +4 -3
- data/lib/rack/runtime.rb +6 -4
- data/lib/rack/sendfile.rb +13 -9
- data/lib/rack/server.rb +95 -24
- data/lib/rack/session/abstract/id.rb +33 -21
- data/lib/rack/session/cookie.rb +12 -12
- data/lib/rack/session/memcache.rb +4 -93
- data/lib/rack/session/pool.rb +5 -3
- data/lib/rack/show_exceptions.rb +17 -13
- data/lib/rack/show_status.rb +5 -5
- data/lib/rack/static.rb +23 -11
- data/lib/rack/tempfile_reaper.rb +1 -1
- data/lib/rack/urlmap.rb +12 -6
- data/lib/rack/utils.rb +105 -130
- data/lib/rack/version.rb +29 -0
- data/lib/rack.rb +67 -73
- data/rack.gemspec +40 -28
- metadata +39 -182
- data/HISTORY.md +0 -520
- data/test/builder/an_underscore_app.rb +0 -5
- data/test/builder/anything.rb +0 -5
- data/test/builder/comment.ru +0 -4
- data/test/builder/end.ru +0 -5
- data/test/builder/line.ru +0 -1
- data/test/builder/options.ru +0 -2
- data/test/cgi/assets/folder/test.js +0 -1
- data/test/cgi/assets/fonts/font.eot +0 -1
- data/test/cgi/assets/images/image.png +0 -1
- data/test/cgi/assets/index.html +0 -1
- data/test/cgi/assets/javascripts/app.js +0 -1
- data/test/cgi/assets/stylesheets/app.css +0 -1
- data/test/cgi/lighttpd.conf +0 -26
- data/test/cgi/rackup_stub.rb +0 -6
- data/test/cgi/sample_rackup.ru +0 -5
- data/test/cgi/test +0 -9
- data/test/cgi/test+directory/test+file +0 -1
- data/test/cgi/test.fcgi +0 -9
- data/test/cgi/test.gz +0 -0
- data/test/cgi/test.ru +0 -5
- data/test/gemloader.rb +0 -10
- data/test/helper.rb +0 -34
- data/test/multipart/bad_robots +0 -259
- data/test/multipart/binary +0 -0
- data/test/multipart/content_type_and_no_filename +0 -6
- data/test/multipart/empty +0 -10
- data/test/multipart/fail_16384_nofile +0 -814
- data/test/multipart/file1.txt +0 -1
- data/test/multipart/filename_and_modification_param +0 -7
- data/test/multipart/filename_and_no_name +0 -6
- data/test/multipart/filename_with_encoded_words +0 -7
- data/test/multipart/filename_with_escaped_quotes +0 -6
- data/test/multipart/filename_with_escaped_quotes_and_modification_param +0 -7
- data/test/multipart/filename_with_null_byte +0 -7
- data/test/multipart/filename_with_percent_escaped_quotes +0 -6
- data/test/multipart/filename_with_single_quote +0 -7
- data/test/multipart/filename_with_unescaped_percentages +0 -6
- data/test/multipart/filename_with_unescaped_percentages2 +0 -6
- data/test/multipart/filename_with_unescaped_percentages3 +0 -6
- data/test/multipart/filename_with_unescaped_quotes +0 -6
- data/test/multipart/ie +0 -6
- data/test/multipart/invalid_character +0 -6
- data/test/multipart/mixed_files +0 -21
- data/test/multipart/nested +0 -10
- data/test/multipart/none +0 -9
- data/test/multipart/quoted +0 -15
- data/test/multipart/rack-logo.png +0 -0
- data/test/multipart/semicolon +0 -6
- data/test/multipart/text +0 -15
- data/test/multipart/three_files_three_fields +0 -31
- data/test/multipart/unity3d_wwwform +0 -11
- data/test/multipart/webkit +0 -32
- data/test/rackup/config.ru +0 -31
- data/test/registering_handler/rack/handler/registering_myself.rb +0 -8
- data/test/spec_auth_basic.rb +0 -89
- data/test/spec_auth_digest.rb +0 -260
- data/test/spec_body_proxy.rb +0 -85
- data/test/spec_builder.rb +0 -233
- data/test/spec_cascade.rb +0 -63
- data/test/spec_cgi.rb +0 -84
- data/test/spec_chunked.rb +0 -103
- data/test/spec_common_logger.rb +0 -107
- data/test/spec_conditional_get.rb +0 -103
- data/test/spec_config.rb +0 -23
- data/test/spec_content_length.rb +0 -86
- data/test/spec_content_type.rb +0 -46
- data/test/spec_deflater.rb +0 -375
- data/test/spec_directory.rb +0 -148
- data/test/spec_etag.rb +0 -108
- data/test/spec_events.rb +0 -133
- data/test/spec_fastcgi.rb +0 -85
- data/test/spec_file.rb +0 -264
- data/test/spec_handler.rb +0 -57
- data/test/spec_head.rb +0 -46
- data/test/spec_lint.rb +0 -520
- data/test/spec_lobster.rb +0 -59
- data/test/spec_lock.rb +0 -204
- data/test/spec_logger.rb +0 -24
- data/test/spec_media_type.rb +0 -42
- data/test/spec_method_override.rb +0 -110
- data/test/spec_mime.rb +0 -51
- data/test/spec_mock.rb +0 -359
- data/test/spec_multipart.rb +0 -721
- data/test/spec_null_logger.rb +0 -21
- data/test/spec_recursive.rb +0 -75
- data/test/spec_request.rb +0 -1423
- data/test/spec_response.rb +0 -528
- data/test/spec_rewindable_input.rb +0 -128
- data/test/spec_runtime.rb +0 -50
- data/test/spec_sendfile.rb +0 -125
- data/test/spec_server.rb +0 -193
- data/test/spec_session_abstract_id.rb +0 -31
- data/test/spec_session_abstract_session_hash.rb +0 -45
- data/test/spec_session_cookie.rb +0 -442
- data/test/spec_session_memcache.rb +0 -357
- data/test/spec_session_persisted_secure_secure_session_hash.rb +0 -73
- data/test/spec_session_pool.rb +0 -247
- data/test/spec_show_exceptions.rb +0 -93
- data/test/spec_show_status.rb +0 -104
- data/test/spec_static.rb +0 -184
- data/test/spec_tempfile_reaper.rb +0 -64
- data/test/spec_thin.rb +0 -96
- data/test/spec_urlmap.rb +0 -237
- data/test/spec_utils.rb +0 -742
- data/test/spec_version.rb +0 -11
- data/test/spec_webrick.rb +0 -206
- data/test/static/another/index.html +0 -1
- data/test/static/foo.html +0 -1
- data/test/static/index.html +0 -1
- data/test/testrequest.rb +0 -78
- data/test/unregistered_handler/rack/handler/unregistered.rb +0 -7
- data/test/unregistered_handler/rack/handler/unregistered_long_one.rb +0 -7
data/lib/rack/request.rb
CHANGED
@@ -1,5 +1,4 @@
|
|
1
|
-
|
2
|
-
require 'rack/media_type'
|
1
|
+
# frozen_string_literal: true
|
3
2
|
|
4
3
|
module Rack
|
5
4
|
# Rack::Request provides a convenient interface to a Rack
|
@@ -11,7 +10,18 @@ module Rack
|
|
11
10
|
# req.params["data"]
|
12
11
|
|
13
12
|
class Request
|
14
|
-
|
13
|
+
(require_relative 'core_ext/regexp'; using ::Rack::RegexpExtensions) if RUBY_VERSION < '2.4'
|
14
|
+
|
15
|
+
class << self
|
16
|
+
attr_accessor :ip_filter
|
17
|
+
end
|
18
|
+
|
19
|
+
self.ip_filter = lambda { |ip| /\A127\.0\.0\.1\Z|\A(10|172\.(1[6-9]|2[0-9]|30|31)|192\.168)\.|\A::1\Z|\Afd[0-9a-f]{2}:.+|\Alocalhost\Z|\Aunix\Z|\Aunix:/i.match?(ip) }
|
20
|
+
ALLOWED_SCHEMES = %w(https http).freeze
|
21
|
+
SCHEME_WHITELIST = ALLOWED_SCHEMES
|
22
|
+
if Object.respond_to?(:deprecate_constant)
|
23
|
+
deprecate_constant :SCHEME_WHITELIST
|
24
|
+
end
|
15
25
|
|
16
26
|
def initialize(env)
|
17
27
|
@params = nil
|
@@ -78,7 +88,7 @@ module Rack
|
|
78
88
|
# assert_equal 'image/png,*/*', request.get_header('Accept')
|
79
89
|
#
|
80
90
|
# http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2
|
81
|
-
def add_header
|
91
|
+
def add_header(key, v)
|
82
92
|
if v.nil?
|
83
93
|
get_header key
|
84
94
|
elsif has_header? key
|
@@ -100,7 +110,7 @@ module Rack
|
|
100
110
|
|
101
111
|
module Helpers
|
102
112
|
# The set of form-data media-types. Requests that do not indicate
|
103
|
-
# one of the media types
|
113
|
+
# one of the media types present in this list will not be eligible
|
104
114
|
# for form-data / param parsing.
|
105
115
|
FORM_DATA_MEDIA_TYPES = [
|
106
116
|
'application/x-www-form-urlencoded',
|
@@ -108,7 +118,7 @@ module Rack
|
|
108
118
|
]
|
109
119
|
|
110
120
|
# The set of media-types. Requests that do not indicate
|
111
|
-
# one of the media types
|
121
|
+
# one of the media types present in this list will not be eligible
|
112
122
|
# for param parsing like soap attachments or generic multiparts
|
113
123
|
PARSEABLE_DATA_MEDIA_TYPES = [
|
114
124
|
'multipart/related',
|
@@ -119,11 +129,23 @@ module Rack
|
|
119
129
|
# to include the port in a generated URI.
|
120
130
|
DEFAULT_PORTS = { 'http' => 80, 'https' => 443, 'coffee' => 80 }
|
121
131
|
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
132
|
+
# The address of the client which connected to the proxy.
|
133
|
+
HTTP_X_FORWARDED_FOR = 'HTTP_X_FORWARDED_FOR'
|
134
|
+
|
135
|
+
# The contents of the host/:authority header sent to the proxy.
|
136
|
+
HTTP_X_FORWARDED_HOST = 'HTTP_X_FORWARDED_HOST'
|
137
|
+
|
138
|
+
# The value of the scheme sent to the proxy.
|
139
|
+
HTTP_X_FORWARDED_SCHEME = 'HTTP_X_FORWARDED_SCHEME'
|
140
|
+
|
141
|
+
# The protocol used to connect to the proxy.
|
142
|
+
HTTP_X_FORWARDED_PROTO = 'HTTP_X_FORWARDED_PROTO'
|
143
|
+
|
144
|
+
# The port used to connect to the proxy.
|
145
|
+
HTTP_X_FORWARDED_PORT = 'HTTP_X_FORWARDED_PORT'
|
146
|
+
|
147
|
+
# Another way for specifing https scheme was used.
|
148
|
+
HTTP_X_FORWARDED_SSL = 'HTTP_X_FORWARDED_SSL'
|
127
149
|
|
128
150
|
def body; get_header(RACK_INPUT) end
|
129
151
|
def script_name; get_header(SCRIPT_NAME).to_s end
|
@@ -159,10 +181,10 @@ module Rack
|
|
159
181
|
def delete?; request_method == DELETE end
|
160
182
|
|
161
183
|
# Checks the HTTP request method (or verb) to see if it was of type GET
|
162
|
-
def get?; request_method == GET
|
184
|
+
def get?; request_method == GET end
|
163
185
|
|
164
186
|
# Checks the HTTP request method (or verb) to see if it was of type HEAD
|
165
|
-
def head?; request_method == HEAD
|
187
|
+
def head?; request_method == HEAD end
|
166
188
|
|
167
189
|
# Checks the HTTP request method (or verb) to see if it was of type OPTIONS
|
168
190
|
def options?; request_method == OPTIONS end
|
@@ -197,19 +219,52 @@ module Rack
|
|
197
219
|
end
|
198
220
|
end
|
199
221
|
|
222
|
+
# The authority of the incoming request as defined by RFC3976.
|
223
|
+
# https://tools.ietf.org/html/rfc3986#section-3.2
|
224
|
+
#
|
225
|
+
# In HTTP/1, this is the `host` header.
|
226
|
+
# In HTTP/2, this is the `:authority` pseudo-header.
|
200
227
|
def authority
|
201
|
-
|
228
|
+
forwarded_authority || host_authority || server_authority
|
229
|
+
end
|
230
|
+
|
231
|
+
# The authority as defined by the `SERVER_NAME` and `SERVER_PORT`
|
232
|
+
# variables.
|
233
|
+
def server_authority
|
234
|
+
host = self.server_name
|
235
|
+
port = self.server_port
|
236
|
+
|
237
|
+
if host
|
238
|
+
if port
|
239
|
+
"#{host}:#{port}"
|
240
|
+
else
|
241
|
+
host
|
242
|
+
end
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
def server_name
|
247
|
+
get_header(SERVER_NAME)
|
248
|
+
end
|
249
|
+
|
250
|
+
def server_port
|
251
|
+
if port = get_header(SERVER_PORT)
|
252
|
+
Integer(port)
|
253
|
+
end
|
202
254
|
end
|
203
255
|
|
204
256
|
def cookies
|
205
|
-
hash = fetch_header(RACK_REQUEST_COOKIE_HASH) do |
|
206
|
-
set_header(
|
257
|
+
hash = fetch_header(RACK_REQUEST_COOKIE_HASH) do |key|
|
258
|
+
set_header(key, {})
|
259
|
+
end
|
260
|
+
|
261
|
+
string = get_header(HTTP_COOKIE)
|
262
|
+
|
263
|
+
unless string == get_header(RACK_REQUEST_COOKIE_STRING)
|
264
|
+
hash.replace Utils.parse_cookies_header(string)
|
265
|
+
set_header(RACK_REQUEST_COOKIE_STRING, string)
|
207
266
|
end
|
208
|
-
string = get_header HTTP_COOKIE
|
209
267
|
|
210
|
-
return hash if string == get_header(RACK_REQUEST_COOKIE_STRING)
|
211
|
-
hash.replace Utils.parse_cookies_header get_header HTTP_COOKIE
|
212
|
-
set_header(RACK_REQUEST_COOKIE_STRING, string)
|
213
268
|
hash
|
214
269
|
end
|
215
270
|
|
@@ -222,46 +277,101 @@ module Rack
|
|
222
277
|
get_header("HTTP_X_REQUESTED_WITH") == "XMLHttpRequest"
|
223
278
|
end
|
224
279
|
|
225
|
-
|
226
|
-
|
227
|
-
|
280
|
+
# The `HTTP_HOST` header.
|
281
|
+
def host_authority
|
282
|
+
get_header(HTTP_HOST)
|
283
|
+
end
|
284
|
+
|
285
|
+
def host_with_port(authority = self.authority)
|
286
|
+
host, _, port = split_authority(authority)
|
287
|
+
|
288
|
+
if port == DEFAULT_PORTS[self.scheme]
|
289
|
+
host
|
228
290
|
else
|
229
|
-
|
291
|
+
authority
|
230
292
|
end
|
231
293
|
end
|
232
294
|
|
295
|
+
# Returns a formatted host, suitable for being used in a URI.
|
233
296
|
def host
|
234
|
-
|
235
|
-
|
297
|
+
split_authority(self.authority)[0]
|
298
|
+
end
|
299
|
+
|
300
|
+
# Returns an address suitable for being to resolve to an address.
|
301
|
+
# In the case of a domain name or IPv4 address, the result is the same
|
302
|
+
# as +host+. In the case of IPv6 or future address formats, the square
|
303
|
+
# brackets are removed.
|
304
|
+
def hostname
|
305
|
+
split_authority(self.authority)[1]
|
236
306
|
end
|
237
307
|
|
238
308
|
def port
|
239
|
-
if
|
240
|
-
port.
|
241
|
-
|
242
|
-
port
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
309
|
+
if authority = self.authority
|
310
|
+
_, _, port = split_authority(self.authority)
|
311
|
+
|
312
|
+
if port
|
313
|
+
return port
|
314
|
+
end
|
315
|
+
end
|
316
|
+
|
317
|
+
if forwarded_port = self.forwarded_port
|
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
|
328
|
+
end
|
329
|
+
|
330
|
+
def forwarded_for
|
331
|
+
if value = get_header(HTTP_X_FORWARDED_FOR)
|
332
|
+
split_header(value).map do |authority|
|
333
|
+
split_authority(wrap_ipv6(authority))[1]
|
334
|
+
end
|
335
|
+
end
|
336
|
+
end
|
337
|
+
|
338
|
+
def forwarded_port
|
339
|
+
if value = get_header(HTTP_X_FORWARDED_PORT)
|
340
|
+
split_header(value).map(&:to_i)
|
341
|
+
end
|
342
|
+
end
|
343
|
+
|
344
|
+
def forwarded_authority
|
345
|
+
if value = get_header(HTTP_X_FORWARDED_HOST)
|
346
|
+
wrap_ipv6(split_header(value).first)
|
249
347
|
end
|
250
348
|
end
|
251
349
|
|
252
350
|
def ssl?
|
253
|
-
scheme == 'https'
|
351
|
+
scheme == 'https' || scheme == 'wss'
|
254
352
|
end
|
255
353
|
|
256
354
|
def ip
|
257
|
-
|
258
|
-
|
355
|
+
remote_addresses = split_header(get_header('REMOTE_ADDR'))
|
356
|
+
external_addresses = reject_trusted_ip_addresses(remote_addresses)
|
259
357
|
|
260
|
-
|
358
|
+
unless external_addresses.empty?
|
359
|
+
return external_addresses.first
|
360
|
+
end
|
261
361
|
|
262
|
-
|
362
|
+
if forwarded_for = self.forwarded_for
|
363
|
+
unless forwarded_for.empty?
|
364
|
+
# The forwarded for addresses are ordered: client, proxy1, proxy2.
|
365
|
+
# So we reject all the trusted addresses (proxy*) and return the
|
366
|
+
# last client. Or if we trust everyone, we just return the first
|
367
|
+
# address.
|
368
|
+
return reject_trusted_ip_addresses(forwarded_for).last || forwarded_for.first
|
369
|
+
end
|
370
|
+
end
|
263
371
|
|
264
|
-
|
372
|
+
# If all the addresses are trusted, and we aren't forwarded, just return
|
373
|
+
# the first remote address, which represents the source of the request.
|
374
|
+
remote_addresses.first
|
265
375
|
end
|
266
376
|
|
267
377
|
# The media type (type/subtype) portion of the CONTENT_TYPE header
|
@@ -302,6 +412,7 @@ module Rack
|
|
302
412
|
def form_data?
|
303
413
|
type = media_type
|
304
414
|
meth = get_header(RACK_METHODOVERRIDE_ORIGINAL_METHOD) || get_header(REQUEST_METHOD)
|
415
|
+
|
305
416
|
(meth == POST && type.nil?) || FORM_DATA_MEDIA_TYPES.include?(type)
|
306
417
|
end
|
307
418
|
|
@@ -337,7 +448,7 @@ module Rack
|
|
337
448
|
|
338
449
|
# Fix for Safari Ajax postings that always append \0
|
339
450
|
# form_vars.sub!(/\0\z/, '') # performance replacement:
|
340
|
-
form_vars.slice!(-1) if form_vars
|
451
|
+
form_vars.slice!(-1) if form_vars.end_with?("\0")
|
341
452
|
|
342
453
|
set_header RACK_REQUEST_FORM_VARS, form_vars
|
343
454
|
set_header RACK_REQUEST_FORM_HASH, parse_query(form_vars, '&')
|
@@ -356,8 +467,6 @@ module Rack
|
|
356
467
|
# Note that modifications will not be persisted in the env. Use update_param or delete_param if you want to destructively modify params.
|
357
468
|
def params
|
358
469
|
self.GET.merge(self.POST)
|
359
|
-
rescue EOFError
|
360
|
-
self.GET.dup
|
361
470
|
end
|
362
471
|
|
363
472
|
# Destructively update a parameter, whether it's in GET and/or POST. Returns nil.
|
@@ -386,13 +495,12 @@ module Rack
|
|
386
495
|
#
|
387
496
|
# <tt>env['rack.input']</tt> is not touched.
|
388
497
|
def delete_param(k)
|
389
|
-
|
498
|
+
post_value, get_value = self.POST.delete(k), self.GET.delete(k)
|
499
|
+
post_value || get_value
|
390
500
|
end
|
391
501
|
|
392
502
|
def base_url
|
393
|
-
|
394
|
-
url << ":#{port}" if port != DEFAULT_PORTS[scheme]
|
395
|
-
url
|
503
|
+
"#{scheme}://#{host_with_port}"
|
396
504
|
end
|
397
505
|
|
398
506
|
# Tries to return a remake of the original request URL as a string.
|
@@ -417,7 +525,7 @@ module Rack
|
|
417
525
|
end
|
418
526
|
|
419
527
|
def trusted_proxy?(ip)
|
420
|
-
ip
|
528
|
+
Rack::Request.ip_filter.call(ip)
|
421
529
|
end
|
422
530
|
|
423
531
|
# shortcut for <tt>request.params[key]</tt>
|
@@ -449,6 +557,20 @@ module Rack
|
|
449
557
|
|
450
558
|
def default_session; {}; end
|
451
559
|
|
560
|
+
# Assist with compatibility when processing `X-Forwarded-For`.
|
561
|
+
def wrap_ipv6(host)
|
562
|
+
# Even thought IPv6 addresses should be wrapped in square brackets,
|
563
|
+
# sometimes this is not done in various legacy/underspecified headers.
|
564
|
+
# So we try to fix this situation for compatibility reasons.
|
565
|
+
|
566
|
+
# Try to detect IPv6 addresses which aren't escaped yet:
|
567
|
+
if !host.start_with?('[') && host.count(':') > 1
|
568
|
+
"[#{host}]"
|
569
|
+
else
|
570
|
+
host
|
571
|
+
end
|
572
|
+
end
|
573
|
+
|
452
574
|
def parse_http_accept_header(header)
|
453
575
|
header.to_s.split(/\s*,\s*/).map do |part|
|
454
576
|
attribute, parameters = part.split(/\s*;\s*/, 2)
|
@@ -464,7 +586,7 @@ module Rack
|
|
464
586
|
Utils.default_query_parser
|
465
587
|
end
|
466
588
|
|
467
|
-
def parse_query(qs, d='&')
|
589
|
+
def parse_query(qs, d = '&')
|
468
590
|
query_parser.parse_nested_query(qs, d)
|
469
591
|
end
|
470
592
|
|
@@ -472,8 +594,39 @@ module Rack
|
|
472
594
|
Rack::Multipart.extract_multipart(self, query_parser)
|
473
595
|
end
|
474
596
|
|
475
|
-
def
|
476
|
-
|
597
|
+
def split_header(value)
|
598
|
+
value ? value.strip.split(/[,\s]+/) : []
|
599
|
+
end
|
600
|
+
|
601
|
+
AUTHORITY = /
|
602
|
+
# The host:
|
603
|
+
(?<host>
|
604
|
+
# An IPv6 address:
|
605
|
+
(\[(?<ip6>.*)\])
|
606
|
+
|
|
607
|
+
# An IPv4 address:
|
608
|
+
(?<ip4>[\d\.]+)
|
609
|
+
|
|
610
|
+
# A hostname:
|
611
|
+
(?<name>[a-zA-Z0-9\.\-]+)
|
612
|
+
)
|
613
|
+
# The optional port:
|
614
|
+
(:(?<port>\d+))?
|
615
|
+
/x
|
616
|
+
|
617
|
+
private_constant :AUTHORITY
|
618
|
+
|
619
|
+
def split_authority(authority)
|
620
|
+
if match = AUTHORITY.match(authority)
|
621
|
+
if address = match[:ip6]
|
622
|
+
return match[:host], address, match[:port]&.to_i
|
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
|
477
630
|
end
|
478
631
|
|
479
632
|
def reject_trusted_ip_addresses(ip_addresses)
|
@@ -481,16 +634,22 @@ module Rack
|
|
481
634
|
end
|
482
635
|
|
483
636
|
def forwarded_scheme
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
]
|
637
|
+
allowed_scheme(get_header(HTTP_X_FORWARDED_SCHEME)) ||
|
638
|
+
allowed_scheme(extract_proto_header(get_header(HTTP_X_FORWARDED_PROTO)))
|
639
|
+
end
|
488
640
|
|
489
|
-
|
490
|
-
|
491
|
-
|
641
|
+
def allowed_scheme(header)
|
642
|
+
header if ALLOWED_SCHEMES.include?(header)
|
643
|
+
end
|
492
644
|
|
493
|
-
|
645
|
+
def extract_proto_header(header)
|
646
|
+
if header
|
647
|
+
if (comma_index = header.index(','))
|
648
|
+
header[0, comma_index]
|
649
|
+
else
|
650
|
+
header
|
651
|
+
end
|
652
|
+
end
|
494
653
|
end
|
495
654
|
end
|
496
655
|
|