rack 2.0.3 → 2.2.4
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 +5 -5
- data/CHANGELOG.md +708 -0
- data/CONTRIBUTING.md +136 -0
- data/{COPYING → MIT-LICENSE} +4 -2
- data/README.rdoc +152 -150
- data/Rakefile +37 -23
- data/{SPEC → SPEC.rdoc} +35 -10
- 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 +8 -5
- 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 +14 -4
- 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 +98 -21
- data/lib/rack/multipart/generator.rb +17 -13
- data/lib/rack/multipart/parser.rb +61 -63
- data/lib/rack/multipart/uploaded_file.rb +15 -7
- data/lib/rack/multipart.rb +5 -4
- 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 +228 -56
- 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 +22 -18
- 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 +12 -6
- data/lib/rack/utils.rb +107 -111
- data/lib/rack/version.rb +29 -0
- data/lib/rack.rb +67 -73
- data/rack.gemspec +40 -29
- metadata +25 -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 -194
- 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 -209
- 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,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Rack
|
|
2
4
|
module Multipart
|
|
3
5
|
class Generator
|
|
@@ -15,9 +17,13 @@ module Rack
|
|
|
15
17
|
|
|
16
18
|
flattened_params.map do |name, file|
|
|
17
19
|
if file.respond_to?(:original_filename)
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
20
|
+
if file.path
|
|
21
|
+
::File.open(file.path, 'rb') do |f|
|
|
22
|
+
f.set_encoding(Encoding::BINARY)
|
|
23
|
+
content_for_tempfile(f, file, name)
|
|
24
|
+
end
|
|
25
|
+
else
|
|
26
|
+
content_for_tempfile(file, file, name)
|
|
21
27
|
end
|
|
22
28
|
else
|
|
23
29
|
content_for_other(file, name)
|
|
@@ -27,21 +33,18 @@ module Rack
|
|
|
27
33
|
|
|
28
34
|
private
|
|
29
35
|
def multipart?
|
|
30
|
-
multipart = false
|
|
31
|
-
|
|
32
36
|
query = lambda { |value|
|
|
33
37
|
case value
|
|
34
38
|
when Array
|
|
35
|
-
value.
|
|
39
|
+
value.any?(&query)
|
|
36
40
|
when Hash
|
|
37
|
-
value.values.
|
|
41
|
+
value.values.any?(&query)
|
|
38
42
|
when Rack::Multipart::UploadedFile
|
|
39
|
-
|
|
43
|
+
true
|
|
40
44
|
end
|
|
41
45
|
}
|
|
42
|
-
@params.values.each(&query)
|
|
43
46
|
|
|
44
|
-
|
|
47
|
+
@params.values.any?(&query)
|
|
45
48
|
end
|
|
46
49
|
|
|
47
50
|
def flattened_params
|
|
@@ -70,12 +73,13 @@ module Rack
|
|
|
70
73
|
end
|
|
71
74
|
|
|
72
75
|
def content_for_tempfile(io, file, name)
|
|
76
|
+
length = ::File.stat(file.path).size if file.path
|
|
77
|
+
filename = "; filename=\"#{Utils.escape(file.original_filename)}\"" if file.original_filename
|
|
73
78
|
<<-EOF
|
|
74
79
|
--#{MULTIPART_BOUNDARY}\r
|
|
75
|
-
Content-Disposition: form-data; name="#{name}"
|
|
80
|
+
Content-Disposition: form-data; name="#{name}"#{filename}\r
|
|
76
81
|
Content-Type: #{file.content_type}\r
|
|
77
|
-
Content-Length: #{
|
|
78
|
-
\r
|
|
82
|
+
#{"Content-Length: #{length}\r\n" if length}\r
|
|
79
83
|
#{io.read}\r
|
|
80
84
|
EOF
|
|
81
85
|
end
|
|
@@ -1,16 +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
8
|
|
|
7
9
|
class Parser
|
|
8
|
-
|
|
10
|
+
(require_relative '../core_ext/regexp'; using ::Rack::RegexpExtensions) if RUBY_VERSION < '2.4'
|
|
11
|
+
|
|
12
|
+
BUFSIZE = 1_048_576
|
|
9
13
|
TEXT_PLAIN = "text/plain"
|
|
10
14
|
TEMPFILE_FACTORY = lambda { |filename, content_type|
|
|
11
|
-
Tempfile.new(["RackMultipart", ::File.extname(filename.gsub("\0"
|
|
15
|
+
Tempfile.new(["RackMultipart", ::File.extname(filename.gsub("\0", '%00'))])
|
|
12
16
|
}
|
|
13
17
|
|
|
18
|
+
BOUNDARY_REGEX = /\A([^\n]*(?:\n|\Z))/
|
|
19
|
+
|
|
14
20
|
class BoundedIO # :nodoc:
|
|
15
21
|
def initialize(io, content_length)
|
|
16
22
|
@io = io
|
|
@@ -18,15 +24,15 @@ module Rack
|
|
|
18
24
|
@cursor = 0
|
|
19
25
|
end
|
|
20
26
|
|
|
21
|
-
def read(size)
|
|
27
|
+
def read(size, outbuf = nil)
|
|
22
28
|
return if @cursor >= @content_length
|
|
23
29
|
|
|
24
30
|
left = @content_length - @cursor
|
|
25
31
|
|
|
26
32
|
str = if left < size
|
|
27
|
-
@io.read left
|
|
33
|
+
@io.read left, outbuf
|
|
28
34
|
else
|
|
29
|
-
@io.read size
|
|
35
|
+
@io.read size, outbuf
|
|
30
36
|
end
|
|
31
37
|
|
|
32
38
|
if str
|
|
@@ -39,8 +45,6 @@ module Rack
|
|
|
39
45
|
str
|
|
40
46
|
end
|
|
41
47
|
|
|
42
|
-
def eof?; @content_length == @cursor; end
|
|
43
|
-
|
|
44
48
|
def rewind
|
|
45
49
|
@io.rewind
|
|
46
50
|
end
|
|
@@ -63,13 +67,14 @@ module Rack
|
|
|
63
67
|
return EMPTY unless boundary
|
|
64
68
|
|
|
65
69
|
io = BoundedIO.new(io, content_length) if content_length
|
|
70
|
+
outbuf = String.new
|
|
66
71
|
|
|
67
72
|
parser = new(boundary, tmpfile, bufsize, qp)
|
|
68
|
-
parser.on_read io.read(bufsize
|
|
73
|
+
parser.on_read io.read(bufsize, outbuf)
|
|
69
74
|
|
|
70
75
|
loop do
|
|
71
76
|
break if parser.state == :DONE
|
|
72
|
-
parser.on_read io.read(bufsize
|
|
77
|
+
parser.on_read io.read(bufsize, outbuf)
|
|
73
78
|
end
|
|
74
79
|
|
|
75
80
|
io.rewind
|
|
@@ -92,14 +97,8 @@ module Rack
|
|
|
92
97
|
# those which give the lone filename.
|
|
93
98
|
fn = filename.split(/[\/\\]/).last
|
|
94
99
|
|
|
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}
|
|
100
|
+
data = { filename: fn, type: content_type,
|
|
101
|
+
name: name, tempfile: body, head: head }
|
|
103
102
|
end
|
|
104
103
|
|
|
105
104
|
yield data
|
|
@@ -118,7 +117,7 @@ module Rack
|
|
|
118
117
|
|
|
119
118
|
include Enumerable
|
|
120
119
|
|
|
121
|
-
def initialize
|
|
120
|
+
def initialize(tempfile)
|
|
122
121
|
@tempfile = tempfile
|
|
123
122
|
@mime_parts = []
|
|
124
123
|
@open_files = 0
|
|
@@ -128,26 +127,27 @@ module Rack
|
|
|
128
127
|
@mime_parts.each { |part| yield part }
|
|
129
128
|
end
|
|
130
129
|
|
|
131
|
-
def on_mime_head
|
|
130
|
+
def on_mime_head(mime_index, head, filename, content_type, name)
|
|
132
131
|
if filename
|
|
133
132
|
body = @tempfile.call(filename, content_type)
|
|
134
133
|
body.binmode if body.respond_to?(:binmode)
|
|
135
134
|
klass = TempfilePart
|
|
136
135
|
@open_files += 1
|
|
137
136
|
else
|
|
138
|
-
body =
|
|
137
|
+
body = String.new
|
|
139
138
|
klass = BufferPart
|
|
140
139
|
end
|
|
141
140
|
|
|
142
141
|
@mime_parts[mime_index] = klass.new(body, head, filename, content_type, name)
|
|
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
|
|
@@ -165,25 +165,26 @@ module Rack
|
|
|
165
165
|
attr_reader :state
|
|
166
166
|
|
|
167
167
|
def initialize(boundary, tempfile, bufsize, query_parser)
|
|
168
|
-
@buf = "".force_encoding(Encoding::ASCII_8BIT)
|
|
169
|
-
|
|
170
168
|
@query_parser = query_parser
|
|
171
169
|
@params = query_parser.make_params
|
|
172
170
|
@boundary = "--#{boundary}"
|
|
173
|
-
@boundary_size = @boundary.bytesize + EOL.size
|
|
174
171
|
@bufsize = bufsize
|
|
175
172
|
|
|
176
|
-
@rx = /(?:#{EOL})?#{Regexp.quote(@boundary)}(#{EOL}|--)/n
|
|
177
173
|
@full_boundary = @boundary
|
|
178
174
|
@end_boundary = @boundary + '--'
|
|
179
175
|
@state = :FAST_FORWARD
|
|
180
176
|
@mime_index = 0
|
|
181
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
|
|
182
183
|
end
|
|
183
184
|
|
|
184
|
-
def on_read
|
|
185
|
-
handle_empty_content!(content
|
|
186
|
-
@
|
|
185
|
+
def on_read(content)
|
|
186
|
+
handle_empty_content!(content)
|
|
187
|
+
@sbuf.concat content
|
|
187
188
|
run_parser
|
|
188
189
|
end
|
|
189
190
|
|
|
@@ -194,7 +195,6 @@ module Rack
|
|
|
194
195
|
@query_parser.normalize_params(@params, part.name, data, @query_parser.param_depth_limit)
|
|
195
196
|
end
|
|
196
197
|
end
|
|
197
|
-
|
|
198
198
|
MultipartInfo.new @params.to_params_hash, @collector.find_all(&:file?).map(&:body)
|
|
199
199
|
end
|
|
200
200
|
|
|
@@ -221,7 +221,7 @@ module Rack
|
|
|
221
221
|
if consume_boundary
|
|
222
222
|
@state = :MIME_HEAD
|
|
223
223
|
else
|
|
224
|
-
raise EOFError, "bad content body" if @
|
|
224
|
+
raise EOFError, "bad content body" if @sbuf.rest_size >= @bufsize
|
|
225
225
|
:want_read
|
|
226
226
|
end
|
|
227
227
|
end
|
|
@@ -229,19 +229,16 @@ module Rack
|
|
|
229
229
|
def handle_consume_token
|
|
230
230
|
tok = consume_boundary
|
|
231
231
|
# 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
|
-
|
|
232
|
+
@state = if tok == :END_BOUNDARY || (@sbuf.eos? && tok != :BOUNDARY)
|
|
233
|
+
:DONE
|
|
234
234
|
else
|
|
235
|
-
|
|
235
|
+
:MIME_HEAD
|
|
236
236
|
end
|
|
237
237
|
end
|
|
238
238
|
|
|
239
239
|
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
|
-
|
|
240
|
+
if @sbuf.scan_until(@head_regex)
|
|
241
|
+
head = @sbuf[1]
|
|
245
242
|
content_type = head[MULTIPART_CONTENT_TYPE, 1]
|
|
246
243
|
if name = head[MULTIPART_CONTENT_DISPOSITION, 1]
|
|
247
244
|
name = Rack::Auth::Digest::Params::dequote(name)
|
|
@@ -252,7 +249,7 @@ module Rack
|
|
|
252
249
|
filename = get_filename(head)
|
|
253
250
|
|
|
254
251
|
if name.nil? || name.empty?
|
|
255
|
-
name = filename || "#{content_type || TEXT_PLAIN}[]"
|
|
252
|
+
name = filename || "#{content_type || TEXT_PLAIN}[]".dup
|
|
256
253
|
end
|
|
257
254
|
|
|
258
255
|
@collector.on_mime_head @mime_index, head, filename, content_type, name
|
|
@@ -263,31 +260,33 @@ module Rack
|
|
|
263
260
|
end
|
|
264
261
|
|
|
265
262
|
def handle_mime_body
|
|
266
|
-
if @
|
|
267
|
-
#
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
@buf.slice!(0, 2) # Remove \r\n after the content
|
|
271
|
-
end
|
|
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
|
|
272
267
|
@state = :CONSUME_TOKEN
|
|
273
268
|
@mime_index += 1
|
|
274
269
|
else
|
|
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
|
|
276
|
+
end
|
|
275
277
|
:want_read
|
|
276
278
|
end
|
|
277
279
|
end
|
|
278
280
|
|
|
279
281
|
def full_boundary; @full_boundary; end
|
|
280
282
|
|
|
281
|
-
def rx; @rx; end
|
|
282
|
-
|
|
283
283
|
def consume_boundary
|
|
284
|
-
while @
|
|
285
|
-
read_buffer = $1
|
|
284
|
+
while read_buffer = @sbuf.scan_until(BOUNDARY_REGEX)
|
|
286
285
|
case read_buffer.strip
|
|
287
286
|
when full_boundary then return :BOUNDARY
|
|
288
287
|
when @end_boundary then return :END_BOUNDARY
|
|
289
288
|
end
|
|
290
|
-
return if @
|
|
289
|
+
return if @sbuf.eos?
|
|
291
290
|
end
|
|
292
291
|
end
|
|
293
292
|
|
|
@@ -302,14 +301,15 @@ module Rack
|
|
|
302
301
|
elsif filename = params['filename*']
|
|
303
302
|
encoding, _, filename = filename.split("'", 3)
|
|
304
303
|
end
|
|
305
|
-
when
|
|
304
|
+
when BROKEN
|
|
306
305
|
filename = $1
|
|
306
|
+
filename = $1 if filename =~ /^"(.*)"$/
|
|
307
307
|
end
|
|
308
308
|
|
|
309
309
|
return unless filename
|
|
310
310
|
|
|
311
|
-
if filename.scan(/%.?.?/).all? { |s|
|
|
312
|
-
filename = Utils.
|
|
311
|
+
if filename.scan(/%.?.?/).all? { |s| /%[0-9a-fA-F]{2}/.match?(s) }
|
|
312
|
+
filename = Utils.unescape_path(filename)
|
|
313
313
|
end
|
|
314
314
|
|
|
315
315
|
filename.scrub!
|
|
@@ -325,7 +325,7 @@ module Rack
|
|
|
325
325
|
filename
|
|
326
326
|
end
|
|
327
327
|
|
|
328
|
-
CHARSET
|
|
328
|
+
CHARSET = "charset"
|
|
329
329
|
|
|
330
330
|
def tag_multipart_encoding(filename, content_type, name, body)
|
|
331
331
|
name = name.to_s
|
|
@@ -340,12 +340,12 @@ module Rack
|
|
|
340
340
|
type_subtype = list.first
|
|
341
341
|
type_subtype.strip!
|
|
342
342
|
if TEXT_PLAIN == type_subtype
|
|
343
|
-
rest
|
|
343
|
+
rest = list.drop 1
|
|
344
344
|
rest.each do |param|
|
|
345
|
-
k,v = param.split('=', 2)
|
|
345
|
+
k, v = param.split('=', 2)
|
|
346
346
|
k.strip!
|
|
347
347
|
v.strip!
|
|
348
|
-
v = v[1..-2] if v
|
|
348
|
+
v = v[1..-2] if v.start_with?('"') && v.end_with?('"')
|
|
349
349
|
encoding = Encoding.find v if k == CHARSET
|
|
350
350
|
end
|
|
351
351
|
end
|
|
@@ -355,11 +355,9 @@ module Rack
|
|
|
355
355
|
body.force_encoding(encoding)
|
|
356
356
|
end
|
|
357
357
|
|
|
358
|
-
|
|
359
|
-
def handle_empty_content!(content, eof)
|
|
358
|
+
def handle_empty_content!(content)
|
|
360
359
|
if content.nil? || content.empty?
|
|
361
|
-
raise EOFError
|
|
362
|
-
return true
|
|
360
|
+
raise EOFError
|
|
363
361
|
end
|
|
364
362
|
end
|
|
365
363
|
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,10 +16,9 @@ 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
24
|
ATTRIBUTE_CHAR = %r{[^ \t\v\n\r)(><@,;:\\"/\[\]?='*%]}
|
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
|