rack 1.6.13 → 2.0.9
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 +138 -8
- data/README.rdoc +18 -28
- data/Rakefile +6 -14
- data/SPEC +3 -3
- 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} +3 -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 +40 -40
- 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 +270 -157
- data/lib/rack/multipart/uploaded_file.rb +1 -2
- data/lib/rack/multipart.rb +35 -6
- 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 +138 -211
- 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_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} +23 -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 +164 -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 +322 -200
- data/test/{spec_nulllogger.rb → spec_null_logger.rb} +5 -4
- data/test/spec_recursive.rb +17 -14
- data/test/spec_request.rb +780 -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 +57 -36
- 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,315 @@ 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"
|
154
174
|
end
|
155
175
|
|
156
|
-
|
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"
|
187
|
+
end
|
188
|
+
|
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.
|
380
|
+
params["files"][:name].must_equal "files"
|
381
|
+
params["files"][:tempfile].read.must_equal "contents"
|
334
382
|
end
|
335
383
|
|
336
|
-
|
384
|
+
it "parse filename with unescaped quotes" do
|
337
385
|
env = Rack::MockRequest.env_for("/", multipart_fixture(:filename_with_unescaped_quotes))
|
338
386
|
params = Rack::Multipart.parse_multipart(env)
|
339
|
-
params["files"][:type].
|
340
|
-
params["files"][:filename].
|
341
|
-
params["files"][:head].
|
387
|
+
params["files"][:type].must_equal "application/octet-stream"
|
388
|
+
params["files"][:filename].must_equal "escape \"quotes"
|
389
|
+
params["files"][:head].must_equal "Content-Disposition: form-data; " +
|
342
390
|
"name=\"files\"; " +
|
343
391
|
"filename=\"escape \"quotes\"\r\n" +
|
344
392
|
"Content-Type: application/octet-stream\r\n"
|
345
|
-
params["files"][:name].
|
346
|
-
params["files"][:tempfile].read.
|
393
|
+
params["files"][:name].must_equal "files"
|
394
|
+
params["files"][:tempfile].read.must_equal "contents"
|
347
395
|
end
|
348
396
|
|
349
|
-
|
397
|
+
it "parse filename with escaped quotes and modification param" do
|
350
398
|
env = Rack::MockRequest.env_for("/", multipart_fixture(:filename_with_escaped_quotes_and_modification_param))
|
351
399
|
params = Rack::Multipart.parse_multipart(env)
|
352
|
-
params["files"][:type].
|
353
|
-
params["files"][:filename].
|
354
|
-
params["files"][:head].
|
400
|
+
params["files"][:type].must_equal "image/jpeg"
|
401
|
+
params["files"][:filename].must_equal "\"human\" genome.jpeg"
|
402
|
+
params["files"][:head].must_equal "Content-Type: image/jpeg\r\n" +
|
355
403
|
"Content-Disposition: attachment; " +
|
356
404
|
"name=\"files\"; " +
|
357
405
|
"filename=\"\"human\" genome.jpeg\"; " +
|
358
406
|
"modification-date=\"Wed, 12 Feb 1997 16:29:51 -0500\";\r\n" +
|
359
407
|
"Content-Description: a complete map of the human genome\r\n"
|
360
|
-
params["files"][:name].
|
361
|
-
params["files"][:tempfile].read.
|
408
|
+
params["files"][:name].must_equal "files"
|
409
|
+
params["files"][:tempfile].read.must_equal "contents"
|
362
410
|
end
|
363
411
|
|
364
|
-
|
412
|
+
it "parse filename with unescaped percentage characters" do
|
365
413
|
env = Rack::MockRequest.env_for("/", multipart_fixture(:filename_with_unescaped_percentages, "----WebKitFormBoundary2NHc7OhsgU68l3Al"))
|
366
414
|
params = Rack::Multipart.parse_multipart(env)
|
367
415
|
files = params["document"]["attachment"]
|
368
|
-
files[:type].
|
369
|
-
files[:filename].
|
370
|
-
files[:head].
|
416
|
+
files[:type].must_equal "image/jpeg"
|
417
|
+
files[:filename].must_equal "100% of a photo.jpeg"
|
418
|
+
files[:head].must_equal <<-MULTIPART
|
371
419
|
Content-Disposition: form-data; name="document[attachment]"; filename="100% of a photo.jpeg"\r
|
372
420
|
Content-Type: image/jpeg\r
|
373
421
|
MULTIPART
|
374
422
|
|
375
|
-
files[:name].
|
376
|
-
files[:tempfile].read.
|
423
|
+
files[:name].must_equal "document[attachment]"
|
424
|
+
files[:tempfile].read.must_equal "contents"
|
377
425
|
end
|
378
426
|
|
379
|
-
|
427
|
+
it "parse filename with unescaped percentage characters that look like partial hex escapes" do
|
380
428
|
env = Rack::MockRequest.env_for("/", multipart_fixture(:filename_with_unescaped_percentages2, "----WebKitFormBoundary2NHc7OhsgU68l3Al"))
|
381
429
|
params = Rack::Multipart.parse_multipart(env)
|
382
430
|
files = params["document"]["attachment"]
|
383
|
-
files[:type].
|
384
|
-
files[:filename].
|
385
|
-
files[:head].
|
431
|
+
files[:type].must_equal "image/jpeg"
|
432
|
+
files[:filename].must_equal "100%a"
|
433
|
+
files[:head].must_equal <<-MULTIPART
|
386
434
|
Content-Disposition: form-data; name="document[attachment]"; filename="100%a"\r
|
387
435
|
Content-Type: image/jpeg\r
|
388
436
|
MULTIPART
|
389
437
|
|
390
|
-
files[:name].
|
391
|
-
files[:tempfile].read.
|
438
|
+
files[:name].must_equal "document[attachment]"
|
439
|
+
files[:tempfile].read.must_equal "contents"
|
392
440
|
end
|
393
441
|
|
394
|
-
|
442
|
+
it "parse filename with unescaped percentage characters that look like partial hex escapes" do
|
395
443
|
env = Rack::MockRequest.env_for("/", multipart_fixture(:filename_with_unescaped_percentages3, "----WebKitFormBoundary2NHc7OhsgU68l3Al"))
|
396
444
|
params = Rack::Multipart.parse_multipart(env)
|
397
445
|
files = params["document"]["attachment"]
|
398
|
-
files[:type].
|
399
|
-
files[:filename].
|
400
|
-
files[:head].
|
446
|
+
files[:type].must_equal "image/jpeg"
|
447
|
+
files[:filename].must_equal "100%"
|
448
|
+
files[:head].must_equal <<-MULTIPART
|
401
449
|
Content-Disposition: form-data; name="document[attachment]"; filename="100%"\r
|
402
450
|
Content-Type: image/jpeg\r
|
403
451
|
MULTIPART
|
404
452
|
|
405
|
-
files[:name].
|
406
|
-
files[:tempfile].read.
|
453
|
+
files[:name].must_equal "document[attachment]"
|
454
|
+
files[:tempfile].read.must_equal "contents"
|
407
455
|
end
|
408
456
|
|
409
457
|
it "rewinds input after parsing upload" do
|
@@ -411,9 +459,9 @@ Content-Type: image/jpeg\r
|
|
411
459
|
input = options[:input]
|
412
460
|
env = Rack::MockRequest.env_for("/", options)
|
413
461
|
params = Rack::Multipart.parse_multipart(env)
|
414
|
-
params["submit-name"].
|
415
|
-
params["files"][:filename].
|
416
|
-
input.read.length.
|
462
|
+
params["submit-name"].must_equal "Larry"
|
463
|
+
params["files"][:filename].must_equal "file1.txt"
|
464
|
+
input.read.length.must_equal 307
|
417
465
|
end
|
418
466
|
|
419
467
|
it "builds multipart body" do
|
@@ -427,9 +475,9 @@ Content-Type: image/jpeg\r
|
|
427
475
|
}
|
428
476
|
env = Rack::MockRequest.env_for("/", options)
|
429
477
|
params = Rack::Multipart.parse_multipart(env)
|
430
|
-
params["submit-name"].
|
431
|
-
params["files"][:filename].
|
432
|
-
params["files"][:tempfile].read.
|
478
|
+
params["submit-name"].must_equal "Larry"
|
479
|
+
params["files"][:filename].must_equal "file1.txt"
|
480
|
+
params["files"][:tempfile].read.must_equal "contents"
|
433
481
|
end
|
434
482
|
|
435
483
|
it "builds nested multipart body" do
|
@@ -443,9 +491,9 @@ Content-Type: image/jpeg\r
|
|
443
491
|
}
|
444
492
|
env = Rack::MockRequest.env_for("/", options)
|
445
493
|
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.
|
494
|
+
params["people"][0]["submit-name"].must_equal "Larry"
|
495
|
+
params["people"][0]["files"][:filename].must_equal "file1.txt"
|
496
|
+
params["people"][0]["files"][:tempfile].read.must_equal "contents"
|
449
497
|
end
|
450
498
|
|
451
499
|
it "can parse fields that end at the end of the buffer" do
|
@@ -456,8 +504,8 @@ Content-Type: image/jpeg\r
|
|
456
504
|
"CONTENT_LENGTH" => input.size,
|
457
505
|
:input => input)
|
458
506
|
|
459
|
-
req.POST['file.path'].
|
460
|
-
req.POST['addresses'].
|
507
|
+
req.POST['file.path'].must_equal "/var/tmp/uploads/4/0001728414"
|
508
|
+
req.POST['addresses'].wont_equal nil
|
461
509
|
end
|
462
510
|
|
463
511
|
it "builds complete params with the chunk size of 16384 slicing exactly on boundary" do
|
@@ -474,54 +522,54 @@ Content-Type: image/jpeg\r
|
|
474
522
|
env = Rack::MockRequest.env_for("/", options)
|
475
523
|
params = Rack::Multipart.parse_multipart(env)
|
476
524
|
|
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"].
|
525
|
+
params.wont_equal nil
|
526
|
+
params.keys.must_include "AAAAAAAAAAAAAAAAAAA"
|
527
|
+
params["AAAAAAAAAAAAAAAAAAA"].keys.must_include "PLAPLAPLA_MEMMEMMEMM_ATTRATTRER"
|
528
|
+
params["AAAAAAAAAAAAAAAAAAA"]["PLAPLAPLA_MEMMEMMEMM_ATTRATTRER"].keys.must_include "new"
|
529
|
+
params["AAAAAAAAAAAAAAAAAAA"]["PLAPLAPLA_MEMMEMMEMM_ATTRATTRER"]["new"].keys.must_include "-2"
|
530
|
+
params["AAAAAAAAAAAAAAAAAAA"]["PLAPLAPLA_MEMMEMMEMM_ATTRATTRER"]["new"]["-2"].keys.must_include "ba_unit_id"
|
531
|
+
params["AAAAAAAAAAAAAAAAAAA"]["PLAPLAPLA_MEMMEMMEMM_ATTRATTRER"]["new"]["-2"]["ba_unit_id"].must_equal "1017"
|
484
532
|
ensure
|
485
533
|
Rack::Utils.multipart_part_limit = previous_limit
|
486
534
|
end
|
487
535
|
end
|
488
536
|
|
489
|
-
|
537
|
+
it "not reach a multi-part limit" do
|
490
538
|
begin
|
491
539
|
previous_limit = Rack::Utils.multipart_part_limit
|
492
540
|
Rack::Utils.multipart_part_limit = 4
|
493
541
|
|
494
542
|
env = Rack::MockRequest.env_for '/', multipart_fixture(:three_files_three_fields)
|
495
543
|
params = Rack::Multipart.parse_multipart(env)
|
496
|
-
params['reply'].
|
497
|
-
params['to'].
|
498
|
-
params['from'].
|
544
|
+
params['reply'].must_equal 'yes'
|
545
|
+
params['to'].must_equal 'people'
|
546
|
+
params['from'].must_equal 'others'
|
499
547
|
ensure
|
500
548
|
Rack::Utils.multipart_part_limit = previous_limit
|
501
549
|
end
|
502
550
|
end
|
503
551
|
|
504
|
-
|
552
|
+
it "reach a multipart limit" do
|
505
553
|
begin
|
506
554
|
previous_limit = Rack::Utils.multipart_part_limit
|
507
555
|
Rack::Utils.multipart_part_limit = 3
|
508
556
|
|
509
557
|
env = Rack::MockRequest.env_for '/', multipart_fixture(:three_files_three_fields)
|
510
|
-
lambda { Rack::Multipart.parse_multipart(env) }.
|
558
|
+
lambda { Rack::Multipart.parse_multipart(env) }.must_raise Rack::Multipart::MultipartPartLimitError
|
511
559
|
ensure
|
512
560
|
Rack::Utils.multipart_part_limit = previous_limit
|
513
561
|
end
|
514
562
|
end
|
515
563
|
|
516
|
-
|
564
|
+
it "return nil if no UploadedFiles were used" do
|
517
565
|
data = Rack::Multipart.build_multipart("people" => [{"submit-name" => "Larry", "files" => "contents"}])
|
518
|
-
data.
|
566
|
+
data.must_be_nil
|
519
567
|
end
|
520
568
|
|
521
|
-
|
522
|
-
lambda {
|
523
|
-
|
524
|
-
|
569
|
+
it "raise ArgumentError if params is not a Hash" do
|
570
|
+
lambda {
|
571
|
+
Rack::Multipart.build_multipart("foo=bar")
|
572
|
+
}.must_raise(ArgumentError).message.must_equal "value must be a Hash"
|
525
573
|
end
|
526
574
|
|
527
575
|
it "can parse fields with a content type" do
|
@@ -539,20 +587,20 @@ EOF
|
|
539
587
|
:input => StringIO.new(data)
|
540
588
|
}
|
541
589
|
env = Rack::MockRequest.env_for("/", options)
|
542
|
-
params = Rack::
|
590
|
+
params = Rack::Multipart.parse_multipart(env)
|
543
591
|
|
544
|
-
params.
|
592
|
+
params.must_equal "description"=>"Very very blue"
|
545
593
|
end
|
546
594
|
|
547
|
-
|
595
|
+
it "parse multipart upload with no content-length header" do
|
548
596
|
env = Rack::MockRequest.env_for '/', multipart_fixture(:webkit)
|
549
597
|
env['CONTENT_TYPE'] = "multipart/form-data; boundary=----WebKitFormBoundaryWLHCs9qmcJJoyjKR"
|
550
598
|
env.delete 'CONTENT_LENGTH'
|
551
599
|
params = Rack::Multipart.parse_multipart(env)
|
552
|
-
params['profile']['bio'].
|
600
|
+
params['profile']['bio'].must_include 'hello'
|
553
601
|
end
|
554
602
|
|
555
|
-
|
603
|
+
it "parse very long unquoted multipart file names" do
|
556
604
|
data = <<-EOF
|
557
605
|
--AaB03x\r
|
558
606
|
Content-Type: text/plain\r
|
@@ -568,17 +616,57 @@ contents\r
|
|
568
616
|
:input => StringIO.new(data)
|
569
617
|
}
|
570
618
|
env = Rack::MockRequest.env_for("/", options)
|
571
|
-
params = Rack::
|
619
|
+
params = Rack::Multipart.parse_multipart(env)
|
620
|
+
|
621
|
+
params["file"][:filename].must_equal 'long' * 100
|
622
|
+
end
|
623
|
+
|
624
|
+
it "parse unquoted parameter values at end of line" do
|
625
|
+
data = <<-EOF
|
626
|
+
--AaB03x\r
|
627
|
+
Content-Type: text/plain\r
|
628
|
+
Content-Disposition: attachment; name=inline\r
|
629
|
+
\r
|
630
|
+
true\r
|
631
|
+
--AaB03x--\r
|
632
|
+
EOF
|
633
|
+
|
634
|
+
options = {
|
635
|
+
"CONTENT_TYPE" => "multipart/form-data; boundary=AaB03x",
|
636
|
+
"CONTENT_LENGTH" => data.length.to_s,
|
637
|
+
:input => StringIO.new(data)
|
638
|
+
}
|
639
|
+
env = Rack::MockRequest.env_for("/", options)
|
640
|
+
params = Rack::Multipart.parse_multipart(env)
|
641
|
+
params["inline"].must_equal 'true'
|
642
|
+
end
|
572
643
|
|
573
|
-
|
644
|
+
it "parse quoted chars in name parameter" do
|
645
|
+
data = <<-EOF
|
646
|
+
--AaB03x\r
|
647
|
+
Content-Type: text/plain\r
|
648
|
+
Content-Disposition: attachment; name="quoted\\\\chars\\"in\rname"\r
|
649
|
+
\r
|
650
|
+
true\r
|
651
|
+
--AaB03x--\r
|
652
|
+
EOF
|
653
|
+
|
654
|
+
options = {
|
655
|
+
"CONTENT_TYPE" => "multipart/form-data; boundary=AaB03x",
|
656
|
+
"CONTENT_LENGTH" => data.length.to_s,
|
657
|
+
:input => StringIO.new(data)
|
658
|
+
}
|
659
|
+
env = Rack::MockRequest.env_for("/", options)
|
660
|
+
params = Rack::Multipart.parse_multipart(env)
|
661
|
+
params["quoted\\chars\"in\rname"].must_equal 'true'
|
574
662
|
end
|
575
663
|
|
576
|
-
|
664
|
+
it "support mixed case metadata" do
|
577
665
|
file = multipart_file(:text)
|
578
666
|
data = File.open(file, 'rb') { |io| io.read }
|
579
667
|
|
580
668
|
type = "Multipart/Form-Data; Boundary=AaB03x"
|
581
|
-
length = data.
|
669
|
+
length = data.bytesize
|
582
670
|
|
583
671
|
e = { "CONTENT_TYPE" => type,
|
584
672
|
"CONTENT_LENGTH" => length.to_s,
|
@@ -586,15 +674,49 @@ contents\r
|
|
586
674
|
|
587
675
|
env = Rack::MockRequest.env_for("/", e)
|
588
676
|
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].
|
677
|
+
params["submit-name"].must_equal "Larry"
|
678
|
+
params["submit-name-with-content"].must_equal "Berry"
|
679
|
+
params["files"][:type].must_equal "text/plain"
|
680
|
+
params["files"][:filename].must_equal "file1.txt"
|
681
|
+
params["files"][:head].must_equal "Content-Disposition: form-data; " +
|
594
682
|
"name=\"files\"; filename=\"file1.txt\"\r\n" +
|
595
683
|
"Content-Type: text/plain\r\n"
|
596
|
-
params["files"][:name].
|
597
|
-
params["files"][:tempfile].read.
|
684
|
+
params["files"][:name].must_equal "files"
|
685
|
+
params["files"][:tempfile].read.must_equal "contents"
|
598
686
|
end
|
599
687
|
|
688
|
+
it "fallback to content-type for name" do
|
689
|
+
rack_logo = File.read(multipart_file("rack-logo.png"))
|
690
|
+
|
691
|
+
data = <<-EOF
|
692
|
+
--AaB03x\r
|
693
|
+
Content-Type: text/plain\r
|
694
|
+
\r
|
695
|
+
some text\r
|
696
|
+
--AaB03x\r
|
697
|
+
\r
|
698
|
+
\r
|
699
|
+
some more text (I didn't specify Content-Type)\r
|
700
|
+
--AaB03x\r
|
701
|
+
Content-Type: image/png\r
|
702
|
+
\r
|
703
|
+
#{rack_logo}\r
|
704
|
+
--AaB03x--\r
|
705
|
+
EOF
|
706
|
+
|
707
|
+
options = {
|
708
|
+
"CONTENT_TYPE" => "multipart/related; boundary=AaB03x",
|
709
|
+
"CONTENT_LENGTH" => data.bytesize.to_s,
|
710
|
+
:input => StringIO.new(data)
|
711
|
+
}
|
712
|
+
env = Rack::MockRequest.env_for("/", options)
|
713
|
+
params = Rack::Multipart.parse_multipart(env)
|
714
|
+
|
715
|
+
params["text/plain"].must_equal ["some text", "some more text (I didn't specify Content-Type)"]
|
716
|
+
params["image/png"].length.must_equal 1
|
717
|
+
|
718
|
+
f = Tempfile.new("rack-logo")
|
719
|
+
f.write(params["image/png"][0])
|
720
|
+
f.length.must_equal 26473
|
721
|
+
end
|
600
722
|
end
|