rack 2.2.8.1 → 3.0.9.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (86) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +213 -83
  3. data/CONTRIBUTING.md +53 -47
  4. data/MIT-LICENSE +1 -1
  5. data/README.md +309 -0
  6. data/SPEC.rdoc +174 -126
  7. data/lib/rack/auth/abstract/handler.rb +3 -1
  8. data/lib/rack/auth/abstract/request.rb +3 -1
  9. data/lib/rack/auth/basic.rb +0 -2
  10. data/lib/rack/auth/digest/md5.rb +1 -131
  11. data/lib/rack/auth/digest/nonce.rb +1 -54
  12. data/lib/rack/auth/digest/params.rb +1 -54
  13. data/lib/rack/auth/digest/request.rb +1 -43
  14. data/lib/rack/auth/digest.rb +256 -0
  15. data/lib/rack/body_proxy.rb +3 -1
  16. data/lib/rack/builder.rb +83 -63
  17. data/lib/rack/cascade.rb +2 -0
  18. data/lib/rack/chunked.rb +16 -13
  19. data/lib/rack/common_logger.rb +23 -18
  20. data/lib/rack/conditional_get.rb +18 -15
  21. data/lib/rack/constants.rb +64 -0
  22. data/lib/rack/content_length.rb +12 -16
  23. data/lib/rack/content_type.rb +8 -5
  24. data/lib/rack/deflater.rb +40 -26
  25. data/lib/rack/directory.rb +9 -3
  26. data/lib/rack/etag.rb +14 -23
  27. data/lib/rack/events.rb +4 -0
  28. data/lib/rack/file.rb +2 -0
  29. data/lib/rack/files.rb +15 -17
  30. data/lib/rack/head.rb +9 -8
  31. data/lib/rack/headers.rb +154 -0
  32. data/lib/rack/lint.rb +758 -646
  33. data/lib/rack/lock.rb +2 -5
  34. data/lib/rack/logger.rb +2 -0
  35. data/lib/rack/method_override.rb +5 -1
  36. data/lib/rack/mime.rb +8 -0
  37. data/lib/rack/mock.rb +1 -271
  38. data/lib/rack/mock_request.rb +166 -0
  39. data/lib/rack/mock_response.rb +126 -0
  40. data/lib/rack/multipart/generator.rb +7 -5
  41. data/lib/rack/multipart/parser.rb +120 -64
  42. data/lib/rack/multipart/uploaded_file.rb +4 -0
  43. data/lib/rack/multipart.rb +20 -40
  44. data/lib/rack/null_logger.rb +9 -0
  45. data/lib/rack/query_parser.rb +78 -46
  46. data/lib/rack/recursive.rb +2 -0
  47. data/lib/rack/reloader.rb +0 -2
  48. data/lib/rack/request.rb +224 -106
  49. data/lib/rack/response.rb +138 -61
  50. data/lib/rack/rewindable_input.rb +24 -5
  51. data/lib/rack/runtime.rb +7 -6
  52. data/lib/rack/sendfile.rb +30 -25
  53. data/lib/rack/show_exceptions.rb +15 -2
  54. data/lib/rack/show_status.rb +17 -7
  55. data/lib/rack/static.rb +8 -8
  56. data/lib/rack/tempfile_reaper.rb +15 -4
  57. data/lib/rack/urlmap.rb +3 -1
  58. data/lib/rack/utils.rb +203 -176
  59. data/lib/rack/version.rb +9 -4
  60. data/lib/rack.rb +6 -76
  61. metadata +13 -33
  62. data/README.rdoc +0 -320
  63. data/Rakefile +0 -130
  64. data/bin/rackup +0 -5
  65. data/contrib/rack.png +0 -0
  66. data/contrib/rack.svg +0 -150
  67. data/contrib/rack_logo.svg +0 -164
  68. data/contrib/rdoc.css +0 -412
  69. data/example/lobster.ru +0 -6
  70. data/example/protectedlobster.rb +0 -16
  71. data/example/protectedlobster.ru +0 -10
  72. data/lib/rack/core_ext/regexp.rb +0 -14
  73. data/lib/rack/handler/cgi.rb +0 -59
  74. data/lib/rack/handler/fastcgi.rb +0 -100
  75. data/lib/rack/handler/lsws.rb +0 -61
  76. data/lib/rack/handler/scgi.rb +0 -71
  77. data/lib/rack/handler/thin.rb +0 -36
  78. data/lib/rack/handler/webrick.rb +0 -129
  79. data/lib/rack/handler.rb +0 -104
  80. data/lib/rack/lobster.rb +0 -70
  81. data/lib/rack/server.rb +0 -466
  82. data/lib/rack/session/abstract/id.rb +0 -523
  83. data/lib/rack/session/cookie.rb +0 -204
  84. data/lib/rack/session/memcache.rb +0 -10
  85. data/lib/rack/session/pool.rb +0 -85
  86. 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
