rack 2.0.6 → 2.2.7
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/CHANGELOG.md +735 -0
- data/CONTRIBUTING.md +136 -0
- data/{COPYING → MIT-LICENSE} +4 -2
- data/README.rdoc +163 -145
- data/Rakefile +37 -23
- data/{SPEC → SPEC.rdoc} +29 -5
- 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/auth/abstract/handler.rb +3 -1
- data/lib/rack/auth/abstract/request.rb +1 -1
- data/lib/rack/auth/basic.rb +7 -4
- data/lib/rack/auth/digest/md5.rb +13 -11
- 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 +5 -3
- data/lib/rack/body_proxy.rb +15 -14
- data/lib/rack/builder.rb +116 -23
- data/lib/rack/cascade.rb +28 -12
- data/lib/rack/chunked.rb +68 -20
- data/lib/rack/common_logger.rb +36 -25
- data/lib/rack/conditional_get.rb +20 -16
- data/lib/rack/config.rb +2 -0
- data/lib/rack/content_length.rb +8 -7
- data/lib/rack/content_type.rb +5 -4
- data/lib/rack/core_ext/regexp.rb +14 -0
- data/lib/rack/deflater.rb +59 -34
- data/lib/rack/directory.rb +84 -64
- data/lib/rack/etag.rb +7 -4
- data/lib/rack/events.rb +19 -20
- data/lib/rack/file.rb +4 -173
- data/lib/rack/files.rb +218 -0
- data/lib/rack/handler/cgi.rb +2 -3
- data/lib/rack/handler/fastcgi.rb +4 -4
- data/lib/rack/handler/lsws.rb +3 -3
- data/lib/rack/handler/scgi.rb +9 -8
- data/lib/rack/handler/thin.rb +3 -3
- data/lib/rack/handler/webrick.rb +15 -6
- data/lib/rack/handler.rb +7 -2
- data/lib/rack/head.rb +1 -1
- data/lib/rack/lint.rb +72 -26
- data/lib/rack/lobster.rb +10 -10
- data/lib/rack/lock.rb +2 -1
- data/lib/rack/logger.rb +2 -0
- data/lib/rack/media_type.rb +10 -5
- data/lib/rack/method_override.rb +5 -3
- data/lib/rack/mime.rb +9 -1
- data/lib/rack/mock.rb +97 -20
- data/lib/rack/multipart/generator.rb +17 -13
- data/lib/rack/multipart/parser.rb +74 -67
- data/lib/rack/multipart/uploaded_file.rb +15 -7
- data/lib/rack/multipart.rb +6 -5
- data/lib/rack/null_logger.rb +2 -0
- data/lib/rack/query_parser.rb +59 -30
- data/lib/rack/recursive.rb +7 -5
- data/lib/rack/reloader.rb +8 -4
- data/lib/rack/request.rb +222 -63
- data/lib/rack/response.rb +127 -44
- data/lib/rack/rewindable_input.rb +4 -3
- data/lib/rack/runtime.rb +6 -4
- data/lib/rack/sendfile.rb +13 -9
- data/lib/rack/server.rb +95 -24
- data/lib/rack/session/abstract/id.rb +100 -22
- data/lib/rack/session/cookie.rb +22 -14
- data/lib/rack/session/memcache.rb +4 -87
- data/lib/rack/session/pool.rb +18 -9
- data/lib/rack/show_exceptions.rb +21 -17
- data/lib/rack/show_status.rb +9 -9
- data/lib/rack/static.rb +23 -11
- data/lib/rack/tempfile_reaper.rb +1 -1
- data/lib/rack/urlmap.rb +13 -7
- data/lib/rack/utils.rb +127 -119
- data/lib/rack/version.rb +29 -0
- data/lib/rack.rb +67 -73
- data/rack.gemspec +40 -28
- metadata +39 -181
- data/HISTORY.md +0 -505
- 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 -110
- 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 -1398
- 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 -93
- 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
@@ -1,16 +1,23 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'strscan'
|
2
4
|
|
3
5
|
module Rack
|
4
6
|
module Multipart
|
5
7
|
class MultipartPartLimitError < Errno::EMFILE; end
|
8
|
+
class MultipartTotalPartLimitError < StandardError; end
|
6
9
|
|
7
10
|
class Parser
|
8
|
-
|
11
|
+
(require_relative '../core_ext/regexp'; using ::Rack::RegexpExtensions) if RUBY_VERSION < '2.4'
|
12
|
+
|
13
|
+
BUFSIZE = 1_048_576
|
9
14
|
TEXT_PLAIN = "text/plain"
|
10
15
|
TEMPFILE_FACTORY = lambda { |filename, content_type|
|
11
|
-
Tempfile.new(["RackMultipart", ::File.extname(filename.gsub("\0"
|
16
|
+
Tempfile.new(["RackMultipart", ::File.extname(filename.gsub("\0", '%00'))])
|
12
17
|
}
|
13
18
|
|
19
|
+
BOUNDARY_REGEX = /\A([^\n]*(?:\n|\Z))/
|
20
|
+
|
14
21
|
class BoundedIO # :nodoc:
|
15
22
|
def initialize(io, content_length)
|
16
23
|
@io = io
|
@@ -18,15 +25,15 @@ module Rack
|
|
18
25
|
@cursor = 0
|
19
26
|
end
|
20
27
|
|
21
|
-
def read(size)
|
28
|
+
def read(size, outbuf = nil)
|
22
29
|
return if @cursor >= @content_length
|
23
30
|
|
24
31
|
left = @content_length - @cursor
|
25
32
|
|
26
33
|
str = if left < size
|
27
|
-
@io.read left
|
34
|
+
@io.read left, outbuf
|
28
35
|
else
|
29
|
-
@io.read size
|
36
|
+
@io.read size, outbuf
|
30
37
|
end
|
31
38
|
|
32
39
|
if str
|
@@ -39,8 +46,6 @@ module Rack
|
|
39
46
|
str
|
40
47
|
end
|
41
48
|
|
42
|
-
def eof?; @content_length == @cursor; end
|
43
|
-
|
44
49
|
def rewind
|
45
50
|
@io.rewind
|
46
51
|
end
|
@@ -63,13 +68,14 @@ module Rack
|
|
63
68
|
return EMPTY unless boundary
|
64
69
|
|
65
70
|
io = BoundedIO.new(io, content_length) if content_length
|
71
|
+
outbuf = String.new
|
66
72
|
|
67
73
|
parser = new(boundary, tmpfile, bufsize, qp)
|
68
|
-
parser.on_read io.read(bufsize
|
74
|
+
parser.on_read io.read(bufsize, outbuf)
|
69
75
|
|
70
76
|
loop do
|
71
77
|
break if parser.state == :DONE
|
72
|
-
parser.on_read io.read(bufsize
|
78
|
+
parser.on_read io.read(bufsize, outbuf)
|
73
79
|
end
|
74
80
|
|
75
81
|
io.rewind
|
@@ -92,14 +98,8 @@ module Rack
|
|
92
98
|
# those which give the lone filename.
|
93
99
|
fn = filename.split(/[\/\\]/).last
|
94
100
|
|
95
|
-
data = {:
|
96
|
-
:
|
97
|
-
elsif !filename && content_type && body.is_a?(IO)
|
98
|
-
body.rewind
|
99
|
-
|
100
|
-
# Generic multipart cases, not coming from a form
|
101
|
-
data = {:type => content_type,
|
102
|
-
:name => name, :tempfile => body, :head => head}
|
101
|
+
data = { filename: fn, type: content_type,
|
102
|
+
name: name, tempfile: body, head: head }
|
103
103
|
end
|
104
104
|
|
105
105
|
yield data
|
@@ -118,7 +118,7 @@ module Rack
|
|
118
118
|
|
119
119
|
include Enumerable
|
120
120
|
|
121
|
-
def initialize
|
121
|
+
def initialize(tempfile)
|
122
122
|
@tempfile = tempfile
|
123
123
|
@mime_parts = []
|
124
124
|
@open_files = 0
|
@@ -128,7 +128,7 @@ module Rack
|
|
128
128
|
@mime_parts.each { |part| yield part }
|
129
129
|
end
|
130
130
|
|
131
|
-
def on_mime_head
|
131
|
+
def on_mime_head(mime_index, head, filename, content_type, name)
|
132
132
|
if filename
|
133
133
|
body = @tempfile.call(filename, content_type)
|
134
134
|
body.binmode if body.respond_to?(:binmode)
|
@@ -140,50 +140,62 @@ module Rack
|
|
140
140
|
end
|
141
141
|
|
142
142
|
@mime_parts[mime_index] = klass.new(body, head, filename, content_type, name)
|
143
|
-
|
143
|
+
|
144
|
+
check_part_limits
|
144
145
|
end
|
145
146
|
|
146
|
-
def on_mime_body
|
147
|
+
def on_mime_body(mime_index, content)
|
147
148
|
@mime_parts[mime_index].body << content
|
148
149
|
end
|
149
150
|
|
150
|
-
def on_mime_finish
|
151
|
+
def on_mime_finish(mime_index)
|
151
152
|
end
|
152
153
|
|
153
154
|
private
|
154
155
|
|
155
|
-
def
|
156
|
-
|
157
|
-
|
156
|
+
def check_part_limits
|
157
|
+
file_limit = Utils.multipart_file_limit
|
158
|
+
part_limit = Utils.multipart_total_part_limit
|
159
|
+
|
160
|
+
if file_limit && file_limit > 0
|
161
|
+
if @open_files >= file_limit
|
158
162
|
@mime_parts.each(&:close)
|
159
163
|
raise MultipartPartLimitError, 'Maximum file multiparts in content reached'
|
160
164
|
end
|
161
165
|
end
|
166
|
+
|
167
|
+
if part_limit && part_limit > 0
|
168
|
+
if @mime_parts.size >= part_limit
|
169
|
+
@mime_parts.each(&:close)
|
170
|
+
raise MultipartTotalPartLimitError, 'Maximum total multiparts in content reached'
|
171
|
+
end
|
172
|
+
end
|
162
173
|
end
|
163
174
|
end
|
164
175
|
|
165
176
|
attr_reader :state
|
166
177
|
|
167
178
|
def initialize(boundary, tempfile, bufsize, query_parser)
|
168
|
-
@buf = String.new
|
169
|
-
|
170
179
|
@query_parser = query_parser
|
171
180
|
@params = query_parser.make_params
|
172
181
|
@boundary = "--#{boundary}"
|
173
182
|
@bufsize = bufsize
|
174
183
|
|
175
|
-
@rx = /(?:#{EOL})?#{Regexp.quote(@boundary)}(#{EOL}|--)/n
|
176
|
-
@rx_max_size = EOL.size + @boundary.bytesize + [EOL.size, '--'.size].max
|
177
184
|
@full_boundary = @boundary
|
178
185
|
@end_boundary = @boundary + '--'
|
179
186
|
@state = :FAST_FORWARD
|
180
187
|
@mime_index = 0
|
181
188
|
@collector = Collector.new tempfile
|
189
|
+
|
190
|
+
@sbuf = StringScanner.new("".dup)
|
191
|
+
@body_regex = /(?:#{EOL})?#{Regexp.quote(@boundary)}(?:#{EOL}|--)/m
|
192
|
+
@rx_max_size = EOL.size + @boundary.bytesize + [EOL.size, '--'.size].max
|
193
|
+
@head_regex = /(.*?#{EOL})#{EOL}/m
|
182
194
|
end
|
183
195
|
|
184
|
-
def on_read
|
185
|
-
handle_empty_content!(content
|
186
|
-
@
|
196
|
+
def on_read(content)
|
197
|
+
handle_empty_content!(content)
|
198
|
+
@sbuf.concat content
|
187
199
|
run_parser
|
188
200
|
end
|
189
201
|
|
@@ -194,7 +206,6 @@ module Rack
|
|
194
206
|
@query_parser.normalize_params(@params, part.name, data, @query_parser.param_depth_limit)
|
195
207
|
end
|
196
208
|
end
|
197
|
-
|
198
209
|
MultipartInfo.new @params.to_params_hash, @collector.find_all(&:file?).map(&:body)
|
199
210
|
end
|
200
211
|
|
@@ -221,7 +232,7 @@ module Rack
|
|
221
232
|
if consume_boundary
|
222
233
|
@state = :MIME_HEAD
|
223
234
|
else
|
224
|
-
raise EOFError, "bad content body" if @
|
235
|
+
raise EOFError, "bad content body" if @sbuf.rest_size >= @bufsize
|
225
236
|
:want_read
|
226
237
|
end
|
227
238
|
end
|
@@ -229,19 +240,16 @@ module Rack
|
|
229
240
|
def handle_consume_token
|
230
241
|
tok = consume_boundary
|
231
242
|
# 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
|
-
|
243
|
+
@state = if tok == :END_BOUNDARY || (@sbuf.eos? && tok != :BOUNDARY)
|
244
|
+
:DONE
|
234
245
|
else
|
235
|
-
|
246
|
+
:MIME_HEAD
|
236
247
|
end
|
237
248
|
end
|
238
249
|
|
239
250
|
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
|
-
|
251
|
+
if @sbuf.scan_until(@head_regex)
|
252
|
+
head = @sbuf[1]
|
245
253
|
content_type = head[MULTIPART_CONTENT_TYPE, 1]
|
246
254
|
if name = head[MULTIPART_CONTENT_DISPOSITION, 1]
|
247
255
|
name = Rack::Auth::Digest::Params::dequote(name)
|
@@ -252,7 +260,7 @@ module Rack
|
|
252
260
|
filename = get_filename(head)
|
253
261
|
|
254
262
|
if name.nil? || name.empty?
|
255
|
-
name = filename || "#{content_type || TEXT_PLAIN}[]"
|
263
|
+
name = filename || "#{content_type || TEXT_PLAIN}[]".dup
|
256
264
|
end
|
257
265
|
|
258
266
|
@collector.on_mime_head @mime_index, head, filename, content_type, name
|
@@ -263,16 +271,19 @@ module Rack
|
|
263
271
|
end
|
264
272
|
|
265
273
|
def handle_mime_body
|
266
|
-
if
|
267
|
-
#
|
268
|
-
@collector.on_mime_body @mime_index,
|
269
|
-
@
|
274
|
+
if (body_with_boundary = @sbuf.check_until(@body_regex)) # check but do not advance the pointer yet
|
275
|
+
body = body_with_boundary.sub(/#{@body_regex}\z/m, '') # remove the boundary from the string
|
276
|
+
@collector.on_mime_body @mime_index, body
|
277
|
+
@sbuf.pos += body.length + 2 # skip \r\n after the content
|
270
278
|
@state = :CONSUME_TOKEN
|
271
279
|
@mime_index += 1
|
272
280
|
else
|
273
|
-
# Save
|
274
|
-
if @rx_max_size < @
|
275
|
-
|
281
|
+
# Save what we have so far
|
282
|
+
if @rx_max_size < @sbuf.rest_size
|
283
|
+
delta = @sbuf.rest_size - @rx_max_size
|
284
|
+
@collector.on_mime_body @mime_index, @sbuf.peek(delta)
|
285
|
+
@sbuf.pos += delta
|
286
|
+
@sbuf.string = @sbuf.rest
|
276
287
|
end
|
277
288
|
:want_read
|
278
289
|
end
|
@@ -280,16 +291,13 @@ module Rack
|
|
280
291
|
|
281
292
|
def full_boundary; @full_boundary; end
|
282
293
|
|
283
|
-
def rx; @rx; end
|
284
|
-
|
285
294
|
def consume_boundary
|
286
|
-
while @
|
287
|
-
read_buffer = $1
|
295
|
+
while read_buffer = @sbuf.scan_until(BOUNDARY_REGEX)
|
288
296
|
case read_buffer.strip
|
289
297
|
when full_boundary then return :BOUNDARY
|
290
298
|
when @end_boundary then return :END_BOUNDARY
|
291
299
|
end
|
292
|
-
return if @
|
300
|
+
return if @sbuf.eos?
|
293
301
|
end
|
294
302
|
end
|
295
303
|
|
@@ -304,14 +312,15 @@ module Rack
|
|
304
312
|
elsif filename = params['filename*']
|
305
313
|
encoding, _, filename = filename.split("'", 3)
|
306
314
|
end
|
307
|
-
when
|
315
|
+
when BROKEN
|
308
316
|
filename = $1
|
317
|
+
filename = $1 if filename =~ /^"(.*)"$/
|
309
318
|
end
|
310
319
|
|
311
320
|
return unless filename
|
312
321
|
|
313
|
-
if filename.scan(/%.?.?/).all? { |s|
|
314
|
-
filename = Utils.
|
322
|
+
if filename.scan(/%.?.?/).all? { |s| /%[0-9a-fA-F]{2}/.match?(s) }
|
323
|
+
filename = Utils.unescape_path(filename)
|
315
324
|
end
|
316
325
|
|
317
326
|
filename.scrub!
|
@@ -327,7 +336,7 @@ module Rack
|
|
327
336
|
filename
|
328
337
|
end
|
329
338
|
|
330
|
-
CHARSET
|
339
|
+
CHARSET = "charset"
|
331
340
|
|
332
341
|
def tag_multipart_encoding(filename, content_type, name, body)
|
333
342
|
name = name.to_s
|
@@ -342,12 +351,12 @@ module Rack
|
|
342
351
|
type_subtype = list.first
|
343
352
|
type_subtype.strip!
|
344
353
|
if TEXT_PLAIN == type_subtype
|
345
|
-
rest
|
354
|
+
rest = list.drop 1
|
346
355
|
rest.each do |param|
|
347
|
-
k,v = param.split('=', 2)
|
356
|
+
k, v = param.split('=', 2)
|
348
357
|
k.strip!
|
349
358
|
v.strip!
|
350
|
-
v = v[1..-2] if v
|
359
|
+
v = v[1..-2] if v.start_with?('"') && v.end_with?('"')
|
351
360
|
encoding = Encoding.find v if k == CHARSET
|
352
361
|
end
|
353
362
|
end
|
@@ -357,11 +366,9 @@ module Rack
|
|
357
366
|
body.force_encoding(encoding)
|
358
367
|
end
|
359
368
|
|
360
|
-
|
361
|
-
def handle_empty_content!(content, eof)
|
369
|
+
def handle_empty_content!(content)
|
362
370
|
if content.nil? || content.empty?
|
363
|
-
raise EOFError
|
364
|
-
return true
|
371
|
+
raise EOFError
|
365
372
|
end
|
366
373
|
end
|
367
374
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Rack
|
2
4
|
module Multipart
|
3
5
|
class UploadedFile
|
@@ -7,17 +9,23 @@ module Rack
|
|
7
9
|
# The content type of the "uploaded" file
|
8
10
|
attr_accessor :content_type
|
9
11
|
|
10
|
-
def initialize(
|
11
|
-
|
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
|
12
24
|
@content_type = content_type
|
13
|
-
@original_filename = ::File.basename(path)
|
14
|
-
@tempfile = Tempfile.new([@original_filename, ::File.extname(path)], encoding: Encoding::BINARY)
|
15
|
-
@tempfile.binmode if binary
|
16
|
-
FileUtils.copy_file(path, @tempfile.path)
|
17
25
|
end
|
18
26
|
|
19
27
|
def path
|
20
|
-
@tempfile.path
|
28
|
+
@tempfile.path if @tempfile.respond_to?(:path)
|
21
29
|
end
|
22
30
|
alias_method :local_path, :path
|
23
31
|
|
data/lib/rack/multipart.rb
CHANGED
@@ -1,4 +1,6 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'multipart/parser'
|
2
4
|
|
3
5
|
module Rack
|
4
6
|
# A multipart form data parser, adapted from IOWA.
|
@@ -14,13 +16,12 @@ module Rack
|
|
14
16
|
TOKEN = /[^\s()<>,;:\\"\/\[\]?=]+/
|
15
17
|
CONDISP = /Content-Disposition:\s*#{TOKEN}\s*/i
|
16
18
|
VALUE = /"(?:\\"|[^"])*"|#{TOKEN}/
|
17
|
-
|
18
|
-
BROKEN_UNQUOTED = /^#{CONDISP}.*;\sfilename=(#{TOKEN})/i
|
19
|
+
BROKEN = /^#{CONDISP}.*;\s*filename=(#{VALUE})/i
|
19
20
|
MULTIPART_CONTENT_TYPE = /Content-Type: (.*)#{EOL}/ni
|
20
|
-
MULTIPART_CONTENT_DISPOSITION = /Content-Disposition
|
21
|
+
MULTIPART_CONTENT_DISPOSITION = /Content-Disposition:[^:]*;\s*name=(#{VALUE})/ni
|
21
22
|
MULTIPART_CONTENT_ID = /Content-ID:\s*([^#{EOL}]*)/ni
|
22
23
|
# Updated definitions from RFC 2231
|
23
|
-
ATTRIBUTE_CHAR = %r{[^ \
|
24
|
+
ATTRIBUTE_CHAR = %r{[^ \x00-\x1f\x7f)(><@,;:\\"/\[\]?='*%]}
|
24
25
|
ATTRIBUTE = /#{ATTRIBUTE_CHAR}+/
|
25
26
|
SECTION = /\*[0-9]+/
|
26
27
|
REGULAR_PARAMETER_NAME = /#{ATTRIBUTE}#{SECTION}?/
|
data/lib/rack/null_logger.rb
CHANGED
data/lib/rack/query_parser.rb
CHANGED
@@ -1,5 +1,9 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Rack
|
2
4
|
class QueryParser
|
5
|
+
(require_relative 'core_ext/regexp'; using ::Rack::RegexpExtensions) if RUBY_VERSION < '2.4'
|
6
|
+
|
3
7
|
DEFAULT_SEP = /[&;] */n
|
4
8
|
COMMON_SEP = { ";" => /[;] */n, ";," => /[;,] */n, "&" => /[&] */n }
|
5
9
|
|
@@ -12,6 +16,10 @@ module Rack
|
|
12
16
|
# sequence.
|
13
17
|
class InvalidParameterError < ArgumentError; end
|
14
18
|
|
19
|
+
# ParamsTooDeepError is the error that is raised when params are recursively
|
20
|
+
# nested over the specified limit.
|
21
|
+
class ParamsTooDeepError < RangeError; end
|
22
|
+
|
15
23
|
def self.make_default(key_space_limit, param_depth_limit)
|
16
24
|
new Params, key_space_limit, param_depth_limit
|
17
25
|
end
|
@@ -36,7 +44,7 @@ module Rack
|
|
36
44
|
|
37
45
|
(qs || '').split(d ? (COMMON_SEP[d] || /[#{d}] */n) : DEFAULT_SEP).each do |p|
|
38
46
|
next if p.empty?
|
39
|
-
k, v = p.split('='
|
47
|
+
k, v = p.split('=', 2).map!(&unescaper)
|
40
48
|
|
41
49
|
if cur = params[k]
|
42
50
|
if cur.class == Array
|
@@ -49,7 +57,7 @@ module Rack
|
|
49
57
|
end
|
50
58
|
end
|
51
59
|
|
52
|
-
return params.
|
60
|
+
return params.to_h
|
53
61
|
end
|
54
62
|
|
55
63
|
# parse_nested_query expands a query string into structural types. Supported
|
@@ -58,43 +66,44 @@ module Rack
|
|
58
66
|
# ParameterTypeError is raised. Users are encouraged to return a 400 in this
|
59
67
|
# case.
|
60
68
|
def parse_nested_query(qs, d = nil)
|
61
|
-
return {} if qs.nil? || qs.empty?
|
62
69
|
params = make_params
|
63
70
|
|
64
|
-
|
65
|
-
|
71
|
+
unless qs.nil? || qs.empty?
|
72
|
+
(qs || '').split(d ? (COMMON_SEP[d] || /[#{d}] */n) : DEFAULT_SEP).each do |p|
|
73
|
+
k, v = p.split('=', 2).map! { |s| unescape(s) }
|
66
74
|
|
67
|
-
|
75
|
+
normalize_params(params, k, v, param_depth_limit)
|
76
|
+
end
|
68
77
|
end
|
69
78
|
|
70
|
-
return params.
|
79
|
+
return params.to_h
|
71
80
|
rescue ArgumentError => e
|
72
|
-
raise InvalidParameterError, e.message
|
81
|
+
raise InvalidParameterError, e.message, e.backtrace
|
73
82
|
end
|
74
83
|
|
75
84
|
# normalize_params recursively expands parameters into structural types. If
|
76
85
|
# the structural types represented by two different parameter names are in
|
77
86
|
# conflict, a ParameterTypeError is raised.
|
78
87
|
def normalize_params(params, name, v, depth)
|
79
|
-
raise
|
88
|
+
raise ParamsTooDeepError if depth <= 0
|
80
89
|
|
81
90
|
name =~ %r(\A[\[\]]*([^\[\]]+)\]*)
|
82
|
-
k = $1 || ''
|
83
|
-
after = $' || ''
|
91
|
+
k = $1 || ''
|
92
|
+
after = $' || ''
|
84
93
|
|
85
94
|
if k.empty?
|
86
|
-
if !v.nil? && name == "[]"
|
95
|
+
if !v.nil? && name == "[]"
|
87
96
|
return Array(v)
|
88
97
|
else
|
89
98
|
return
|
90
99
|
end
|
91
100
|
end
|
92
101
|
|
93
|
-
if after == ''
|
102
|
+
if after == ''
|
94
103
|
params[k] = v
|
95
|
-
elsif after == "["
|
104
|
+
elsif after == "["
|
96
105
|
params[name] = v
|
97
|
-
elsif after == "[]"
|
106
|
+
elsif after == "[]"
|
98
107
|
params[k] ||= []
|
99
108
|
raise ParameterTypeError, "expected Array (got #{params[k].class.name}) for param `#{k}'" unless params[k].is_a?(Array)
|
100
109
|
params[k] << v
|
@@ -135,7 +144,7 @@ module Rack
|
|
135
144
|
end
|
136
145
|
|
137
146
|
def params_hash_has_key?(hash, key)
|
138
|
-
return false if
|
147
|
+
return false if /\[\]/.match?(key)
|
139
148
|
|
140
149
|
key.split(/[\[\]]+/).inject(hash) do |h, part|
|
141
150
|
next h if part == ''
|
@@ -163,7 +172,7 @@ module Rack
|
|
163
172
|
|
164
173
|
def []=(key, value)
|
165
174
|
@size += key.size if key && !@params.key?(key)
|
166
|
-
raise
|
175
|
+
raise ParamsTooDeepError, 'exceeded available parameter key space' if @size > @limit
|
167
176
|
@params[key] = value
|
168
177
|
end
|
169
178
|
|
@@ -171,22 +180,42 @@ module Rack
|
|
171
180
|
@params.key?(key)
|
172
181
|
end
|
173
182
|
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
183
|
+
# Recursively unwraps nested `Params` objects and constructs an object
|
184
|
+
# of the same shape, but using the objects' internal representations
|
185
|
+
# (Ruby hashes) in place of the objects. The result is a hash consisting
|
186
|
+
# purely of Ruby primitives.
|
187
|
+
#
|
188
|
+
# Mutation warning!
|
189
|
+
#
|
190
|
+
# 1. This method mutates the internal representation of the `Params`
|
191
|
+
# objects in order to save object allocations.
|
192
|
+
#
|
193
|
+
# 2. The value you get back is a reference to the internal hash
|
194
|
+
# representation, not a copy.
|
195
|
+
#
|
196
|
+
# 3. Because the `Params` object's internal representation is mutable
|
197
|
+
# through the `#[]=` method, it is not thread safe. The result of
|
198
|
+
# getting the hash representation while another thread is adding a
|
199
|
+
# key to it is non-deterministic.
|
200
|
+
#
|
201
|
+
def to_h
|
202
|
+
@params.each do |key, value|
|
203
|
+
case value
|
204
|
+
when self
|
205
|
+
# Handle circular references gracefully.
|
206
|
+
@params[key] = @params
|
207
|
+
when Params
|
208
|
+
@params[key] = value.to_h
|
209
|
+
when Array
|
210
|
+
value.map! { |v| v.kind_of?(Params) ? v.to_h : v }
|
211
|
+
else
|
212
|
+
# Ignore anything that is not a `Params` object or
|
213
|
+
# a collection that can contain one.
|
186
214
|
end
|
187
215
|
end
|
188
|
-
|
216
|
+
@params
|
189
217
|
end
|
218
|
+
alias_method :to_params_hash, :to_h
|
190
219
|
end
|
191
220
|
end
|
192
221
|
end
|
data/lib/rack/recursive.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'uri'
|
2
4
|
|
3
5
|
module Rack
|
@@ -10,14 +12,14 @@ module Rack
|
|
10
12
|
class ForwardRequest < Exception
|
11
13
|
attr_reader :url, :env
|
12
14
|
|
13
|
-
def initialize(url, env={})
|
15
|
+
def initialize(url, env = {})
|
14
16
|
@url = URI(url)
|
15
17
|
@env = env
|
16
18
|
|
17
|
-
@env[PATH_INFO]
|
18
|
-
@env[QUERY_STRING]
|
19
|
-
@env[HTTP_HOST]
|
20
|
-
@env[
|
19
|
+
@env[PATH_INFO] = @url.path
|
20
|
+
@env[QUERY_STRING] = @url.query if @url.query
|
21
|
+
@env[HTTP_HOST] = @url.host if @url.host
|
22
|
+
@env[HTTP_PORT] = @url.port if @url.port
|
21
23
|
@env[RACK_URL_SCHEME] = @url.scheme if @url.scheme
|
22
24
|
|
23
25
|
super "forwarding to #{url}"
|
data/lib/rack/reloader.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
|
-
#
|
2
|
-
|
3
|
-
#
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Copyright (C) 2009-2018 Michael Fellinger <m.fellinger@gmail.com>
|
4
|
+
# Rack::Reloader is subject to the terms of an MIT-style license.
|
5
|
+
# See MIT-LICENSE or https://opensource.org/licenses/MIT.
|
4
6
|
|
5
7
|
require 'pathname'
|
6
8
|
|
@@ -20,6 +22,8 @@ module Rack
|
|
20
22
|
# It is performing a check/reload cycle at the start of every request, but
|
21
23
|
# also respects a cool down time, during which nothing will be done.
|
22
24
|
class Reloader
|
25
|
+
(require_relative 'core_ext/regexp'; using ::Rack::RegexpExtensions) if RUBY_VERSION < '2.4'
|
26
|
+
|
23
27
|
def initialize(app, cooldown = 10, backend = Stat)
|
24
28
|
@app = app
|
25
29
|
@cooldown = cooldown
|
@@ -69,7 +73,7 @@ module Rack
|
|
69
73
|
paths = ['./', *$LOAD_PATH].uniq
|
70
74
|
|
71
75
|
files.map{|file|
|
72
|
-
next if
|
76
|
+
next if /\.(so|bundle)$/.match?(file) # cannot reload compiled files
|
73
77
|
|
74
78
|
found, stat = figure_path(file, paths)
|
75
79
|
next unless found && stat && mtime = stat.mtime
|