rack 1.6.13 → 2.0.9.3
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/COPYING +1 -1
- data/HISTORY.md +153 -8
- data/README.rdoc +35 -31
- data/Rakefile +6 -14
- data/SPEC +10 -11
- data/contrib/rack_logo.svg +164 -111
- data/example/protectedlobster.rb +1 -1
- data/example/protectedlobster.ru +1 -1
- data/lib/rack/auth/abstract/request.rb +5 -1
- data/lib/rack/auth/digest/params.rb +2 -3
- data/lib/rack/auth/digest/request.rb +1 -1
- data/lib/rack/body_proxy.rb +14 -9
- data/lib/rack/builder.rb +3 -3
- data/lib/rack/chunked.rb +5 -5
- data/lib/rack/{commonlogger.rb → common_logger.rb} +6 -3
- data/lib/rack/content_length.rb +2 -2
- data/lib/rack/deflater.rb +4 -39
- data/lib/rack/directory.rb +66 -54
- data/lib/rack/etag.rb +5 -4
- data/lib/rack/events.rb +154 -0
- data/lib/rack/file.rb +64 -40
- data/lib/rack/handler/cgi.rb +15 -16
- data/lib/rack/handler/fastcgi.rb +13 -14
- data/lib/rack/handler/lsws.rb +11 -11
- data/lib/rack/handler/scgi.rb +15 -15
- data/lib/rack/handler/thin.rb +3 -0
- data/lib/rack/handler/webrick.rb +24 -26
- data/lib/rack/handler.rb +3 -25
- data/lib/rack/head.rb +15 -17
- data/lib/rack/lint.rb +41 -41
- data/lib/rack/lobster.rb +1 -1
- data/lib/rack/lock.rb +15 -10
- data/lib/rack/logger.rb +2 -2
- data/lib/rack/media_type.rb +38 -0
- data/lib/rack/{methodoverride.rb → method_override.rb} +6 -6
- data/lib/rack/mime.rb +18 -5
- data/lib/rack/mock.rb +36 -54
- data/lib/rack/multipart/generator.rb +5 -5
- data/lib/rack/multipart/parser.rb +283 -157
- data/lib/rack/multipart/uploaded_file.rb +1 -2
- data/lib/rack/multipart.rb +36 -8
- data/lib/rack/{nulllogger.rb → null_logger.rb} +1 -1
- data/lib/rack/query_parser.rb +192 -0
- data/lib/rack/recursive.rb +8 -8
- data/lib/rack/request.rb +394 -305
- data/lib/rack/response.rb +130 -57
- data/lib/rack/rewindable_input.rb +1 -12
- data/lib/rack/runtime.rb +10 -18
- data/lib/rack/sendfile.rb +5 -7
- data/lib/rack/server.rb +30 -23
- data/lib/rack/session/abstract/id.rb +110 -75
- data/lib/rack/session/cookie.rb +24 -17
- data/lib/rack/session/memcache.rb +9 -9
- data/lib/rack/session/pool.rb +8 -8
- data/lib/rack/show_exceptions.rb +386 -0
- data/lib/rack/{showstatus.rb → show_status.rb} +3 -3
- data/lib/rack/static.rb +30 -5
- data/lib/rack/tempfile_reaper.rb +2 -2
- data/lib/rack/urlmap.rb +15 -14
- data/lib/rack/utils.rb +156 -217
- data/lib/rack.rb +70 -21
- data/rack.gemspec +10 -9
- data/test/builder/an_underscore_app.rb +5 -0
- data/test/builder/options.ru +1 -1
- data/test/cgi/test.fcgi +1 -0
- data/test/cgi/test.gz +0 -0
- data/test/helper.rb +34 -0
- data/test/multipart/filename_with_encoded_words +7 -0
- data/test/multipart/filename_with_escaped_quotes_and_modification_param +1 -1
- data/test/multipart/filename_with_single_quote +7 -0
- data/test/multipart/quoted +15 -0
- data/test/multipart/rack-logo.png +0 -0
- data/test/multipart/unity3d_wwwform +11 -0
- data/test/registering_handler/rack/handler/registering_myself.rb +1 -1
- data/test/spec_auth_basic.rb +27 -19
- data/test/spec_auth_digest.rb +47 -46
- data/test/spec_body_proxy.rb +27 -27
- data/test/spec_builder.rb +51 -41
- data/test/spec_cascade.rb +24 -22
- data/test/spec_cgi.rb +49 -67
- data/test/spec_chunked.rb +37 -35
- data/test/{spec_commonlogger.rb → spec_common_logger.rb} +35 -21
- data/test/{spec_conditionalget.rb → spec_conditional_get.rb} +29 -28
- data/test/spec_config.rb +3 -2
- data/test/spec_content_length.rb +18 -17
- data/test/spec_content_type.rb +13 -12
- data/test/spec_deflater.rb +85 -49
- data/test/spec_directory.rb +87 -27
- data/test/spec_etag.rb +32 -31
- data/test/spec_events.rb +133 -0
- data/test/spec_fastcgi.rb +50 -72
- data/test/spec_file.rb +120 -77
- data/test/spec_handler.rb +19 -34
- data/test/spec_head.rb +15 -14
- data/test/spec_lint.rb +169 -199
- data/test/spec_lobster.rb +24 -23
- data/test/spec_lock.rb +79 -39
- data/test/spec_logger.rb +4 -3
- data/test/spec_media_type.rb +42 -0
- data/test/{spec_methodoverride.rb → spec_method_override.rb} +34 -35
- data/test/spec_mime.rb +19 -19
- data/test/spec_mock.rb +206 -144
- data/test/spec_multipart.rb +329 -208
- data/test/{spec_nulllogger.rb → spec_null_logger.rb} +5 -4
- data/test/spec_recursive.rb +17 -14
- data/test/spec_request.rb +796 -605
- data/test/spec_response.rb +233 -112
- data/test/spec_rewindable_input.rb +50 -40
- data/test/spec_runtime.rb +11 -10
- data/test/spec_sendfile.rb +30 -35
- data/test/spec_server.rb +78 -52
- data/test/spec_session_abstract_id.rb +11 -33
- data/test/spec_session_abstract_session_hash.rb +45 -0
- data/test/spec_session_cookie.rb +99 -67
- data/test/spec_session_memcache.rb +67 -68
- data/test/spec_session_pool.rb +52 -51
- data/test/{spec_showexceptions.rb → spec_show_exceptions.rb} +23 -28
- data/test/{spec_showstatus.rb → spec_show_status.rb} +36 -35
- data/test/spec_static.rb +71 -32
- data/test/spec_tempfile_reaper.rb +11 -10
- data/test/spec_thin.rb +55 -50
- data/test/spec_urlmap.rb +79 -78
- data/test/spec_utils.rb +441 -346
- data/test/spec_version.rb +2 -8
- data/test/spec_webrick.rb +93 -71
- data/test/static/foo.html +1 -0
- data/test/testrequest.rb +1 -1
- data/test/unregistered_handler/rack/handler/unregistered.rb +1 -1
- data/test/unregistered_handler/rack/handler/unregistered_long_one.rb +1 -1
- metadata +95 -74
- data/KNOWN-ISSUES +0 -44
- data/lib/rack/backports/uri/common_18.rb +0 -56
- data/lib/rack/backports/uri/common_192.rb +0 -52
- data/lib/rack/backports/uri/common_193.rb +0 -29
- data/lib/rack/handler/evented_mongrel.rb +0 -8
- data/lib/rack/handler/mongrel.rb +0 -106
- data/lib/rack/handler/swiftiplied_mongrel.rb +0 -8
- data/lib/rack/showexceptions.rb +0 -387
- data/lib/rack/utils/okjson.rb +0 -600
- data/test/spec_mongrel.rb +0 -182
- /data/lib/rack/{conditionalget.rb → conditional_get.rb} +0 -0
data/test/spec_multipart.rb
CHANGED
@@ -1,3 +1,9 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require 'minitest/autorun'
|
4
|
+
require 'rack'
|
5
|
+
require 'rack/multipart'
|
6
|
+
require 'rack/multipart/parser'
|
1
7
|
require 'rack/utils'
|
2
8
|
require 'rack/mock'
|
3
9
|
|
@@ -6,8 +12,8 @@ describe Rack::Multipart do
|
|
6
12
|
file = multipart_file(name)
|
7
13
|
data = File.open(file, 'rb') { |io| io.read }
|
8
14
|
|
9
|
-
type =
|
10
|
-
length = data.
|
15
|
+
type = %(multipart/form-data; boundary=#{boundary})
|
16
|
+
length = data.bytesize
|
11
17
|
|
12
18
|
{ "CONTENT_TYPE" => type,
|
13
19
|
"CONTENT_LENGTH" => length.to_s,
|
@@ -18,75 +24,81 @@ describe Rack::Multipart do
|
|
18
24
|
File.join(File.dirname(__FILE__), "multipart", name.to_s)
|
19
25
|
end
|
20
26
|
|
21
|
-
|
27
|
+
it "return nil if content type is not multipart" do
|
22
28
|
env = Rack::MockRequest.env_for("/",
|
23
29
|
"CONTENT_TYPE" => 'application/x-www-form-urlencoded')
|
24
|
-
Rack::Multipart.parse_multipart(env).
|
30
|
+
Rack::Multipart.parse_multipart(env).must_be_nil
|
25
31
|
end
|
26
32
|
|
27
|
-
|
33
|
+
it "parse multipart content when content type present but filename is not" do
|
28
34
|
env = Rack::MockRequest.env_for("/", multipart_fixture(:content_type_and_no_filename))
|
29
35
|
params = Rack::Multipart.parse_multipart(env)
|
30
|
-
params["text"].
|
36
|
+
params["text"].must_equal "contents"
|
31
37
|
end
|
32
38
|
|
33
|
-
|
34
|
-
should "set US_ASCII encoding based on charset" do
|
39
|
+
it "set US_ASCII encoding based on charset" do
|
35
40
|
env = Rack::MockRequest.env_for("/", multipart_fixture(:content_type_and_no_filename))
|
36
41
|
params = Rack::Multipart.parse_multipart(env)
|
37
|
-
params["text"].encoding.
|
42
|
+
params["text"].encoding.must_equal Encoding::US_ASCII
|
38
43
|
|
39
44
|
# I'm not 100% sure if making the param name encoding match the
|
40
45
|
# Content-Type charset is the right thing to do. We should revisit this.
|
41
46
|
params.keys.each do |key|
|
42
|
-
key.encoding.
|
47
|
+
key.encoding.must_equal Encoding::US_ASCII
|
43
48
|
end
|
44
49
|
end
|
45
50
|
|
46
|
-
|
51
|
+
it "set BINARY encoding on things without content type" do
|
47
52
|
env = Rack::MockRequest.env_for("/", multipart_fixture(:none))
|
48
53
|
params = Rack::Multipart.parse_multipart(env)
|
49
|
-
params["submit-name"].encoding.
|
54
|
+
params["submit-name"].encoding.must_equal Encoding::UTF_8
|
50
55
|
end
|
51
56
|
|
52
|
-
|
57
|
+
it "set UTF8 encoding on names of things without content type" do
|
53
58
|
env = Rack::MockRequest.env_for("/", multipart_fixture(:none))
|
54
59
|
params = Rack::Multipart.parse_multipart(env)
|
55
60
|
params.keys.each do |key|
|
56
|
-
key.encoding.
|
61
|
+
key.encoding.must_equal Encoding::UTF_8
|
57
62
|
end
|
58
63
|
end
|
59
64
|
|
60
|
-
|
65
|
+
it "default text to UTF8" do
|
61
66
|
env = Rack::MockRequest.env_for("/", multipart_fixture(:text))
|
62
67
|
params = Rack::Multipart.parse_multipart(env)
|
63
|
-
params['submit-name'].encoding.
|
64
|
-
params['submit-name-with-content'].encoding.
|
68
|
+
params['submit-name'].encoding.must_equal Encoding::UTF_8
|
69
|
+
params['submit-name-with-content'].encoding.must_equal Encoding::UTF_8
|
65
70
|
params.keys.each do |key|
|
66
|
-
key.encoding.
|
71
|
+
key.encoding.must_equal Encoding::UTF_8
|
67
72
|
end
|
68
73
|
end
|
74
|
+
|
75
|
+
it "handles quoted encodings" do
|
76
|
+
# See #905
|
77
|
+
env = Rack::MockRequest.env_for("/", multipart_fixture(:unity3d_wwwform))
|
78
|
+
params = Rack::Multipart.parse_multipart(env)
|
79
|
+
params['user_sid'].encoding.must_equal Encoding::UTF_8
|
69
80
|
end
|
70
81
|
|
71
|
-
|
82
|
+
it "raise RangeError if the key space is exhausted" do
|
72
83
|
env = Rack::MockRequest.env_for("/", multipart_fixture(:content_type_and_no_filename))
|
73
84
|
|
74
85
|
old, Rack::Utils.key_space_limit = Rack::Utils.key_space_limit, 1
|
75
86
|
begin
|
76
|
-
lambda { Rack::Multipart.parse_multipart(env) }.
|
87
|
+
lambda { Rack::Multipart.parse_multipart(env) }.must_raise(RangeError)
|
77
88
|
ensure
|
78
89
|
Rack::Utils.key_space_limit = old
|
79
90
|
end
|
80
91
|
end
|
81
92
|
|
82
|
-
|
93
|
+
it "parse multipart form webkit style" do
|
83
94
|
env = Rack::MockRequest.env_for '/', multipart_fixture(:webkit)
|
84
95
|
env['CONTENT_TYPE'] = "multipart/form-data; boundary=----WebKitFormBoundaryWLHCs9qmcJJoyjKR"
|
85
96
|
params = Rack::Multipart.parse_multipart(env)
|
86
|
-
params['profile']['bio'].
|
97
|
+
params['profile']['bio'].must_include 'hello'
|
98
|
+
params['profile'].keys.must_include 'public_email'
|
87
99
|
end
|
88
100
|
|
89
|
-
|
101
|
+
it "reject insanely long boundaries" do
|
90
102
|
# using a pipe since a tempfile can use up too much space
|
91
103
|
rd, wr = IO.pipe
|
92
104
|
|
@@ -131,279 +143,302 @@ describe Rack::Multipart do
|
|
131
143
|
env = Rack::MockRequest.env_for '/', fixture
|
132
144
|
lambda {
|
133
145
|
Rack::Multipart.parse_multipart(env)
|
134
|
-
}.
|
146
|
+
}.must_raise EOFError
|
135
147
|
rd.close
|
136
148
|
|
137
149
|
err = thr.value
|
138
|
-
err.
|
150
|
+
err.must_be_instance_of Errno::EPIPE
|
139
151
|
wr.close
|
140
152
|
end
|
141
153
|
|
142
|
-
|
154
|
+
it 'raises an EOF error on content-length mistmatch' do
|
155
|
+
env = Rack::MockRequest.env_for("/", multipart_fixture(:empty))
|
156
|
+
env['rack.input'] = StringIO.new
|
157
|
+
assert_raises(EOFError) do
|
158
|
+
Rack::Multipart.parse_multipart(env)
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
it "parse multipart upload with text file" do
|
143
163
|
env = Rack::MockRequest.env_for("/", multipart_fixture(:text))
|
144
164
|
params = Rack::Multipart.parse_multipart(env)
|
145
|
-
params["submit-name"].
|
146
|
-
params["submit-name-with-content"].
|
147
|
-
params["files"][:type].
|
148
|
-
params["files"][:filename].
|
149
|
-
params["files"][:head].
|
165
|
+
params["submit-name"].must_equal "Larry"
|
166
|
+
params["submit-name-with-content"].must_equal "Berry"
|
167
|
+
params["files"][:type].must_equal "text/plain"
|
168
|
+
params["files"][:filename].must_equal "file1.txt"
|
169
|
+
params["files"][:head].must_equal "Content-Disposition: form-data; " +
|
150
170
|
"name=\"files\"; filename=\"file1.txt\"\r\n" +
|
151
171
|
"Content-Type: text/plain\r\n"
|
152
|
-
params["files"][:name].
|
153
|
-
params["files"][:tempfile].read.
|
172
|
+
params["files"][:name].must_equal "files"
|
173
|
+
params["files"][:tempfile].read.must_equal "contents"
|
174
|
+
end
|
175
|
+
|
176
|
+
it "accept the params hash class to use for multipart parsing" do
|
177
|
+
c = Class.new(Rack::QueryParser::Params) do
|
178
|
+
def initialize(*)
|
179
|
+
super
|
180
|
+
@params = Hash.new{|h,k| h[k.to_s] if k.is_a?(Symbol)}
|
181
|
+
end
|
182
|
+
end
|
183
|
+
query_parser = Rack::QueryParser.new c, 65536, 100
|
184
|
+
env = Rack::MockRequest.env_for("/", multipart_fixture(:text))
|
185
|
+
params = Rack::Multipart.parse_multipart(env, query_parser)
|
186
|
+
params[:files][:type].must_equal "text/plain"
|
154
187
|
end
|
155
188
|
|
156
|
-
|
189
|
+
it "preserve extension in the created tempfile" do
|
157
190
|
env = Rack::MockRequest.env_for("/", multipart_fixture(:text))
|
158
191
|
params = Rack::Multipart.parse_multipart(env)
|
159
|
-
File.extname(params["files"][:tempfile].path).
|
192
|
+
File.extname(params["files"][:tempfile].path).must_equal ".txt"
|
160
193
|
end
|
161
194
|
|
162
|
-
|
195
|
+
it "parse multipart upload with text file with no name field" do
|
163
196
|
env = Rack::MockRequest.env_for("/", multipart_fixture(:filename_and_no_name))
|
164
197
|
params = Rack::Multipart.parse_multipart(env)
|
165
|
-
params["file1.txt"][:type].
|
166
|
-
params["file1.txt"][:filename].
|
167
|
-
params["file1.txt"][:head].
|
198
|
+
params["file1.txt"][:type].must_equal "text/plain"
|
199
|
+
params["file1.txt"][:filename].must_equal "file1.txt"
|
200
|
+
params["file1.txt"][:head].must_equal "Content-Disposition: form-data; " +
|
168
201
|
"filename=\"file1.txt\"\r\n" +
|
169
202
|
"Content-Type: text/plain\r\n"
|
170
|
-
params["file1.txt"][:name].
|
171
|
-
params["file1.txt"][:tempfile].read.
|
203
|
+
params["file1.txt"][:name].must_equal "file1.txt"
|
204
|
+
params["file1.txt"][:tempfile].read.must_equal "contents"
|
172
205
|
end
|
173
206
|
|
174
|
-
|
207
|
+
it "parse multipart upload file using custom tempfile class" do
|
175
208
|
env = Rack::MockRequest.env_for("/", multipart_fixture(:text))
|
176
209
|
my_tempfile = ""
|
177
210
|
env['rack.multipart.tempfile_factory'] = lambda { |filename, content_type| my_tempfile }
|
178
211
|
params = Rack::Multipart.parse_multipart(env)
|
179
|
-
params["files"][:tempfile].object_id.
|
180
|
-
my_tempfile.
|
212
|
+
params["files"][:tempfile].object_id.must_equal my_tempfile.object_id
|
213
|
+
my_tempfile.must_equal "contents"
|
181
214
|
end
|
182
215
|
|
183
|
-
|
216
|
+
it "parse multipart upload with nested parameters" do
|
184
217
|
env = Rack::MockRequest.env_for("/", multipart_fixture(:nested))
|
185
218
|
params = Rack::Multipart.parse_multipart(env)
|
186
|
-
params["foo"]["submit-name"].
|
187
|
-
params["foo"]["files"][:type].
|
188
|
-
params["foo"]["files"][:filename].
|
189
|
-
params["foo"]["files"][:head].
|
219
|
+
params["foo"]["submit-name"].must_equal "Larry"
|
220
|
+
params["foo"]["files"][:type].must_equal "text/plain"
|
221
|
+
params["foo"]["files"][:filename].must_equal "file1.txt"
|
222
|
+
params["foo"]["files"][:head].must_equal "Content-Disposition: form-data; " +
|
190
223
|
"name=\"foo[files]\"; filename=\"file1.txt\"\r\n" +
|
191
224
|
"Content-Type: text/plain\r\n"
|
192
|
-
params["foo"]["files"][:name].
|
193
|
-
params["foo"]["files"][:tempfile].read.
|
225
|
+
params["foo"]["files"][:name].must_equal "foo[files]"
|
226
|
+
params["foo"]["files"][:tempfile].read.must_equal "contents"
|
194
227
|
end
|
195
228
|
|
196
|
-
|
229
|
+
it "parse multipart upload with binary file" do
|
197
230
|
env = Rack::MockRequest.env_for("/", multipart_fixture(:binary))
|
198
231
|
params = Rack::Multipart.parse_multipart(env)
|
199
|
-
params["submit-name"].
|
200
|
-
|
201
|
-
params["files"][:
|
202
|
-
params["files"][:
|
232
|
+
params["submit-name"].must_equal "Larry"
|
233
|
+
|
234
|
+
params["files"][:type].must_equal "image/png"
|
235
|
+
params["files"][:filename].must_equal "rack-logo.png"
|
236
|
+
params["files"][:head].must_equal "Content-Disposition: form-data; " +
|
203
237
|
"name=\"files\"; filename=\"rack-logo.png\"\r\n" +
|
204
238
|
"Content-Type: image/png\r\n"
|
205
|
-
params["files"][:name].
|
206
|
-
params["files"][:tempfile].read.length.
|
239
|
+
params["files"][:name].must_equal "files"
|
240
|
+
params["files"][:tempfile].read.length.must_equal 26473
|
207
241
|
end
|
208
242
|
|
209
|
-
|
243
|
+
it "parse multipart upload with empty file" do
|
210
244
|
env = Rack::MockRequest.env_for("/", multipart_fixture(:empty))
|
211
245
|
params = Rack::Multipart.parse_multipart(env)
|
212
|
-
params["submit-name"].
|
213
|
-
params["files"][:type].
|
214
|
-
params["files"][:filename].
|
215
|
-
params["files"][:head].
|
246
|
+
params["submit-name"].must_equal "Larry"
|
247
|
+
params["files"][:type].must_equal "text/plain"
|
248
|
+
params["files"][:filename].must_equal "file1.txt"
|
249
|
+
params["files"][:head].must_equal "Content-Disposition: form-data; " +
|
216
250
|
"name=\"files\"; filename=\"file1.txt\"\r\n" +
|
217
251
|
"Content-Type: text/plain\r\n"
|
218
|
-
params["files"][:name].
|
219
|
-
params["files"][:tempfile].read.
|
252
|
+
params["files"][:name].must_equal "files"
|
253
|
+
params["files"][:tempfile].read.must_equal ""
|
220
254
|
end
|
221
255
|
|
222
|
-
|
256
|
+
it "parse multipart upload with filename with semicolons" do
|
223
257
|
env = Rack::MockRequest.env_for("/", multipart_fixture(:semicolon))
|
224
258
|
params = Rack::Multipart.parse_multipart(env)
|
225
|
-
params["files"][:type].
|
226
|
-
params["files"][:filename].
|
227
|
-
params["files"][:head].
|
259
|
+
params["files"][:type].must_equal "text/plain"
|
260
|
+
params["files"][:filename].must_equal "fi;le1.txt"
|
261
|
+
params["files"][:head].must_equal "Content-Disposition: form-data; " +
|
228
262
|
"name=\"files\"; filename=\"fi;le1.txt\"\r\n" +
|
229
263
|
"Content-Type: text/plain\r\n"
|
230
|
-
params["files"][:name].
|
231
|
-
params["files"][:tempfile].read.
|
264
|
+
params["files"][:name].must_equal "files"
|
265
|
+
params["files"][:tempfile].read.must_equal "contents"
|
266
|
+
end
|
267
|
+
|
268
|
+
it "parse multipart upload with quoted boundary" do
|
269
|
+
env = Rack::MockRequest.env_for("/", multipart_fixture(:quoted, %("AaB:03x")))
|
270
|
+
params = Rack::Multipart.parse_multipart(env)
|
271
|
+
params["submit-name"].must_equal "Larry"
|
272
|
+
params["submit-name-with-content"].must_equal "Berry"
|
273
|
+
params["files"][:type].must_equal "text/plain"
|
274
|
+
params["files"][:filename].must_equal "file1.txt"
|
275
|
+
params["files"][:head].must_equal "Content-Disposition: form-data; " +
|
276
|
+
"name=\"files\"; filename=\"file1.txt\"\r\n" +
|
277
|
+
"Content-Type: text/plain\r\n"
|
278
|
+
params["files"][:name].must_equal "files"
|
279
|
+
params["files"][:tempfile].read.must_equal "contents"
|
232
280
|
end
|
233
281
|
|
234
|
-
|
282
|
+
it "parse multipart upload with filename with invalid characters" do
|
235
283
|
env = Rack::MockRequest.env_for("/", multipart_fixture(:invalid_character))
|
236
284
|
params = Rack::Multipart.parse_multipart(env)
|
237
|
-
params["files"][:type].
|
238
|
-
params["files"][:filename].
|
285
|
+
params["files"][:type].must_equal "text/plain"
|
286
|
+
params["files"][:filename].must_match(/invalid/)
|
239
287
|
head = "Content-Disposition: form-data; " +
|
240
288
|
"name=\"files\"; filename=\"invalid\xC3.txt\"\r\n" +
|
241
289
|
"Content-Type: text/plain\r\n"
|
242
|
-
head = head.force_encoding(
|
243
|
-
params["files"][:head].
|
244
|
-
params["files"][:name].
|
245
|
-
params["files"][:tempfile].read.
|
290
|
+
head = head.force_encoding(Encoding::ASCII_8BIT)
|
291
|
+
params["files"][:head].must_equal head
|
292
|
+
params["files"][:name].must_equal "files"
|
293
|
+
params["files"][:tempfile].read.must_equal "contents"
|
246
294
|
end
|
247
295
|
|
248
|
-
|
249
|
-
env = Rack::MockRequest.env_for
|
296
|
+
it "parse multipart form with an encoded word filename" do
|
297
|
+
env = Rack::MockRequest.env_for '/', multipart_fixture(:filename_with_encoded_words)
|
250
298
|
params = Rack::Multipart.parse_multipart(env)
|
251
|
-
params["
|
252
|
-
params["files"].should.equal nil
|
253
|
-
params.keys.should.not.include "files"
|
299
|
+
params["files"][:filename].must_equal "файл"
|
254
300
|
end
|
255
301
|
|
256
|
-
|
257
|
-
env = Rack::MockRequest.env_for
|
258
|
-
params = Rack::
|
259
|
-
params["
|
260
|
-
params["files"].should.be.instance_of String
|
261
|
-
params["files"].size.should.equal 252
|
302
|
+
it "parse multipart form with a single quote in the filename" do
|
303
|
+
env = Rack::MockRequest.env_for '/', multipart_fixture(:filename_with_single_quote)
|
304
|
+
params = Rack::Multipart.parse_multipart(env)
|
305
|
+
params["files"][:filename].must_equal "bob's flowers.jpg"
|
262
306
|
end
|
263
307
|
|
264
|
-
|
308
|
+
it "parse multipart form with a null byte in the filename" do
|
265
309
|
env = Rack::MockRequest.env_for '/', multipart_fixture(:filename_with_null_byte)
|
266
310
|
params = Rack::Multipart.parse_multipart(env)
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
311
|
+
params["files"][:filename].must_equal "flowers.exe\u0000.jpg"
|
312
|
+
end
|
313
|
+
|
314
|
+
it "not include file params if no file was selected" do
|
315
|
+
env = Rack::MockRequest.env_for("/", multipart_fixture(:none))
|
316
|
+
params = Rack::Multipart.parse_multipart(env)
|
317
|
+
params["submit-name"].must_equal "Larry"
|
318
|
+
params["files"].must_be_nil
|
319
|
+
params.keys.wont_include "files"
|
272
320
|
end
|
273
321
|
|
274
|
-
|
322
|
+
it "parse multipart/mixed" do
|
275
323
|
env = Rack::MockRequest.env_for("/", multipart_fixture(:mixed_files))
|
276
|
-
params = Rack::
|
277
|
-
params["foo"].
|
278
|
-
params["files"].
|
279
|
-
params["files"].size.
|
324
|
+
params = Rack::Multipart.parse_multipart(env)
|
325
|
+
params["foo"].must_equal "bar"
|
326
|
+
params["files"].must_be_instance_of String
|
327
|
+
params["files"].size.must_equal 252
|
280
328
|
end
|
281
329
|
|
282
|
-
|
330
|
+
it "parse IE multipart upload and clean up filename" do
|
283
331
|
env = Rack::MockRequest.env_for("/", multipart_fixture(:ie))
|
284
332
|
params = Rack::Multipart.parse_multipart(env)
|
285
|
-
params["files"][:type].
|
286
|
-
params["files"][:filename].
|
287
|
-
params["files"][:head].
|
333
|
+
params["files"][:type].must_equal "text/plain"
|
334
|
+
params["files"][:filename].must_equal "file1.txt"
|
335
|
+
params["files"][:head].must_equal "Content-Disposition: form-data; " +
|
288
336
|
"name=\"files\"; " +
|
289
337
|
'filename="C:\Documents and Settings\Administrator\Desktop\file1.txt"' +
|
290
338
|
"\r\nContent-Type: text/plain\r\n"
|
291
|
-
params["files"][:name].
|
292
|
-
params["files"][:tempfile].read.
|
339
|
+
params["files"][:name].must_equal "files"
|
340
|
+
params["files"][:tempfile].read.must_equal "contents"
|
293
341
|
end
|
294
342
|
|
295
|
-
|
343
|
+
it "parse filename and modification param" do
|
296
344
|
env = Rack::MockRequest.env_for("/", multipart_fixture(:filename_and_modification_param))
|
297
345
|
params = Rack::Multipart.parse_multipart(env)
|
298
|
-
params["files"][:type].
|
299
|
-
params["files"][:filename].
|
300
|
-
params["files"][:head].
|
346
|
+
params["files"][:type].must_equal "image/jpeg"
|
347
|
+
params["files"][:filename].must_equal "genome.jpeg"
|
348
|
+
params["files"][:head].must_equal "Content-Type: image/jpeg\r\n" +
|
301
349
|
"Content-Disposition: attachment; " +
|
302
350
|
"name=\"files\"; " +
|
303
351
|
"filename=genome.jpeg; " +
|
304
352
|
"modification-date=\"Wed, 12 Feb 1997 16:29:51 -0500\";\r\n" +
|
305
353
|
"Content-Description: a complete map of the human genome\r\n"
|
306
|
-
params["files"][:name].
|
307
|
-
params["files"][:tempfile].read.
|
354
|
+
params["files"][:name].must_equal "files"
|
355
|
+
params["files"][:tempfile].read.must_equal "contents"
|
308
356
|
end
|
309
357
|
|
310
|
-
|
358
|
+
it "parse filename with escaped quotes" do
|
311
359
|
env = Rack::MockRequest.env_for("/", multipart_fixture(:filename_with_escaped_quotes))
|
312
360
|
params = Rack::Multipart.parse_multipart(env)
|
313
|
-
params["files"][:type].
|
314
|
-
params["files"][:filename].
|
315
|
-
params["files"][:head].
|
361
|
+
params["files"][:type].must_equal "application/octet-stream"
|
362
|
+
params["files"][:filename].must_equal "escape \"quotes"
|
363
|
+
params["files"][:head].must_equal "Content-Disposition: form-data; " +
|
316
364
|
"name=\"files\"; " +
|
317
365
|
"filename=\"escape \\\"quotes\"\r\n" +
|
318
366
|
"Content-Type: application/octet-stream\r\n"
|
319
|
-
params["files"][:name].
|
320
|
-
params["files"][:tempfile].read.
|
367
|
+
params["files"][:name].must_equal "files"
|
368
|
+
params["files"][:tempfile].read.must_equal "contents"
|
321
369
|
end
|
322
370
|
|
323
|
-
|
371
|
+
it "parse filename with percent escaped quotes" do
|
324
372
|
env = Rack::MockRequest.env_for("/", multipart_fixture(:filename_with_percent_escaped_quotes))
|
325
373
|
params = Rack::Multipart.parse_multipart(env)
|
326
|
-
params["files"][:type].
|
327
|
-
params["files"][:filename].
|
328
|
-
params["files"][:head].
|
374
|
+
params["files"][:type].must_equal "application/octet-stream"
|
375
|
+
params["files"][:filename].must_equal "escape \"quotes"
|
376
|
+
params["files"][:head].must_equal "Content-Disposition: form-data; " +
|
329
377
|
"name=\"files\"; " +
|
330
378
|
"filename=\"escape %22quotes\"\r\n" +
|
331
379
|
"Content-Type: application/octet-stream\r\n"
|
332
|
-
params["files"][:name].
|
333
|
-
params["files"][:tempfile].read.
|
334
|
-
end
|
335
|
-
|
336
|
-
should "parse filename with unescaped quotes" do
|
337
|
-
env = Rack::MockRequest.env_for("/", multipart_fixture(:filename_with_unescaped_quotes))
|
338
|
-
params = Rack::Multipart.parse_multipart(env)
|
339
|
-
params["files"][:type].should.equal "application/octet-stream"
|
340
|
-
params["files"][:filename].should.equal "escape \"quotes"
|
341
|
-
params["files"][:head].should.equal "Content-Disposition: form-data; " +
|
342
|
-
"name=\"files\"; " +
|
343
|
-
"filename=\"escape \"quotes\"\r\n" +
|
344
|
-
"Content-Type: application/octet-stream\r\n"
|
345
|
-
params["files"][:name].should.equal "files"
|
346
|
-
params["files"][:tempfile].read.should.equal "contents"
|
380
|
+
params["files"][:name].must_equal "files"
|
381
|
+
params["files"][:tempfile].read.must_equal "contents"
|
347
382
|
end
|
348
383
|
|
349
|
-
|
384
|
+
it "parse filename with escaped quotes and modification param" do
|
350
385
|
env = Rack::MockRequest.env_for("/", multipart_fixture(:filename_with_escaped_quotes_and_modification_param))
|
351
386
|
params = Rack::Multipart.parse_multipart(env)
|
352
|
-
params["files"][:type].
|
353
|
-
params["files"][:filename].
|
354
|
-
params["files"][:head].
|
387
|
+
params["files"][:type].must_equal "image/jpeg"
|
388
|
+
params["files"][:filename].must_equal "\"human\" genome.jpeg"
|
389
|
+
params["files"][:head].must_equal "Content-Type: image/jpeg\r\n" +
|
355
390
|
"Content-Disposition: attachment; " +
|
356
391
|
"name=\"files\"; " +
|
357
|
-
"filename=\"
|
392
|
+
"filename=\"\\\"human\\\" genome.jpeg\"; " +
|
358
393
|
"modification-date=\"Wed, 12 Feb 1997 16:29:51 -0500\";\r\n" +
|
359
394
|
"Content-Description: a complete map of the human genome\r\n"
|
360
|
-
params["files"][:name].
|
361
|
-
params["files"][:tempfile].read.
|
395
|
+
params["files"][:name].must_equal "files"
|
396
|
+
params["files"][:tempfile].read.must_equal "contents"
|
362
397
|
end
|
363
398
|
|
364
|
-
|
399
|
+
it "parse filename with unescaped percentage characters" do
|
365
400
|
env = Rack::MockRequest.env_for("/", multipart_fixture(:filename_with_unescaped_percentages, "----WebKitFormBoundary2NHc7OhsgU68l3Al"))
|
366
401
|
params = Rack::Multipart.parse_multipart(env)
|
367
402
|
files = params["document"]["attachment"]
|
368
|
-
files[:type].
|
369
|
-
files[:filename].
|
370
|
-
files[:head].
|
403
|
+
files[:type].must_equal "image/jpeg"
|
404
|
+
files[:filename].must_equal "100% of a photo.jpeg"
|
405
|
+
files[:head].must_equal <<-MULTIPART
|
371
406
|
Content-Disposition: form-data; name="document[attachment]"; filename="100% of a photo.jpeg"\r
|
372
407
|
Content-Type: image/jpeg\r
|
373
408
|
MULTIPART
|
374
409
|
|
375
|
-
files[:name].
|
376
|
-
files[:tempfile].read.
|
410
|
+
files[:name].must_equal "document[attachment]"
|
411
|
+
files[:tempfile].read.must_equal "contents"
|
377
412
|
end
|
378
413
|
|
379
|
-
|
414
|
+
it "parse filename with unescaped percentage characters that look like partial hex escapes" do
|
380
415
|
env = Rack::MockRequest.env_for("/", multipart_fixture(:filename_with_unescaped_percentages2, "----WebKitFormBoundary2NHc7OhsgU68l3Al"))
|
381
416
|
params = Rack::Multipart.parse_multipart(env)
|
382
417
|
files = params["document"]["attachment"]
|
383
|
-
files[:type].
|
384
|
-
files[:filename].
|
385
|
-
files[:head].
|
418
|
+
files[:type].must_equal "image/jpeg"
|
419
|
+
files[:filename].must_equal "100%a"
|
420
|
+
files[:head].must_equal <<-MULTIPART
|
386
421
|
Content-Disposition: form-data; name="document[attachment]"; filename="100%a"\r
|
387
422
|
Content-Type: image/jpeg\r
|
388
423
|
MULTIPART
|
389
424
|
|
390
|
-
files[:name].
|
391
|
-
files[:tempfile].read.
|
425
|
+
files[:name].must_equal "document[attachment]"
|
426
|
+
files[:tempfile].read.must_equal "contents"
|
392
427
|
end
|
393
428
|
|
394
|
-
|
429
|
+
it "parse filename with unescaped percentage characters that look like partial hex escapes" do
|
395
430
|
env = Rack::MockRequest.env_for("/", multipart_fixture(:filename_with_unescaped_percentages3, "----WebKitFormBoundary2NHc7OhsgU68l3Al"))
|
396
431
|
params = Rack::Multipart.parse_multipart(env)
|
397
432
|
files = params["document"]["attachment"]
|
398
|
-
files[:type].
|
399
|
-
files[:filename].
|
400
|
-
files[:head].
|
433
|
+
files[:type].must_equal "image/jpeg"
|
434
|
+
files[:filename].must_equal "100%"
|
435
|
+
files[:head].must_equal <<-MULTIPART
|
401
436
|
Content-Disposition: form-data; name="document[attachment]"; filename="100%"\r
|
402
437
|
Content-Type: image/jpeg\r
|
403
438
|
MULTIPART
|
404
439
|
|
405
|
-
files[:name].
|
406
|
-
files[:tempfile].read.
|
440
|
+
files[:name].must_equal "document[attachment]"
|
441
|
+
files[:tempfile].read.must_equal "contents"
|
407
442
|
end
|
408
443
|
|
409
444
|
it "rewinds input after parsing upload" do
|
@@ -411,9 +446,9 @@ Content-Type: image/jpeg\r
|
|
411
446
|
input = options[:input]
|
412
447
|
env = Rack::MockRequest.env_for("/", options)
|
413
448
|
params = Rack::Multipart.parse_multipart(env)
|
414
|
-
params["submit-name"].
|
415
|
-
params["files"][:filename].
|
416
|
-
input.read.length.
|
449
|
+
params["submit-name"].must_equal "Larry"
|
450
|
+
params["files"][:filename].must_equal "file1.txt"
|
451
|
+
input.read.length.must_equal 307
|
417
452
|
end
|
418
453
|
|
419
454
|
it "builds multipart body" do
|
@@ -427,9 +462,9 @@ Content-Type: image/jpeg\r
|
|
427
462
|
}
|
428
463
|
env = Rack::MockRequest.env_for("/", options)
|
429
464
|
params = Rack::Multipart.parse_multipart(env)
|
430
|
-
params["submit-name"].
|
431
|
-
params["files"][:filename].
|
432
|
-
params["files"][:tempfile].read.
|
465
|
+
params["submit-name"].must_equal "Larry"
|
466
|
+
params["files"][:filename].must_equal "file1.txt"
|
467
|
+
params["files"][:tempfile].read.must_equal "contents"
|
433
468
|
end
|
434
469
|
|
435
470
|
it "builds nested multipart body" do
|
@@ -443,9 +478,9 @@ Content-Type: image/jpeg\r
|
|
443
478
|
}
|
444
479
|
env = Rack::MockRequest.env_for("/", options)
|
445
480
|
params = Rack::Multipart.parse_multipart(env)
|
446
|
-
params["people"][0]["submit-name"].
|
447
|
-
params["people"][0]["files"][:filename].
|
448
|
-
params["people"][0]["files"][:tempfile].read.
|
481
|
+
params["people"][0]["submit-name"].must_equal "Larry"
|
482
|
+
params["people"][0]["files"][:filename].must_equal "file1.txt"
|
483
|
+
params["people"][0]["files"][:tempfile].read.must_equal "contents"
|
449
484
|
end
|
450
485
|
|
451
486
|
it "can parse fields that end at the end of the buffer" do
|
@@ -456,8 +491,8 @@ Content-Type: image/jpeg\r
|
|
456
491
|
"CONTENT_LENGTH" => input.size,
|
457
492
|
:input => input)
|
458
493
|
|
459
|
-
req.POST['file.path'].
|
460
|
-
req.POST['addresses'].
|
494
|
+
req.POST['file.path'].must_equal "/var/tmp/uploads/4/0001728414"
|
495
|
+
req.POST['addresses'].wont_equal nil
|
461
496
|
end
|
462
497
|
|
463
498
|
it "builds complete params with the chunk size of 16384 slicing exactly on boundary" do
|
@@ -474,54 +509,66 @@ Content-Type: image/jpeg\r
|
|
474
509
|
env = Rack::MockRequest.env_for("/", options)
|
475
510
|
params = Rack::Multipart.parse_multipart(env)
|
476
511
|
|
477
|
-
params.
|
478
|
-
params.keys.
|
479
|
-
params["AAAAAAAAAAAAAAAAAAA"].keys.
|
480
|
-
params["AAAAAAAAAAAAAAAAAAA"]["PLAPLAPLA_MEMMEMMEMM_ATTRATTRER"].keys.
|
481
|
-
params["AAAAAAAAAAAAAAAAAAA"]["PLAPLAPLA_MEMMEMMEMM_ATTRATTRER"]["new"].keys.
|
482
|
-
params["AAAAAAAAAAAAAAAAAAA"]["PLAPLAPLA_MEMMEMMEMM_ATTRATTRER"]["new"]["-2"].keys.
|
483
|
-
params["AAAAAAAAAAAAAAAAAAA"]["PLAPLAPLA_MEMMEMMEMM_ATTRATTRER"]["new"]["-2"]["ba_unit_id"].
|
512
|
+
params.wont_equal nil
|
513
|
+
params.keys.must_include "AAAAAAAAAAAAAAAAAAA"
|
514
|
+
params["AAAAAAAAAAAAAAAAAAA"].keys.must_include "PLAPLAPLA_MEMMEMMEMM_ATTRATTRER"
|
515
|
+
params["AAAAAAAAAAAAAAAAAAA"]["PLAPLAPLA_MEMMEMMEMM_ATTRATTRER"].keys.must_include "new"
|
516
|
+
params["AAAAAAAAAAAAAAAAAAA"]["PLAPLAPLA_MEMMEMMEMM_ATTRATTRER"]["new"].keys.must_include "-2"
|
517
|
+
params["AAAAAAAAAAAAAAAAAAA"]["PLAPLAPLA_MEMMEMMEMM_ATTRATTRER"]["new"]["-2"].keys.must_include "ba_unit_id"
|
518
|
+
params["AAAAAAAAAAAAAAAAAAA"]["PLAPLAPLA_MEMMEMMEMM_ATTRATTRER"]["new"]["-2"]["ba_unit_id"].must_equal "1017"
|
484
519
|
ensure
|
485
520
|
Rack::Utils.multipart_part_limit = previous_limit
|
486
521
|
end
|
487
522
|
end
|
488
523
|
|
489
|
-
|
524
|
+
it "not reach a multi-part limit" do
|
490
525
|
begin
|
491
526
|
previous_limit = Rack::Utils.multipart_part_limit
|
492
527
|
Rack::Utils.multipart_part_limit = 4
|
493
528
|
|
494
529
|
env = Rack::MockRequest.env_for '/', multipart_fixture(:three_files_three_fields)
|
495
530
|
params = Rack::Multipart.parse_multipart(env)
|
496
|
-
params['reply'].
|
497
|
-
params['to'].
|
498
|
-
params['from'].
|
531
|
+
params['reply'].must_equal 'yes'
|
532
|
+
params['to'].must_equal 'people'
|
533
|
+
params['from'].must_equal 'others'
|
499
534
|
ensure
|
500
535
|
Rack::Utils.multipart_part_limit = previous_limit
|
501
536
|
end
|
502
537
|
end
|
503
538
|
|
504
|
-
|
539
|
+
it "reach a multipart limit" do
|
505
540
|
begin
|
506
541
|
previous_limit = Rack::Utils.multipart_part_limit
|
507
542
|
Rack::Utils.multipart_part_limit = 3
|
508
543
|
|
509
544
|
env = Rack::MockRequest.env_for '/', multipart_fixture(:three_files_three_fields)
|
510
|
-
lambda { Rack::Multipart.parse_multipart(env) }.
|
545
|
+
lambda { Rack::Multipart.parse_multipart(env) }.must_raise Rack::Multipart::MultipartPartLimitError
|
511
546
|
ensure
|
512
547
|
Rack::Utils.multipart_part_limit = previous_limit
|
513
548
|
end
|
514
549
|
end
|
515
550
|
|
516
|
-
|
551
|
+
it "reach a multipart total limit" do
|
552
|
+
begin
|
553
|
+
previous_limit = Rack::Utils.multipart_total_part_limit
|
554
|
+
Rack::Utils.multipart_total_part_limit = 5
|
555
|
+
|
556
|
+
env = Rack::MockRequest.env_for '/', multipart_fixture(:three_files_three_fields)
|
557
|
+
lambda { Rack::Multipart.parse_multipart(env) }.must_raise Rack::Multipart::MultipartTotalPartLimitError
|
558
|
+
ensure
|
559
|
+
Rack::Utils.multipart_total_part_limit = previous_limit
|
560
|
+
end
|
561
|
+
end
|
562
|
+
|
563
|
+
it "return nil if no UploadedFiles were used" do
|
517
564
|
data = Rack::Multipart.build_multipart("people" => [{"submit-name" => "Larry", "files" => "contents"}])
|
518
|
-
data.
|
565
|
+
data.must_be_nil
|
519
566
|
end
|
520
567
|
|
521
|
-
|
522
|
-
lambda {
|
523
|
-
|
524
|
-
|
568
|
+
it "raise ArgumentError if params is not a Hash" do
|
569
|
+
lambda {
|
570
|
+
Rack::Multipart.build_multipart("foo=bar")
|
571
|
+
}.must_raise(ArgumentError).message.must_equal "value must be a Hash"
|
525
572
|
end
|
526
573
|
|
527
574
|
it "can parse fields with a content type" do
|
@@ -539,20 +586,20 @@ EOF
|
|
539
586
|
:input => StringIO.new(data)
|
540
587
|
}
|
541
588
|
env = Rack::MockRequest.env_for("/", options)
|
542
|
-
params = Rack::
|
589
|
+
params = Rack::Multipart.parse_multipart(env)
|
543
590
|
|
544
|
-
params.
|
591
|
+
params.must_equal "description"=>"Very very blue"
|
545
592
|
end
|
546
593
|
|
547
|
-
|
594
|
+
it "parse multipart upload with no content-length header" do
|
548
595
|
env = Rack::MockRequest.env_for '/', multipart_fixture(:webkit)
|
549
596
|
env['CONTENT_TYPE'] = "multipart/form-data; boundary=----WebKitFormBoundaryWLHCs9qmcJJoyjKR"
|
550
597
|
env.delete 'CONTENT_LENGTH'
|
551
598
|
params = Rack::Multipart.parse_multipart(env)
|
552
|
-
params['profile']['bio'].
|
599
|
+
params['profile']['bio'].must_include 'hello'
|
553
600
|
end
|
554
601
|
|
555
|
-
|
602
|
+
it "parse very long unquoted multipart file names" do
|
556
603
|
data = <<-EOF
|
557
604
|
--AaB03x\r
|
558
605
|
Content-Type: text/plain\r
|
@@ -568,17 +615,57 @@ contents\r
|
|
568
615
|
:input => StringIO.new(data)
|
569
616
|
}
|
570
617
|
env = Rack::MockRequest.env_for("/", options)
|
571
|
-
params = Rack::
|
618
|
+
params = Rack::Multipart.parse_multipart(env)
|
619
|
+
|
620
|
+
params["file"][:filename].must_equal 'long' * 100
|
621
|
+
end
|
622
|
+
|
623
|
+
it "parse unquoted parameter values at end of line" do
|
624
|
+
data = <<-EOF
|
625
|
+
--AaB03x\r
|
626
|
+
Content-Type: text/plain\r
|
627
|
+
Content-Disposition: attachment; name=inline\r
|
628
|
+
\r
|
629
|
+
true\r
|
630
|
+
--AaB03x--\r
|
631
|
+
EOF
|
632
|
+
|
633
|
+
options = {
|
634
|
+
"CONTENT_TYPE" => "multipart/form-data; boundary=AaB03x",
|
635
|
+
"CONTENT_LENGTH" => data.length.to_s,
|
636
|
+
:input => StringIO.new(data)
|
637
|
+
}
|
638
|
+
env = Rack::MockRequest.env_for("/", options)
|
639
|
+
params = Rack::Multipart.parse_multipart(env)
|
640
|
+
params["inline"].must_equal 'true'
|
641
|
+
end
|
642
|
+
|
643
|
+
it "parse quoted chars in name parameter" do
|
644
|
+
data = <<-EOF
|
645
|
+
--AaB03x\r
|
646
|
+
Content-Type: text/plain\r
|
647
|
+
Content-Disposition: attachment; name="quoted\\\\chars\\"in\rname"\r
|
648
|
+
\r
|
649
|
+
true\r
|
650
|
+
--AaB03x--\r
|
651
|
+
EOF
|
572
652
|
|
573
|
-
|
653
|
+
options = {
|
654
|
+
"CONTENT_TYPE" => "multipart/form-data; boundary=AaB03x",
|
655
|
+
"CONTENT_LENGTH" => data.length.to_s,
|
656
|
+
:input => StringIO.new(data)
|
657
|
+
}
|
658
|
+
env = Rack::MockRequest.env_for("/", options)
|
659
|
+
params = Rack::Multipart.parse_multipart(env)
|
660
|
+
params["quoted\\chars\"in\rname"].must_equal 'true'
|
574
661
|
end
|
575
662
|
|
576
|
-
|
663
|
+
it "support mixed case metadata" do
|
577
664
|
file = multipart_file(:text)
|
578
665
|
data = File.open(file, 'rb') { |io| io.read }
|
579
666
|
|
580
667
|
type = "Multipart/Form-Data; Boundary=AaB03x"
|
581
|
-
length = data.
|
668
|
+
length = data.bytesize
|
582
669
|
|
583
670
|
e = { "CONTENT_TYPE" => type,
|
584
671
|
"CONTENT_LENGTH" => length.to_s,
|
@@ -586,15 +673,49 @@ contents\r
|
|
586
673
|
|
587
674
|
env = Rack::MockRequest.env_for("/", e)
|
588
675
|
params = Rack::Multipart.parse_multipart(env)
|
589
|
-
params["submit-name"].
|
590
|
-
params["submit-name-with-content"].
|
591
|
-
params["files"][:type].
|
592
|
-
params["files"][:filename].
|
593
|
-
params["files"][:head].
|
676
|
+
params["submit-name"].must_equal "Larry"
|
677
|
+
params["submit-name-with-content"].must_equal "Berry"
|
678
|
+
params["files"][:type].must_equal "text/plain"
|
679
|
+
params["files"][:filename].must_equal "file1.txt"
|
680
|
+
params["files"][:head].must_equal "Content-Disposition: form-data; " +
|
594
681
|
"name=\"files\"; filename=\"file1.txt\"\r\n" +
|
595
682
|
"Content-Type: text/plain\r\n"
|
596
|
-
params["files"][:name].
|
597
|
-
params["files"][:tempfile].read.
|
683
|
+
params["files"][:name].must_equal "files"
|
684
|
+
params["files"][:tempfile].read.must_equal "contents"
|
598
685
|
end
|
599
686
|
|
687
|
+
it "fallback to content-type for name" do
|
688
|
+
rack_logo = File.read(multipart_file("rack-logo.png"))
|
689
|
+
|
690
|
+
data = <<-EOF
|
691
|
+
--AaB03x\r
|
692
|
+
Content-Type: text/plain\r
|
693
|
+
\r
|
694
|
+
some text\r
|
695
|
+
--AaB03x\r
|
696
|
+
\r
|
697
|
+
\r
|
698
|
+
some more text (I didn't specify Content-Type)\r
|
699
|
+
--AaB03x\r
|
700
|
+
Content-Type: image/png\r
|
701
|
+
\r
|
702
|
+
#{rack_logo}\r
|
703
|
+
--AaB03x--\r
|
704
|
+
EOF
|
705
|
+
|
706
|
+
options = {
|
707
|
+
"CONTENT_TYPE" => "multipart/related; boundary=AaB03x",
|
708
|
+
"CONTENT_LENGTH" => data.bytesize.to_s,
|
709
|
+
:input => StringIO.new(data)
|
710
|
+
}
|
711
|
+
env = Rack::MockRequest.env_for("/", options)
|
712
|
+
params = Rack::Multipart.parse_multipart(env)
|
713
|
+
|
714
|
+
params["text/plain"].must_equal ["some text", "some more text (I didn't specify Content-Type)"]
|
715
|
+
params["image/png"].length.must_equal 1
|
716
|
+
|
717
|
+
f = Tempfile.new("rack-logo")
|
718
|
+
f.write(params["image/png"][0])
|
719
|
+
f.length.must_equal 26473
|
720
|
+
end
|
600
721
|
end
|