rack 2.1.4.4 → 2.2.17
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 +713 -10
- data/CONTRIBUTING.md +136 -0
- data/README.rdoc +109 -38
- data/Rakefile +14 -7
- data/{SPEC → SPEC.rdoc} +35 -6
- data/lib/rack/auth/abstract/request.rb +0 -2
- data/lib/rack/auth/basic.rb +4 -5
- data/lib/rack/auth/digest/md5.rb +4 -4
- data/lib/rack/auth/digest/nonce.rb +2 -3
- data/lib/rack/auth/digest/request.rb +3 -3
- data/lib/rack/body_proxy.rb +13 -9
- data/lib/rack/builder.rb +77 -8
- data/lib/rack/cascade.rb +23 -8
- data/lib/rack/chunked.rb +48 -23
- data/lib/rack/common_logger.rb +27 -19
- data/lib/rack/conditional_get.rb +18 -16
- data/lib/rack/content_length.rb +6 -7
- data/lib/rack/content_type.rb +3 -4
- data/lib/rack/deflater.rb +45 -35
- data/lib/rack/directory.rb +77 -60
- data/lib/rack/etag.rb +4 -3
- data/lib/rack/events.rb +15 -18
- data/lib/rack/file.rb +1 -1
- data/lib/rack/files.rb +96 -56
- data/lib/rack/handler/cgi.rb +1 -4
- data/lib/rack/handler/fastcgi.rb +1 -3
- data/lib/rack/handler/lsws.rb +1 -3
- data/lib/rack/handler/scgi.rb +1 -3
- data/lib/rack/handler/thin.rb +1 -3
- data/lib/rack/handler/webrick.rb +12 -5
- data/lib/rack/head.rb +0 -2
- data/lib/rack/lint.rb +211 -179
- data/lib/rack/lobster.rb +3 -5
- data/lib/rack/lock.rb +0 -1
- data/lib/rack/media_type.rb +17 -7
- data/lib/rack/method_override.rb +1 -1
- data/lib/rack/mock.rb +54 -7
- data/lib/rack/multipart/generator.rb +11 -6
- data/lib/rack/multipart/parser.rb +17 -17
- data/lib/rack/multipart/uploaded_file.rb +13 -7
- data/lib/rack/multipart.rb +1 -1
- data/lib/rack/query_parser.rb +62 -16
- data/lib/rack/recursive.rb +1 -1
- data/lib/rack/reloader.rb +1 -3
- data/lib/rack/request.rb +184 -78
- data/lib/rack/response.rb +62 -19
- data/lib/rack/rewindable_input.rb +0 -1
- data/lib/rack/runtime.rb +3 -3
- data/lib/rack/sendfile.rb +1 -4
- data/lib/rack/server.rb +9 -8
- data/lib/rack/session/abstract/id.rb +21 -18
- data/lib/rack/session/cookie.rb +4 -6
- data/lib/rack/session/pool.rb +7 -2
- data/lib/rack/show_exceptions.rb +6 -8
- data/lib/rack/show_status.rb +5 -7
- data/lib/rack/static.rb +15 -7
- data/lib/rack/tempfile_reaper.rb +0 -2
- data/lib/rack/urlmap.rb +2 -5
- data/lib/rack/utils.rb +69 -58
- data/lib/rack/version.rb +29 -0
- data/lib/rack.rb +7 -16
- data/rack.gemspec +31 -29
- metadata +12 -16
data/lib/rack/lobster.rb
CHANGED
@@ -2,9 +2,6 @@
|
|
2
2
|
|
3
3
|
require 'zlib'
|
4
4
|
|
5
|
-
require 'rack/request'
|
6
|
-
require 'rack/response'
|
7
|
-
|
8
5
|
module Rack
|
9
6
|
# Paste has a Pony, Rack has a Lobster!
|
10
7
|
class Lobster
|
@@ -64,9 +61,10 @@ module Rack
|
|
64
61
|
end
|
65
62
|
|
66
63
|
if $0 == __FILE__
|
67
|
-
|
68
|
-
|
64
|
+
# :nocov:
|
65
|
+
require_relative '../rack'
|
69
66
|
Rack::Server.start(
|
70
67
|
app: Rack::ShowExceptions.new(Rack::Lint.new(Rack::Lobster.new)), Port: 9292
|
71
68
|
)
|
69
|
+
# :nocov:
|
72
70
|
end
|
data/lib/rack/lock.rb
CHANGED
data/lib/rack/media_type.rb
CHANGED
@@ -4,7 +4,7 @@ module Rack
|
|
4
4
|
# Rack::MediaType parse media type and parameters out of content_type string
|
5
5
|
|
6
6
|
class MediaType
|
7
|
-
SPLIT_PATTERN =
|
7
|
+
SPLIT_PATTERN = /[;,]/
|
8
8
|
|
9
9
|
class << self
|
10
10
|
# The media type (type/subtype) portion of the CONTENT_TYPE header
|
@@ -15,7 +15,11 @@ module Rack
|
|
15
15
|
# http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.7
|
16
16
|
def type(content_type)
|
17
17
|
return nil unless content_type
|
18
|
-
content_type.split(SPLIT_PATTERN, 2).first
|
18
|
+
if type = content_type.split(SPLIT_PATTERN, 2).first
|
19
|
+
type.rstrip!
|
20
|
+
type.downcase!
|
21
|
+
type
|
22
|
+
end
|
19
23
|
end
|
20
24
|
|
21
25
|
# The media type parameters provided in CONTENT_TYPE as a Hash, or
|
@@ -23,21 +27,27 @@ module Rack
|
|
23
27
|
# provided. e.g., when the CONTENT_TYPE is "text/plain;charset=utf-8",
|
24
28
|
# this method responds with the following Hash:
|
25
29
|
# { 'charset' => 'utf-8' }
|
30
|
+
#
|
31
|
+
# This will pass back parameters with empty strings in the hash if they
|
32
|
+
# lack a value (e.g., "text/plain;charset=" will return { 'charset' => '' },
|
33
|
+
# and "text/plain;charset" will return { 'charset' => '' }, similarly to
|
34
|
+
# the query params parser (barring the latter case, which returns nil instead)).
|
26
35
|
def params(content_type)
|
27
36
|
return {} if content_type.nil?
|
28
37
|
|
29
38
|
content_type.split(SPLIT_PATTERN)[1..-1].each_with_object({}) do |s, hsh|
|
39
|
+
s.strip!
|
30
40
|
k, v = s.split('=', 2)
|
31
|
-
|
32
|
-
hsh[k
|
41
|
+
k.downcase!
|
42
|
+
hsh[k] = strip_doublequotes(v)
|
33
43
|
end
|
34
44
|
end
|
35
45
|
|
36
46
|
private
|
37
47
|
|
38
|
-
|
39
|
-
|
40
|
-
|
48
|
+
def strip_doublequotes(str)
|
49
|
+
(str && str.start_with?('"') && str.end_with?('"')) ? str[1..-2] : str || ''
|
50
|
+
end
|
41
51
|
end
|
42
52
|
end
|
43
53
|
end
|
data/lib/rack/method_override.rb
CHANGED
@@ -43,7 +43,7 @@ module Rack
|
|
43
43
|
|
44
44
|
def method_override_param(req)
|
45
45
|
req.POST[METHOD_OVERRIDE_PARAM_KEY]
|
46
|
-
rescue Utils::InvalidParameterError, Utils::ParameterTypeError
|
46
|
+
rescue Utils::InvalidParameterError, Utils::ParameterTypeError, QueryParser::ParamsTooDeepError
|
47
47
|
req.get_header(RACK_ERRORS).puts "Invalid or incomplete POST params"
|
48
48
|
rescue EOFError
|
49
49
|
req.get_header(RACK_ERRORS).puts "Bad request content body"
|
data/lib/rack/mock.rb
CHANGED
@@ -2,11 +2,7 @@
|
|
2
2
|
|
3
3
|
require 'uri'
|
4
4
|
require 'stringio'
|
5
|
-
|
6
|
-
require 'rack/lint'
|
7
|
-
require 'rack/utils'
|
8
|
-
require 'rack/response'
|
9
|
-
require 'cgi/cookie'
|
5
|
+
require_relative '../rack'
|
10
6
|
|
11
7
|
module Rack
|
12
8
|
# Rack::MockRequest helps testing your Rack application without
|
@@ -56,14 +52,24 @@ module Rack
|
|
56
52
|
@app = app
|
57
53
|
end
|
58
54
|
|
55
|
+
# Make a GET request and return a MockResponse. See #request.
|
59
56
|
def get(uri, opts = {}) request(GET, uri, opts) end
|
57
|
+
# Make a POST request and return a MockResponse. See #request.
|
60
58
|
def post(uri, opts = {}) request(POST, uri, opts) end
|
59
|
+
# Make a PUT request and return a MockResponse. See #request.
|
61
60
|
def put(uri, opts = {}) request(PUT, uri, opts) end
|
61
|
+
# Make a PATCH request and return a MockResponse. See #request.
|
62
62
|
def patch(uri, opts = {}) request(PATCH, uri, opts) end
|
63
|
+
# Make a DELETE request and return a MockResponse. See #request.
|
63
64
|
def delete(uri, opts = {}) request(DELETE, uri, opts) end
|
65
|
+
# Make a HEAD request and return a MockResponse. See #request.
|
64
66
|
def head(uri, opts = {}) request(HEAD, uri, opts) end
|
67
|
+
# Make an OPTIONS request and return a MockResponse. See #request.
|
65
68
|
def options(uri, opts = {}) request(OPTIONS, uri, opts) end
|
66
69
|
|
70
|
+
# Make a request using the given request method for the given
|
71
|
+
# uri to the rack application and return a MockResponse.
|
72
|
+
# Options given are passed to MockRequest.env_for.
|
67
73
|
def request(method = GET, uri = "", opts = {})
|
68
74
|
env = self.class.env_for(uri, opts.merge(method: method))
|
69
75
|
|
@@ -88,6 +94,13 @@ module Rack
|
|
88
94
|
end
|
89
95
|
|
90
96
|
# Return the Rack environment used for a request to +uri+.
|
97
|
+
# All options that are strings are added to the returned environment.
|
98
|
+
# Options:
|
99
|
+
# :fatal :: Whether to raise an exception if request outputs to rack.errors
|
100
|
+
# :input :: The rack.input to set
|
101
|
+
# :method :: The HTTP request method to use
|
102
|
+
# :params :: The params to use
|
103
|
+
# :script_name :: The SCRIPT_NAME to set
|
91
104
|
def self.env_for(uri = "", opts = {})
|
92
105
|
uri = parse_uri_rfc2396(uri)
|
93
106
|
uri.path = "/#{uri.path}" unless uri.path[0] == ?/
|
@@ -157,6 +170,40 @@ module Rack
|
|
157
170
|
# MockRequest.
|
158
171
|
|
159
172
|
class MockResponse < Rack::Response
|
173
|
+
begin
|
174
|
+
# Recent versions of the CGI gem may not provide `CGI::Cookie`.
|
175
|
+
require 'cgi/cookie'
|
176
|
+
Cookie = CGI::Cookie
|
177
|
+
rescue LoadError
|
178
|
+
class Cookie
|
179
|
+
attr_reader :name, :value, :path, :domain, :expires, :secure
|
180
|
+
|
181
|
+
def initialize(args)
|
182
|
+
@name = args["name"]
|
183
|
+
@value = args["value"]
|
184
|
+
@path = args["path"]
|
185
|
+
@domain = args["domain"]
|
186
|
+
@expires = args["expires"]
|
187
|
+
@secure = args["secure"]
|
188
|
+
end
|
189
|
+
|
190
|
+
def method_missing(method_name, *args, &block)
|
191
|
+
@value.send(method_name, *args, &block)
|
192
|
+
end
|
193
|
+
# :nocov:
|
194
|
+
ruby2_keywords(:method_missing) if respond_to?(:ruby2_keywords, true)
|
195
|
+
# :nocov:
|
196
|
+
|
197
|
+
def respond_to_missing?(method_name, include_all = false)
|
198
|
+
@value.respond_to?(method_name, include_all) || super
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
class << self
|
204
|
+
alias [] new
|
205
|
+
end
|
206
|
+
|
160
207
|
# Headers
|
161
208
|
attr_reader :original_headers, :cookies
|
162
209
|
|
@@ -218,7 +265,7 @@ module Rack
|
|
218
265
|
set_cookie_header.split("\n").each do |cookie|
|
219
266
|
cookie_name, cookie_filling = cookie.split('=', 2)
|
220
267
|
cookie_attributes = identify_cookie_attributes cookie_filling
|
221
|
-
parsed_cookie =
|
268
|
+
parsed_cookie = Cookie.new(
|
222
269
|
'name' => cookie_name.strip,
|
223
270
|
'value' => cookie_attributes.fetch('value'),
|
224
271
|
'path' => cookie_attributes.fetch('path', nil),
|
@@ -235,7 +282,7 @@ module Rack
|
|
235
282
|
def identify_cookie_attributes(cookie_filling)
|
236
283
|
cookie_bits = cookie_filling.split(';')
|
237
284
|
cookie_attributes = Hash.new
|
238
|
-
cookie_attributes.store('value', cookie_bits[0].strip)
|
285
|
+
cookie_attributes.store('value', Array(cookie_bits[0].strip))
|
239
286
|
cookie_bits.each do |bit|
|
240
287
|
if bit.include? '='
|
241
288
|
cookie_attribute, attribute_value = bit.split('=')
|
@@ -17,9 +17,13 @@ module Rack
|
|
17
17
|
|
18
18
|
flattened_params.map do |name, file|
|
19
19
|
if file.respond_to?(:original_filename)
|
20
|
-
|
21
|
-
|
22
|
-
|
20
|
+
if file.path
|
21
|
+
::File.open(file.path, 'rb') do |f|
|
22
|
+
f.set_encoding(Encoding::BINARY)
|
23
|
+
content_for_tempfile(f, file, name)
|
24
|
+
end
|
25
|
+
else
|
26
|
+
content_for_tempfile(file, file, name)
|
23
27
|
end
|
24
28
|
else
|
25
29
|
content_for_other(file, name)
|
@@ -69,12 +73,13 @@ module Rack
|
|
69
73
|
end
|
70
74
|
|
71
75
|
def content_for_tempfile(io, file, name)
|
76
|
+
length = ::File.stat(file.path).size if file.path
|
77
|
+
filename = "; filename=\"#{Utils.escape(file.original_filename)}\"" if file.original_filename
|
72
78
|
<<-EOF
|
73
79
|
--#{MULTIPART_BOUNDARY}\r
|
74
|
-
Content-Disposition: form-data; name="#{name}"
|
80
|
+
Content-Disposition: form-data; name="#{name}"#{filename}\r
|
75
81
|
Content-Type: #{file.content_type}\r
|
76
|
-
Content-Length: #{
|
77
|
-
\r
|
82
|
+
#{"Content-Length: #{length}\r\n" if length}\r
|
78
83
|
#{io.read}\r
|
79
84
|
EOF
|
80
85
|
end
|
@@ -1,8 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'rack/utils'
|
4
3
|
require 'strscan'
|
5
|
-
require 'rack/core_ext/regexp'
|
6
4
|
|
7
5
|
module Rack
|
8
6
|
module Multipart
|
@@ -10,12 +8,14 @@ module Rack
|
|
10
8
|
class MultipartTotalPartLimitError < StandardError; end
|
11
9
|
|
12
10
|
class Parser
|
13
|
-
using ::Rack::RegexpExtensions
|
11
|
+
(require_relative '../core_ext/regexp'; using ::Rack::RegexpExtensions) if RUBY_VERSION < '2.4'
|
14
12
|
|
15
13
|
BUFSIZE = 1_048_576
|
16
14
|
TEXT_PLAIN = "text/plain"
|
17
15
|
TEMPFILE_FACTORY = lambda { |filename, content_type|
|
18
|
-
|
16
|
+
extension = ::File.extname(filename.gsub("\0", '%00'))[0, 129]
|
17
|
+
|
18
|
+
Tempfile.new(["RackMultipart", extension])
|
19
19
|
}
|
20
20
|
|
21
21
|
BOUNDARY_REGEX = /\A([^\n]*(?:\n|\Z))/
|
@@ -102,12 +102,6 @@ module Rack
|
|
102
102
|
|
103
103
|
data = { filename: fn, type: content_type,
|
104
104
|
name: name, tempfile: body, head: head }
|
105
|
-
elsif !filename && content_type && body.is_a?(IO)
|
106
|
-
body.rewind
|
107
|
-
|
108
|
-
# Generic multipart cases, not coming from a form
|
109
|
-
data = { type: content_type,
|
110
|
-
name: name, tempfile: body, head: head }
|
111
105
|
end
|
112
106
|
|
113
107
|
yield data
|
@@ -126,7 +120,7 @@ module Rack
|
|
126
120
|
|
127
121
|
include Enumerable
|
128
122
|
|
129
|
-
def initialize
|
123
|
+
def initialize(tempfile)
|
130
124
|
@tempfile = tempfile
|
131
125
|
@mime_parts = []
|
132
126
|
@open_files = 0
|
@@ -136,7 +130,7 @@ module Rack
|
|
136
130
|
@mime_parts.each { |part| yield part }
|
137
131
|
end
|
138
132
|
|
139
|
-
def on_mime_head
|
133
|
+
def on_mime_head(mime_index, head, filename, content_type, name)
|
140
134
|
if filename
|
141
135
|
body = @tempfile.call(filename, content_type)
|
142
136
|
body.binmode if body.respond_to?(:binmode)
|
@@ -152,11 +146,11 @@ module Rack
|
|
152
146
|
check_part_limits
|
153
147
|
end
|
154
148
|
|
155
|
-
def on_mime_body
|
149
|
+
def on_mime_body(mime_index, content)
|
156
150
|
@mime_parts[mime_index].body << content
|
157
151
|
end
|
158
152
|
|
159
|
-
def on_mime_finish
|
153
|
+
def on_mime_finish(mime_index)
|
160
154
|
end
|
161
155
|
|
162
156
|
private
|
@@ -197,11 +191,12 @@ module Rack
|
|
197
191
|
|
198
192
|
@sbuf = StringScanner.new("".dup)
|
199
193
|
@body_regex = /(?:#{EOL})?#{Regexp.quote(@boundary)}(?:#{EOL}|--)/m
|
194
|
+
@end_boundary_size = boundary.bytesize + 6 # (-- at start, -- at finish, EOL at end)
|
200
195
|
@rx_max_size = EOL.size + @boundary.bytesize + [EOL.size, '--'.size].max
|
201
196
|
@head_regex = /(.*?#{EOL})#{EOL}/m
|
202
197
|
end
|
203
198
|
|
204
|
-
def on_read
|
199
|
+
def on_read(content)
|
205
200
|
handle_empty_content!(content)
|
206
201
|
@sbuf.concat content
|
207
202
|
run_parser
|
@@ -237,7 +232,12 @@ module Rack
|
|
237
232
|
end
|
238
233
|
|
239
234
|
def handle_fast_forward
|
240
|
-
|
235
|
+
tok = consume_boundary
|
236
|
+
|
237
|
+
if tok == :END_BOUNDARY && @sbuf.pos == @end_boundary_size && @sbuf.eos?
|
238
|
+
# stop parsing a buffer if a buffer is only an end boundary.
|
239
|
+
@state = :DONE
|
240
|
+
elsif tok
|
241
241
|
@state = :MIME_HEAD
|
242
242
|
else
|
243
243
|
raise EOFError, "bad content body" if @sbuf.rest_size >= @bufsize
|
@@ -359,7 +359,7 @@ module Rack
|
|
359
359
|
type_subtype = list.first
|
360
360
|
type_subtype.strip!
|
361
361
|
if TEXT_PLAIN == type_subtype
|
362
|
-
rest
|
362
|
+
rest = list.drop 1
|
363
363
|
rest.each do |param|
|
364
364
|
k, v = param.split('=', 2)
|
365
365
|
k.strip!
|
@@ -9,17 +9,23 @@ module Rack
|
|
9
9
|
# The content type of the "uploaded" file
|
10
10
|
attr_accessor :content_type
|
11
11
|
|
12
|
-
def initialize(
|
13
|
-
|
12
|
+
def initialize(filepath = nil, ct = "text/plain", bin = false,
|
13
|
+
path: filepath, content_type: ct, binary: bin, filename: nil, io: nil)
|
14
|
+
if io
|
15
|
+
@tempfile = io
|
16
|
+
@original_filename = filename
|
17
|
+
else
|
18
|
+
raise "#{path} file does not exist" unless ::File.exist?(path)
|
19
|
+
@original_filename = filename || ::File.basename(path)
|
20
|
+
@tempfile = Tempfile.new([@original_filename, ::File.extname(path)], encoding: Encoding::BINARY)
|
21
|
+
@tempfile.binmode if binary
|
22
|
+
FileUtils.copy_file(path, @tempfile.path)
|
23
|
+
end
|
14
24
|
@content_type = content_type
|
15
|
-
@original_filename = ::File.basename(path)
|
16
|
-
@tempfile = Tempfile.new([@original_filename, ::File.extname(path)], encoding: Encoding::BINARY)
|
17
|
-
@tempfile.binmode if binary
|
18
|
-
FileUtils.copy_file(path, @tempfile.path)
|
19
25
|
end
|
20
26
|
|
21
27
|
def path
|
22
|
-
@tempfile.path
|
28
|
+
@tempfile.path if @tempfile.respond_to?(:path)
|
23
29
|
end
|
24
30
|
alias_method :local_path, :path
|
25
31
|
|
data/lib/rack/multipart.rb
CHANGED
data/lib/rack/query_parser.rb
CHANGED
@@ -1,10 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative 'core_ext/regexp'
|
4
|
-
|
5
3
|
module Rack
|
6
4
|
class QueryParser
|
7
|
-
using ::Rack::RegexpExtensions
|
5
|
+
(require_relative 'core_ext/regexp'; using ::Rack::RegexpExtensions) if RUBY_VERSION < '2.4'
|
8
6
|
|
9
7
|
DEFAULT_SEP = /[&;] */n
|
10
8
|
COMMON_SEP = { ";" => /[;] */n, ";," => /[;,] */n, "&" => /[&] */n }
|
@@ -18,16 +16,47 @@ module Rack
|
|
18
16
|
# sequence.
|
19
17
|
class InvalidParameterError < ArgumentError; end
|
20
18
|
|
21
|
-
|
22
|
-
|
19
|
+
# QueryLimitError is for errors raised when the query provided exceeds one
|
20
|
+
# of the query parser limits.
|
21
|
+
class QueryLimitError < RangeError
|
22
|
+
end
|
23
|
+
|
24
|
+
# ParamsTooDeepError is the old name for the error that is raised when params
|
25
|
+
# are recursively nested over the specified limit. Make it the same as
|
26
|
+
# as QueryLimitError, so that code that rescues ParamsTooDeepError error
|
27
|
+
# to handle bad query strings also now handles other limits.
|
28
|
+
ParamsTooDeepError = QueryLimitError
|
29
|
+
|
30
|
+
def self.make_default(key_space_limit, param_depth_limit, **options)
|
31
|
+
new(Params, key_space_limit, param_depth_limit, **options)
|
23
32
|
end
|
24
33
|
|
25
34
|
attr_reader :key_space_limit, :param_depth_limit
|
26
35
|
|
27
|
-
|
36
|
+
env_int = lambda do |key, val|
|
37
|
+
if str_val = ENV[key]
|
38
|
+
begin
|
39
|
+
val = Integer(str_val, 10)
|
40
|
+
rescue ArgumentError
|
41
|
+
raise ArgumentError, "non-integer value provided for environment variable #{key}"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
val
|
46
|
+
end
|
47
|
+
|
48
|
+
BYTESIZE_LIMIT = env_int.call("RACK_QUERY_PARSER_BYTESIZE_LIMIT", 4194304)
|
49
|
+
private_constant :BYTESIZE_LIMIT
|
50
|
+
|
51
|
+
PARAMS_LIMIT = env_int.call("RACK_QUERY_PARSER_PARAMS_LIMIT", 4096)
|
52
|
+
private_constant :PARAMS_LIMIT
|
53
|
+
|
54
|
+
def initialize(params_class, key_space_limit, param_depth_limit, bytesize_limit: BYTESIZE_LIMIT, params_limit: PARAMS_LIMIT)
|
28
55
|
@params_class = params_class
|
29
56
|
@key_space_limit = key_space_limit
|
30
57
|
@param_depth_limit = param_depth_limit
|
58
|
+
@bytesize_limit = bytesize_limit
|
59
|
+
@params_limit = params_limit
|
31
60
|
end
|
32
61
|
|
33
62
|
# Stolen from Mongrel, with some small modifications:
|
@@ -40,7 +69,7 @@ module Rack
|
|
40
69
|
|
41
70
|
params = make_params
|
42
71
|
|
43
|
-
(qs
|
72
|
+
check_query_string(qs, d).split(d ? (COMMON_SEP[d] || /[#{d}] */n) : DEFAULT_SEP).each do |p|
|
44
73
|
next if p.empty?
|
45
74
|
k, v = p.split('=', 2).map!(&unescaper)
|
46
75
|
|
@@ -64,25 +93,26 @@ module Rack
|
|
64
93
|
# ParameterTypeError is raised. Users are encouraged to return a 400 in this
|
65
94
|
# case.
|
66
95
|
def parse_nested_query(qs, d = nil)
|
67
|
-
return {} if qs.nil? || qs.empty?
|
68
96
|
params = make_params
|
69
97
|
|
70
|
-
qs.
|
71
|
-
|
98
|
+
unless qs.nil? || qs.empty?
|
99
|
+
check_query_string(qs, d).split(d ? (COMMON_SEP[d] || /[#{d}] */n) : DEFAULT_SEP).each do |p|
|
100
|
+
k, v = p.split('=', 2).map! { |s| unescape(s) }
|
72
101
|
|
73
|
-
|
102
|
+
normalize_params(params, k, v, param_depth_limit)
|
103
|
+
end
|
74
104
|
end
|
75
105
|
|
76
106
|
return params.to_h
|
77
107
|
rescue ArgumentError => e
|
78
|
-
raise InvalidParameterError, e.message
|
108
|
+
raise InvalidParameterError, e.message, e.backtrace
|
79
109
|
end
|
80
110
|
|
81
111
|
# normalize_params recursively expands parameters into structural types. If
|
82
112
|
# the structural types represented by two different parameter names are in
|
83
113
|
# conflict, a ParameterTypeError is raised.
|
84
114
|
def normalize_params(params, name, v, depth)
|
85
|
-
raise
|
115
|
+
raise ParamsTooDeepError if depth <= 0
|
86
116
|
|
87
117
|
name =~ %r(\A[\[\]]*([^\[\]]+)\]*)
|
88
118
|
k = $1 || ''
|
@@ -152,8 +182,24 @@ module Rack
|
|
152
182
|
true
|
153
183
|
end
|
154
184
|
|
155
|
-
def
|
156
|
-
|
185
|
+
def check_query_string(qs, sep)
|
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
|
194
|
+
|
195
|
+
qs
|
196
|
+
else
|
197
|
+
''
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
def unescape(string)
|
202
|
+
Utils.unescape(string)
|
157
203
|
end
|
158
204
|
|
159
205
|
class Params
|
@@ -169,7 +215,7 @@ module Rack
|
|
169
215
|
|
170
216
|
def []=(key, value)
|
171
217
|
@size += key.size if key && !@params.key?(key)
|
172
|
-
raise
|
218
|
+
raise ParamsTooDeepError, 'exceeded available parameter key space' if @size > @limit
|
173
219
|
@params[key] = value
|
174
220
|
end
|
175
221
|
|
data/lib/rack/recursive.rb
CHANGED
@@ -19,7 +19,7 @@ module Rack
|
|
19
19
|
@env[PATH_INFO] = @url.path
|
20
20
|
@env[QUERY_STRING] = @url.query if @url.query
|
21
21
|
@env[HTTP_HOST] = @url.host if @url.host
|
22
|
-
@env[
|
22
|
+
@env[HTTP_PORT] = @url.port if @url.port
|
23
23
|
@env[RACK_URL_SCHEME] = @url.scheme if @url.scheme
|
24
24
|
|
25
25
|
super "forwarding to #{url}"
|
data/lib/rack/reloader.rb
CHANGED
@@ -6,8 +6,6 @@
|
|
6
6
|
|
7
7
|
require 'pathname'
|
8
8
|
|
9
|
-
require_relative 'core_ext/regexp'
|
10
|
-
|
11
9
|
module Rack
|
12
10
|
|
13
11
|
# High performant source reloader
|
@@ -24,7 +22,7 @@ module Rack
|
|
24
22
|
# It is performing a check/reload cycle at the start of every request, but
|
25
23
|
# also respects a cool down time, during which nothing will be done.
|
26
24
|
class Reloader
|
27
|
-
using ::Rack::RegexpExtensions
|
25
|
+
(require_relative 'core_ext/regexp'; using ::Rack::RegexpExtensions) if RUBY_VERSION < '2.4'
|
28
26
|
|
29
27
|
def initialize(app, cooldown = 10, backend = Stat)
|
30
28
|
@app = app
|