- 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
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 specifing https scheme was used.
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
- if port = get_header(SERVER_PORT)
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(self.authority)
311
-
312
- if port
313
- return port
314
- end
347
+ _, _, port = split_authority(authority)
315
348
  end
316
349
 
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
350
+ port || forwarded_port&.last || DEFAULT_PORTS[scheme] || server_port
328
351
  end
329
352
 
330
353
  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]
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
- if value = get_header(HTTP_X_FORWARDED_PORT)
340
- split_header(value).map(&:to_i)
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
- if value = get_header(HTTP_X_FORWARDED_HOST)
346
- wrap_ipv6(split_header(value).first)
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.first
419
+ return external_addresses.last
360
420
  end
361
421
 
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
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 Content-Type for one of the media-types:
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
- # Content-Type header is provided and the request_method is POST.
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
@@ -438,27 +496,46 @@ module Rack
438
496
  # This method support both application/x-www-form-urlencoded and
439
497
  # multipart/form-data.
440
498
  def POST
441
- if get_header(RACK_INPUT).nil?
442
- raise "Missing rack.input"
443
- elsif get_header(RACK_REQUEST_FORM_INPUT) == get_header(RACK_INPUT)
444
- get_header(RACK_REQUEST_FORM_HASH)
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
499
+ if error = get_header(RACK_REQUEST_FORM_ERROR)
500
+ raise error.class, error.message, cause: error.cause
501
+ end
502
+
503
+ begin
504
+ rack_input = get_header(RACK_INPUT)
505
+
506
+ # If the form hash was already memoized:
507
+ if form_hash = get_header(RACK_REQUEST_FORM_HASH)
508
+ # And it was memoized from the same input:
509
+ if get_header(RACK_REQUEST_FORM_INPUT).equal?(rack_input)
510
+ return form_hash
511
+ end
457
512
  end
458
- set_header RACK_REQUEST_FORM_INPUT, get_header(RACK_INPUT)
459
- get_header RACK_REQUEST_FORM_HASH
460
- else
461
- {}
513
+
514
+ # Otherwise, figure out how to parse the input:
515
+ if rack_input.nil?
516
+ set_header RACK_REQUEST_FORM_INPUT, nil
517
+ set_header(RACK_REQUEST_FORM_HASH, {})
518
+ elsif form_data? || parseable_data?
519
+ unless set_header(RACK_REQUEST_FORM_HASH, parse_multipart)
520
+ form_vars = get_header(RACK_INPUT).read
521
+
522
+ # Fix for Safari Ajax postings that always append \0
523
+ # form_vars.sub!(/\0\z/, '') # performance replacement:
524
+ form_vars.slice!(-1) if form_vars.end_with?("\0")
525
+
526
+ set_header RACK_REQUEST_FORM_VARS, form_vars
527
+ set_header RACK_REQUEST_FORM_HASH, parse_query(form_vars, '&')
528
+ end
529
+
530
+ set_header RACK_REQUEST_FORM_INPUT, get_header(RACK_INPUT)
531
+ get_header RACK_REQUEST_FORM_HASH
532
+ else
533
+ set_header RACK_REQUEST_FORM_INPUT, get_header(RACK_INPUT)
534
+ set_header(RACK_REQUEST_FORM_HASH, {})
535
+ end
536
+ rescue => error
537
+ set_header(RACK_REQUEST_FORM_ERROR, error)
538
+ raise
462
539
  end
463
540
  end
464
541
 
@@ -530,9 +607,7 @@ module Rack
530
607
 
531
608
  # shortcut for <tt>request.params[key]</tt>
532
609
  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
610
+ warn("Request#[] is deprecated and will be removed in a future version of Rack. Please use request.params[] instead", uplevel: 1)
536
611
 
537
612
  params[key.to_s]
538
613
  end
@@ -541,9 +616,7 @@ module Rack
541
616
  #
542
617
  # Note that modifications will not be persisted in the env. Use update_param or delete_param if you want to destructively modify params.
543
618
  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
619
+ warn("Request#[]= is deprecated and will be removed in a future version of Rack. Please use request.params[]= instead", uplevel: 1)
547
620
 
548
621
  params[key.to_s] = value
549
622
  end
@@ -582,6 +655,11 @@ module Rack
582
655
  end
583
656
  end
584
657
 
658
+ # Get an array of values set in the RFC 7239 `Forwarded` request header.
659
+ def get_http_forwarded(token)
660
+ Utils.forwarded_values(get_header(HTTP_FORWARDED))&.[](token)
661
+ end
662
+
585
663
  def query_parser
