rack 2.2.17 → 3.2.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +712 -74
- data/CONTRIBUTING.md +63 -55
- data/MIT-LICENSE +1 -1
- data/README.md +384 -0
- data/SPEC.rdoc +243 -277
- data/lib/rack/auth/abstract/handler.rb +3 -1
- data/lib/rack/auth/abstract/request.rb +5 -1
- data/lib/rack/auth/basic.rb +1 -3
- data/lib/rack/bad_request.rb +8 -0
- data/lib/rack/body_proxy.rb +21 -3
- data/lib/rack/builder.rb +108 -69
- data/lib/rack/cascade.rb +2 -3
- data/lib/rack/common_logger.rb +22 -17
- data/lib/rack/conditional_get.rb +20 -16
- data/lib/rack/constants.rb +68 -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 +15 -6
- data/lib/rack/etag.rb +17 -23
- data/lib/rack/events.rb +25 -6
- data/lib/rack/files.rb +16 -18
- data/lib/rack/head.rb +8 -8
- data/lib/rack/headers.rb +238 -0
- data/lib/rack/lint.rb +817 -648
- data/lib/rack/lock.rb +2 -5
- data/lib/rack/media_type.rb +6 -7
- data/lib/rack/method_override.rb +5 -1
- data/lib/rack/mime.rb +14 -5
- data/lib/rack/mock.rb +1 -300
- data/lib/rack/mock_request.rb +161 -0
- data/lib/rack/mock_response.rb +156 -0
- data/lib/rack/multipart/generator.rb +7 -5
- data/lib/rack/multipart/parser.rb +337 -95
- data/lib/rack/multipart/uploaded_file.rb +46 -4
- data/lib/rack/multipart.rb +53 -40
- data/lib/rack/null_logger.rb +9 -0
- data/lib/rack/query_parser.rb +118 -121
- data/lib/rack/recursive.rb +2 -0
- data/lib/rack/reloader.rb +0 -2
- data/lib/rack/request.rb +277 -143
- data/lib/rack/response.rb +151 -66
- data/lib/rack/rewindable_input.rb +27 -5
- data/lib/rack/runtime.rb +7 -6
- data/lib/rack/sendfile.rb +69 -34
- data/lib/rack/show_exceptions.rb +25 -6
- data/lib/rack/show_status.rb +17 -9
- data/lib/rack/static.rb +15 -11
- data/lib/rack/tempfile_reaper.rb +15 -4
- data/lib/rack/urlmap.rb +3 -1
- data/lib/rack/utils.rb +326 -244
- data/lib/rack/version.rb +3 -15
- data/lib/rack.rb +13 -90
- metadata +15 -41
- data/README.rdoc +0 -347
- data/Rakefile +0 -130
- 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/contrib/rdoc.css +0 -412
- data/example/lobster.ru +0 -6
- data/example/protectedlobster.rb +0 -16
- data/example/protectedlobster.ru +0 -10
- data/lib/rack/auth/digest/md5.rb +0 -131
- data/lib/rack/auth/digest/nonce.rb +0 -53
- data/lib/rack/auth/digest/params.rb +0 -54
- data/lib/rack/auth/digest/request.rb +0 -43
- data/lib/rack/chunked.rb +0 -117
- data/lib/rack/core_ext/regexp.rb +0 -14
- data/lib/rack/file.rb +0 -7
- 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/logger.rb +0 -20
- data/lib/rack/server.rb +0 -466
- data/lib/rack/session/abstract/id.rb +0 -523
- data/lib/rack/session/cookie.rb +0 -203
- data/lib/rack/session/memcache.rb +0 -10
- data/lib/rack/session/pool.rb +0 -90
- data/rack.gemspec +0 -46
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,59 @@ 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
|
|
64
|
+
@ip = nil
|
|
27
65
|
@params = nil
|
|
28
|
-
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def ip
|
|
69
|
+
@ip ||= super
|
|
29
70
|
end
|
|
30
71
|
|
|
31
72
|
def params
|
|
@@ -49,6 +90,8 @@ module Rack
|
|
|
49
90
|
|
|
50
91
|
def initialize(env)
|
|
51
92
|
@env = env
|
|
93
|
+
# This module is included at least in `ActionDispatch::Request`
|
|
94
|
+
# The call to `super()` allows additional mixed-in initializers are called
|
|
52
95
|
super()
|
|
53
96
|
end
|
|
54
97
|
|
|
@@ -135,6 +178,8 @@ module Rack
|
|
|
135
178
|
# The contents of the host/:authority header sent to the proxy.
|
|
136
179
|
HTTP_X_FORWARDED_HOST = 'HTTP_X_FORWARDED_HOST'
|
|
137
180
|
|
|
181
|
+
HTTP_FORWARDED = 'HTTP_FORWARDED'
|
|
182
|
+
|
|
138
183
|
# The value of the scheme sent to the proxy.
|
|
139
184
|
HTTP_X_FORWARDED_SCHEME = 'HTTP_X_FORWARDED_SCHEME'
|
|
140
185
|
|
|
@@ -144,7 +189,7 @@ module Rack
|
|
|
144
189
|
# The port used to connect to the proxy.
|
|
145
190
|
HTTP_X_FORWARDED_PORT = 'HTTP_X_FORWARDED_PORT'
|
|
146
191
|
|
|
147
|
-
# Another way for
|
|
192
|
+
# Another way for specifying https scheme was used.
|
|
148
193
|
HTTP_X_FORWARDED_SSL = 'HTTP_X_FORWARDED_SSL'
|
|
149
194
|
|
|
150
195
|
def body; get_header(RACK_INPUT) end
|
|
@@ -159,7 +204,6 @@ module Rack
|
|
|
159
204
|
def content_length; get_header('CONTENT_LENGTH') end
|
|
160
205
|
def logger; get_header(RACK_LOGGER) end
|
|
161
206
|
def user_agent; get_header('HTTP_USER_AGENT') end
|
|
162
|
-
def multithread?; get_header(RACK_MULTITHREAD) end
|
|
163
207
|
|
|
164
208
|
# the referer of the client
|
|
165
209
|
def referer; get_header('HTTP_REFERER') end
|
|
@@ -248,9 +292,7 @@ module Rack
|
|
|
248
292
|
end
|
|
249
293
|
|
|
250
294
|
def server_port
|
|
251
|
-
|
|
252
|
-
Integer(port)
|
|
253
|
-
end
|
|
295
|
+
get_header(SERVER_PORT)
|
|
254
296
|
end
|
|
255
297
|
|
|
256
298
|
def cookies
|
|
@@ -283,8 +325,10 @@ module Rack
|
|
|
283
325
|
end
|
|
284
326
|
|
|
285
327
|
def host_with_port(authority = self.authority)
|
|
286
|
-
|
|
328
|
+
parts = split_authority(authority)
|
|
329
|
+
return nil if parts.empty?
|
|
287
330
|
|
|
331
|
+
host, _, port = parts
|
|
288
332
|
if port == DEFAULT_PORTS[self.scheme]
|
|
289
333
|
host
|
|
290
334
|
else
|
|
@@ -307,44 +351,67 @@ module Rack
|
|
|
307
351
|
|
|
308
352
|
def port
|
|
309
353
|
if authority = self.authority
|
|
310
|
-
_, _, port = split_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
|
|
354
|
+
_, _, port = split_authority(authority)
|
|
325
355
|
end
|
|
326
356
|
|
|
327
|
-
|
|
357
|
+
port || forwarded_port&.last || DEFAULT_PORTS[scheme] || server_port
|
|
328
358
|
end
|
|
329
359
|
|
|
330
360
|
def forwarded_for
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
361
|
+
forwarded_priority.each do |type|
|
|
362
|
+
case type
|
|
363
|
+
when :forwarded
|
|
364
|
+
if forwarded_for = get_http_forwarded(:for)
|
|
365
|
+
return(forwarded_for.map! do |authority|
|
|
366
|
+
split_authority(authority)[1]
|
|
367
|
+
end)
|
|
368
|
+
end
|
|
369
|
+
when :x_forwarded
|
|
370
|
+
if value = get_header(HTTP_X_FORWARDED_FOR)
|
|
371
|
+
return(split_header(value).map do |authority|
|
|
372
|
+
split_authority(wrap_ipv6(authority))[1]
|
|
373
|
+
end)
|
|
374
|
+
end
|
|
334
375
|
end
|
|
335
376
|
end
|
|
377
|
+
|
|
378
|
+
nil
|
|
336
379
|
end
|
|
337
380
|
|
|
338
381
|
def forwarded_port
|
|
339
|
-
|
|
340
|
-
|
|
382
|
+
forwarded_priority.each do |type|
|
|
383
|
+
case type
|
|
384
|
+
when :forwarded
|
|
385
|
+
if forwarded = get_http_forwarded(:for)
|
|
386
|
+
return(forwarded.map do |authority|
|
|
387
|
+
split_authority(authority)[2]
|
|
388
|
+
end.compact)
|
|
389
|
+
end
|
|
390
|
+
when :x_forwarded
|
|
391
|
+
if value = get_header(HTTP_X_FORWARDED_PORT)
|
|
392
|
+
return split_header(value).map(&:to_i)
|
|
393
|
+
end
|
|
394
|
+
end
|
|
341
395
|
end
|
|
396
|
+
|
|
397
|
+
nil
|
|
342
398
|
end
|
|
343
399
|
|
|
344
400
|
def forwarded_authority
|
|
345
|
-
|
|
346
|
-
|
|
401
|
+
forwarded_priority.each do |type|
|
|
402
|
+
case type
|
|
403
|
+
when :forwarded
|
|
404
|
+
if forwarded = get_http_forwarded(:host)
|
|
405
|
+
return forwarded.last
|
|
406
|
+
end
|
|
407
|
+
when :x_forwarded
|
|
408
|
+
if (value = get_header(HTTP_X_FORWARDED_HOST)) && (x_forwarded_host = split_header(value).last)
|
|
409
|
+
return wrap_ipv6(x_forwarded_host)
|
|
410
|
+
end
|
|
411
|
+
end
|
|
347
412
|
end
|
|
413
|
+
|
|
414
|
+
nil
|
|
348
415
|
end
|
|
349
416
|
|
|
350
417
|
def ssl?
|
|
@@ -353,20 +420,20 @@ module Rack
|
|
|
353
420
|
|
|
354
421
|
def ip
|
|
355
422
|
remote_addresses = split_header(get_header('REMOTE_ADDR'))
|
|
356
|
-
external_addresses = reject_trusted_ip_addresses(remote_addresses)
|
|
357
423
|
|
|
358
|
-
|
|
359
|
-
return
|
|
424
|
+
remote_addresses.reverse_each do |ip|
|
|
425
|
+
return ip unless trusted_proxy?(ip)
|
|
360
426
|
end
|
|
361
427
|
|
|
362
|
-
if forwarded_for = self.forwarded_for
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
return
|
|
428
|
+
if (forwarded_for = self.forwarded_for) && !forwarded_for.empty?
|
|
429
|
+
# The forwarded for addresses are ordered: client, proxy1, proxy2.
|
|
430
|
+
# So we reject all the trusted addresses (proxy*) and return the
|
|
431
|
+
# last client. Or if we trust everyone, we just return the first
|
|
432
|
+
# address.
|
|
433
|
+
forwarded_for.reverse_each do |ip|
|
|
434
|
+
return ip unless trusted_proxy?(ip)
|
|
369
435
|
end
|
|
436
|
+
return forwarded_for.first
|
|
370
437
|
end
|
|
371
438
|
|
|
372
439
|
# If all the addresses are trusted, and we aren't forwarded, just return
|
|
@@ -402,13 +469,13 @@ module Rack
|
|
|
402
469
|
end
|
|
403
470
|
|
|
404
471
|
# Determine whether the request body contains form-data by checking
|
|
405
|
-
# the request
|
|
472
|
+
# the request content-type for one of the media-types:
|
|
406
473
|
# "application/x-www-form-urlencoded" or "multipart/form-data". The
|
|
407
474
|
# list of form-data media types can be modified through the
|
|
408
475
|
# +FORM_DATA_MEDIA_TYPES+ array.
|
|
409
476
|
#
|
|
410
477
|
# A request body is also assumed to contain form-data when no
|
|
411
|
-
#
|
|
478
|
+
# content-type header is provided and the request_method is POST.
|
|
412
479
|
def form_data?
|
|
413
480
|
type = media_type
|
|
414
481
|
meth = get_header(RACK_METHODOVERRIDE_ORIGINAL_METHOD) || get_header(REQUEST_METHOD)
|
|
@@ -424,12 +491,49 @@ module Rack
|
|
|
424
491
|
|
|
425
492
|
# Returns the data received in the query string.
|
|
426
493
|
def GET
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
494
|
+
get_header(RACK_REQUEST_QUERY_HASH) || set_header(RACK_REQUEST_QUERY_HASH, parse_query(query_string, '&'))
|
|
495
|
+
end
|
|
496
|
+
|
|
497
|
+
# Returns the form data pairs received in the request body.
|
|
498
|
+
#
|
|
499
|
+
# This method support both application/x-www-form-urlencoded and
|
|
500
|
+
# multipart/form-data.
|
|
501
|
+
def form_pairs
|
|
502
|
+
if pairs = get_header(RACK_REQUEST_FORM_PAIRS)
|
|
503
|
+
return pairs
|
|
504
|
+
elsif error = get_header(RACK_REQUEST_FORM_ERROR)
|
|
505
|
+
raise error.class, error.message, cause: error.cause
|
|
506
|
+
end
|
|
507
|
+
|
|
508
|
+
begin
|
|
509
|
+
rack_input = get_header(RACK_INPUT)
|
|
510
|
+
|
|
511
|
+
# Otherwise, figure out how to parse the input:
|
|
512
|
+
if rack_input.nil?
|
|
513
|
+
set_header(RACK_REQUEST_FORM_PAIRS, [])
|
|
514
|
+
elsif form_data? || parseable_data?
|
|
515
|
+
if pairs = Rack::Multipart.parse_multipart(env, Rack::Multipart::ParamList)
|
|
516
|
+
set_header RACK_REQUEST_FORM_PAIRS, pairs
|
|
517
|
+
else
|
|
518
|
+
# Add 2 bytes. One to check whether it is over the limit, and a second
|
|
519
|
+
# in case the slice! call below removes the last byte
|
|
520
|
+
# If read returns nil, use the empty string
|
|
521
|
+
form_vars = get_header(RACK_INPUT).read(query_parser.bytesize_limit + 2) || ''
|
|
522
|
+
|
|
523
|
+
# Fix for Safari Ajax postings that always append \0
|
|
524
|
+
# form_vars.sub!(/\0\z/, '') # performance replacement:
|
|
525
|
+
form_vars.slice!(-1) if form_vars.end_with?("\0")
|
|
526
|
+
|
|
527
|
+
set_header RACK_REQUEST_FORM_VARS, form_vars
|
|
528
|
+
pairs = query_parser.parse_query_pairs(form_vars, '&')
|
|
529
|
+
set_header(RACK_REQUEST_FORM_PAIRS, pairs)
|
|
530
|
+
end
|
|
531
|
+
else
|
|
532
|
+
set_header(RACK_REQUEST_FORM_PAIRS, [])
|
|
533
|
+
end
|
|
534
|
+
rescue => error
|
|
535
|
+
set_header(RACK_REQUEST_FORM_ERROR, error)
|
|
536
|
+
raise
|
|
433
537
|
end
|
|
434
538
|
end
|
|
435
539
|
|
|
@@ -438,28 +542,14 @@ module Rack
|
|
|
438
542
|
# This method support both application/x-www-form-urlencoded and
|
|
439
543
|
# multipart/form-data.
|
|
440
544
|
def POST
|
|
441
|
-
if get_header(
|
|
442
|
-
|
|
443
|
-
elsif
|
|
444
|
-
|
|
445
|
-
elsif form_data? || parseable_data?
|
|
446
|
-
unless set_header(RACK_REQUEST_FORM_HASH, parse_multipart)
|
|
447
|
-
form_vars = get_header(RACK_INPUT).read
|
|
448
|
-
|
|
449
|
-
# Fix for Safari Ajax postings that always append \0
|
|
450
|
-
# form_vars.sub!(/\0\z/, '') # performance replacement:
|
|
451
|
-
form_vars.slice!(-1) if form_vars.end_with?("\0")
|
|
452
|
-
|
|
453
|
-
set_header RACK_REQUEST_FORM_VARS, form_vars
|
|
454
|
-
set_header RACK_REQUEST_FORM_HASH, parse_query(form_vars, '&')
|
|
455
|
-
|
|
456
|
-
get_header(RACK_INPUT).rewind
|
|
457
|
-
end
|
|
458
|
-
set_header RACK_REQUEST_FORM_INPUT, get_header(RACK_INPUT)
|
|
459
|
-
get_header RACK_REQUEST_FORM_HASH
|
|
460
|
-
else
|
|
461
|
-
{}
|
|
545
|
+
if form_hash = get_header(RACK_REQUEST_FORM_HASH)
|
|
546
|
+
return form_hash
|
|
547
|
+
elsif error = get_header(RACK_REQUEST_FORM_ERROR)
|
|
548
|
+
raise error.class, error.message, cause: error.cause
|
|
462
549
|
end
|
|
550
|
+
|
|
551
|
+
pairs = form_pairs
|
|
552
|
+
set_header RACK_REQUEST_FORM_HASH, expand_param_pairs(pairs)
|
|
463
553
|
end
|
|
464
554
|
|
|
465
555
|
# The union of GET and POST data.
|
|
@@ -469,6 +559,10 @@ module Rack
|
|
|
469
559
|
self.GET.merge(self.POST)
|
|
470
560
|
end
|
|
471
561
|
|
|
562
|
+
# Allow overriding the query parser that the receiver will use.
|
|
563
|
+
# By default Rack::Utils.default_query_parser is used.
|
|
564
|
+
attr_writer :query_parser
|
|
565
|
+
|
|
472
566
|
# Destructively update a parameter, whether it's in GET and/or POST. Returns nil.
|
|
473
567
|
#
|
|
474
568
|
# The parameter is updated wherever it was previous defined, so GET, POST, or both. If it wasn't previously defined, it's inserted into GET.
|
|
@@ -500,7 +594,8 @@ module Rack
|
|
|
500
594
|
end
|
|
501
595
|
|
|
502
596
|
def base_url
|
|
503
|
-
|
|
597
|
+
hostport = host_with_port || host_with_port(server_authority)
|
|
598
|
+
"#{scheme}://#{hostport}"
|
|
504
599
|
end
|
|
505
600
|
|
|
506
601
|
# Tries to return a remake of the original request URL as a string.
|
|
@@ -528,31 +623,6 @@ module Rack
|
|
|
528
623
|
Rack::Request.ip_filter.call(ip)
|
|
529
624
|
end
|
|
530
625
|
|
|
531
|
-
# shortcut for <tt>request.params[key]</tt>
|
|
532
|
-
def [](key)
|
|
533
|
-
if $VERBOSE
|
|
534
|
-
warn("Request#[] is deprecated and will be removed in a future version of Rack. Please use request.params[] instead")
|
|
535
|
-
end
|
|
536
|
-
|
|
537
|
-
params[key.to_s]
|
|
538
|
-
end
|
|
539
|
-
|
|
540
|
-
# shortcut for <tt>request.params[key] = value</tt>
|
|
541
|
-
#
|
|
542
|
-
# Note that modifications will not be persisted in the env. Use update_param or delete_param if you want to destructively modify params.
|
|
543
|
-
def []=(key, value)
|
|
544
|
-
if $VERBOSE
|
|
545
|
-
warn("Request#[]= is deprecated and will be removed in a future version of Rack. Please use request.params[]= instead")
|
|
546
|
-
end
|
|
547
|
-
|
|
548
|
-
params[key.to_s] = value
|
|
549
|
-
end
|
|
550
|
-
|
|
551
|
-
# like Hash#values_at
|
|
552
|
-
def values_at(*keys)
|
|
553
|
-
keys.map { |key| params[key] }
|
|
554
|
-
end
|
|
555
|
-
|
|
556
626
|
private
|
|
557
627
|
|
|
558
628
|
def default_session; {}; end
|
|
@@ -572,18 +642,35 @@ module Rack
|
|
|
572
642
|
end
|
|
573
643
|
|
|
574
644
|
def parse_http_accept_header(header)
|
|
575
|
-
|
|
576
|
-
|
|
645
|
+
# It would be nice to use filter_map here, but it's Ruby 2.7+
|
|
646
|
+
parts = header.to_s.split(',')
|
|
647
|
+
|
|
648
|
+
parts.map! do |part|
|
|
649
|
+
part.strip!
|
|
650
|
+
next if part.empty?
|
|
651
|
+
|
|
652
|
+
attribute, parameters = part.split(';', 2)
|
|
653
|
+
attribute.strip!
|
|
654
|
+
parameters&.strip!
|
|
577
655
|
quality = 1.0
|
|
578
656
|
if parameters and /\Aq=([\d.]+)/ =~ parameters
|
|
579
657
|
quality = $1.to_f
|
|
580
658
|
end
|
|
581
659
|
[attribute, quality]
|
|
582
660
|
end
|
|
661
|
+
|
|
662
|
+
parts.compact!
|
|
663
|
+
|
|
664
|
+
parts
|
|
665
|
+
end
|
|
666
|
+
|
|
667
|
+
# Get an array of values set in the RFC 7239 `Forwarded` request header.
|
|
668
|
+
def get_http_forwarded(token)
|
|
669
|
+
Utils.forwarded_values(get_header(HTTP_FORWARDED))&.[](token)
|
|
583
670
|
end
|
|
584
671
|
|
|
585
672
|
def query_parser
|
|
586
|
-
Utils.default_query_parser
|
|
673
|
+
@query_parser || Utils.default_query_parser
|
|
587
674
|
end
|
|
588
675
|
|
|
589
676
|
def parse_query(qs, d = '&')
|
|
@@ -591,65 +678,108 @@ module Rack
|
|
|
591
678
|
end
|
|
592
679
|
|
|
593
680
|
def parse_multipart
|
|
681
|
+
warn "Rack::Request#parse_multipart is deprecated and will be removed in a future version of Rack.", uplevel: 1
|
|
594
682
|
Rack::Multipart.extract_multipart(self, query_parser)
|
|
595
683
|
end
|
|
596
684
|
|
|
597
|
-
def
|
|
598
|
-
|
|
685
|
+
def expand_param_pairs(pairs, query_parser = query_parser())
|
|
686
|
+
params = query_parser.make_params
|
|
687
|
+
|
|
688
|
+
pairs.each do |k, v|
|
|
689
|
+
query_parser.normalize_params(params, k, v)
|
|
690
|
+
end
|
|
691
|
+
|
|
692
|
+
params.to_params_hash
|
|
599
693
|
end
|
|
600
694
|
|
|
601
|
-
|
|
602
|
-
|
|
695
|
+
def split_header(value)
|
|
696
|
+
value ? value.strip.split(/[, \t]+/) : []
|
|
697
|
+
end
|
|
698
|
+
|
|
699
|
+
# ipv6 extracted from resolv stdlib, simplified
|
|
700
|
+
# to remove numbered match group creation.
|
|
701
|
+
ipv6 = Regexp.union(
|
|
702
|
+
/(?:[0-9A-Fa-f]{1,4}:){7}
|
|
703
|
+
[0-9A-Fa-f]{1,4}/x,
|
|
704
|
+
/(?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)? ::
|
|
705
|
+
(?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?/x,
|
|
706
|
+
/(?:[0-9A-Fa-f]{1,4}:){6,6}
|
|
707
|
+
\d+\.\d+\.\d+\.\d+/x,
|
|
708
|
+
/(?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)? ::
|
|
709
|
+
(?:[0-9A-Fa-f]{1,4}:)*
|
|
710
|
+
\d+\.\d+\.\d+\.\d+/x,
|
|
711
|
+
/[Ff][Ee]80
|
|
712
|
+
(?::[0-9A-Fa-f]{1,4}){7}
|
|
713
|
+
%[-0-9A-Za-z._~]+/x,
|
|
714
|
+
/[Ff][Ee]80:
|
|
715
|
+
(?:
|
|
716
|
+
(?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)? ::
|
|
717
|
+
(?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?
|
|
718
|
+
|
|
|
719
|
+
:(?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?
|
|
720
|
+
)?
|
|
721
|
+
:[0-9A-Fa-f]{1,4}%[-0-9A-Za-z._~]+/x)
|
|
722
|
+
|
|
723
|
+
AUTHORITY = /
|
|
724
|
+
\A
|
|
603
725
|
(?<host>
|
|
604
|
-
#
|
|
605
|
-
|
|
726
|
+
# Match IPv6 as a string of hex digits and colons in square brackets
|
|
727
|
+
\[(?<address>#{ipv6})\]
|
|
606
728
|
|
|
|
607
|
-
#
|
|
608
|
-
(?<
|
|
609
|
-
|
|
|
610
|
-
# A hostname:
|
|
611
|
-
(?<name>[a-zA-Z0-9\.\-_]+)
|
|
729
|
+
# Match characters allowed by RFC 3986 Section 3.2.2
|
|
730
|
+
(?<address>[-a-zA-Z0-9._~%!$&'()*+,;=]*?)
|
|
612
731
|
)
|
|
613
|
-
# The optional port:
|
|
614
732
|
(:(?<port>\d+))?
|
|
615
|
-
|
|
733
|
+
\z
|
|
734
|
+
/x
|
|
616
735
|
|
|
617
736
|
private_constant :AUTHORITY
|
|
618
737
|
|
|
619
738
|
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
|
|
630
|
-
end
|
|
631
|
-
|
|
632
|
-
def reject_trusted_ip_addresses(ip_addresses)
|
|
633
|
-
ip_addresses.reject { |ip| trusted_proxy?(ip) }
|
|
739
|
+
return [] if authority.nil?
|
|
740
|
+
return [] unless match = AUTHORITY.match(authority)
|
|
741
|
+
return match[:host], match[:address], match[:port]&.to_i
|
|
634
742
|
end
|
|
635
743
|
|
|
744
|
+
FORWARDED_SCHEME_HEADERS = {
|
|
745
|
+
proto: HTTP_X_FORWARDED_PROTO,
|
|
746
|
+
scheme: HTTP_X_FORWARDED_SCHEME
|
|
747
|
+
}.freeze
|
|
748
|
+
private_constant :FORWARDED_SCHEME_HEADERS
|
|
636
749
|
def forwarded_scheme
|
|
637
|
-
|
|
638
|
-
|
|
750
|
+
forwarded_priority.each do |type|
|
|
751
|
+
case type
|
|
752
|
+
when :forwarded
|
|
753
|
+
if (forwarded_proto = get_http_forwarded(:proto)) &&
|
|
754
|
+
(scheme = allowed_scheme(forwarded_proto.last))
|
|
755
|
+
return scheme
|
|
756
|
+
end
|
|
757
|
+
when :x_forwarded
|
|
758
|
+
x_forwarded_proto_priority.each do |x_type|
|
|
759
|
+
if header = FORWARDED_SCHEME_HEADERS[x_type]
|
|
760
|
+
split_header(get_header(header)).reverse_each do |scheme|
|
|
761
|
+
if allowed_scheme(scheme)
|
|
762
|
+
return scheme
|
|
763
|
+
end
|
|
764
|
+
end
|
|
765
|
+
end
|
|
766
|
+
end
|
|
767
|
+
end
|
|
768
|
+
end
|
|
769
|
+
|
|
770
|
+
nil
|
|
639
771
|
end
|
|
640
772
|
|
|
641
773
|
def allowed_scheme(header)
|
|
642
774
|
header if ALLOWED_SCHEMES.include?(header)
|
|
643
775
|
end
|
|
644
776
|
|
|
645
|
-
def
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
end
|
|
652
|
-
end
|
|
777
|
+
def forwarded_priority
|
|
778
|
+
Request.forwarded_priority
|
|
779
|
+
end
|
|
780
|
+
|
|
781
|
+
def x_forwarded_proto_priority
|
|
782
|
+
Request.x_forwarded_proto_priority
|
|
653
783
|
end
|
|
654
784
|
end
|
|
655
785
|
|
|
@@ -657,3 +787,7 @@ module Rack
|
|
|
657
787
|
include Helpers
|
|
658
788
|
end
|
|
659
789
|
end
|
|
790
|
+
|
|
791
|
+
# :nocov:
|
|
792
|
+
require_relative 'multipart' unless defined?(Rack::Multipart)
|
|
793
|
+
# :nocov:
|