rack 2.0.9.3 → 2.2.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/CHANGELOG.md +675 -0
- data/CONTRIBUTING.md +136 -0
- data/{COPYING → MIT-LICENSE} +4 -2
- data/README.rdoc +152 -162
- data/Rakefile +37 -23
- data/{SPEC → SPEC.rdoc} +44 -15
- 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 +33 -28
- 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 +5 -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 +17 -11
- 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 +4 -2
- 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 +58 -73
- data/lib/rack/multipart/uploaded_file.rb +15 -7
- data/lib/rack/multipart.rb +7 -4
- data/lib/rack/null_logger.rb +2 -0
- data/lib/rack/query_parser.rb +53 -28
- data/lib/rack/recursive.rb +7 -5
- data/lib/rack/reloader.rb +8 -4
- data/lib/rack/request.rb +210 -61
- 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 +33 -21
- data/lib/rack/session/cookie.rb +12 -12
- data/lib/rack/session/memcache.rb +4 -93
- data/lib/rack/session/pool.rb +5 -3
- data/lib/rack/show_exceptions.rb +17 -13
- data/lib/rack/show_status.rb +5 -5
- data/lib/rack/static.rb +23 -11
- data/lib/rack/tempfile_reaper.rb +1 -1
- data/lib/rack/urlmap.rb +12 -6
- data/lib/rack/utils.rb +105 -130
- data/lib/rack/version.rb +29 -0
- data/lib/rack.rb +67 -73
- data/rack.gemspec +40 -28
- metadata +39 -182
- data/HISTORY.md +0 -520
- 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 -107
- 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 -520
- 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 -721
- data/test/spec_null_logger.rb +0 -21
- data/test/spec_recursive.rb +0 -75
- data/test/spec_request.rb +0 -1423
- data/test/spec_response.rb +0 -528
- 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 -357
- data/test/spec_session_persisted_secure_secure_session_hash.rb +0 -73
- data/test/spec_session_pool.rb +0 -247
- 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,17 +1,22 @@
|
|
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
|
6
|
-
class MultipartTotalPartLimitError < StandardError; end
|
7
8
|
|
8
9
|
class Parser
|
9
|
-
|
10
|
+
(require_relative '../core_ext/regexp'; using ::Rack::RegexpExtensions) if RUBY_VERSION < '2.4'
|
11
|
+
|
12
|
+
BUFSIZE = 1_048_576
|
10
13
|
TEXT_PLAIN = "text/plain"
|
11
14
|
TEMPFILE_FACTORY = lambda { |filename, content_type|
|
12
|
-
Tempfile.new(["RackMultipart", ::File.extname(filename.gsub("\0"
|
15
|
+
Tempfile.new(["RackMultipart", ::File.extname(filename.gsub("\0", '%00'))])
|
13
16
|
}
|
14
17
|
|
18
|
+
BOUNDARY_REGEX = /\A([^\n]*(?:\n|\Z))/
|
19
|
+
|
15
20
|
class BoundedIO # :nodoc:
|
16
21
|
def initialize(io, content_length)
|
17
22
|
@io = io
|
@@ -19,15 +24,15 @@ module Rack
|
|
19
24
|
@cursor = 0
|
20
25
|
end
|
21
26
|
|
22
|
-
def read(size)
|
27
|
+
def read(size, outbuf = nil)
|
23
28
|
return if @cursor >= @content_length
|
24
29
|
|
25
30
|
left = @content_length - @cursor
|
26
31
|
|
27
32
|
str = if left < size
|
28
|
-
@io.read left
|
33
|
+
@io.read left, outbuf
|
29
34
|
else
|
30
|
-
@io.read size
|
35
|
+
@io.read size, outbuf
|
31
36
|
end
|
32
37
|
|
33
38
|
if str
|
@@ -62,13 +67,14 @@ module Rack
|
|
62
67
|
return EMPTY unless boundary
|
63
68
|
|
64
69
|
io = BoundedIO.new(io, content_length) if content_length
|
70
|
+
outbuf = String.new
|
65
71
|
|
66
72
|
parser = new(boundary, tmpfile, bufsize, qp)
|
67
|
-
parser.on_read io.read(bufsize)
|
73
|
+
parser.on_read io.read(bufsize, outbuf)
|
68
74
|
|
69
75
|
loop do
|
70
76
|
break if parser.state == :DONE
|
71
|
-
parser.on_read io.read(bufsize)
|
77
|
+
parser.on_read io.read(bufsize, outbuf)
|
72
78
|
end
|
73
79
|
|
74
80
|
io.rewind
|
@@ -91,14 +97,8 @@ module Rack
|
|
91
97
|
# those which give the lone filename.
|
92
98
|
fn = filename.split(/[\/\\]/).last
|
93
99
|
|
94
|
-
data = {:
|
95
|
-
:
|
96
|
-
elsif !filename && content_type && body.is_a?(IO)
|
97
|
-
body.rewind
|
98
|
-
|
99
|
-
# Generic multipart cases, not coming from a form
|
100
|
-
data = {:type => content_type,
|
101
|
-
:name => name, :tempfile => body, :head => head}
|
100
|
+
data = { filename: fn, type: content_type,
|
101
|
+
name: name, tempfile: body, head: head }
|
102
102
|
end
|
103
103
|
|
104
104
|
yield data
|
@@ -117,7 +117,7 @@ module Rack
|
|
117
117
|
|
118
118
|
include Enumerable
|
119
119
|
|
120
|
-
def initialize
|
120
|
+
def initialize(tempfile)
|
121
121
|
@tempfile = tempfile
|
122
122
|
@mime_parts = []
|
123
123
|
@open_files = 0
|
@@ -127,7 +127,7 @@ module Rack
|
|
127
127
|
@mime_parts.each { |part| yield part }
|
128
128
|
end
|
129
129
|
|
130
|
-
def on_mime_head
|
130
|
+
def on_mime_head(mime_index, head, filename, content_type, name)
|
131
131
|
if filename
|
132
132
|
body = @tempfile.call(filename, content_type)
|
133
133
|
body.binmode if body.respond_to?(:binmode)
|
@@ -140,60 +140,51 @@ module Rack
|
|
140
140
|
|
141
141
|
@mime_parts[mime_index] = klass.new(body, head, filename, content_type, name)
|
142
142
|
|
143
|
-
|
143
|
+
check_open_files
|
144
144
|
end
|
145
145
|
|
146
|
-
def on_mime_body
|
146
|
+
def on_mime_body(mime_index, content)
|
147
147
|
@mime_parts[mime_index].body << content
|
148
148
|
end
|
149
149
|
|
150
|
-
def on_mime_finish
|
150
|
+
def on_mime_finish(mime_index)
|
151
151
|
end
|
152
152
|
|
153
153
|
private
|
154
154
|
|
155
|
-
def
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
if file_limit && file_limit > 0
|
160
|
-
if @open_files >= file_limit
|
155
|
+
def check_open_files
|
156
|
+
if Utils.multipart_part_limit > 0
|
157
|
+
if @open_files >= Utils.multipart_part_limit
|
161
158
|
@mime_parts.each(&:close)
|
162
159
|
raise MultipartPartLimitError, 'Maximum file multiparts in content reached'
|
163
160
|
end
|
164
161
|
end
|
165
|
-
|
166
|
-
if part_limit && part_limit > 0
|
167
|
-
if @mime_parts.size >= part_limit
|
168
|
-
@mime_parts.each(&:close)
|
169
|
-
raise MultipartTotalPartLimitError, 'Maximum total multiparts in content reached'
|
170
|
-
end
|
171
|
-
end
|
172
162
|
end
|
173
163
|
end
|
174
164
|
|
175
165
|
attr_reader :state
|
176
166
|
|
177
167
|
def initialize(boundary, tempfile, bufsize, query_parser)
|
178
|
-
@buf = String.new
|
179
|
-
|
180
168
|
@query_parser = query_parser
|
181
169
|
@params = query_parser.make_params
|
182
170
|
@boundary = "--#{boundary}"
|
183
171
|
@bufsize = bufsize
|
184
172
|
|
185
|
-
@rx = /(?:#{EOL})?#{Regexp.quote(@boundary)}(#{EOL}|--)/n
|
186
|
-
@rx_max_size = EOL.size + @boundary.bytesize + [EOL.size, '--'.size].max
|
187
173
|
@full_boundary = @boundary
|
188
174
|
@end_boundary = @boundary + '--'
|
189
175
|
@state = :FAST_FORWARD
|
190
176
|
@mime_index = 0
|
191
177
|
@collector = Collector.new tempfile
|
178
|
+
|
179
|
+
@sbuf = StringScanner.new("".dup)
|
180
|
+
@body_regex = /(?:#{EOL})?#{Regexp.quote(@boundary)}(?:#{EOL}|--)/m
|
181
|
+
@rx_max_size = EOL.size + @boundary.bytesize + [EOL.size, '--'.size].max
|
182
|
+
@head_regex = /(.*?#{EOL})#{EOL}/m
|
192
183
|
end
|
193
184
|
|
194
|
-
def on_read
|
185
|
+
def on_read(content)
|
195
186
|
handle_empty_content!(content)
|
196
|
-
@
|
187
|
+
@sbuf.concat content
|
197
188
|
run_parser
|
198
189
|
end
|
199
190
|
|
@@ -204,7 +195,6 @@ module Rack
|
|
204
195
|
@query_parser.normalize_params(@params, part.name, data, @query_parser.param_depth_limit)
|
205
196
|
end
|
206
197
|
end
|
207
|
-
|
208
198
|
MultipartInfo.new @params.to_params_hash, @collector.find_all(&:file?).map(&:body)
|
209
199
|
end
|
210
200
|
|
@@ -231,7 +221,7 @@ module Rack
|
|
231
221
|
if consume_boundary
|
232
222
|
@state = :MIME_HEAD
|
233
223
|
else
|
234
|
-
raise EOFError, "bad content body" if @
|
224
|
+
raise EOFError, "bad content body" if @sbuf.rest_size >= @bufsize
|
235
225
|
:want_read
|
236
226
|
end
|
237
227
|
end
|
@@ -239,19 +229,16 @@ module Rack
|
|
239
229
|
def handle_consume_token
|
240
230
|
tok = consume_boundary
|
241
231
|
# break if we're at the end of a buffer, but not if it is the end of a field
|
242
|
-
if tok == :END_BOUNDARY || (@
|
243
|
-
|
232
|
+
@state = if tok == :END_BOUNDARY || (@sbuf.eos? && tok != :BOUNDARY)
|
233
|
+
:DONE
|
244
234
|
else
|
245
|
-
|
235
|
+
:MIME_HEAD
|
246
236
|
end
|
247
237
|
end
|
248
238
|
|
249
239
|
def handle_mime_head
|
250
|
-
if @
|
251
|
-
|
252
|
-
head = @buf.slice!(0, i+2) # First \r\n
|
253
|
-
@buf.slice!(0, 2) # Second \r\n
|
254
|
-
|
240
|
+
if @sbuf.scan_until(@head_regex)
|
241
|
+
head = @sbuf[1]
|
255
242
|
content_type = head[MULTIPART_CONTENT_TYPE, 1]
|
256
243
|
if name = head[MULTIPART_CONTENT_DISPOSITION, 1]
|
257
244
|
name = Rack::Auth::Digest::Params::dequote(name)
|
@@ -262,7 +249,7 @@ module Rack
|
|
262
249
|
filename = get_filename(head)
|
263
250
|
|
264
251
|
if name.nil? || name.empty?
|
265
|
-
name = filename || "#{content_type || TEXT_PLAIN}[]"
|
252
|
+
name = filename || "#{content_type || TEXT_PLAIN}[]".dup
|
266
253
|
end
|
267
254
|
|
268
255
|
@collector.on_mime_head @mime_index, head, filename, content_type, name
|
@@ -273,16 +260,19 @@ module Rack
|
|
273
260
|
end
|
274
261
|
|
275
262
|
def handle_mime_body
|
276
|
-
if
|
277
|
-
#
|
278
|
-
@collector.on_mime_body @mime_index,
|
279
|
-
@
|
263
|
+
if (body_with_boundary = @sbuf.check_until(@body_regex)) # check but do not advance the pointer yet
|
264
|
+
body = body_with_boundary.sub(/#{@body_regex}\z/m, '') # remove the boundary from the string
|
265
|
+
@collector.on_mime_body @mime_index, body
|
266
|
+
@sbuf.pos += body.length + 2 # skip \r\n after the content
|
280
267
|
@state = :CONSUME_TOKEN
|
281
268
|
@mime_index += 1
|
282
269
|
else
|
283
|
-
# Save
|
284
|
-
if @rx_max_size < @
|
285
|
-
|
270
|
+
# Save what we have so far
|
271
|
+
if @rx_max_size < @sbuf.rest_size
|
272
|
+
delta = @sbuf.rest_size - @rx_max_size
|
273
|
+
@collector.on_mime_body @mime_index, @sbuf.peek(delta)
|
274
|
+
@sbuf.pos += delta
|
275
|
+
@sbuf.string = @sbuf.rest
|
286
276
|
end
|
287
277
|
:want_read
|
288
278
|
end
|
@@ -290,16 +280,13 @@ module Rack
|
|
290
280
|
|
291
281
|
def full_boundary; @full_boundary; end
|
292
282
|
|
293
|
-
def rx; @rx; end
|
294
|
-
|
295
283
|
def consume_boundary
|
296
|
-
while @
|
297
|
-
read_buffer = $1
|
284
|
+
while read_buffer = @sbuf.scan_until(BOUNDARY_REGEX)
|
298
285
|
case read_buffer.strip
|
299
286
|
when full_boundary then return :BOUNDARY
|
300
287
|
when @end_boundary then return :END_BOUNDARY
|
301
288
|
end
|
302
|
-
return if @
|
289
|
+
return if @sbuf.eos?
|
303
290
|
end
|
304
291
|
end
|
305
292
|
|
@@ -314,15 +301,14 @@ module Rack
|
|
314
301
|
elsif filename = params['filename*']
|
315
302
|
encoding, _, filename = filename.split("'", 3)
|
316
303
|
end
|
317
|
-
when
|
304
|
+
when BROKEN_QUOTED, BROKEN_UNQUOTED
|
318
305
|
filename = $1
|
319
|
-
filename = $1 if filename =~ /^"(.*)"$/
|
320
306
|
end
|
321
307
|
|
322
308
|
return unless filename
|
323
309
|
|
324
|
-
if filename.scan(/%.?.?/).all? { |s|
|
325
|
-
filename = Utils.
|
310
|
+
if filename.scan(/%.?.?/).all? { |s| /%[0-9a-fA-F]{2}/.match?(s) }
|
311
|
+
filename = Utils.unescape_path(filename)
|
326
312
|
end
|
327
313
|
|
328
314
|
filename.scrub!
|
@@ -338,7 +324,7 @@ module Rack
|
|
338
324
|
filename
|
339
325
|
end
|
340
326
|
|
341
|
-
CHARSET
|
327
|
+
CHARSET = "charset"
|
342
328
|
|
343
329
|
def tag_multipart_encoding(filename, content_type, name, body)
|
344
330
|
name = name.to_s
|
@@ -353,12 +339,12 @@ module Rack
|
|
353
339
|
type_subtype = list.first
|
354
340
|
type_subtype.strip!
|
355
341
|
if TEXT_PLAIN == type_subtype
|
356
|
-
rest
|
342
|
+
rest = list.drop 1
|
357
343
|
rest.each do |param|
|
358
|
-
k,v = param.split('=', 2)
|
344
|
+
k, v = param.split('=', 2)
|
359
345
|
k.strip!
|
360
346
|
v.strip!
|
361
|
-
v = v[1..-2] if v
|
347
|
+
v = v[1..-2] if v.start_with?('"') && v.end_with?('"')
|
362
348
|
encoding = Encoding.find v if k == CHARSET
|
363
349
|
end
|
364
350
|
end
|
@@ -368,7 +354,6 @@ module Rack
|
|
368
354
|
body.force_encoding(encoding)
|
369
355
|
end
|
370
356
|
|
371
|
-
|
372
357
|
def handle_empty_content!(content)
|
373
358
|
if content.nil? || content.empty?
|
374
359
|
raise EOFError
|
@@ -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,12 +16,13 @@ module Rack
|
|
14
16
|
TOKEN = /[^\s()<>,;:\\"\/\[\]?=]+/
|
15
17
|
CONDISP = /Content-Disposition:\s*#{TOKEN}\s*/i
|
16
18
|
VALUE = /"(?:\\"|[^"])*"|#{TOKEN}/
|
17
|
-
|
19
|
+
BROKEN_QUOTED = /^#{CONDISP}.*;\s*filename="(.*?)"(?:\s*$|\s*;\s*#{TOKEN}=)/i
|
20
|
+
BROKEN_UNQUOTED = /^#{CONDISP}.*;\s*filename=(#{TOKEN})/i
|
18
21
|
MULTIPART_CONTENT_TYPE = /Content-Type: (.*)#{EOL}/ni
|
19
|
-
MULTIPART_CONTENT_DISPOSITION = /Content-Disposition
|
22
|
+
MULTIPART_CONTENT_DISPOSITION = /Content-Disposition:.*;\s*name=(#{VALUE})/ni
|
20
23
|
MULTIPART_CONTENT_ID = /Content-ID:\s*([^#{EOL}]*)/ni
|
21
24
|
# Updated definitions from RFC 2231
|
22
|
-
ATTRIBUTE_CHAR = %r{[^ \
|
25
|
+
ATTRIBUTE_CHAR = %r{[^ \t\v\n\r)(><@,;:\\"/\[\]?='*%]}
|
23
26
|
ATTRIBUTE = /#{ATTRIBUTE_CHAR}+/
|
24
27
|
SECTION = /\*[0-9]+/
|
25
28
|
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
|
|
@@ -36,7 +40,7 @@ module Rack
|
|
36
40
|
|
37
41
|
(qs || '').split(d ? (COMMON_SEP[d] || /[#{d}] */n) : DEFAULT_SEP).each do |p|
|
38
42
|
next if p.empty?
|
39
|
-
k, v = p.split('='
|
43
|
+
k, v = p.split('=', 2).map!(&unescaper)
|
40
44
|
|
41
45
|
if cur = params[k]
|
42
46
|
if cur.class == Array
|
@@ -49,7 +53,7 @@ module Rack
|
|
49
53
|
end
|
50
54
|
end
|
51
55
|
|
52
|
-
return params.
|
56
|
+
return params.to_h
|
53
57
|
end
|
54
58
|
|
55
59
|
# parse_nested_query expands a query string into structural types. Supported
|
@@ -58,18 +62,19 @@ module Rack
|
|
58
62
|
# ParameterTypeError is raised. Users are encouraged to return a 400 in this
|
59
63
|
# case.
|
60
64
|
def parse_nested_query(qs, d = nil)
|
61
|
-
return {} if qs.nil? || qs.empty?
|
62
65
|
params = make_params
|
63
66
|
|
64
|
-
|
65
|
-
|
67
|
+
unless qs.nil? || qs.empty?
|
68
|
+
(qs || '').split(d ? (COMMON_SEP[d] || /[#{d}] */n) : DEFAULT_SEP).each do |p|
|
69
|
+
k, v = p.split('=', 2).map! { |s| unescape(s) }
|
66
70
|
|
67
|
-
|
71
|
+
normalize_params(params, k, v, param_depth_limit)
|
72
|
+
end
|
68
73
|
end
|
69
74
|
|
70
|
-
return params.
|
75
|
+
return params.to_h
|
71
76
|
rescue ArgumentError => e
|
72
|
-
raise InvalidParameterError, e.message
|
77
|
+
raise InvalidParameterError, e.message, e.backtrace
|
73
78
|
end
|
74
79
|
|
75
80
|
# normalize_params recursively expands parameters into structural types. If
|
@@ -79,22 +84,22 @@ module Rack
|
|
79
84
|
raise RangeError if depth <= 0
|
80
85
|
|
81
86
|
name =~ %r(\A[\[\]]*([^\[\]]+)\]*)
|
82
|
-
k = $1 || ''
|
83
|
-
after = $' || ''
|
87
|
+
k = $1 || ''
|
88
|
+
after = $' || ''
|
84
89
|
|
85
90
|
if k.empty?
|
86
|
-
if !v.nil? && name == "[]"
|
91
|
+
if !v.nil? && name == "[]"
|
87
92
|
return Array(v)
|
88
93
|
else
|
89
94
|
return
|
90
95
|
end
|
91
96
|
end
|
92
97
|
|
93
|
-
if after == ''
|
98
|
+
if after == ''
|
94
99
|
params[k] = v
|
95
|
-
elsif after == "["
|
100
|
+
elsif after == "["
|
96
101
|
params[name] = v
|
97
|
-
elsif after == "[]"
|
102
|
+
elsif after == "[]"
|
98
103
|
params[k] ||= []
|
99
104
|
raise ParameterTypeError, "expected Array (got #{params[k].class.name}) for param `#{k}'" unless params[k].is_a?(Array)
|
100
105
|
params[k] << v
|
@@ -135,7 +140,7 @@ module Rack
|
|
135
140
|
end
|
136
141
|
|
137
142
|
def params_hash_has_key?(hash, key)
|
138
|
-
return false if
|
143
|
+
return false if /\[\]/.match?(key)
|
139
144
|
|
140
145
|
key.split(/[\[\]]+/).inject(hash) do |h, part|
|
141
146
|
next h if part == ''
|
@@ -171,22 +176,42 @@ module Rack
|
|
171
176
|
@params.key?(key)
|
172
177
|
end
|
173
178
|
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
179
|
+
# Recursively unwraps nested `Params` objects and constructs an object
|
180
|
+
# of the same shape, but using the objects' internal representations
|
181
|
+
# (Ruby hashes) in place of the objects. The result is a hash consisting
|
182
|
+
# purely of Ruby primitives.
|
183
|
+
#
|
184
|
+
# Mutation warning!
|
185
|
+
#
|
186
|
+
# 1. This method mutates the internal representation of the `Params`
|
187
|
+
# objects in order to save object allocations.
|
188
|
+
#
|
189
|
+
# 2. The value you get back is a reference to the internal hash
|
190
|
+
# representation, not a copy.
|
191
|
+
#
|
192
|
+
# 3. Because the `Params` object's internal representation is mutable
|
193
|
+
# through the `#[]=` method, it is not thread safe. The result of
|
194
|
+
# getting the hash representation while another thread is adding a
|
195
|
+
# key to it is non-deterministic.
|
196
|
+
#
|
197
|
+
def to_h
|
198
|
+
@params.each do |key, value|
|
199
|
+
case value
|
200
|
+
when self
|
201
|
+
# Handle circular references gracefully.
|
202
|
+
@params[key] = @params
|
203
|
+
when Params
|
204
|
+
@params[key] = value.to_h
|
205
|
+
when Array
|
206
|
+
value.map! { |v| v.kind_of?(Params) ? v.to_h : v }
|
207
|
+
else
|
208
|
+
# Ignore anything that is not a `Params` object or
|
209
|
+
# a collection that can contain one.
|
186
210
|
end
|
187
211
|
end
|
188
|
-
|
212
|
+
@params
|
189
213
|
end
|
214
|
+
alias_method :to_params_hash, :to_h
|
190
215
|
end
|
191
216
|
end
|
192
217
|
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
|