586
664
  Utils.default_query_parser
587
665
  end
@@ -598,58 +676,94 @@ module Rack
598
676
  value ? value.strip.split(/[,\s]+/) : []
599
677
  end
600
678
 
601
- AUTHORITY = /^
602
- # The host:
679
+ # ipv6 extracted from resolv stdlib, simplified
680
+ # to remove numbered match group creation.
681
+ ipv6 = Regexp.union(
682
+ /(?:[0-9A-Fa-f]{1,4}:){7}
683
+ [0-9A-Fa-f]{1,4}/x,
684
+ /(?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)? ::
685
+ (?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?/x,
686
+ /(?:[0-9A-Fa-f]{1,4}:){6,6}
687
+ \d+\.\d+\.\d+\.\d+/x,
688
+ /(?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)? ::
689
+ (?:[0-9A-Fa-f]{1,4}:)*
690
+ \d+\.\d+\.\d+\.\d+/x,
691
+ /[Ff][Ee]80
692
+ (?::[0-9A-Fa-f]{1,4}){7}
693
+ %[-0-9A-Za-z._~]+/x,
694
+ /[Ff][Ee]80:
695
+ (?:
696
+ (?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)? ::
697
+ (?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?
698
+ |
699
+ :(?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?
700
+ )?
701
+ :[0-9A-Fa-f]{1,4}%[-0-9A-Za-z._~]+/x)
702
+
703
+ AUTHORITY = /
704
+ \A
603
705
  (?<host>
604
- # An IPv6 address:
605
- (\[(?<ip6>.*)\])
606
- |
607
- # An IPv4 address:
608
- (?<ip4>[\d\.]+)
706
+ # Match IPv6 as a string of hex digits and colons in square brackets
707
+ \[(?<address>#{ipv6})\]
609
708
  |
610
- # A hostname:
611
- (?<name>[a-zA-Z0-9\.\-_]+)
709
+ # Match any other printable string (except square brackets) as a hostname
710
+ (?<address>[[[:graph:]&&[^\[\]]]]*?)
612
711
  )
613
- # The optional port:
614
712
  (:(?<port>\d+))?
615
- $/x
713
+ \z
714
+ /x
616
715
 
617
716
  private_constant :AUTHORITY
618
717
 
619
718
  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
719
+ return [] if authority.nil?
720
+ return [] unless match = AUTHORITY.match(authority)
721
+ return match[:host], match[:address], match[:port]&.to_i
630
722
  end
631
723
 
632
724
  def reject_trusted_ip_addresses(ip_addresses)
633
725
  ip_addresses.reject { |ip| trusted_proxy?(ip) }
634
726
  end
635
727
 
728
+ FORWARDED_SCHEME_HEADERS = {
729
+ proto: HTTP_X_FORWARDED_PROTO,
730
+ scheme: HTTP_X_FORWARDED_SCHEME
731
+ }.freeze
732
+ private_constant :FORWARDED_SCHEME_HEADERS
636
733
  def forwarded_scheme
637
- allowed_scheme(get_header(HTTP_X_FORWARDED_SCHEME)) ||
638
- allowed_scheme(extract_proto_header(get_header(HTTP_X_FORWARDED_PROTO)))
734
+ forwarded_priority.each do |type|
735
+ case type
736
+ when :forwarded
737
+ if (forwarded_proto = get_http_forwarded(:proto)) &&
738
+ (scheme = allowed_scheme(forwarded_proto.last))
739
+ return scheme
740
+ end
741
+ when :x_forwarded
742
+ x_forwarded_proto_priority.each do |x_type|
743
+ if header = FORWARDED_SCHEME_HEADERS[x_type]
744
+ split_header(get_header(header)).reverse_each do |scheme|
745
+ if allowed_scheme(scheme)
746
+ return scheme
747
+ end
748
+ end
749
+ end
750
+ end
751
+ end
752
+ end
753
+
754
+ nil
639
755
  end
640
756
 
641
757
  def allowed_scheme(header)
642
758
  header if ALLOWED_SCHEMES.include?(header)
643
759
  end
644
760
 
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
761
+ def forwarded_priority
762
+ Request.forwarded_priority
763
+ end
764
+
765
+ def x_forwarded_proto_priority
766
+ Request.x_forwarded_proto_priority
653
767
  end
654
768
  end
655
769
 
@@ -657,3 +771,7 @@ module Rack
657
771
  include Helpers
658
772
  end
659
773
  end
774
+
775
+ # :nocov:
776
+ require_relative 'multipart' unless defined?(Rack::Multipart)
777
+ # :nocov: