rack 2.2.17 → 3.2.0
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.
Potentially problematic release.
This version of rack might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +501 -70
- data/CONTRIBUTING.md +63 -55
- data/MIT-LICENSE +1 -1
- data/README.md +376 -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 +9 -3
- data/lib/rack/etag.rb +17 -23
- data/lib/rack/events.rb +4 -0
- data/lib/rack/files.rb +15 -17
- 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 +147 -0
- data/lib/rack/multipart/generator.rb +7 -5
- data/lib/rack/multipart/parser.rb +241 -95
- data/lib/rack/multipart/uploaded_file.rb +45 -4
- data/lib/rack/multipart.rb +53 -40
- data/lib/rack/null_logger.rb +9 -0
- data/lib/rack/query_parser.rb +116 -121
- data/lib/rack/recursive.rb +2 -0
- data/lib/rack/reloader.rb +0 -2
- data/lib/rack/request.rb +269 -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 +30 -25
- data/lib/rack/show_exceptions.rb +25 -6
- data/lib/rack/show_status.rb +17 -9
- 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 +228 -238
- 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
@@ -1,14 +1,51 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'tempfile'
|
4
|
+
require 'fileutils'
|
5
|
+
|
3
6
|
module Rack
|
4
7
|
module Multipart
|
8
|
+
# Despite the misleading name, UploadedFile is designed for use for
|
9
|
+
# preparing multipart file upload bodies, generally for use in tests.
|
10
|
+
# It is not designed for and should not be used for handling uploaded
|
11
|
+
# files (there is no need for that, since Rack's multipart parser
|
12
|
+
# already creates Tempfiles for that). Using this with non-trusted
|
13
|
+
# filenames can create a security vulnerability.
|
14
|
+
#
|
15
|
+
# You should only use this class if you plan on passing the instances
|
16
|
+
# to Rack::MockRequest for use in creating multipart request bodies.
|
17
|
+
#
|
18
|
+
# UploadedFile delegates most methods to the tempfile it contains.
|
5
19
|
class UploadedFile
|
6
|
-
# The
|
20
|
+
# The provided name of the file. This generally is the basename of
|
21
|
+
# path provided during initialization, but it can contain slashes if they
|
22
|
+
# were present in the filename argument when the instance was created.
|
7
23
|
attr_reader :original_filename
|
8
24
|
|
9
|
-
# The content type of the
|
25
|
+
# The content type of the instance.
|
10
26
|
attr_accessor :content_type
|
11
27
|
|
28
|
+
# Create a new UploadedFile. For backwards compatibility, this accepts
|
29
|
+
# both positional and keyword versions of the same arguments:
|
30
|
+
#
|
31
|
+
# filepath/path :: The path to the file
|
32
|
+
# ct/content_type :: The content_type of the file
|
33
|
+
# bin/binary :: Whether to set binmode on the file before copying data into it.
|
34
|
+
#
|
35
|
+
# If both positional and keyword arguments are present, the keyword arguments
|
36
|
+
# take precedence.
|
37
|
+
#
|
38
|
+
# The following keyword-only arguments are also accepted:
|
39
|
+
#
|
40
|
+
# filename :: Override the filename to use for the file. This is so the
|
41
|
+
# filename for the upload does not need to match the basename of
|
42
|
+
# the file path. This should not contain slashes, unless you are
|
43
|
+
# trying to test how an application handles invalid filenames in
|
44
|
+
# multipart upload bodies.
|
45
|
+
# io :: Use the given IO-like instance as the tempfile, instead of creating
|
46
|
+
# a Tempfile instance. This is useful for building multipart file
|
47
|
+
# upload bodies without a file being present on the filesystem. If you are
|
48
|
+
# providing this, you should also provide the filename argument.
|
12
49
|
def initialize(filepath = nil, ct = "text/plain", bin = false,
|
13
50
|
path: filepath, content_type: ct, binary: bin, filename: nil, io: nil)
|
14
51
|
if io
|
@@ -24,15 +61,19 @@ module Rack
|
|
24
61
|
@content_type = content_type
|
25
62
|
end
|
26
63
|
|
64
|
+
# The path of the tempfile for the instance, if the tempfile has a path.
|
65
|
+
# nil if the tempfile does not have a path.
|
27
66
|
def path
|
28
67
|
@tempfile.path if @tempfile.respond_to?(:path)
|
29
68
|
end
|
30
69
|
alias_method :local_path, :path
|
31
70
|
|
32
|
-
|
33
|
-
|
71
|
+
# Return true if the tempfile responds to the method.
|
72
|
+
def respond_to_missing?(*args)
|
73
|
+
@tempfile.respond_to?(*args)
|
34
74
|
end
|
35
75
|
|
76
|
+
# Delegate method missing calls to the tempfile.
|
36
77
|
def method_missing(method_name, *args, &block) #:nodoc:
|
37
78
|
@tempfile.__send__(method_name, *args, &block)
|
38
79
|
end
|
data/lib/rack/multipart.rb
CHANGED
@@ -1,64 +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
|
-
|
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
|
38
46
|
|
39
47
|
class << self
|
40
48
|
def parse_multipart(env, params = Rack::Utils.default_query_parser)
|
41
|
-
|
42
|
-
|
49
|
+
unless io = env[RACK_INPUT]
|
50
|
+
raise MissingInputError, "Missing input stream!"
|
51
|
+
end
|
43
52
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
content_length = req.content_length
|
48
|
-
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
|
49
56
|
|
50
|
-
|
51
|
-
bufsize = req.get_header(RACK_MULTIPART_BUFFER_SIZE) || Parser::BUFSIZE
|
57
|
+
content_type = env['CONTENT_TYPE']
|
52
58
|
|
53
|
-
|
54
|
-
|
55
|
-
|
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)
|
56
70
|
end
|
57
71
|
|
58
72
|
def build_multipart(params, first = true)
|
59
73
|
Generator.new(params, first).dump
|
60
74
|
end
|
61
75
|
end
|
62
|
-
|
63
76
|
end
|
64
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,24 +1,30 @@
|
|
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
|
18
23
|
|
19
24
|
# QueryLimitError is for errors raised when the query provided exceeds one
|
20
25
|
# of the query parser limits.
|
21
26
|
class QueryLimitError < RangeError
|
27
|
+
include BadRequest
|
22
28
|
end
|
23
29
|
|
24
30
|
# ParamsTooDeepError is the old name for the error that is raised when params
|
@@ -27,11 +33,11 @@ module Rack
|
|
27
33
|
# to handle bad query strings also now handles other limits.
|
28
34
|
ParamsTooDeepError = QueryLimitError
|
29
35
|
|
30
|
-
def self.make_default(
|
31
|
-
new(Params,
|
36
|
+
def self.make_default(param_depth_limit, **options)
|
37
|
+
new(Params, param_depth_limit, **options)
|
32
38
|
end
|
33
39
|
|
34
|
-
attr_reader :
|
40
|
+
attr_reader :param_depth_limit
|
35
41
|
|
36
42
|
env_int = lambda do |key, val|
|
37
43
|
if str_val = ENV[key]
|
@@ -51,28 +57,21 @@ module Rack
|
|
51
57
|
PARAMS_LIMIT = env_int.call("RACK_QUERY_PARSER_PARAMS_LIMIT", 4096)
|
52
58
|
private_constant :PARAMS_LIMIT
|
53
59
|
|
54
|
-
def initialize(params_class,
|
60
|
+
def initialize(params_class, param_depth_limit, bytesize_limit: BYTESIZE_LIMIT, params_limit: PARAMS_LIMIT)
|
55
61
|
@params_class = params_class
|
56
|
-
@key_space_limit = key_space_limit
|
57
62
|
@param_depth_limit = param_depth_limit
|
58
63
|
@bytesize_limit = bytesize_limit
|
59
64
|
@params_limit = params_limit
|
60
65
|
end
|
61
66
|
|
62
67
|
# Stolen from Mongrel, with some small modifications:
|
63
|
-
# Parses a query string by breaking it up at the '&'
|
64
|
-
#
|
65
|
-
#
|
66
|
-
|
67
|
-
def parse_query(qs, d = nil, &unescaper)
|
68
|
-
unescaper ||= method(:unescape)
|
69
|
-
|
68
|
+
# Parses a query string by breaking it up at the '&'. You can also use this
|
69
|
+
# to parse cookies by changing the characters used in the second parameter
|
70
|
+
# (which defaults to '&').
|
71
|
+
def parse_query(qs, separator = nil, &unescaper)
|
70
72
|
params = make_params
|
71
73
|
|
72
|
-
|
73
|
-
next if p.empty?
|
74
|
-
k, v = p.split('=', 2).map!(&unescaper)
|
75
|
-
|
74
|
+
each_query_pair(qs, separator, unescaper) do |k, v|
|
76
75
|
if cur = params[k]
|
77
76
|
if cur.class == Array
|
78
77
|
params[k] << v
|
@@ -87,81 +86,117 @@ module Rack
|
|
87
86
|
return params.to_h
|
88
87
|
end
|
89
88
|
|
89
|
+
# Parses a query string by breaking it up at the '&', returning all key-value
|
90
|
+
# pairs as an array of [key, value] arrays. Unlike parse_query, this preserves
|
91
|
+
# all duplicate keys rather than collapsing them.
|
92
|
+
def parse_query_pairs(qs, separator = nil)
|
93
|
+
pairs = []
|
94
|
+
|
95
|
+
each_query_pair(qs, separator) do |k, v|
|
96
|
+
pairs << [k, v]
|
97
|
+
end
|
98
|
+
|
99
|
+
pairs
|
100
|
+
end
|
101
|
+
|
90
102
|
# parse_nested_query expands a query string into structural types. Supported
|
91
103
|
# types are Arrays, Hashes and basic value types. It is possible to supply
|
92
104
|
# query strings with parameters of conflicting types, in this case a
|
93
105
|
# ParameterTypeError is raised. Users are encouraged to return a 400 in this
|
94
106
|
# case.
|
95
|
-
def parse_nested_query(qs,
|
107
|
+
def parse_nested_query(qs, separator = nil)
|
96
108
|
params = make_params
|
97
109
|
|
98
|
-
|
99
|
-
|
100
|
-
k, v = p.split('=', 2).map! { |s| unescape(s) }
|
101
|
-
|
102
|
-
normalize_params(params, k, v, param_depth_limit)
|
103
|
-
end
|
110
|
+
each_query_pair(qs, separator) do |k, v|
|
111
|
+
_normalize_params(params, k, v, 0)
|
104
112
|
end
|
105
113
|
|
106
114
|
return params.to_h
|
107
|
-
rescue ArgumentError => e
|
108
|
-
raise InvalidParameterError, e.message, e.backtrace
|
109
115
|
end
|
110
116
|
|
111
117
|
# normalize_params recursively expands parameters into structural types. If
|
112
118
|
# the structural types represented by two different parameter names are in
|
113
|
-
# conflict, a ParameterTypeError is raised.
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
name
|
118
|
-
|
119
|
-
after = $' || ''
|
119
|
+
# conflict, a ParameterTypeError is raised. The depth argument is deprecated
|
120
|
+
# and should no longer be used, it is kept for backwards compatibility with
|
121
|
+
# earlier versions of rack.
|
122
|
+
def normalize_params(params, name, v, _depth=nil)
|
123
|
+
_normalize_params(params, name, v, 0)
|
124
|
+
end
|
120
125
|
|
121
|
-
|
122
|
-
|
123
|
-
|
126
|
+
private def _normalize_params(params, name, v, depth)
|
127
|
+
raise ParamsTooDeepError if depth >= param_depth_limit
|
128
|
+
|
129
|
+
if !name
|
130
|
+
# nil name, treat same as empty string (required by tests)
|
131
|
+
k = after = ''
|
132
|
+
elsif depth == 0
|
133
|
+
# Start of parsing, don't treat [] or [ at start of string specially
|
134
|
+
if start = name.index('[', 1)
|
135
|
+
# Start of parameter nesting, use part before brackets as key
|
136
|
+
k = name[0, start]
|
137
|
+
after = name[start, name.length]
|
124
138
|
else
|
125
|
-
|
139
|
+
# Plain parameter with no nesting
|
140
|
+
k = name
|
141
|
+
after = ''
|
126
142
|
end
|
143
|
+
elsif name.start_with?('[]')
|
144
|
+
# Array nesting
|
145
|
+
k = '[]'
|
146
|
+
after = name[2, name.length]
|
147
|
+
elsif name.start_with?('[') && (start = name.index(']', 1))
|
148
|
+
# Hash nesting, use the part inside brackets as the key
|
149
|
+
k = name[1, start-1]
|
150
|
+
after = name[start+1, name.length]
|
151
|
+
else
|
152
|
+
# Probably malformed input, nested but not starting with [
|
153
|
+
# treat full name as key for backwards compatibility.
|
154
|
+
k = name
|
155
|
+
after = ''
|
127
156
|
end
|
128
157
|
|
158
|
+
return if k.empty?
|
159
|
+
|
129
160
|
if after == ''
|
130
|
-
|
161
|
+
if k == '[]' && depth != 0
|
162
|
+
return [v]
|
163
|
+
else
|
164
|
+
params[k] = v
|
165
|
+
end
|
131
166
|
elsif after == "["
|
132
167
|
params[name] = v
|
133
168
|
elsif after == "[]"
|
134
169
|
params[k] ||= []
|
135
170
|
raise ParameterTypeError, "expected Array (got #{params[k].class.name}) for param `#{k}'" unless params[k].is_a?(Array)
|
136
171
|
params[k] << v
|
137
|
-
elsif after
|
138
|
-
|
172
|
+
elsif after.start_with?('[]')
|
173
|
+
# Recognize x[][y] (hash inside array) parameters
|
174
|
+
unless after[2] == '[' && after.end_with?(']') && (child_key = after[3, after.length-4]) && !child_key.empty? && !child_key.index('[') && !child_key.index(']')
|
175
|
+
# Handle other nested array parameters
|
176
|
+
child_key = after[2, after.length]
|
177
|
+
end
|
139
178
|
params[k] ||= []
|
140
179
|
raise ParameterTypeError, "expected Array (got #{params[k].class.name}) for param `#{k}'" unless params[k].is_a?(Array)
|
141
180
|
if params_hash_type?(params[k].last) && !params_hash_has_key?(params[k].last, child_key)
|
142
|
-
|
181
|
+
_normalize_params(params[k].last, child_key, v, depth + 1)
|
143
182
|
else
|
144
|
-
params[k] <<
|
183
|
+
params[k] << _normalize_params(make_params, child_key, v, depth + 1)
|
145
184
|
end
|
146
185
|
else
|
147
186
|
params[k] ||= make_params
|
148
187
|
raise ParameterTypeError, "expected Hash (got #{params[k].class.name}) for param `#{k}'" unless params_hash_type?(params[k])
|
149
|
-
params[k] =
|
188
|
+
params[k] = _normalize_params(params[k], after, v, depth + 1)
|
150
189
|
end
|
151
190
|
|
152
191
|
params
|
153
192
|
end
|
154
193
|
|
155
194
|
def make_params
|
156
|
-
@params_class.new
|
157
|
-
end
|
158
|
-
|
159
|
-
def new_space_limit(key_space_limit)
|
160
|
-
self.class.new @params_class, key_space_limit, param_depth_limit
|
195
|
+
@params_class.new
|
161
196
|
end
|
162
197
|
|
163
198
|
def new_depth_limit(param_depth_limit)
|
164
|
-
self.class.new @params_class,
|
199
|
+
self.class.new @params_class, param_depth_limit
|
165
200
|
end
|
166
201
|
|
167
202
|
private
|
@@ -182,82 +217,42 @@ module Rack
|
|
182
217
|
true
|
183
218
|
end
|
184
219
|
|
185
|
-
def
|
186
|
-
if qs
|
187
|
-
if qs.bytesize > @bytesize_limit
|
188
|
-
raise QueryLimitError, "total query size (#{qs.bytesize}) exceeds limit (#{@bytesize_limit})"
|
189
|
-
end
|
190
|
-
|
191
|
-
if (param_count = qs.count(sep.is_a?(String) ? sep : '&')) >= @params_limit
|
192
|
-
raise QueryLimitError, "total number of query parameters (#{param_count+1}) exceeds limit (#{@params_limit})"
|
193
|
-
end
|
220
|
+
def each_query_pair(qs, separator, unescaper = nil)
|
221
|
+
return if !qs || qs.empty?
|
194
222
|
|
195
|
-
|
196
|
-
|
197
|
-
''
|
223
|
+
if qs.bytesize > @bytesize_limit
|
224
|
+
raise QueryLimitError, "total query size (#{qs.bytesize}) exceeds limit (#{@bytesize_limit})"
|
198
225
|
end
|
199
|
-
end
|
200
|
-
|
201
|
-
def unescape(string)
|
202
|
-
Utils.unescape(string)
|
203
|
-
end
|
204
226
|
|
205
|
-
|
206
|
-
def initialize(limit)
|
207
|
-
@limit = limit
|
208
|
-
@size = 0
|
209
|
-
@params = {}
|
210
|
-
end
|
227
|
+
pairs = qs.split(separator ? (COMMON_SEP[separator] || /[#{separator}] */n) : DEFAULT_SEP, @params_limit + 1)
|
211
228
|
|
212
|
-
|
213
|
-
|
229
|
+
if pairs.size > @params_limit
|
230
|
+
param_count = pairs.size + pairs.last.count(separator || "&")
|
231
|
+
raise QueryLimitError, "total number of query parameters (#{param_count}) exceeds limit (#{@params_limit})"
|
214
232
|
end
|
215
233
|
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
234
|
+
if unescaper
|
235
|
+
pairs.each do |p|
|
236
|
+
next if p.empty?
|
237
|
+
k, v = p.split('=', 2).map!(&unescaper)
|
238
|
+
yield k, v
|
239
|
+
end
|
240
|
+
else
|
241
|
+
pairs.each do |p|
|
242
|
+
next if p.empty?
|
243
|
+
k, v = p.split('=', 2).map! { |s| unescape(s) }
|
244
|
+
yield k, v
|
245
|
+
end
|
220
246
|
end
|
247
|
+
rescue ArgumentError => e
|
248
|
+
raise InvalidParameterError, e.message, e.backtrace
|
249
|
+
end
|
221
250
|
|
222
|
-
|
223
|
-
|
224
|
-
|
251
|
+
def unescape(string, encoding = Encoding::UTF_8)
|
252
|
+
URI.decode_www_form_component(string, encoding)
|
253
|
+
end
|
225
254
|
|
226
|
-
|
227
|
-
# of the same shape, but using the objects' internal representations
|
228
|
-
# (Ruby hashes) in place of the objects. The result is a hash consisting
|
229
|
-
# purely of Ruby primitives.
|
230
|
-
#
|
231
|
-
# Mutation warning!
|
232
|
-
#
|
233
|
-
# 1. This method mutates the internal representation of the `Params`
|
234
|
-
# objects in order to save object allocations.
|
235
|
-
#
|
236
|
-
# 2. The value you get back is a reference to the internal hash
|
237
|
-
# representation, not a copy.
|
238
|
-
#
|
239
|
-
# 3. Because the `Params` object's internal representation is mutable
|
240
|
-
# through the `#[]=` method, it is not thread safe. The result of
|
241
|
-
# getting the hash representation while another thread is adding a
|
242
|
-
# key to it is non-deterministic.
|
243
|
-
#
|
244
|
-
def to_h
|
245
|
-
@params.each do |key, value|
|
246
|
-
case value
|
247
|
-
when self
|
248
|
-
# Handle circular references gracefully.
|
249
|
-
@params[key] = @params
|
250
|
-
when Params
|
251
|
-
@params[key] = value.to_h
|
252
|
-
when Array
|
253
|
-
value.map! { |v| v.kind_of?(Params) ? v.to_h : v }
|
254
|
-
else
|
255
|
-
# Ignore anything that is not a `Params` object or
|
256
|
-
# a collection that can contain one.
|
257
|
-
end
|
258
|
-
end
|
259
|
-
@params
|
260
|
-
end
|
255
|
+
class Params < Hash
|
261
256
|
alias_method :to_params_hash, :to_h
|
262
257
|
end
|
263
258
|
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
|