rack 2.2.8.1 → 3.1.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +301 -81
- data/CONTRIBUTING.md +63 -55
- data/MIT-LICENSE +1 -1
- data/README.md +328 -0
- data/SPEC.rdoc +204 -131
- data/lib/rack/auth/abstract/handler.rb +3 -1
- data/lib/rack/auth/abstract/request.rb +3 -1
- data/lib/rack/auth/basic.rb +1 -4
- data/lib/rack/bad_request.rb +8 -0
- data/lib/rack/body_proxy.rb +21 -3
- data/lib/rack/builder.rb +102 -69
- data/lib/rack/cascade.rb +2 -3
- data/lib/rack/common_logger.rb +23 -18
- data/lib/rack/conditional_get.rb +18 -15
- data/lib/rack/constants.rb +67 -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/files.rb +15 -17
- data/lib/rack/head.rb +9 -8
- data/lib/rack/headers.rb +238 -0
- data/lib/rack/lint.rb +838 -644
- data/lib/rack/lock.rb +2 -5
- data/lib/rack/logger.rb +3 -0
- data/lib/rack/method_override.rb +5 -1
- data/lib/rack/mime.rb +14 -5
- data/lib/rack/mock.rb +1 -271
- data/lib/rack/mock_request.rb +171 -0
- data/lib/rack/mock_response.rb +124 -0
- data/lib/rack/multipart/generator.rb +7 -5
- data/lib/rack/multipart/parser.rb +215 -90
- data/lib/rack/multipart/uploaded_file.rb +4 -0
- data/lib/rack/multipart.rb +53 -40
- data/lib/rack/null_logger.rb +9 -0
- data/lib/rack/query_parser.rb +81 -102
- data/lib/rack/recursive.rb +2 -0
- data/lib/rack/reloader.rb +0 -2
- data/lib/rack/request.rb +250 -123
- data/lib/rack/response.rb +146 -66
- 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 +21 -4
- 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 +3 -1
- data/lib/rack/utils.rb +232 -233
- data/lib/rack/version.rb +1 -9
- data/lib/rack.rb +13 -89
- metadata +15 -41
- data/README.rdoc +0 -320
- 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 -54
- 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/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/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,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
|
315
|
-
end
|
316
|
-
|
317
|
-
if forwarded_port = self.forwarded_port
|
318
|
-
return forwarded_port.first
|
347
|
+
_, _, port = split_authority(authority)
|
319
348
|
end
|
320
349
|
|
321
|
-
|
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)
|
@@ -424,10 +482,15 @@ module Rack
|
|
424
482
|
|
425
483
|
# Returns the data received in the query string.
|
426
484
|
def GET
|
427
|
-
|
485
|
+
rr_query_string = get_header(RACK_REQUEST_QUERY_STRING)
|
486
|
+
query_string = self.query_string
|
487
|
+
if rr_query_string == query_string
|
428
488
|
get_header(RACK_REQUEST_QUERY_HASH)
|
429
489
|
else
|
430
|
-
|
490
|
+
if rr_query_string
|
491
|
+
warn "query string used for GET parsing different from current query string. Starting in Rack 3.2, Rack will used the cached GET value instead of parsing the current query string.", uplevel: 1
|
492
|
+
end
|
493
|
+
query_hash = parse_query(query_string, '&')
|
431
494
|
set_header(RACK_REQUEST_QUERY_STRING, query_string)
|
432
495
|
set_header(RACK_REQUEST_QUERY_HASH, query_hash)
|
433
496
|
end
|
@@ -438,27 +501,52 @@ module Rack
|
|
438
501
|
# This method support both application/x-www-form-urlencoded and
|
439
502
|
# multipart/form-data.
|
440
503
|
def POST
|
441
|
-
if get_header(
|
442
|
-
raise
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
504
|
+
if 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
|
+
# If the form hash was already memoized:
|
512
|
+
if form_hash = get_header(RACK_REQUEST_FORM_HASH)
|
513
|
+
form_input = get_header(RACK_REQUEST_FORM_INPUT)
|
514
|
+
# And it was memoized from the same input:
|
515
|
+
if form_input.equal?(rack_input)
|
516
|
+
return form_hash
|
517
|
+
elsif form_input
|
518
|
+
warn "input stream used for POST parsing different from current input stream. Starting in Rack 3.2, Rack will used the cached POST value instead of parsing the current input stream.", uplevel: 1
|
519
|
+
end
|
457
520
|
end
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
521
|
+
|
522
|
+
# Otherwise, figure out how to parse the input:
|
523
|
+
if rack_input.nil?
|
524
|
+
set_header RACK_REQUEST_FORM_INPUT, nil
|
525
|
+
set_header(RACK_REQUEST_FORM_HASH, {})
|
526
|
+
elsif form_data? || parseable_data?
|
527
|
+
if pairs = Rack::Multipart.parse_multipart(env, Rack::Multipart::ParamList)
|
528
|
+
set_header RACK_REQUEST_FORM_PAIRS, pairs
|
529
|
+
set_header RACK_REQUEST_FORM_HASH, expand_param_pairs(pairs)
|
530
|
+
else
|
531
|
+
form_vars = get_header(RACK_INPUT).read
|
532
|
+
|
533
|
+
# Fix for Safari Ajax postings that always append \0
|
534
|
+
# form_vars.sub!(/\0\z/, '') # performance replacement:
|
535
|
+
form_vars.slice!(-1) if form_vars.end_with?("\0")
|
536
|
+
|
537
|
+
set_header RACK_REQUEST_FORM_VARS, form_vars
|
538
|
+
set_header RACK_REQUEST_FORM_HASH, parse_query(form_vars, '&')
|
539
|
+
end
|
540
|
+
|
541
|
+
set_header RACK_REQUEST_FORM_INPUT, get_header(RACK_INPUT)
|
542
|
+
get_header RACK_REQUEST_FORM_HASH
|
543
|
+
else
|
544
|
+
set_header RACK_REQUEST_FORM_INPUT, get_header(RACK_INPUT)
|
545
|
+
set_header(RACK_REQUEST_FORM_HASH, {})
|
546
|
+
end
|
547
|
+
rescue => error
|
548
|
+
set_header(RACK_REQUEST_FORM_ERROR, error)
|
549
|
+
raise
|
462
550
|
end
|
463
551
|
end
|
464
552
|
|
@@ -528,28 +616,10 @@ module Rack
|
|
528
616
|
Rack::Request.ip_filter.call(ip)
|
529
617
|
end
|
530
618
|
|
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
619
|
# like Hash#values_at
|
552
620
|
def values_at(*keys)
|
621
|
+
warn("Request#values_at is deprecated and will be removed in a future version of Rack. Please use request.params.values_at instead", uplevel: 1)
|
622
|
+
|
553
623
|
keys.map { |key| params[key] }
|
554
624
|
end
|
555
625
|
|
@@ -572,8 +642,10 @@ module Rack
|
|
572
642
|
end
|
573
643
|
|
574
644
|
def parse_http_accept_header(header)
|
575
|
-
header.to_s.split(
|
576
|
-
attribute, parameters = part.split(
|
645
|
+
header.to_s.split(',').map do |part|
|
646
|
+
attribute, parameters = part.split(';', 2)
|
647
|
+
attribute.strip!
|
648
|
+
parameters&.strip!
|
577
649
|
quality = 1.0
|
578
650
|
if parameters and /\Aq=([\d.]+)/ =~ parameters
|
579
651
|
quality = $1.to_f
|
@@ -582,6 +654,11 @@ module Rack
|
|
582
654
|
end
|
583
655
|
end
|
584
656
|
|
657
|
+
# Get an array of values set in the RFC 7239 `Forwarded` request header.
|
658
|
+
def get_http_forwarded(token)
|
659
|
+
Utils.forwarded_values(get_header(HTTP_FORWARDED))&.[](token)
|
660
|
+
end
|
661
|
+
|
585
662
|
def query_parser
|
586
663
|
Utils.default_query_parser
|
587
664
|
end
|
@@ -594,62 +671,108 @@ module Rack
|
|
594
671
|
Rack::Multipart.extract_multipart(self, query_parser)
|
595
672
|
end
|
596
673
|
|
674
|
+
def expand_param_pairs(pairs, query_parser = query_parser())
|
675
|
+
params = query_parser.make_params
|
676
|
+
|
677
|
+
pairs.each do |k, v|
|
678
|
+
query_parser.normalize_params(params, k, v)
|
679
|
+
end
|
680
|
+
|
681
|
+
params.to_params_hash
|
682
|
+
end
|
683
|
+
|
597
684
|
def split_header(value)
|
598
685
|
value ? value.strip.split(/[,\s]+/) : []
|
599
686
|
end
|
600
687
|
|
601
|
-
|
602
|
-
|
688
|
+
# ipv6 extracted from resolv stdlib, simplified
|
689
|
+
# to remove numbered match group creation.
|
690
|
+
ipv6 = Regexp.union(
|
691
|
+
/(?:[0-9A-Fa-f]{1,4}:){7}
|
692
|
+
[0-9A-Fa-f]{1,4}/x,
|
693
|
+
/(?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)? ::
|
694
|
+
(?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?/x,
|
695
|
+
/(?:[0-9A-Fa-f]{1,4}:){6,6}
|
696
|
+
\d+\.\d+\.\d+\.\d+/x,
|
697
|
+
/(?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)? ::
|
698
|
+
(?:[0-9A-Fa-f]{1,4}:)*
|
699
|
+
\d+\.\d+\.\d+\.\d+/x,
|
700
|
+
/[Ff][Ee]80
|
701
|
+
(?::[0-9A-Fa-f]{1,4}){7}
|
702
|
+
%[-0-9A-Za-z._~]+/x,
|
703
|
+
/[Ff][Ee]80:
|
704
|
+
(?:
|
705
|
+
(?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)? ::
|
706
|
+
(?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?
|
707
|
+
|
|
708
|
+
:(?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?
|
709
|
+
)?
|
710
|
+
:[0-9A-Fa-f]{1,4}%[-0-9A-Za-z._~]+/x)
|
711
|
+
|
712
|
+
AUTHORITY = /
|
713
|
+
\A
|
603
714
|
(?<host>
|
604
|
-
#
|
605
|
-
|
606
|
-
|
|
607
|
-
# An IPv4 address:
|
608
|
-
(?<ip4>[\d\.]+)
|
715
|
+
# Match IPv6 as a string of hex digits and colons in square brackets
|
716
|
+
\[(?<address>#{ipv6})\]
|
609
717
|
|
|
610
|
-
#
|
611
|
-
(?<
|
718
|
+
# Match any other printable string (except square brackets) as a hostname
|
719
|
+
(?<address>[[[:graph:]&&[^\[\]]]]*?)
|
612
720
|
)
|
613
|
-
# The optional port:
|
614
721
|
(:(?<port>\d+))?
|
615
|
-
|
722
|
+
\z
|
723
|
+
/x
|
616
724
|
|
617
725
|
private_constant :AUTHORITY
|
618
726
|
|
619
727
|
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
|
728
|
+
return [] if authority.nil?
|
729
|
+
return [] unless match = AUTHORITY.match(authority)
|
730
|
+
return match[:host], match[:address], match[:port]&.to_i
|
630
731
|
end
|
631
732
|
|
632
733
|
def reject_trusted_ip_addresses(ip_addresses)
|
633
734
|
ip_addresses.reject { |ip| trusted_proxy?(ip) }
|
634
735
|
end
|
635
736
|
|
737
|
+
FORWARDED_SCHEME_HEADERS = {
|
738
|
+
proto: HTTP_X_FORWARDED_PROTO,
|
739
|
+
scheme: HTTP_X_FORWARDED_SCHEME
|
740
|
+
}.freeze
|
741
|
+
private_constant :FORWARDED_SCHEME_HEADERS
|
636
742
|
def forwarded_scheme
|
637
|
-
|
638
|
-
|
743
|
+
forwarded_priority.each do |type|
|
744
|
+
case type
|
745
|
+
when :forwarded
|
746
|
+
if (forwarded_proto = get_http_forwarded(:proto)) &&
|
747
|
+
(scheme = allowed_scheme(forwarded_proto.last))
|
748
|
+
return scheme
|
749
|
+
end
|
750
|
+
when :x_forwarded
|
751
|
+
x_forwarded_proto_priority.each do |x_type|
|
752
|
+
if header = FORWARDED_SCHEME_HEADERS[x_type]
|
753
|
+
split_header(get_header(header)).reverse_each do |scheme|
|
754
|
+
if allowed_scheme(scheme)
|
755
|
+
return scheme
|
756
|
+
end
|
757
|
+
end
|
758
|
+
end
|
759
|
+
end
|
760
|
+
end
|
761
|
+
end
|
762
|
+
|
763
|
+
nil
|
639
764
|
end
|
640
765
|
|
641
766
|
def allowed_scheme(header)
|
642
767
|
header if ALLOWED_SCHEMES.include?(header)
|
643
768
|
end
|
644
769
|
|
645
|
-
def
|
646
|
-
|
647
|
-
|
648
|
-
|
649
|
-
|
650
|
-
|
651
|
-
end
|
652
|
-
end
|
770
|
+
def forwarded_priority
|
771
|
+
Request.forwarded_priority
|
772
|
+
end
|
773
|
+
|
774
|
+
def x_forwarded_proto_priority
|
775
|
+
Request.x_forwarded_proto_priority
|
653
776
|
end
|
654
777
|
end
|
655
778
|
|
@@ -657,3 +780,7 @@ module Rack
|
|
657
780
|
include Helpers
|
658
781
|
end
|
659
782
|
end
|
783
|
+
|
784
|
+
# :nocov:
|
785
|
+
require_relative 'multipart' unless defined?(Rack::Multipart)
|
786
|
+
# :nocov:
|