rack 2.2.3 → 3.2.7
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 +774 -69
- 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 -4
- 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 +26 -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 -21
- 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 +863 -705
- data/lib/rack/lock.rb +2 -5
- data/lib/rack/media_type.rb +18 -9
- data/lib/rack/method_override.rb +6 -2
- data/lib/rack/mime.rb +14 -5
- data/lib/rack/mock.rb +1 -271
- 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 +349 -92
- data/lib/rack/multipart/uploaded_file.rb +45 -4
- data/lib/rack/multipart.rb +53 -41
- data/lib/rack/null_logger.rb +9 -0
- data/lib/rack/query_parser.rb +150 -106
- data/lib/rack/recursive.rb +2 -0
- data/lib/rack/reloader.rb +0 -2
- data/lib/rack/request.rb +272 -141
- 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 +70 -35
- data/lib/rack/show_exceptions.rb +25 -6
- data/lib/rack/show_status.rb +17 -9
- data/lib/rack/static.rb +17 -12
- data/lib/rack/tempfile_reaper.rb +15 -4
- data/lib/rack/urlmap.rb +4 -2
- data/lib/rack/utils.rb +351 -250
- data/lib/rack/version.rb +3 -15
- data/lib/rack.rb +13 -90
- metadata +16 -45
- data/README.rdoc +0 -306
- 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/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 -85
- data/rack.gemspec +0 -46
data/lib/rack/multipart.rb
CHANGED
|
@@ -1,65 +1,77 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require_relative 'constants'
|
|
4
|
+
require_relative 'utils'
|
|
5
|
+
|
|
3
6
|
require_relative 'multipart/parser'
|
|
7
|
+
require_relative 'multipart/generator'
|
|
8
|
+
|
|
9
|
+
require_relative 'bad_request'
|
|
4
10
|
|
|
5
11
|
module Rack
|
|
6
12
|
# A multipart form data parser, adapted from IOWA.
|
|
7
13
|
#
|
|
8
14
|
# Usually, Rack::Request#POST takes care of calling this.
|
|
9
15
|
module Multipart
|
|
10
|
-
autoload :UploadedFile, 'rack/multipart/uploaded_file'
|
|
11
|
-
autoload :Generator, 'rack/multipart/generator'
|
|
12
|
-
|
|
13
|
-
EOL = "\r\n"
|
|
14
16
|
MULTIPART_BOUNDARY = "AaB03x"
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
17
|
+
|
|
18
|
+
class MissingInputError < StandardError
|
|
19
|
+
include BadRequest
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Accumulator for multipart form data, conforming to the QueryParser API.
|
|
23
|
+
# In future, the Parser could return the pair list directly, but that would
|
|
24
|
+
# change its API.
|
|
25
|
+
class ParamList # :nodoc:
|
|
26
|
+
def self.make_params
|
|
27
|
+
new
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def self.normalize_params(params, key, value)
|
|
31
|
+
params << [key, value]
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def initialize
|
|
35
|
+
@pairs = []
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def <<(pair)
|
|
39
|
+
@pairs << pair
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def to_params_hash
|
|
43
|
+
@pairs
|
|
44
|
+
end
|
|
45
|
+
end
|
|
39
46
|
|
|
40
47
|
class << self
|
|
41
48
|
def parse_multipart(env, params = Rack::Utils.default_query_parser)
|
|
42
|
-
|
|
43
|
-
|
|
49
|
+
unless io = env[RACK_INPUT]
|
|
50
|
+
raise MissingInputError, "Missing input stream!"
|
|
51
|
+
end
|
|
44
52
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
content_length = req.content_length
|
|
49
|
-
content_length = content_length.to_i if content_length
|
|
53
|
+
if content_length = env['CONTENT_LENGTH']
|
|
54
|
+
content_length = content_length.to_i
|
|
55
|
+
end
|
|
50
56
|
|
|
51
|
-
|
|
52
|
-
bufsize = req.get_header(RACK_MULTIPART_BUFFER_SIZE) || Parser::BUFSIZE
|
|
57
|
+
content_type = env['CONTENT_TYPE']
|
|
53
58
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
59
|
+
tempfile = env[RACK_MULTIPART_TEMPFILE_FACTORY] || Parser::TEMPFILE_FACTORY
|
|
60
|
+
bufsize = env[RACK_MULTIPART_BUFFER_SIZE] || Parser::BUFSIZE
|
|
61
|
+
|
|
62
|
+
info = Parser.parse(io, content_length, content_type, tempfile, bufsize, params)
|
|
63
|
+
env[RACK_TEMPFILES] = info.tmp_files
|
|
64
|
+
|
|
65
|
+
return info.params
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def extract_multipart(request, params = Rack::Utils.default_query_parser)
|
|
69
|
+
parse_multipart(request.env)
|
|
57
70
|
end
|
|
58
71
|
|
|
59
72
|
def build_multipart(params, first = true)
|
|
60
73
|
Generator.new(params, first).dump
|
|
61
74
|
end
|
|
62
75
|
end
|
|
63
|
-
|
|
64
76
|
end
|
|
65
77
|
end
|
data/lib/rack/null_logger.rb
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require_relative 'constants'
|
|
4
|
+
|
|
3
5
|
module Rack
|
|
4
6
|
class NullLogger
|
|
5
7
|
def initialize(app)
|
|
@@ -22,6 +24,11 @@ module Rack
|
|
|
22
24
|
def warn? ; end
|
|
23
25
|
def error? ; end
|
|
24
26
|
def fatal? ; end
|
|
27
|
+
def debug! ; end
|
|
28
|
+
def error! ; end
|
|
29
|
+
def fatal! ; end
|
|
30
|
+
def info! ; end
|
|
31
|
+
def warn! ; end
|
|
25
32
|
def level ; end
|
|
26
33
|
def progname ; end
|
|
27
34
|
def datetime_format ; end
|
|
@@ -34,6 +41,8 @@ module Rack
|
|
|
34
41
|
def sev_threshold=(sev_threshold); end
|
|
35
42
|
def close ; end
|
|
36
43
|
def add(severity, message = nil, progname = nil, &block); end
|
|
44
|
+
def log(severity, message = nil, progname = nil, &block); end
|
|
37
45
|
def <<(msg); end
|
|
46
|
+
def reopen(logdev = nil); end
|
|
38
47
|
end
|
|
39
48
|
end
|
data/lib/rack/query_parser.rb
CHANGED
|
@@ -1,47 +1,79 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require_relative 'bad_request'
|
|
4
|
+
require 'uri'
|
|
5
|
+
|
|
3
6
|
module Rack
|
|
4
7
|
class QueryParser
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
DEFAULT_SEP = /[&;] */n
|
|
8
|
-
COMMON_SEP = { ";" => /[;] */n, ";," => /[;,] */n, "&" => /[&] */n }
|
|
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
|
|
23
|
+
|
|
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
|
|
29
|
+
|
|
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
|
|
35
|
+
|
|
36
|
+
def self.make_default(param_depth_limit, **options)
|
|
37
|
+
new(Params, param_depth_limit, **options)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
attr_reader :param_depth_limit
|
|
41
|
+
|
|
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
|
|
49
|
+
end
|
|
18
50
|
|
|
19
|
-
|
|
20
|
-
new Params, key_space_limit, param_depth_limit
|
|
51
|
+
val
|
|
21
52
|
end
|
|
22
53
|
|
|
23
|
-
|
|
54
|
+
BYTESIZE_LIMIT = env_int.call("RACK_QUERY_PARSER_BYTESIZE_LIMIT", 4194304)
|
|
55
|
+
private_constant :BYTESIZE_LIMIT
|
|
24
56
|
|
|
25
|
-
|
|
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)
|
|
26
63
|
@params_class = params_class
|
|
27
|
-
@key_space_limit = key_space_limit
|
|
28
64
|
@param_depth_limit = param_depth_limit
|
|
65
|
+
@bytesize_limit = bytesize_limit
|
|
66
|
+
@params_limit = params_limit
|
|
29
67
|
end
|
|
30
68
|
|
|
31
69
|
# Stolen from Mongrel, with some small modifications:
|
|
32
|
-
# Parses a query string by breaking it up at the '&'
|
|
33
|
-
#
|
|
34
|
-
#
|
|
35
|
-
|
|
36
|
-
def parse_query(qs, d = nil, &unescaper)
|
|
37
|
-
unescaper ||= method(:unescape)
|
|
38
|
-
|
|
70
|
+
# Parses a query string by breaking it up at the '&'. You can also use this
|
|
71
|
+
# to parse cookies by changing the characters used in the second parameter
|
|
72
|
+
# (which defaults to '&').
|
|
73
|
+
def parse_query(qs, separator = nil, &unescaper)
|
|
39
74
|
params = make_params
|
|
40
75
|
|
|
41
|
-
(qs
|
|
42
|
-
next if p.empty?
|
|
43
|
-
k, v = p.split('=', 2).map!(&unescaper)
|
|
44
|
-
|
|
76
|
+
each_query_pair(qs, separator, unescaper) do |k, v|
|
|
45
77
|
if cur = params[k]
|
|
46
78
|
if cur.class == Array
|
|
47
79
|
params[k] << v
|
|
@@ -56,81 +88,117 @@ module Rack
|
|
|
56
88
|
return params.to_h
|
|
57
89
|
end
|
|
58
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
|
+
|
|
59
104
|
# parse_nested_query expands a query string into structural types. Supported
|
|
60
105
|
# types are Arrays, Hashes and basic value types. It is possible to supply
|
|
61
106
|
# query strings with parameters of conflicting types, in this case a
|
|
62
107
|
# ParameterTypeError is raised. Users are encouraged to return a 400 in this
|
|
63
108
|
# case.
|
|
64
|
-
def parse_nested_query(qs,
|
|
109
|
+
def parse_nested_query(qs, separator = nil)
|
|
65
110
|
params = make_params
|
|
66
111
|
|
|
67
|
-
|
|
68
|
-
(
|
|
69
|
-
k, v = p.split('=', 2).map! { |s| unescape(s) }
|
|
70
|
-
|
|
71
|
-
normalize_params(params, k, v, param_depth_limit)
|
|
72
|
-
end
|
|
112
|
+
each_query_pair(qs, separator) do |k, v|
|
|
113
|
+
_normalize_params(params, k, v, 0)
|
|
73
114
|
end
|
|
74
115
|
|
|
75
116
|
return params.to_h
|
|
76
|
-
rescue ArgumentError => e
|
|
77
|
-
raise InvalidParameterError, e.message, e.backtrace
|
|
78
117
|
end
|
|
79
118
|
|
|
80
119
|
# normalize_params recursively expands parameters into structural types. If
|
|
81
120
|
# the structural types represented by two different parameter names are in
|
|
82
|
-
# conflict, a ParameterTypeError is raised.
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
name
|
|
87
|
-
|
|
88
|
-
after = $' || ''
|
|
121
|
+
# conflict, a ParameterTypeError is raised. The depth argument is deprecated
|
|
122
|
+
# and should no longer be used, it is kept for backwards compatibility with
|
|
123
|
+
# earlier versions of rack.
|
|
124
|
+
def normalize_params(params, name, v, _depth=nil)
|
|
125
|
+
_normalize_params(params, name, v, 0)
|
|
126
|
+
end
|
|
89
127
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
128
|
+
private def _normalize_params(params, name, v, depth)
|
|
129
|
+
raise ParamsTooDeepError if depth >= param_depth_limit
|
|
130
|
+
|
|
131
|
+
if !name
|
|
132
|
+
# nil name, treat same as empty string (required by tests)
|
|
133
|
+
k = after = ''
|
|
134
|
+
elsif depth == 0
|
|
135
|
+
# Start of parsing, don't treat [] or [ at start of string specially
|
|
136
|
+
if start = name.index('[', 1)
|
|
137
|
+
# Start of parameter nesting, use part before brackets as key
|
|
138
|
+
k = name[0, start]
|
|
139
|
+
after = name[start, name.length]
|
|
93
140
|
else
|
|
94
|
-
|
|
141
|
+
# Plain parameter with no nesting
|
|
142
|
+
k = name
|
|
143
|
+
after = ''
|
|
95
144
|
end
|
|
145
|
+
elsif name.start_with?('[]')
|
|
146
|
+
# Array nesting
|
|
147
|
+
k = '[]'
|
|
148
|
+
after = name[2, name.length]
|
|
149
|
+
elsif name.start_with?('[') && (start = name.index(']', 1))
|
|
150
|
+
# Hash nesting, use the part inside brackets as the key
|
|
151
|
+
k = name[1, start-1]
|
|
152
|
+
after = name[start+1, name.length]
|
|
153
|
+
else
|
|
154
|
+
# Probably malformed input, nested but not starting with [
|
|
155
|
+
# treat full name as key for backwards compatibility.
|
|
156
|
+
k = name
|
|
157
|
+
after = ''
|
|
96
158
|
end
|
|
97
159
|
|
|
160
|
+
return if k.empty?
|
|
161
|
+
|
|
98
162
|
if after == ''
|
|
99
|
-
|
|
163
|
+
if k == '[]' && depth != 0
|
|
164
|
+
return [v]
|
|
165
|
+
else
|
|
166
|
+
params[k] = v
|
|
167
|
+
end
|
|
100
168
|
elsif after == "["
|
|
101
169
|
params[name] = v
|
|
102
170
|
elsif after == "[]"
|
|
103
171
|
params[k] ||= []
|
|
104
172
|
raise ParameterTypeError, "expected Array (got #{params[k].class.name}) for param `#{k}'" unless params[k].is_a?(Array)
|
|
105
173
|
params[k] << v
|
|
106
|
-
elsif after
|
|
107
|
-
|
|
174
|
+
elsif after.start_with?('[]')
|
|
175
|
+
# Recognize x[][y] (hash inside array) parameters
|
|
176
|
+
unless after[2] == '[' && after.end_with?(']') && (child_key = after[3, after.length-4]) && !child_key.empty? && !child_key.index('[') && !child_key.index(']')
|
|
177
|
+
# Handle other nested array parameters
|
|
178
|
+
child_key = after[2, after.length]
|
|
179
|
+
end
|
|
108
180
|
params[k] ||= []
|
|
109
181
|
raise ParameterTypeError, "expected Array (got #{params[k].class.name}) for param `#{k}'" unless params[k].is_a?(Array)
|
|
110
182
|
if params_hash_type?(params[k].last) && !params_hash_has_key?(params[k].last, child_key)
|
|
111
|
-
|
|
183
|
+
_normalize_params(params[k].last, child_key, v, depth + 1)
|
|
112
184
|
else
|
|
113
|
-
params[k] <<
|
|
185
|
+
params[k] << _normalize_params(make_params, child_key, v, depth + 1)
|
|
114
186
|
end
|
|
115
187
|
else
|
|
116
188
|
params[k] ||= make_params
|
|
117
189
|
raise ParameterTypeError, "expected Hash (got #{params[k].class.name}) for param `#{k}'" unless params_hash_type?(params[k])
|
|
118
|
-
params[k] =
|
|
190
|
+
params[k] = _normalize_params(params[k], after, v, depth + 1)
|
|
119
191
|
end
|
|
120
192
|
|
|
121
193
|
params
|
|
122
194
|
end
|
|
123
195
|
|
|
124
196
|
def make_params
|
|
125
|
-
@params_class.new
|
|
126
|
-
end
|
|
127
|
-
|
|
128
|
-
def new_space_limit(key_space_limit)
|
|
129
|
-
self.class.new @params_class, key_space_limit, param_depth_limit
|
|
197
|
+
@params_class.new
|
|
130
198
|
end
|
|
131
199
|
|
|
132
200
|
def new_depth_limit(param_depth_limit)
|
|
133
|
-
self.class.new @params_class,
|
|
201
|
+
self.class.new @params_class, param_depth_limit
|
|
134
202
|
end
|
|
135
203
|
|
|
136
204
|
private
|
|
@@ -151,66 +219,42 @@ module Rack
|
|
|
151
219
|
true
|
|
152
220
|
end
|
|
153
221
|
|
|
154
|
-
def
|
|
155
|
-
|
|
156
|
-
end
|
|
157
|
-
|
|
158
|
-
class Params
|
|
159
|
-
def initialize(limit)
|
|
160
|
-
@limit = limit
|
|
161
|
-
@size = 0
|
|
162
|
-
@params = {}
|
|
163
|
-
end
|
|
222
|
+
def each_query_pair(qs, separator, unescaper = nil)
|
|
223
|
+
return if !qs || qs.empty?
|
|
164
224
|
|
|
165
|
-
|
|
166
|
-
@
|
|
225
|
+
if qs.bytesize > @bytesize_limit
|
|
226
|
+
raise QueryLimitError, "total query size exceeds limit (#{@bytesize_limit})"
|
|
167
227
|
end
|
|
168
228
|
|
|
169
|
-
|
|
170
|
-
@size += key.size if key && !@params.key?(key)
|
|
171
|
-
raise RangeError, 'exceeded available parameter key space' if @size > @limit
|
|
172
|
-
@params[key] = value
|
|
173
|
-
end
|
|
229
|
+
pairs = qs.split(separator ? (COMMON_SEP[separator] || /[#{separator}] */n) : DEFAULT_SEP, @params_limit + 1)
|
|
174
230
|
|
|
175
|
-
|
|
176
|
-
|
|
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})"
|
|
177
234
|
end
|
|
178
235
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
# representation, not a copy.
|
|
191
|
-
#
|
|
192
|
-
# 3. Because the `Params` object's internal representation is mutable
|
|
193
|
-
# through the `#[]=` method, it is not thread safe. The result of
|
|
194
|
-
# getting the hash representation while another thread is adding a
|
|
195
|
-
# key to it is non-deterministic.
|
|
196
|
-
#
|
|
197
|
-
def to_h
|
|
198
|
-
@params.each do |key, value|
|
|
199
|
-
case value
|
|
200
|
-
when self
|
|
201
|
-
# Handle circular references gracefully.
|
|
202
|
-
@params[key] = @params
|
|
203
|
-
when Params
|
|
204
|
-
@params[key] = value.to_h
|
|
205
|
-
when Array
|
|
206
|
-
value.map! { |v| v.kind_of?(Params) ? v.to_h : v }
|
|
207
|
-
else
|
|
208
|
-
# Ignore anything that is not a `Params` object or
|
|
209
|
-
# a collection that can contain one.
|
|
210
|
-
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
|
|
211
247
|
end
|
|
212
|
-
@params
|
|
213
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
|
|
214
258
|
alias_method :to_params_hash, :to_h
|
|
215
259
|
end
|
|
216
260
|
end
|
data/lib/rack/recursive.rb
CHANGED
data/lib/rack/reloader.rb
CHANGED
|
@@ -22,8 +22,6 @@ module Rack
|
|
|
22
22
|
# It is performing a check/reload cycle at the start of every request, but
|
|
23
23
|
# also respects a cool down time, during which nothing will be done.
|
|
24
24
|
class Reloader
|
|
25
|
-
(require_relative 'core_ext/regexp'; using ::Rack::RegexpExtensions) if RUBY_VERSION < '2.4'
|
|
26
|
-
|
|
27
25
|
def initialize(app, cooldown = 10, backend = Stat)
|
|
28
26
|
@app = app
|
|
29
27
|
@cooldown = cooldown
|