rack 3.0.15 → 3.2.6
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 +368 -6
- data/CONTRIBUTING.md +11 -9
- data/README.md +103 -28
- data/SPEC.rdoc +206 -288
- data/lib/rack/auth/abstract/request.rb +2 -0
- data/lib/rack/auth/basic.rb +1 -2
- data/lib/rack/bad_request.rb +8 -0
- data/lib/rack/builder.rb +29 -10
- data/lib/rack/cascade.rb +0 -3
- data/lib/rack/conditional_get.rb +4 -3
- data/lib/rack/constants.rb +4 -0
- data/lib/rack/directory.rb +6 -3
- data/lib/rack/events.rb +21 -6
- data/lib/rack/files.rb +1 -1
- data/lib/rack/head.rb +2 -3
- data/lib/rack/headers.rb +86 -2
- data/lib/rack/lint.rb +482 -425
- data/lib/rack/media_type.rb +14 -10
- data/lib/rack/mime.rb +6 -5
- data/lib/rack/mock_request.rb +10 -15
- data/lib/rack/mock_response.rb +50 -20
- data/lib/rack/multipart/parser.rb +255 -76
- data/lib/rack/multipart/uploaded_file.rb +42 -5
- data/lib/rack/multipart.rb +34 -1
- data/lib/rack/query_parser.rb +86 -78
- data/lib/rack/request.rb +78 -65
- data/lib/rack/response.rb +28 -20
- data/lib/rack/rewindable_input.rb +4 -1
- data/lib/rack/sendfile.rb +51 -21
- data/lib/rack/show_exceptions.rb +10 -4
- data/lib/rack/show_status.rb +0 -2
- data/lib/rack/static.rb +7 -3
- data/lib/rack/utils.rb +175 -119
- data/lib/rack/version.rb +3 -20
- data/lib/rack.rb +1 -4
- metadata +6 -12
- data/lib/rack/auth/digest/md5.rb +0 -1
- data/lib/rack/auth/digest/nonce.rb +0 -1
- data/lib/rack/auth/digest/params.rb +0 -1
- data/lib/rack/auth/digest/request.rb +0 -1
- data/lib/rack/auth/digest.rb +0 -256
- data/lib/rack/chunked.rb +0 -120
- data/lib/rack/file.rb +0 -9
- data/lib/rack/logger.rb +0 -22
data/lib/rack/query_parser.rb
CHANGED
|
@@ -1,42 +1,69 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require_relative 'bad_request'
|
|
3
4
|
require 'uri'
|
|
4
5
|
|
|
5
6
|
module Rack
|
|
6
7
|
class QueryParser
|
|
7
|
-
DEFAULT_SEP =
|
|
8
|
-
COMMON_SEP = { ";" =>
|
|
8
|
+
DEFAULT_SEP = /& */n
|
|
9
|
+
COMMON_SEP = { ";" => /; */n, ";," => /[;,] */n, "&" => /& */n }
|
|
9
10
|
|
|
10
11
|
# ParameterTypeError is the error that is raised when incoming structural
|
|
11
12
|
# parameters (parsed by parse_nested_query) contain conflicting types.
|
|
12
|
-
class ParameterTypeError < TypeError
|
|
13
|
+
class ParameterTypeError < TypeError
|
|
14
|
+
include BadRequest
|
|
15
|
+
end
|
|
13
16
|
|
|
14
17
|
# InvalidParameterError is the error that is raised when incoming structural
|
|
15
18
|
# parameters (parsed by parse_nested_query) contain invalid format or byte
|
|
16
19
|
# sequence.
|
|
17
|
-
class InvalidParameterError < ArgumentError
|
|
20
|
+
class InvalidParameterError < ArgumentError
|
|
21
|
+
include BadRequest
|
|
22
|
+
end
|
|
18
23
|
|
|
19
|
-
#
|
|
20
|
-
#
|
|
21
|
-
class
|
|
24
|
+
# QueryLimitError is for errors raised when the query provided exceeds one
|
|
25
|
+
# of the query parser limits.
|
|
26
|
+
class QueryLimitError < RangeError
|
|
27
|
+
include BadRequest
|
|
28
|
+
end
|
|
22
29
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
30
|
+
# ParamsTooDeepError is the old name for the error that is raised when params
|
|
31
|
+
# are recursively nested over the specified limit. Make it the same as
|
|
32
|
+
# as QueryLimitError, so that code that rescues ParamsTooDeepError error
|
|
33
|
+
# to handle bad query strings also now handles other limits.
|
|
34
|
+
ParamsTooDeepError = QueryLimitError
|
|
27
35
|
|
|
28
|
-
|
|
36
|
+
def self.make_default(param_depth_limit, **options)
|
|
37
|
+
new(Params, param_depth_limit, **options)
|
|
29
38
|
end
|
|
30
39
|
|
|
31
40
|
attr_reader :param_depth_limit
|
|
32
41
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
42
|
+
env_int = lambda do |key, val|
|
|
43
|
+
if str_val = ENV[key]
|
|
44
|
+
begin
|
|
45
|
+
val = Integer(str_val, 10)
|
|
46
|
+
rescue ArgumentError
|
|
47
|
+
raise ArgumentError, "non-integer value provided for environment variable #{key}"
|
|
48
|
+
end
|
|
36
49
|
end
|
|
37
50
|
|
|
51
|
+
val
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
BYTESIZE_LIMIT = env_int.call("RACK_QUERY_PARSER_BYTESIZE_LIMIT", 4194304)
|
|
55
|
+
private_constant :BYTESIZE_LIMIT
|
|
56
|
+
|
|
57
|
+
PARAMS_LIMIT = env_int.call("RACK_QUERY_PARSER_PARAMS_LIMIT", 4096)
|
|
58
|
+
private_constant :PARAMS_LIMIT
|
|
59
|
+
|
|
60
|
+
attr_reader :bytesize_limit
|
|
61
|
+
|
|
62
|
+
def initialize(params_class, param_depth_limit, bytesize_limit: BYTESIZE_LIMIT, params_limit: PARAMS_LIMIT)
|
|
38
63
|
@params_class = params_class
|
|
39
64
|
@param_depth_limit = param_depth_limit
|
|
65
|
+
@bytesize_limit = bytesize_limit
|
|
66
|
+
@params_limit = params_limit
|
|
40
67
|
end
|
|
41
68
|
|
|
42
69
|
# Stolen from Mongrel, with some small modifications:
|
|
@@ -44,14 +71,9 @@ module Rack
|
|
|
44
71
|
# to parse cookies by changing the characters used in the second parameter
|
|
45
72
|
# (which defaults to '&').
|
|
46
73
|
def parse_query(qs, separator = nil, &unescaper)
|
|
47
|
-
unescaper ||= method(:unescape)
|
|
48
|
-
|
|
49
74
|
params = make_params
|
|
50
75
|
|
|
51
|
-
(qs
|
|
52
|
-
next if p.empty?
|
|
53
|
-
k, v = p.split('=', 2).map!(&unescaper)
|
|
54
|
-
|
|
76
|
+
each_query_pair(qs, separator, unescaper) do |k, v|
|
|
55
77
|
if cur = params[k]
|
|
56
78
|
if cur.class == Array
|
|
57
79
|
params[k] << v
|
|
@@ -66,6 +88,19 @@ module Rack
|
|
|
66
88
|
return params.to_h
|
|
67
89
|
end
|
|
68
90
|
|
|
91
|
+
# Parses a query string by breaking it up at the '&', returning all key-value
|
|
92
|
+
# pairs as an array of [key, value] arrays. Unlike parse_query, this preserves
|
|
93
|
+
# all duplicate keys rather than collapsing them.
|
|
94
|
+
def parse_query_pairs(qs, separator = nil)
|
|
95
|
+
pairs = []
|
|
96
|
+
|
|
97
|
+
each_query_pair(qs, separator) do |k, v|
|
|
98
|
+
pairs << [k, v]
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
pairs
|
|
102
|
+
end
|
|
103
|
+
|
|
69
104
|
# parse_nested_query expands a query string into structural types. Supported
|
|
70
105
|
# types are Arrays, Hashes and basic value types. It is possible to supply
|
|
71
106
|
# query strings with parameters of conflicting types, in this case a
|
|
@@ -74,17 +109,11 @@ module Rack
|
|
|
74
109
|
def parse_nested_query(qs, separator = nil)
|
|
75
110
|
params = make_params
|
|
76
111
|
|
|
77
|
-
|
|
78
|
-
(
|
|
79
|
-
k, v = p.split('=', 2).map! { |s| unescape(s) }
|
|
80
|
-
|
|
81
|
-
_normalize_params(params, k, v, 0)
|
|
82
|
-
end
|
|
112
|
+
each_query_pair(qs, separator) do |k, v|
|
|
113
|
+
_normalize_params(params, k, v, 0)
|
|
83
114
|
end
|
|
84
115
|
|
|
85
116
|
return params.to_h
|
|
86
|
-
rescue ArgumentError => e
|
|
87
|
-
raise InvalidParameterError, e.message, e.backtrace
|
|
88
117
|
end
|
|
89
118
|
|
|
90
119
|
# normalize_params recursively expands parameters into structural types. If
|
|
@@ -190,63 +219,42 @@ module Rack
|
|
|
190
219
|
true
|
|
191
220
|
end
|
|
192
221
|
|
|
193
|
-
def
|
|
194
|
-
|
|
195
|
-
end
|
|
196
|
-
|
|
197
|
-
class Params
|
|
198
|
-
def initialize
|
|
199
|
-
@size = 0
|
|
200
|
-
@params = {}
|
|
201
|
-
end
|
|
222
|
+
def each_query_pair(qs, separator, unescaper = nil)
|
|
223
|
+
return if !qs || qs.empty?
|
|
202
224
|
|
|
203
|
-
|
|
204
|
-
@
|
|
225
|
+
if qs.bytesize > @bytesize_limit
|
|
226
|
+
raise QueryLimitError, "total query size exceeds limit (#{@bytesize_limit})"
|
|
205
227
|
end
|
|
206
228
|
|
|
207
|
-
|
|
208
|
-
@params[key] = value
|
|
209
|
-
end
|
|
229
|
+
pairs = qs.split(separator ? (COMMON_SEP[separator] || /[#{separator}] */n) : DEFAULT_SEP, @params_limit + 1)
|
|
210
230
|
|
|
211
|
-
|
|
212
|
-
|
|
231
|
+
if pairs.size > @params_limit
|
|
232
|
+
param_count = pairs.size + pairs.last.count(separator || "&")
|
|
233
|
+
raise QueryLimitError, "total number of query parameters (#{param_count}) exceeds limit (#{@params_limit})"
|
|
213
234
|
end
|
|
214
235
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
# representation, not a copy.
|
|
227
|
-
#
|
|
228
|
-
# 3. Because the `Params` object's internal representation is mutable
|
|
229
|
-
# through the `#[]=` method, it is not thread safe. The result of
|
|
230
|
-
# getting the hash representation while another thread is adding a
|
|
231
|
-
# key to it is non-deterministic.
|
|
232
|
-
#
|
|
233
|
-
def to_h
|
|
234
|
-
@params.each do |key, value|
|
|
235
|
-
case value
|
|
236
|
-
when self
|
|
237
|
-
# Handle circular references gracefully.
|
|
238
|
-
@params[key] = @params
|
|
239
|
-
when Params
|
|
240
|
-
@params[key] = value.to_h
|
|
241
|
-
when Array
|
|
242
|
-
value.map! { |v| v.kind_of?(Params) ? v.to_h : v }
|
|
243
|
-
else
|
|
244
|
-
# Ignore anything that is not a `Params` object or
|
|
245
|
-
# a collection that can contain one.
|
|
246
|
-
end
|
|
236
|
+
if unescaper
|
|
237
|
+
pairs.each do |p|
|
|
238
|
+
next if p.empty?
|
|
239
|
+
k, v = p.split('=', 2).map!(&unescaper)
|
|
240
|
+
yield k, v
|
|
241
|
+
end
|
|
242
|
+
else
|
|
243
|
+
pairs.each do |p|
|
|
244
|
+
next if p.empty?
|
|
245
|
+
k, v = p.split('=', 2).map! { |s| unescape(s) }
|
|
246
|
+
yield k, v
|
|
247
247
|
end
|
|
248
|
-
@params
|
|
249
248
|
end
|
|
249
|
+
rescue ArgumentError => e
|
|
250
|
+
raise InvalidParameterError, e.message, e.backtrace
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
def unescape(string, encoding = Encoding::UTF_8)
|
|
254
|
+
URI.decode_www_form_component(string, encoding)
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
class Params < Hash
|
|
250
258
|
alias_method :to_params_hash, :to_h
|
|
251
259
|
end
|
|
252
260
|
end
|
data/lib/rack/request.rb
CHANGED
|
@@ -61,9 +61,14 @@ module Rack
|
|
|
61
61
|
|
|
62
62
|
def initialize(env)
|
|
63
63
|
@env = env
|
|
64
|
+
@ip = nil
|
|
64
65
|
@params = nil
|
|
65
66
|
end
|
|
66
67
|
|
|
68
|
+
def ip
|
|
69
|
+
@ip ||= super
|
|
70
|
+
end
|
|
71
|
+
|
|
67
72
|
def params
|
|
68
73
|
@params ||= super
|
|
69
74
|
end
|
|
@@ -398,8 +403,8 @@ module Rack
|
|
|
398
403
|
return forwarded.last
|
|
399
404
|
end
|
|
400
405
|
when :x_forwarded
|
|
401
|
-
if value = get_header(HTTP_X_FORWARDED_HOST)
|
|
402
|
-
return wrap_ipv6(
|
|
406
|
+
if (value = get_header(HTTP_X_FORWARDED_HOST)) && (x_forwarded_host = split_header(value).last)
|
|
407
|
+
return wrap_ipv6(x_forwarded_host)
|
|
403
408
|
end
|
|
404
409
|
end
|
|
405
410
|
end
|
|
@@ -413,10 +418,9 @@ module Rack
|
|
|
413
418
|
|
|
414
419
|
def ip
|
|
415
420
|
remote_addresses = split_header(get_header('REMOTE_ADDR'))
|
|
416
|
-
external_addresses = reject_trusted_ip_addresses(remote_addresses)
|
|
417
421
|
|
|
418
|
-
|
|
419
|
-
return
|
|
422
|
+
remote_addresses.reverse_each do |ip|
|
|
423
|
+
return ip unless trusted_proxy?(ip)
|
|
420
424
|
end
|
|
421
425
|
|
|
422
426
|
if (forwarded_for = self.forwarded_for) && !forwarded_for.empty?
|
|
@@ -424,7 +428,10 @@ module Rack
|
|
|
424
428
|
# So we reject all the trusted addresses (proxy*) and return the
|
|
425
429
|
# last client. Or if we trust everyone, we just return the first
|
|
426
430
|
# address.
|
|
427
|
-
|
|
431
|
+
forwarded_for.reverse_each do |ip|
|
|
432
|
+
return ip unless trusted_proxy?(ip)
|
|
433
|
+
end
|
|
434
|
+
return forwarded_for.first
|
|
428
435
|
end
|
|
429
436
|
|
|
430
437
|
# If all the addresses are trusted, and we aren't forwarded, just return
|
|
@@ -482,56 +489,45 @@ module Rack
|
|
|
482
489
|
|
|
483
490
|
# Returns the data received in the query string.
|
|
484
491
|
def GET
|
|
485
|
-
|
|
486
|
-
get_header(RACK_REQUEST_QUERY_HASH)
|
|
487
|
-
else
|
|
488
|
-
query_hash = parse_query(query_string, '&')
|
|
489
|
-
set_header(RACK_REQUEST_QUERY_STRING, query_string)
|
|
490
|
-
set_header(RACK_REQUEST_QUERY_HASH, query_hash)
|
|
491
|
-
end
|
|
492
|
+
get_header(RACK_REQUEST_QUERY_HASH) || set_header(RACK_REQUEST_QUERY_HASH, parse_query(query_string, '&'))
|
|
492
493
|
end
|
|
493
494
|
|
|
494
|
-
# Returns the data received in the request body.
|
|
495
|
+
# Returns the form data pairs received in the request body.
|
|
495
496
|
#
|
|
496
497
|
# This method support both application/x-www-form-urlencoded and
|
|
497
498
|
# multipart/form-data.
|
|
498
|
-
def
|
|
499
|
-
if
|
|
499
|
+
def form_pairs
|
|
500
|
+
if pairs = get_header(RACK_REQUEST_FORM_PAIRS)
|
|
501
|
+
return pairs
|
|
502
|
+
elsif error = get_header(RACK_REQUEST_FORM_ERROR)
|
|
500
503
|
raise error.class, error.message, cause: error.cause
|
|
501
504
|
end
|
|
502
505
|
|
|
503
506
|
begin
|
|
504
507
|
rack_input = get_header(RACK_INPUT)
|
|
505
508
|
|
|
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
|
|
512
|
-
end
|
|
513
|
-
|
|
514
509
|
# Otherwise, figure out how to parse the input:
|
|
515
510
|
if rack_input.nil?
|
|
516
|
-
set_header
|
|
517
|
-
set_header(RACK_REQUEST_FORM_HASH, {})
|
|
511
|
+
set_header(RACK_REQUEST_FORM_PAIRS, [])
|
|
518
512
|
elsif form_data? || parseable_data?
|
|
519
|
-
|
|
520
|
-
|
|
513
|
+
if pairs = Rack::Multipart.parse_multipart(env, Rack::Multipart::ParamList)
|
|
514
|
+
set_header RACK_REQUEST_FORM_PAIRS, pairs
|
|
515
|
+
else
|
|
516
|
+
# Add 2 bytes. One to check whether it is over the limit, and a second
|
|
517
|
+
# in case the slice! call below removes the last byte
|
|
518
|
+
# If read returns nil, use the empty string
|
|
519
|
+
form_vars = get_header(RACK_INPUT).read(query_parser.bytesize_limit + 2) || ''
|
|
521
520
|
|
|
522
521
|
# Fix for Safari Ajax postings that always append \0
|
|
523
522
|
# form_vars.sub!(/\0\z/, '') # performance replacement:
|
|
524
523
|
form_vars.slice!(-1) if form_vars.end_with?("\0")
|
|
525
524
|
|
|
526
525
|
set_header RACK_REQUEST_FORM_VARS, form_vars
|
|
527
|
-
|
|
526
|
+
pairs = query_parser.parse_query_pairs(form_vars, '&')
|
|
527
|
+
set_header(RACK_REQUEST_FORM_PAIRS, pairs)
|
|
528
528
|
end
|
|
529
|
-
|
|
530
|
-
set_header RACK_REQUEST_FORM_INPUT, get_header(RACK_INPUT)
|
|
531
|
-
get_header RACK_REQUEST_FORM_HASH
|
|
532
529
|
else
|
|
533
|
-
set_header
|
|
534
|
-
set_header(RACK_REQUEST_FORM_HASH, {})
|
|
530
|
+
set_header(RACK_REQUEST_FORM_PAIRS, [])
|
|
535
531
|
end
|
|
536
532
|
rescue => error
|
|
537
533
|
set_header(RACK_REQUEST_FORM_ERROR, error)
|
|
@@ -539,6 +535,21 @@ module Rack
|
|
|
539
535
|
end
|
|
540
536
|
end
|
|
541
537
|
|
|
538
|
+
# Returns the data received in the request body.
|
|
539
|
+
#
|
|
540
|
+
# This method support both application/x-www-form-urlencoded and
|
|
541
|
+
# multipart/form-data.
|
|
542
|
+
def POST
|
|
543
|
+
if form_hash = get_header(RACK_REQUEST_FORM_HASH)
|
|
544
|
+
return form_hash
|
|
545
|
+
elsif error = get_header(RACK_REQUEST_FORM_ERROR)
|
|
546
|
+
raise error.class, error.message, cause: error.cause
|
|
547
|
+
end
|
|
548
|
+
|
|
549
|
+
pairs = form_pairs
|
|
550
|
+
set_header RACK_REQUEST_FORM_HASH, expand_param_pairs(pairs)
|
|
551
|
+
end
|
|
552
|
+
|
|
542
553
|
# The union of GET and POST data.
|
|
543
554
|
#
|
|
544
555
|
# Note that modifications will not be persisted in the env. Use update_param or delete_param if you want to destructively modify params.
|
|
@@ -546,6 +557,10 @@ module Rack
|
|
|
546
557
|
self.GET.merge(self.POST)
|
|
547
558
|
end
|
|
548
559
|
|
|
560
|
+
# Allow overriding the query parser that the receiver will use.
|
|
561
|
+
# By default Rack::Utils.default_query_parser is used.
|
|
562
|
+
attr_writer :query_parser
|
|
563
|
+
|
|
549
564
|
# Destructively update a parameter, whether it's in GET and/or POST. Returns nil.
|
|
550
565
|
#
|
|
551
566
|
# 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.
|
|
@@ -605,27 +620,6 @@ module Rack
|
|
|
605
620
|
Rack::Request.ip_filter.call(ip)
|
|
606
621
|
end
|
|
607
622
|
|
|
608
|
-
# shortcut for <tt>request.params[key]</tt>
|
|
609
|
-
def [](key)
|
|
610
|
-
warn("Request#[] is deprecated and will be removed in a future version of Rack. Please use request.params[] instead", uplevel: 1)
|
|
611
|
-
|
|
612
|
-
params[key.to_s]
|
|
613
|
-
end
|
|
614
|
-
|
|
615
|
-
# shortcut for <tt>request.params[key] = value</tt>
|
|
616
|
-
#
|
|
617
|
-
# Note that modifications will not be persisted in the env. Use update_param or delete_param if you want to destructively modify params.
|
|
618
|
-
def []=(key, value)
|
|
619
|
-
warn("Request#[]= is deprecated and will be removed in a future version of Rack. Please use request.params[]= instead", uplevel: 1)
|
|
620
|
-
|
|
621
|
-
params[key.to_s] = value
|
|
622
|
-
end
|
|
623
|
-
|
|
624
|
-
# like Hash#values_at
|
|
625
|
-
def values_at(*keys)
|
|
626
|
-
keys.map { |key| params[key] }
|
|
627
|
-
end
|
|
628
|
-
|
|
629
623
|
private
|
|
630
624
|
|
|
631
625
|
def default_session; {}; end
|
|
@@ -645,14 +639,26 @@ module Rack
|
|
|
645
639
|
end
|
|
646
640
|
|
|
647
641
|
def parse_http_accept_header(header)
|
|
648
|
-
|
|
649
|
-
|
|
642
|
+
# It would be nice to use filter_map here, but it's Ruby 2.7+
|
|
643
|
+
parts = header.to_s.split(',')
|
|
644
|
+
|
|
645
|
+
parts.map! do |part|
|
|
646
|
+
part.strip!
|
|
647
|
+
next if part.empty?
|
|
648
|
+
|
|
649
|
+
attribute, parameters = part.split(';', 2)
|
|
650
|
+
attribute.strip!
|
|
651
|
+
parameters&.strip!
|
|
650
652
|
quality = 1.0
|
|
651
653
|
if parameters and /\Aq=([\d.]+)/ =~ parameters
|
|
652
654
|
quality = $1.to_f
|
|
653
655
|
end
|
|
654
656
|
[attribute, quality]
|
|
655
657
|
end
|
|
658
|
+
|
|
659
|
+
parts.compact!
|
|
660
|
+
|
|
661
|
+
parts
|
|
656
662
|
end
|
|
657
663
|
|
|
658
664
|
# Get an array of values set in the RFC 7239 `Forwarded` request header.
|
|
@@ -661,7 +667,7 @@ module Rack
|
|
|
661
667
|
end
|
|
662
668
|
|
|
663
669
|
def query_parser
|
|
664
|
-
Utils.default_query_parser
|
|
670
|
+
@query_parser || Utils.default_query_parser
|
|
665
671
|
end
|
|
666
672
|
|
|
667
673
|
def parse_query(qs, d = '&')
|
|
@@ -669,11 +675,22 @@ module Rack
|
|
|
669
675
|
end
|
|
670
676
|
|
|
671
677
|
def parse_multipart
|
|
678
|
+
warn "Rack::Request#parse_multipart is deprecated and will be removed in a future version of Rack.", uplevel: 1
|
|
672
679
|
Rack::Multipart.extract_multipart(self, query_parser)
|
|
673
680
|
end
|
|
674
681
|
|
|
682
|
+
def expand_param_pairs(pairs, query_parser = query_parser())
|
|
683
|
+
params = query_parser.make_params
|
|
684
|
+
|
|
685
|
+
pairs.each do |k, v|
|
|
686
|
+
query_parser.normalize_params(params, k, v)
|
|
687
|
+
end
|
|
688
|
+
|
|
689
|
+
params.to_params_hash
|
|
690
|
+
end
|
|
691
|
+
|
|
675
692
|
def split_header(value)
|
|
676
|
-
value ? value.strip.split(/[
|
|
693
|
+
value ? value.strip.split(/[, \t]+/) : []
|
|
677
694
|
end
|
|
678
695
|
|
|
679
696
|
# ipv6 extracted from resolv stdlib, simplified
|
|
@@ -706,8 +723,8 @@ module Rack
|
|
|
706
723
|
# Match IPv6 as a string of hex digits and colons in square brackets
|
|
707
724
|
\[(?<address>#{ipv6})\]
|
|
708
725
|
|
|
|
709
|
-
# Match
|
|
710
|
-
(?<address>[
|
|
726
|
+
# Match characters allowed by RFC 3986 Section 3.2.2
|
|
727
|
+
(?<address>[-a-zA-Z0-9._~%!$&'()*+,;=]*?)
|
|
711
728
|
)
|
|
712
729
|
(:(?<port>\d+))?
|
|
713
730
|
\z
|
|
@@ -721,10 +738,6 @@ module Rack
|
|
|
721
738
|
return match[:host], match[:address], match[:port]&.to_i
|
|
722
739
|
end
|
|
723
740
|
|
|
724
|
-
def reject_trusted_ip_addresses(ip_addresses)
|
|
725
|
-
ip_addresses.reject { |ip| trusted_proxy?(ip) }
|
|
726
|
-
end
|
|
727
|
-
|
|
728
741
|
FORWARDED_SCHEME_HEADERS = {
|
|
729
742
|
proto: HTTP_X_FORWARDED_PROTO,
|
|
730
743
|
scheme: HTTP_X_FORWARDED_SCHEME
|
data/lib/rack/response.rb
CHANGED
|
@@ -31,13 +31,6 @@ module Rack
|
|
|
31
31
|
attr_accessor :length, :status, :body
|
|
32
32
|
attr_reader :headers
|
|
33
33
|
|
|
34
|
-
# Deprecated, use headers instead.
|
|
35
|
-
def header
|
|
36
|
-
warn 'Rack::Response#header is deprecated and will be removed in Rack 3.1, use #headers instead', uplevel: 1
|
|
37
|
-
|
|
38
|
-
headers
|
|
39
|
-
end
|
|
40
|
-
|
|
41
34
|
# Initialize the response object with the specified +body+, +status+
|
|
42
35
|
# and +headers+.
|
|
43
36
|
#
|
|
@@ -62,7 +55,7 @@ module Rack
|
|
|
62
55
|
@status = status.to_i
|
|
63
56
|
|
|
64
57
|
unless headers.is_a?(Hash)
|
|
65
|
-
|
|
58
|
+
raise ArgumentError, "Headers must be a Hash!"
|
|
66
59
|
end
|
|
67
60
|
|
|
68
61
|
@headers = Headers.new
|
|
@@ -79,7 +72,8 @@ module Rack
|
|
|
79
72
|
if body.nil?
|
|
80
73
|
@body = []
|
|
81
74
|
@buffered = true
|
|
82
|
-
|
|
75
|
+
# Body is unspecified - it may be a buffered response, or it may be a HEAD response.
|
|
76
|
+
@length = nil
|
|
83
77
|
elsif body.respond_to?(:to_str)
|
|
84
78
|
@body = [body]
|
|
85
79
|
@buffered = true
|
|
@@ -87,7 +81,7 @@ module Rack
|
|
|
87
81
|
else
|
|
88
82
|
@body = body
|
|
89
83
|
@buffered = nil # undetermined as of yet.
|
|
90
|
-
@length =
|
|
84
|
+
@length = nil
|
|
91
85
|
end
|
|
92
86
|
|
|
93
87
|
yield self if block_given?
|
|
@@ -106,7 +100,7 @@ module Rack
|
|
|
106
100
|
# The response body is an enumerable body and it is not allowed to have an entity body.
|
|
107
101
|
@body.respond_to?(:each) && STATUS_WITH_NO_ENTITY_BODY[@status]
|
|
108
102
|
end
|
|
109
|
-
|
|
103
|
+
|
|
110
104
|
# Generate a response array consistent with the requirements of the SPEC.
|
|
111
105
|
# @return [Array] a 3-tuple suitable of `[status, headers, body]`
|
|
112
106
|
# which is suitable to be returned from the middleware `#call(env)` method.
|
|
@@ -118,9 +112,14 @@ module Rack
|
|
|
118
112
|
return [@status, @headers, []]
|
|
119
113
|
else
|
|
120
114
|
if block_given?
|
|
115
|
+
# We don't add the content-length here as the user has provided a block that can #write additional chunks to the body.
|
|
121
116
|
@block = block
|
|
122
117
|
return [@status, @headers, self]
|
|
123
118
|
else
|
|
119
|
+
# If we know the length of the body, set the content-length header... except if we are chunked? which is a legacy special case where the body might already be encoded and thus the actual encoded body length and the content-length are likely to be different.
|
|
120
|
+
if @length && !chunked?
|
|
121
|
+
@headers[CONTENT_LENGTH] = @length.to_s
|
|
122
|
+
end
|
|
124
123
|
return [@status, @headers, @body]
|
|
125
124
|
end
|
|
126
125
|
end
|
|
@@ -138,7 +137,9 @@ module Rack
|
|
|
138
137
|
end
|
|
139
138
|
end
|
|
140
139
|
|
|
141
|
-
# Append to
|
|
140
|
+
# Append a chunk to the response body.
|
|
141
|
+
#
|
|
142
|
+
# Converts the response into a buffered response if it wasn't already.
|
|
142
143
|
#
|
|
143
144
|
# NOTE: Do not mix #write and direct #body access!
|
|
144
145
|
#
|
|
@@ -320,29 +321,34 @@ module Rack
|
|
|
320
321
|
|
|
321
322
|
protected
|
|
322
323
|
|
|
324
|
+
# Convert the body of this response into an internally buffered Array if possible.
|
|
325
|
+
#
|
|
326
|
+
# `@buffered` is a ternary value which indicates whether the body is buffered. It can be:
|
|
327
|
+
# * `nil` - The body has not been buffered yet.
|
|
328
|
+
# * `true` - The body is buffered as an Array instance.
|
|
329
|
+
# * `false` - The body is not buffered and cannot be buffered.
|
|
330
|
+
#
|
|
331
|
+
# @return [Boolean] whether the body is buffered as an Array instance.
|
|
323
332
|
def buffered_body!
|
|
324
333
|
if @buffered.nil?
|
|
325
334
|
if @body.is_a?(Array)
|
|
326
335
|
# The user supplied body was an array:
|
|
327
336
|
@body = @body.compact
|
|
328
|
-
@body.
|
|
329
|
-
@length += part.to_s.bytesize
|
|
330
|
-
end
|
|
331
|
-
|
|
337
|
+
@length = @body.sum{|part| part.bytesize}
|
|
332
338
|
@buffered = true
|
|
333
339
|
elsif @body.respond_to?(:each)
|
|
334
340
|
# Turn the user supplied body into a buffered array:
|
|
335
341
|
body = @body
|
|
336
342
|
@body = Array.new
|
|
343
|
+
@buffered = true
|
|
337
344
|
|
|
338
345
|
body.each do |part|
|
|
339
346
|
@writer.call(part.to_s)
|
|
340
347
|
end
|
|
341
348
|
|
|
342
349
|
body.close if body.respond_to?(:close)
|
|
343
|
-
|
|
344
|
-
@buffered = true
|
|
345
350
|
else
|
|
351
|
+
# We don't know how to buffer the user-supplied body:
|
|
346
352
|
@buffered = false
|
|
347
353
|
end
|
|
348
354
|
end
|
|
@@ -351,11 +357,13 @@ module Rack
|
|
|
351
357
|
end
|
|
352
358
|
|
|
353
359
|
def append(chunk)
|
|
360
|
+
chunk = chunk.dup unless chunk.frozen?
|
|
354
361
|
@body << chunk
|
|
355
362
|
|
|
356
|
-
|
|
363
|
+
if @length
|
|
357
364
|
@length += chunk.bytesize
|
|
358
|
-
|
|
365
|
+
elsif @buffered
|
|
366
|
+
@length = chunk.bytesize
|
|
359
367
|
end
|
|
360
368
|
|
|
361
369
|
return chunk
|