rack 2.0.9.3 → 2.2.8
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 +740 -0
- data/CONTRIBUTING.md +136 -0
- data/{COPYING → MIT-LICENSE} +4 -2
- data/README.rdoc +151 -147
- 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 -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 +219 -184
- 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 +55 -56
- data/lib/rack/multipart/uploaded_file.rb +15 -7
- data/lib/rack/multipart.rb +4 -2
- 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 +34 -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 +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 +105 -111
- data/lib/rack/version.rb +29 -0
- data/lib/rack.rb +67 -73
- data/rack.gemspec +40 -28
- metadata +36 -179
- 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,4 +1,6 @@
|
|
|
1
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'strscan'
|
|
2
4
|
|
|
3
5
|
module Rack
|
|
4
6
|
module Multipart
|
|
@@ -6,12 +8,18 @@ module Rack
|
|
|
6
8
|
class MultipartTotalPartLimitError < StandardError; end
|
|
7
9
|
|
|
8
10
|
class Parser
|
|
9
|
-
|
|
11
|
+
(require_relative '../core_ext/regexp'; using ::Rack::RegexpExtensions) if RUBY_VERSION < '2.4'
|
|
12
|
+
|
|
13
|
+
BUFSIZE = 1_048_576
|
|
10
14
|
TEXT_PLAIN = "text/plain"
|
|
11
15
|
TEMPFILE_FACTORY = lambda { |filename, content_type|
|
|
12
|
-
|
|
16
|
+
extension = ::File.extname(filename.gsub("\0", '%00'))[0, 129]
|
|
17
|
+
|
|
18
|
+
Tempfile.new(["RackMultipart", extension])
|
|
13
19
|
}
|
|
14
20
|
|
|
21
|
+
BOUNDARY_REGEX = /\A([^\n]*(?:\n|\Z))/
|
|
22
|
+
|
|
15
23
|
class BoundedIO # :nodoc:
|
|
16
24
|
def initialize(io, content_length)
|
|
17
25
|
@io = io
|
|
@@ -19,15 +27,15 @@ module Rack
|
|
|
19
27
|
@cursor = 0
|
|
20
28
|
end
|
|
21
29
|
|
|
22
|
-
def read(size)
|
|
30
|
+
def read(size, outbuf = nil)
|
|
23
31
|
return if @cursor >= @content_length
|
|
24
32
|
|
|
25
33
|
left = @content_length - @cursor
|
|
26
34
|
|
|
27
35
|
str = if left < size
|
|
28
|
-
@io.read left
|
|
36
|
+
@io.read left, outbuf
|
|
29
37
|
else
|
|
30
|
-
@io.read size
|
|
38
|
+
@io.read size, outbuf
|
|
31
39
|
end
|
|
32
40
|
|
|
33
41
|
if str
|
|
@@ -62,13 +70,14 @@ module Rack
|
|
|
62
70
|
return EMPTY unless boundary
|
|
63
71
|
|
|
64
72
|
io = BoundedIO.new(io, content_length) if content_length
|
|
73
|
+
outbuf = String.new
|
|
65
74
|
|
|
66
75
|
parser = new(boundary, tmpfile, bufsize, qp)
|
|
67
|
-
parser.on_read io.read(bufsize)
|
|
76
|
+
parser.on_read io.read(bufsize, outbuf)
|
|
68
77
|
|
|
69
78
|
loop do
|
|
70
79
|
break if parser.state == :DONE
|
|
71
|
-
parser.on_read io.read(bufsize)
|
|
80
|
+
parser.on_read io.read(bufsize, outbuf)
|
|
72
81
|
end
|
|
73
82
|
|
|
74
83
|
io.rewind
|
|
@@ -91,14 +100,8 @@ module Rack
|
|
|
91
100
|
# those which give the lone filename.
|
|
92
101
|
fn = filename.split(/[\/\\]/).last
|
|
93
102
|
|
|
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}
|
|
103
|
+
data = { filename: fn, type: content_type,
|
|
104
|
+
name: name, tempfile: body, head: head }
|
|
102
105
|
end
|
|
103
106
|
|
|
104
107
|
yield data
|
|
@@ -117,7 +120,7 @@ module Rack
|
|
|
117
120
|
|
|
118
121
|
include Enumerable
|
|
119
122
|
|
|
120
|
-
def initialize
|
|
123
|
+
def initialize(tempfile)
|
|
121
124
|
@tempfile = tempfile
|
|
122
125
|
@mime_parts = []
|
|
123
126
|
@open_files = 0
|
|
@@ -127,7 +130,7 @@ module Rack
|
|
|
127
130
|
@mime_parts.each { |part| yield part }
|
|
128
131
|
end
|
|
129
132
|
|
|
130
|
-
def on_mime_head
|
|
133
|
+
def on_mime_head(mime_index, head, filename, content_type, name)
|
|
131
134
|
if filename
|
|
132
135
|
body = @tempfile.call(filename, content_type)
|
|
133
136
|
body.binmode if body.respond_to?(:binmode)
|
|
@@ -143,11 +146,11 @@ module Rack
|
|
|
143
146
|
check_part_limits
|
|
144
147
|
end
|
|
145
148
|
|
|
146
|
-
def on_mime_body
|
|
149
|
+
def on_mime_body(mime_index, content)
|
|
147
150
|
@mime_parts[mime_index].body << content
|
|
148
151
|
end
|
|
149
152
|
|
|
150
|
-
def on_mime_finish
|
|
153
|
+
def on_mime_finish(mime_index)
|
|
151
154
|
end
|
|
152
155
|
|
|
153
156
|
private
|
|
@@ -175,25 +178,26 @@ module Rack
|
|
|
175
178
|
attr_reader :state
|
|
176
179
|
|
|
177
180
|
def initialize(boundary, tempfile, bufsize, query_parser)
|
|
178
|
-
@buf = String.new
|
|
179
|
-
|
|
180
181
|
@query_parser = query_parser
|
|
181
182
|
@params = query_parser.make_params
|
|
182
183
|
@boundary = "--#{boundary}"
|
|
183
184
|
@bufsize = bufsize
|
|
184
185
|
|
|
185
|
-
@rx = /(?:#{EOL})?#{Regexp.quote(@boundary)}(#{EOL}|--)/n
|
|
186
|
-
@rx_max_size = EOL.size + @boundary.bytesize + [EOL.size, '--'.size].max
|
|
187
186
|
@full_boundary = @boundary
|
|
188
187
|
@end_boundary = @boundary + '--'
|
|
189
188
|
@state = :FAST_FORWARD
|
|
190
189
|
@mime_index = 0
|
|
191
190
|
@collector = Collector.new tempfile
|
|
191
|
+
|
|
192
|
+
@sbuf = StringScanner.new("".dup)
|
|
193
|
+
@body_regex = /(?:#{EOL})?#{Regexp.quote(@boundary)}(?:#{EOL}|--)/m
|
|
194
|
+
@rx_max_size = EOL.size + @boundary.bytesize + [EOL.size, '--'.size].max
|
|
195
|
+
@head_regex = /(.*?#{EOL})#{EOL}/m
|
|
192
196
|
end
|
|
193
197
|
|
|
194
|
-
def on_read
|
|
198
|
+
def on_read(content)
|
|
195
199
|
handle_empty_content!(content)
|
|
196
|
-
@
|
|
200
|
+
@sbuf.concat content
|
|
197
201
|
run_parser
|
|
198
202
|
end
|
|
199
203
|
|
|
@@ -204,7 +208,6 @@ module Rack
|
|
|
204
208
|
@query_parser.normalize_params(@params, part.name, data, @query_parser.param_depth_limit)
|
|
205
209
|
end
|
|
206
210
|
end
|
|
207
|
-
|
|
208
211
|
MultipartInfo.new @params.to_params_hash, @collector.find_all(&:file?).map(&:body)
|
|
209
212
|
end
|
|
210
213
|
|
|
@@ -231,7 +234,7 @@ module Rack
|
|
|
231
234
|
if consume_boundary
|
|
232
235
|
@state = :MIME_HEAD
|
|
233
236
|
else
|
|
234
|
-
raise EOFError, "bad content body" if @
|
|
237
|
+
raise EOFError, "bad content body" if @sbuf.rest_size >= @bufsize
|
|
235
238
|
:want_read
|
|
236
239
|
end
|
|
237
240
|
end
|
|
@@ -239,19 +242,16 @@ module Rack
|
|
|
239
242
|
def handle_consume_token
|
|
240
243
|
tok = consume_boundary
|
|
241
244
|
# 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
|
-
|
|
245
|
+
@state = if tok == :END_BOUNDARY || (@sbuf.eos? && tok != :BOUNDARY)
|
|
246
|
+
:DONE
|
|
244
247
|
else
|
|
245
|
-
|
|
248
|
+
:MIME_HEAD
|
|
246
249
|
end
|
|
247
250
|
end
|
|
248
251
|
|
|
249
252
|
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
|
-
|
|
253
|
+
if @sbuf.scan_until(@head_regex)
|
|
254
|
+
head = @sbuf[1]
|
|
255
255
|
content_type = head[MULTIPART_CONTENT_TYPE, 1]
|
|
256
256
|
if name = head[MULTIPART_CONTENT_DISPOSITION, 1]
|
|
257
257
|
name = Rack::Auth::Digest::Params::dequote(name)
|
|
@@ -262,7 +262,7 @@ module Rack
|
|
|
262
262
|
filename = get_filename(head)
|
|
263
263
|
|
|
264
264
|
if name.nil? || name.empty?
|
|
265
|
-
name = filename || "#{content_type || TEXT_PLAIN}[]"
|
|
265
|
+
name = filename || "#{content_type || TEXT_PLAIN}[]".dup
|
|
266
266
|
end
|
|
267
267
|
|
|
268
268
|
@collector.on_mime_head @mime_index, head, filename, content_type, name
|
|
@@ -273,16 +273,19 @@ module Rack
|
|
|
273
273
|
end
|
|
274
274
|
|
|
275
275
|
def handle_mime_body
|
|
276
|
-
if
|
|
277
|
-
#
|
|
278
|
-
@collector.on_mime_body @mime_index,
|
|
279
|
-
@
|
|
276
|
+
if (body_with_boundary = @sbuf.check_until(@body_regex)) # check but do not advance the pointer yet
|
|
277
|
+
body = body_with_boundary.sub(/#{@body_regex}\z/m, '') # remove the boundary from the string
|
|
278
|
+
@collector.on_mime_body @mime_index, body
|
|
279
|
+
@sbuf.pos += body.length + 2 # skip \r\n after the content
|
|
280
280
|
@state = :CONSUME_TOKEN
|
|
281
281
|
@mime_index += 1
|
|
282
282
|
else
|
|
283
|
-
# Save
|
|
284
|
-
if @rx_max_size < @
|
|
285
|
-
|
|
283
|
+
# Save what we have so far
|
|
284
|
+
if @rx_max_size < @sbuf.rest_size
|
|
285
|
+
delta = @sbuf.rest_size - @rx_max_size
|
|
286
|
+
@collector.on_mime_body @mime_index, @sbuf.peek(delta)
|
|
287
|
+
@sbuf.pos += delta
|
|
288
|
+
@sbuf.string = @sbuf.rest
|
|
286
289
|
end
|
|
287
290
|
:want_read
|
|
288
291
|
end
|
|
@@ -290,16 +293,13 @@ module Rack
|
|
|
290
293
|
|
|
291
294
|
def full_boundary; @full_boundary; end
|
|
292
295
|
|
|
293
|
-
def rx; @rx; end
|
|
294
|
-
|
|
295
296
|
def consume_boundary
|
|
296
|
-
while @
|
|
297
|
-
read_buffer = $1
|
|
297
|
+
while read_buffer = @sbuf.scan_until(BOUNDARY_REGEX)
|
|
298
298
|
case read_buffer.strip
|
|
299
299
|
when full_boundary then return :BOUNDARY
|
|
300
300
|
when @end_boundary then return :END_BOUNDARY
|
|
301
301
|
end
|
|
302
|
-
return if @
|
|
302
|
+
return if @sbuf.eos?
|
|
303
303
|
end
|
|
304
304
|
end
|
|
305
305
|
|
|
@@ -321,8 +321,8 @@ module Rack
|
|
|
321
321
|
|
|
322
322
|
return unless filename
|
|
323
323
|
|
|
324
|
-
if filename.scan(/%.?.?/).all? { |s|
|
|
325
|
-
filename = Utils.
|
|
324
|
+
if filename.scan(/%.?.?/).all? { |s| /%[0-9a-fA-F]{2}/.match?(s) }
|
|
325
|
+
filename = Utils.unescape_path(filename)
|
|
326
326
|
end
|
|
327
327
|
|
|
328
328
|
filename.scrub!
|
|
@@ -338,7 +338,7 @@ module Rack
|
|
|
338
338
|
filename
|
|
339
339
|
end
|
|
340
340
|
|
|
341
|
-
CHARSET
|
|
341
|
+
CHARSET = "charset"
|
|
342
342
|
|
|
343
343
|
def tag_multipart_encoding(filename, content_type, name, body)
|
|
344
344
|
name = name.to_s
|
|
@@ -353,12 +353,12 @@ module Rack
|
|
|
353
353
|
type_subtype = list.first
|
|
354
354
|
type_subtype.strip!
|
|
355
355
|
if TEXT_PLAIN == type_subtype
|
|
356
|
-
rest
|
|
356
|
+
rest = list.drop 1
|
|
357
357
|
rest.each do |param|
|
|
358
|
-
k,v = param.split('=', 2)
|
|
358
|
+
k, v = param.split('=', 2)
|
|
359
359
|
k.strip!
|
|
360
360
|
v.strip!
|
|
361
|
-
v = v[1..-2] if v
|
|
361
|
+
v = v[1..-2] if v.start_with?('"') && v.end_with?('"')
|
|
362
362
|
encoding = Encoding.find v if k == CHARSET
|
|
363
363
|
end
|
|
364
364
|
end
|
|
@@ -368,7 +368,6 @@ module Rack
|
|
|
368
368
|
body.force_encoding(encoding)
|
|
369
369
|
end
|
|
370
370
|
|
|
371
|
-
|
|
372
371
|
def handle_empty_content!(content)
|
|
373
372
|
if content.nil? || content.empty?
|
|
374
373
|
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.
|
|
@@ -16,7 +18,7 @@ module Rack
|
|
|
16
18
|
VALUE = /"(?:\\"|[^"])*"|#{TOKEN}/
|
|
17
19
|
BROKEN = /^#{CONDISP}.*;\s*filename=(#{VALUE})/i
|
|
18
20
|
MULTIPART_CONTENT_TYPE = /Content-Type: (.*)#{EOL}/ni
|
|
19
|
-
MULTIPART_CONTENT_DISPOSITION = /Content-Disposition:[^:]*;\s
|
|
21
|
+
MULTIPART_CONTENT_DISPOSITION = /Content-Disposition:[^:]*;\s*name=(#{VALUE})/ni
|
|
20
22
|
MULTIPART_CONTENT_ID = /Content-ID:\s*([^#{EOL}]*)/ni
|
|
21
23
|
# Updated definitions from RFC 2231
|
|
22
24
|
ATTRIBUTE_CHAR = %r{[^ \x00-\x1f\x7f)(><@,;:\\"/\[\]?='*%]}
|
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
|