rack 2.0.4 → 2.1.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of rack might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/{HISTORY.md → CHANGELOG.md} +220 -155
- data/{COPYING → MIT-LICENSE} +4 -2
- data/README.rdoc +77 -119
- data/Rakefile +25 -18
- data/SPEC +3 -4
- data/bin/rackup +1 -0
- data/example/lobster.ru +2 -0
- data/example/protectedlobster.rb +3 -1
- data/example/protectedlobster.ru +2 -0
- data/lib/rack.rb +63 -60
- data/lib/rack/auth/abstract/handler.rb +3 -1
- data/lib/rack/auth/abstract/request.rb +2 -0
- data/lib/rack/auth/basic.rb +4 -1
- data/lib/rack/auth/digest/md5.rb +9 -7
- data/lib/rack/auth/digest/nonce.rb +6 -3
- data/lib/rack/auth/digest/params.rb +4 -2
- data/lib/rack/auth/digest/request.rb +2 -0
- data/lib/rack/body_proxy.rb +3 -6
- data/lib/rack/builder.rb +38 -15
- data/lib/rack/cascade.rb +6 -5
- data/lib/rack/chunked.rb +29 -6
- data/lib/rack/common_logger.rb +9 -8
- data/lib/rack/conditional_get.rb +3 -1
- data/lib/rack/config.rb +2 -0
- data/lib/rack/content_length.rb +3 -1
- data/lib/rack/content_type.rb +3 -1
- data/lib/rack/core_ext/regexp.rb +14 -0
- data/lib/rack/deflater.rb +28 -17
- data/lib/rack/directory.rb +17 -14
- data/lib/rack/etag.rb +3 -1
- data/lib/rack/events.rb +5 -3
- data/lib/rack/file.rb +5 -173
- data/lib/rack/files.rb +178 -0
- data/lib/rack/handler.rb +7 -2
- data/lib/rack/handler/cgi.rb +3 -1
- data/lib/rack/handler/fastcgi.rb +4 -2
- data/lib/rack/handler/lsws.rb +3 -1
- data/lib/rack/handler/scgi.rb +9 -6
- data/lib/rack/handler/thin.rb +3 -1
- data/lib/rack/handler/webrick.rb +4 -2
- data/lib/rack/head.rb +2 -0
- data/lib/rack/lint.rb +14 -11
- data/lib/rack/lobster.rb +7 -5
- data/lib/rack/lock.rb +2 -0
- data/lib/rack/logger.rb +2 -0
- data/lib/rack/media_type.rb +10 -5
- data/lib/rack/method_override.rb +9 -3
- data/lib/rack/mime.rb +9 -1
- data/lib/rack/mock.rb +74 -15
- data/lib/rack/multipart.rb +5 -3
- data/lib/rack/multipart/generator.rb +6 -7
- data/lib/rack/multipart/parser.rb +54 -51
- data/lib/rack/multipart/uploaded_file.rb +2 -0
- data/lib/rack/null_logger.rb +2 -0
- data/lib/rack/query_parser.rb +51 -25
- data/lib/rack/recursive.rb +7 -5
- data/lib/rack/reloader.rb +10 -4
- data/lib/rack/request.rb +89 -23
- data/lib/rack/response.rb +71 -31
- data/lib/rack/rewindable_input.rb +4 -2
- data/lib/rack/runtime.rb +4 -2
- data/lib/rack/sendfile.rb +15 -8
- data/lib/rack/server.rb +88 -16
- data/lib/rack/session/abstract/id.rb +104 -21
- data/lib/rack/session/cookie.rb +21 -11
- data/lib/rack/session/memcache.rb +4 -87
- data/lib/rack/session/pool.rb +17 -8
- data/lib/rack/show_exceptions.rb +16 -10
- data/lib/rack/show_status.rb +4 -2
- data/lib/rack/static.rb +15 -10
- data/lib/rack/tempfile_reaper.rb +2 -0
- data/lib/rack/urlmap.rb +11 -2
- data/lib/rack/utils.rb +55 -70
- data/rack.gemspec +19 -9
- metadata +32 -173
- data/test/builder/an_underscore_app.rb +0 -5
- data/test/builder/anything.rb +0 -5
- data/test/builder/comment.ru +0 -4
- data/test/builder/end.ru +0 -5
- data/test/builder/line.ru +0 -1
- data/test/builder/options.ru +0 -2
- data/test/cgi/assets/folder/test.js +0 -1
- data/test/cgi/assets/fonts/font.eot +0 -1
- data/test/cgi/assets/images/image.png +0 -1
- data/test/cgi/assets/index.html +0 -1
- data/test/cgi/assets/javascripts/app.js +0 -1
- data/test/cgi/assets/stylesheets/app.css +0 -1
- data/test/cgi/lighttpd.conf +0 -26
- data/test/cgi/rackup_stub.rb +0 -6
- data/test/cgi/sample_rackup.ru +0 -5
- data/test/cgi/test +0 -9
- data/test/cgi/test+directory/test+file +0 -1
- data/test/cgi/test.fcgi +0 -9
- data/test/cgi/test.gz +0 -0
- data/test/cgi/test.ru +0 -5
- data/test/gemloader.rb +0 -10
- data/test/helper.rb +0 -34
- data/test/multipart/bad_robots +0 -259
- data/test/multipart/binary +0 -0
- data/test/multipart/content_type_and_no_filename +0 -6
- data/test/multipart/empty +0 -10
- data/test/multipart/fail_16384_nofile +0 -814
- data/test/multipart/file1.txt +0 -1
- data/test/multipart/filename_and_modification_param +0 -7
- data/test/multipart/filename_and_no_name +0 -6
- data/test/multipart/filename_with_encoded_words +0 -7
- data/test/multipart/filename_with_escaped_quotes +0 -6
- data/test/multipart/filename_with_escaped_quotes_and_modification_param +0 -7
- data/test/multipart/filename_with_null_byte +0 -7
- data/test/multipart/filename_with_percent_escaped_quotes +0 -6
- data/test/multipart/filename_with_single_quote +0 -7
- data/test/multipart/filename_with_unescaped_percentages +0 -6
- data/test/multipart/filename_with_unescaped_percentages2 +0 -6
- data/test/multipart/filename_with_unescaped_percentages3 +0 -6
- data/test/multipart/filename_with_unescaped_quotes +0 -6
- data/test/multipart/ie +0 -6
- data/test/multipart/invalid_character +0 -6
- data/test/multipart/mixed_files +0 -21
- data/test/multipart/nested +0 -10
- data/test/multipart/none +0 -9
- data/test/multipart/quoted +0 -15
- data/test/multipart/rack-logo.png +0 -0
- data/test/multipart/semicolon +0 -6
- data/test/multipart/text +0 -15
- data/test/multipart/three_files_three_fields +0 -31
- data/test/multipart/unity3d_wwwform +0 -11
- data/test/multipart/webkit +0 -32
- data/test/rackup/config.ru +0 -31
- data/test/registering_handler/rack/handler/registering_myself.rb +0 -8
- data/test/spec_auth_basic.rb +0 -89
- data/test/spec_auth_digest.rb +0 -260
- data/test/spec_body_proxy.rb +0 -85
- data/test/spec_builder.rb +0 -233
- data/test/spec_cascade.rb +0 -63
- data/test/spec_cgi.rb +0 -84
- data/test/spec_chunked.rb +0 -103
- data/test/spec_common_logger.rb +0 -95
- data/test/spec_conditional_get.rb +0 -103
- data/test/spec_config.rb +0 -23
- data/test/spec_content_length.rb +0 -86
- data/test/spec_content_type.rb +0 -46
- data/test/spec_deflater.rb +0 -375
- data/test/spec_directory.rb +0 -148
- data/test/spec_etag.rb +0 -108
- data/test/spec_events.rb +0 -133
- data/test/spec_fastcgi.rb +0 -85
- data/test/spec_file.rb +0 -264
- data/test/spec_handler.rb +0 -57
- data/test/spec_head.rb +0 -46
- data/test/spec_lint.rb +0 -515
- data/test/spec_lobster.rb +0 -59
- data/test/spec_lock.rb +0 -204
- data/test/spec_logger.rb +0 -24
- data/test/spec_media_type.rb +0 -42
- data/test/spec_method_override.rb +0 -96
- data/test/spec_mime.rb +0 -51
- data/test/spec_mock.rb +0 -359
- data/test/spec_multipart.rb +0 -722
- data/test/spec_null_logger.rb +0 -21
- data/test/spec_recursive.rb +0 -75
- data/test/spec_request.rb +0 -1393
- data/test/spec_response.rb +0 -510
- data/test/spec_rewindable_input.rb +0 -128
- data/test/spec_runtime.rb +0 -50
- data/test/spec_sendfile.rb +0 -125
- data/test/spec_server.rb +0 -193
- data/test/spec_session_abstract_id.rb +0 -31
- data/test/spec_session_abstract_session_hash.rb +0 -45
- data/test/spec_session_cookie.rb +0 -442
- data/test/spec_session_memcache.rb +0 -320
- data/test/spec_session_pool.rb +0 -210
- data/test/spec_show_exceptions.rb +0 -80
- data/test/spec_show_status.rb +0 -104
- data/test/spec_static.rb +0 -184
- data/test/spec_tempfile_reaper.rb +0 -64
- data/test/spec_thin.rb +0 -96
- data/test/spec_urlmap.rb +0 -237
- data/test/spec_utils.rb +0 -742
- data/test/spec_version.rb +0 -11
- data/test/spec_webrick.rb +0 -206
- data/test/static/another/index.html +0 -1
- data/test/static/foo.html +0 -1
- data/test/static/index.html +0 -1
- data/test/testrequest.rb +0 -78
- data/test/unregistered_handler/rack/handler/unregistered.rb +0 -7
- data/test/unregistered_handler/rack/handler/unregistered_long_one.rb +0 -7
data/lib/rack/multipart.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'rack/multipart/parser'
|
2
4
|
|
3
5
|
module Rack
|
@@ -14,10 +16,10 @@ module Rack
|
|
14
16
|
TOKEN = /[^\s()<>,;:\\"\/\[\]?=]+/
|
15
17
|
CONDISP = /Content-Disposition:\s*#{TOKEN}\s*/i
|
16
18
|
VALUE = /"(?:\\"|[^"])*"|#{TOKEN}/
|
17
|
-
BROKEN_QUOTED = /^#{CONDISP}.*;\
|
18
|
-
BROKEN_UNQUOTED = /^#{CONDISP}.*;\
|
19
|
+
BROKEN_QUOTED = /^#{CONDISP}.*;\s*filename="(.*?)"(?:\s*$|\s*;\s*#{TOKEN}=)/i
|
20
|
+
BROKEN_UNQUOTED = /^#{CONDISP}.*;\s*filename=(#{TOKEN})/i
|
19
21
|
MULTIPART_CONTENT_TYPE = /Content-Type: (.*)#{EOL}/ni
|
20
|
-
MULTIPART_CONTENT_DISPOSITION = /Content-Disposition
|
22
|
+
MULTIPART_CONTENT_DISPOSITION = /Content-Disposition:.*;\s*name=(#{VALUE})/ni
|
21
23
|
MULTIPART_CONTENT_ID = /Content-ID:\s*([^#{EOL}]*)/ni
|
22
24
|
# Updated definitions from RFC 2231
|
23
25
|
ATTRIBUTE_CHAR = %r{[^ \t\v\n\r)(><@,;:\\"/\[\]?='*%]}
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Rack
|
2
4
|
module Multipart
|
3
5
|
class Generator
|
@@ -27,21 +29,18 @@ module Rack
|
|
27
29
|
|
28
30
|
private
|
29
31
|
def multipart?
|
30
|
-
multipart = false
|
31
|
-
|
32
32
|
query = lambda { |value|
|
33
33
|
case value
|
34
34
|
when Array
|
35
|
-
value.
|
35
|
+
value.any?(&query)
|
36
36
|
when Hash
|
37
|
-
value.values.
|
37
|
+
value.values.any?(&query)
|
38
38
|
when Rack::Multipart::UploadedFile
|
39
|
-
|
39
|
+
true
|
40
40
|
end
|
41
41
|
}
|
42
|
-
@params.values.each(&query)
|
43
42
|
|
44
|
-
|
43
|
+
@params.values.any?(&query)
|
45
44
|
end
|
46
45
|
|
47
46
|
def flattened_params
|
@@ -1,16 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'rack/utils'
|
4
|
+
require 'strscan'
|
5
|
+
require 'rack/core_ext/regexp'
|
2
6
|
|
3
7
|
module Rack
|
4
8
|
module Multipart
|
5
9
|
class MultipartPartLimitError < Errno::EMFILE; end
|
6
10
|
|
7
11
|
class Parser
|
12
|
+
using ::Rack::RegexpExtensions
|
13
|
+
|
8
14
|
BUFSIZE = 1_048_576
|
9
15
|
TEXT_PLAIN = "text/plain"
|
10
16
|
TEMPFILE_FACTORY = lambda { |filename, content_type|
|
11
|
-
Tempfile.new(["RackMultipart", ::File.extname(filename.gsub("\0"
|
17
|
+
Tempfile.new(["RackMultipart", ::File.extname(filename.gsub("\0", '%00'))])
|
12
18
|
}
|
13
19
|
|
20
|
+
BOUNDARY_REGEX = /\A([^\n]*(?:\n|\Z))/
|
21
|
+
|
14
22
|
class BoundedIO # :nodoc:
|
15
23
|
def initialize(io, content_length)
|
16
24
|
@io = io
|
@@ -18,15 +26,15 @@ module Rack
|
|
18
26
|
@cursor = 0
|
19
27
|
end
|
20
28
|
|
21
|
-
def read(size)
|
29
|
+
def read(size, outbuf = nil)
|
22
30
|
return if @cursor >= @content_length
|
23
31
|
|
24
32
|
left = @content_length - @cursor
|
25
33
|
|
26
34
|
str = if left < size
|
27
|
-
@io.read left
|
35
|
+
@io.read left, outbuf
|
28
36
|
else
|
29
|
-
@io.read size
|
37
|
+
@io.read size, outbuf
|
30
38
|
end
|
31
39
|
|
32
40
|
if str
|
@@ -39,8 +47,6 @@ module Rack
|
|
39
47
|
str
|
40
48
|
end
|
41
49
|
|
42
|
-
def eof?; @content_length == @cursor; end
|
43
|
-
|
44
50
|
def rewind
|
45
51
|
@io.rewind
|
46
52
|
end
|
@@ -63,13 +69,14 @@ module Rack
|
|
63
69
|
return EMPTY unless boundary
|
64
70
|
|
65
71
|
io = BoundedIO.new(io, content_length) if content_length
|
72
|
+
outbuf = String.new
|
66
73
|
|
67
74
|
parser = new(boundary, tmpfile, bufsize, qp)
|
68
|
-
parser.on_read io.read(bufsize
|
75
|
+
parser.on_read io.read(bufsize, outbuf)
|
69
76
|
|
70
77
|
loop do
|
71
78
|
break if parser.state == :DONE
|
72
|
-
parser.on_read io.read(bufsize
|
79
|
+
parser.on_read io.read(bufsize, outbuf)
|
73
80
|
end
|
74
81
|
|
75
82
|
io.rewind
|
@@ -92,14 +99,14 @@ module Rack
|
|
92
99
|
# those which give the lone filename.
|
93
100
|
fn = filename.split(/[\/\\]/).last
|
94
101
|
|
95
|
-
data = {:
|
96
|
-
:
|
102
|
+
data = { filename: fn, type: content_type,
|
103
|
+
name: name, tempfile: body, head: head }
|
97
104
|
elsif !filename && content_type && body.is_a?(IO)
|
98
105
|
body.rewind
|
99
106
|
|
100
107
|
# Generic multipart cases, not coming from a form
|
101
|
-
data = {:
|
102
|
-
:
|
108
|
+
data = { type: content_type,
|
109
|
+
name: name, tempfile: body, head: head }
|
103
110
|
end
|
104
111
|
|
105
112
|
yield data
|
@@ -140,6 +147,7 @@ module Rack
|
|
140
147
|
end
|
141
148
|
|
142
149
|
@mime_parts[mime_index] = klass.new(body, head, filename, content_type, name)
|
150
|
+
|
143
151
|
check_open_files
|
144
152
|
end
|
145
153
|
|
@@ -165,25 +173,26 @@ module Rack
|
|
165
173
|
attr_reader :state
|
166
174
|
|
167
175
|
def initialize(boundary, tempfile, bufsize, query_parser)
|
168
|
-
@buf = String.new
|
169
|
-
|
170
176
|
@query_parser = query_parser
|
171
177
|
@params = query_parser.make_params
|
172
178
|
@boundary = "--#{boundary}"
|
173
179
|
@bufsize = bufsize
|
174
180
|
|
175
|
-
@rx = /(?:#{EOL})?#{Regexp.quote(@boundary)}(#{EOL}|--)/n
|
176
|
-
@rx_max_size = EOL.size + @boundary.bytesize + [EOL.size, '--'.size].max
|
177
181
|
@full_boundary = @boundary
|
178
182
|
@end_boundary = @boundary + '--'
|
179
183
|
@state = :FAST_FORWARD
|
180
184
|
@mime_index = 0
|
181
185
|
@collector = Collector.new tempfile
|
186
|
+
|
187
|
+
@sbuf = StringScanner.new("".dup)
|
188
|
+
@body_regex = /(.*?)(#{EOL})?#{Regexp.quote(@boundary)}(#{EOL}|--)/m
|
189
|
+
@rx_max_size = EOL.size + @boundary.bytesize + [EOL.size, '--'.size].max
|
190
|
+
@head_regex = /(.*?#{EOL})#{EOL}/m
|
182
191
|
end
|
183
192
|
|
184
|
-
def on_read content
|
185
|
-
handle_empty_content!(content
|
186
|
-
@
|
193
|
+
def on_read content
|
194
|
+
handle_empty_content!(content)
|
195
|
+
@sbuf.concat content
|
187
196
|
run_parser
|
188
197
|
end
|
189
198
|
|
@@ -194,7 +203,6 @@ module Rack
|
|
194
203
|
@query_parser.normalize_params(@params, part.name, data, @query_parser.param_depth_limit)
|
195
204
|
end
|
196
205
|
end
|
197
|
-
|
198
206
|
MultipartInfo.new @params.to_params_hash, @collector.find_all(&:file?).map(&:body)
|
199
207
|
end
|
200
208
|
|
@@ -221,7 +229,7 @@ module Rack
|
|
221
229
|
if consume_boundary
|
222
230
|
@state = :MIME_HEAD
|
223
231
|
else
|
224
|
-
raise EOFError, "bad content body" if @
|
232
|
+
raise EOFError, "bad content body" if @sbuf.rest_size >= @bufsize
|
225
233
|
:want_read
|
226
234
|
end
|
227
235
|
end
|
@@ -229,19 +237,16 @@ module Rack
|
|
229
237
|
def handle_consume_token
|
230
238
|
tok = consume_boundary
|
231
239
|
# break if we're at the end of a buffer, but not if it is the end of a field
|
232
|
-
if tok == :END_BOUNDARY || (@
|
233
|
-
|
240
|
+
@state = if tok == :END_BOUNDARY || (@sbuf.eos? && tok != :BOUNDARY)
|
241
|
+
:DONE
|
234
242
|
else
|
235
|
-
|
243
|
+
:MIME_HEAD
|
236
244
|
end
|
237
245
|
end
|
238
246
|
|
239
247
|
def handle_mime_head
|
240
|
-
if @
|
241
|
-
|
242
|
-
head = @buf.slice!(0, i+2) # First \r\n
|
243
|
-
@buf.slice!(0, 2) # Second \r\n
|
244
|
-
|
248
|
+
if @sbuf.scan_until(@head_regex)
|
249
|
+
head = @sbuf[1]
|
245
250
|
content_type = head[MULTIPART_CONTENT_TYPE, 1]
|
246
251
|
if name = head[MULTIPART_CONTENT_DISPOSITION, 1]
|
247
252
|
name = Rack::Auth::Digest::Params::dequote(name)
|
@@ -252,7 +257,7 @@ module Rack
|
|
252
257
|
filename = get_filename(head)
|
253
258
|
|
254
259
|
if name.nil? || name.empty?
|
255
|
-
name = filename || "#{content_type || TEXT_PLAIN}[]"
|
260
|
+
name = filename || "#{content_type || TEXT_PLAIN}[]".dup
|
256
261
|
end
|
257
262
|
|
258
263
|
@collector.on_mime_head @mime_index, head, filename, content_type, name
|
@@ -263,16 +268,19 @@ module Rack
|
|
263
268
|
end
|
264
269
|
|
265
270
|
def handle_mime_body
|
266
|
-
if
|
267
|
-
|
268
|
-
@collector.on_mime_body @mime_index,
|
269
|
-
@
|
271
|
+
if @sbuf.check_until(@body_regex) # check but do not advance the pointer yet
|
272
|
+
body = @sbuf[1]
|
273
|
+
@collector.on_mime_body @mime_index, body
|
274
|
+
@sbuf.pos += body.length + 2 # skip \r\n after the content
|
270
275
|
@state = :CONSUME_TOKEN
|
271
276
|
@mime_index += 1
|
272
277
|
else
|
273
|
-
# Save
|
274
|
-
if @rx_max_size < @
|
275
|
-
|
278
|
+
# Save what we have so far
|
279
|
+
if @rx_max_size < @sbuf.rest_size
|
280
|
+
delta = @sbuf.rest_size - @rx_max_size
|
281
|
+
@collector.on_mime_body @mime_index, @sbuf.peek(delta)
|
282
|
+
@sbuf.pos += delta
|
283
|
+
@sbuf.string = @sbuf.rest
|
276
284
|
end
|
277
285
|
:want_read
|
278
286
|
end
|
@@ -280,16 +288,13 @@ module Rack
|
|
280
288
|
|
281
289
|
def full_boundary; @full_boundary; end
|
282
290
|
|
283
|
-
def rx; @rx; end
|
284
|
-
|
285
291
|
def consume_boundary
|
286
|
-
while @
|
287
|
-
read_buffer = $1
|
292
|
+
while read_buffer = @sbuf.scan_until(BOUNDARY_REGEX)
|
288
293
|
case read_buffer.strip
|
289
294
|
when full_boundary then return :BOUNDARY
|
290
295
|
when @end_boundary then return :END_BOUNDARY
|
291
296
|
end
|
292
|
-
return if @
|
297
|
+
return if @sbuf.eos?
|
293
298
|
end
|
294
299
|
end
|
295
300
|
|
@@ -310,8 +315,8 @@ module Rack
|
|
310
315
|
|
311
316
|
return unless filename
|
312
317
|
|
313
|
-
if filename.scan(/%.?.?/).all? { |s|
|
314
|
-
filename = Utils.
|
318
|
+
if filename.scan(/%.?.?/).all? { |s| /%[0-9a-fA-F]{2}/.match?(s) }
|
319
|
+
filename = Utils.unescape_path(filename)
|
315
320
|
end
|
316
321
|
|
317
322
|
filename.scrub!
|
@@ -327,7 +332,7 @@ module Rack
|
|
327
332
|
filename
|
328
333
|
end
|
329
334
|
|
330
|
-
CHARSET
|
335
|
+
CHARSET = "charset"
|
331
336
|
|
332
337
|
def tag_multipart_encoding(filename, content_type, name, body)
|
333
338
|
name = name.to_s
|
@@ -344,10 +349,10 @@ module Rack
|
|
344
349
|
if TEXT_PLAIN == type_subtype
|
345
350
|
rest = list.drop 1
|
346
351
|
rest.each do |param|
|
347
|
-
k,v = param.split('=', 2)
|
352
|
+
k, v = param.split('=', 2)
|
348
353
|
k.strip!
|
349
354
|
v.strip!
|
350
|
-
v = v[1..-2] if v
|
355
|
+
v = v[1..-2] if v.start_with?('"') && v.end_with?('"')
|
351
356
|
encoding = Encoding.find v if k == CHARSET
|
352
357
|
end
|
353
358
|
end
|
@@ -357,11 +362,9 @@ module Rack
|
|
357
362
|
body.force_encoding(encoding)
|
358
363
|
end
|
359
364
|
|
360
|
-
|
361
|
-
def handle_empty_content!(content, eof)
|
365
|
+
def handle_empty_content!(content)
|
362
366
|
if content.nil? || content.empty?
|
363
|
-
raise EOFError
|
364
|
-
return true
|
367
|
+
raise EOFError
|
365
368
|
end
|
366
369
|
end
|
367
370
|
end
|
data/lib/rack/null_logger.rb
CHANGED
data/lib/rack/query_parser.rb
CHANGED
@@ -1,5 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'core_ext/regexp'
|
4
|
+
|
1
5
|
module Rack
|
2
6
|
class QueryParser
|
7
|
+
using ::Rack::RegexpExtensions
|
8
|
+
|
3
9
|
DEFAULT_SEP = /[&;] */n
|
4
10
|
COMMON_SEP = { ";" => /[;] */n, ";," => /[;,] */n, "&" => /[&] */n }
|
5
11
|
|
@@ -36,7 +42,7 @@ module Rack
|
|
36
42
|
|
37
43
|
(qs || '').split(d ? (COMMON_SEP[d] || /[#{d}] */n) : DEFAULT_SEP).each do |p|
|
38
44
|
next if p.empty?
|
39
|
-
k, v = p.split('='
|
45
|
+
k, v = p.split('=', 2).map!(&unescaper)
|
40
46
|
|
41
47
|
if cur = params[k]
|
42
48
|
if cur.class == Array
|
@@ -49,7 +55,7 @@ module Rack
|
|
49
55
|
end
|
50
56
|
end
|
51
57
|
|
52
|
-
return params.
|
58
|
+
return params.to_h
|
53
59
|
end
|
54
60
|
|
55
61
|
# parse_nested_query expands a query string into structural types. Supported
|
@@ -61,13 +67,13 @@ module Rack
|
|
61
67
|
return {} if qs.nil? || qs.empty?
|
62
68
|
params = make_params
|
63
69
|
|
64
|
-
|
65
|
-
k, v = p.split('='
|
70
|
+
qs.split(d ? (COMMON_SEP[d] || /[#{d}] */n) : DEFAULT_SEP).each do |p|
|
71
|
+
k, v = p.split('=', 2).map! { |s| unescape(s) }
|
66
72
|
|
67
73
|
normalize_params(params, k, v, param_depth_limit)
|
68
74
|
end
|
69
75
|
|
70
|
-
return params.
|
76
|
+
return params.to_h
|
71
77
|
rescue ArgumentError => e
|
72
78
|
raise InvalidParameterError, e.message
|
73
79
|
end
|
@@ -79,22 +85,22 @@ module Rack
|
|
79
85
|
raise RangeError if depth <= 0
|
80
86
|
|
81
87
|
name =~ %r(\A[\[\]]*([^\[\]]+)\]*)
|
82
|
-
k = $1 || ''
|
83
|
-
after = $' || ''
|
88
|
+
k = $1 || ''
|
89
|
+
after = $' || ''
|
84
90
|
|
85
91
|
if k.empty?
|
86
|
-
if !v.nil? && name == "[]"
|
92
|
+
if !v.nil? && name == "[]"
|
87
93
|
return Array(v)
|
88
94
|
else
|
89
95
|
return
|
90
96
|
end
|
91
97
|
end
|
92
98
|
|
93
|
-
if after == ''
|
99
|
+
if after == ''
|
94
100
|
params[k] = v
|
95
|
-
elsif after == "["
|
101
|
+
elsif after == "["
|
96
102
|
params[name] = v
|
97
|
-
elsif after == "[]"
|
103
|
+
elsif after == "[]"
|
98
104
|
params[k] ||= []
|
99
105
|
raise ParameterTypeError, "expected Array (got #{params[k].class.name}) for param `#{k}'" unless params[k].is_a?(Array)
|
100
106
|
params[k] << v
|
@@ -135,7 +141,7 @@ module Rack
|
|
135
141
|
end
|
136
142
|
|
137
143
|
def params_hash_has_key?(hash, key)
|
138
|
-
return false if
|
144
|
+
return false if /\[\]/.match?(key)
|
139
145
|
|
140
146
|
key.split(/[\[\]]+/).inject(hash) do |h, part|
|
141
147
|
next h if part == ''
|
@@ -171,22 +177,42 @@ module Rack
|
|
171
177
|
@params.key?(key)
|
172
178
|
end
|
173
179
|
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
180
|
+
# Recursively unwraps nested `Params` objects and constructs an object
|
181
|
+
# of the same shape, but using the objects' internal representations
|
182
|
+
# (Ruby hashes) in place of the objects. The result is a hash consisting
|
183
|
+
# purely of Ruby primitives.
|
184
|
+
#
|
185
|
+
# Mutation warning!
|
186
|
+
#
|
187
|
+
# 1. This method mutates the internal representation of the `Params`
|
188
|
+
# objects in order to save object allocations.
|
189
|
+
#
|
190
|
+
# 2. The value you get back is a reference to the internal hash
|
191
|
+
# representation, not a copy.
|
192
|
+
#
|
193
|
+
# 3. Because the `Params` object's internal representation is mutable
|
194
|
+
# through the `#[]=` method, it is not thread safe. The result of
|
195
|
+
# getting the hash representation while another thread is adding a
|
196
|
+
# key to it is non-deterministic.
|
197
|
+
#
|
198
|
+
def to_h
|
199
|
+
@params.each do |key, value|
|
200
|
+
case value
|
201
|
+
when self
|
202
|
+
# Handle circular references gracefully.
|
203
|
+
@params[key] = @params
|
204
|
+
when Params
|
205
|
+
@params[key] = value.to_h
|
206
|
+
when Array
|
207
|
+
value.map! { |v| v.kind_of?(Params) ? v.to_h : v }
|
208
|
+
else
|
209
|
+
# Ignore anything that is not a `Params` object or
|
210
|
+
# a collection that can contain one.
|
186
211
|
end
|
187
212
|
end
|
188
|
-
|
213
|
+
@params
|
189
214
|
end
|
215
|
+
alias_method :to_params_hash, :to_h
|
190
216
|
end
|
191
217
|
end
|
192
218
|
end
|