rack 1.6.13 → 2.0.1
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 +5 -5
- data/COPYING +1 -1
- data/HISTORY.md +138 -8
- data/README.rdoc +17 -25
- data/Rakefile +6 -14
- data/SPEC +8 -9
- 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/{conditionalget.rb → conditional_get.rb} +0 -0
- data/lib/rack/content_length.rb +2 -2
- data/lib/rack/deflater.rb +4 -4
- data/lib/rack/directory.rb +66 -54
- data/lib/rack/etag.rb +4 -3
- data/lib/rack/events.rb +154 -0
- data/lib/rack/file.rb +63 -39
- 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 +22 -24
- data/lib/rack/handler.rb +3 -25
- data/lib/rack/head.rb +15 -17
- data/lib/rack/lint.rb +38 -38
- data/lib/rack/lobster.rb +1 -1
- data/lib/rack/lock.rb +6 -10
- data/lib/rack/logger.rb +2 -2
- data/lib/rack/media_type.rb +38 -0
- data/lib/rack/{methodoverride.rb → method_override.rb} +4 -11
- data/lib/rack/mime.rb +18 -5
- data/lib/rack/mock.rb +35 -53
- data/lib/rack/multipart/generator.rb +5 -5
- data/lib/rack/multipart/parser.rb +272 -158
- 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 +383 -307
- data/lib/rack/response.rb +129 -56
- 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 +31 -25
- data/lib/rack/session/abstract/id.rb +95 -135
- data/lib/rack/session/cookie.rb +26 -28
- data/lib/rack/session/memcache.rb +8 -14
- data/lib/rack/session/pool.rb +14 -21
- 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 +135 -210
- data/lib/rack.rb +70 -21
- data/rack.gemspec +7 -5
- 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_null_byte → filename_with_single_quote} +1 -1
- 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 +36 -34
- 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 +66 -40
- 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 +107 -77
- data/test/spec_handler.rb +19 -34
- data/test/spec_head.rb +15 -14
- data/test/spec_lint.rb +162 -197
- data/test/spec_lobster.rb +24 -23
- data/test/spec_lock.rb +69 -39
- data/test/spec_logger.rb +4 -3
- data/test/spec_media_type.rb +42 -0
- data/test/spec_method_override.rb +83 -0
- data/test/spec_mime.rb +19 -19
- data/test/spec_mock.rb +196 -151
- data/test/spec_multipart.rb +317 -201
- data/test/{spec_nulllogger.rb → spec_null_logger.rb} +5 -4
- data/test/spec_recursive.rb +17 -14
- data/test/spec_request.rb +768 -607
- data/test/spec_response.rb +214 -111
- 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 +28 -0
- data/test/spec_session_cookie.rb +97 -65
- data/test/spec_session_memcache.rb +63 -101
- data/test/spec_session_pool.rb +48 -84
- data/test/spec_show_exceptions.rb +80 -0
- 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 +91 -67
- 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 +103 -69
- 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_methodoverride.rb +0 -111
- data/test/spec_mongrel.rb +0 -182
- data/test/spec_session_persisted_secure_secure_session_hash.rb +0 -73
- data/test/spec_showexceptions.rb +0 -98
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_equal 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,309 @@ 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
|
-
|
265
|
-
env = Rack::MockRequest.env_for
|
308
|
+
it "not include file params if no file was selected" do
|
309
|
+
env = Rack::MockRequest.env_for("/", multipart_fixture(:none))
|
266
310
|
params = Rack::Multipart.parse_multipart(env)
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
params["files"][:filename].should.equal "flowers.exe\000.jpg"
|
271
|
-
end
|
311
|
+
params["submit-name"].must_equal "Larry"
|
312
|
+
params["files"].must_equal nil
|
313
|
+
params.keys.wont_include "files"
|
272
314
|
end
|
273
315
|
|
274
|
-
|
316
|
+
it "parse multipart/mixed" do
|
275
317
|
env = Rack::MockRequest.env_for("/", multipart_fixture(:mixed_files))
|
276
|
-
params = Rack::
|
277
|
-
params["foo"].
|
278
|
-
params["files"].
|
279
|
-
params["files"].size.
|
318
|
+
params = Rack::Multipart.parse_multipart(env)
|
319
|
+
params["foo"].must_equal "bar"
|
320
|
+
params["files"].must_be_instance_of String
|
321
|
+
params["files"].size.must_equal 252
|
280
322
|
end
|
281
323
|
|
282
|
-
|
324
|
+
it "parse IE multipart upload and clean up filename" do
|
283
325
|
env = Rack::MockRequest.env_for("/", multipart_fixture(:ie))
|
284
326
|
params = Rack::Multipart.parse_multipart(env)
|
285
|
-
params["files"][:type].
|
286
|
-
params["files"][:filename].
|
287
|
-
params["files"][:head].
|
327
|
+
params["files"][:type].must_equal "text/plain"
|
328
|
+
params["files"][:filename].must_equal "file1.txt"
|
329
|
+
params["files"][:head].must_equal "Content-Disposition: form-data; " +
|
288
330
|
"name=\"files\"; " +
|
289
331
|
'filename="C:\Documents and Settings\Administrator\Desktop\file1.txt"' +
|
290
332
|
"\r\nContent-Type: text/plain\r\n"
|
291
|
-
params["files"][:name].
|
292
|
-
params["files"][:tempfile].read.
|
333
|
+
params["files"][:name].must_equal "files"
|
334
|
+
params["files"][:tempfile].read.must_equal "contents"
|
293
335
|
end
|
294
336
|
|
295
|
-
|
337
|
+
it "parse filename and modification param" do
|
296
338
|
env = Rack::MockRequest.env_for("/", multipart_fixture(:filename_and_modification_param))
|
297
339
|
params = Rack::Multipart.parse_multipart(env)
|
298
|
-
params["files"][:type].
|
299
|
-
params["files"][:filename].
|
300
|
-
params["files"][:head].
|
340
|
+
params["files"][:type].must_equal "image/jpeg"
|
341
|
+
params["files"][:filename].must_equal "genome.jpeg"
|
342
|
+
params["files"][:head].must_equal "Content-Type: image/jpeg\r\n" +
|
301
343
|
"Content-Disposition: attachment; " +
|
302
344
|
"name=\"files\"; " +
|
303
345
|
"filename=genome.jpeg; " +
|
304
346
|
"modification-date=\"Wed, 12 Feb 1997 16:29:51 -0500\";\r\n" +
|
305
347
|
"Content-Description: a complete map of the human genome\r\n"
|
306
|
-
params["files"][:name].
|
307
|
-
params["files"][:tempfile].read.
|
348
|
+
params["files"][:name].must_equal "files"
|
349
|
+
params["files"][:tempfile].read.must_equal "contents"
|
308
350
|
end
|
309
351
|
|
310
|
-
|
352
|
+
it "parse filename with escaped quotes" do
|
311
353
|
env = Rack::MockRequest.env_for("/", multipart_fixture(:filename_with_escaped_quotes))
|
312
354
|
params = Rack::Multipart.parse_multipart(env)
|
313
|
-
params["files"][:type].
|
314
|
-
params["files"][:filename].
|
315
|
-
params["files"][:head].
|
355
|
+
params["files"][:type].must_equal "application/octet-stream"
|
356
|
+
params["files"][:filename].must_equal "escape \"quotes"
|
357
|
+
params["files"][:head].must_equal "Content-Disposition: form-data; " +
|
316
358
|
"name=\"files\"; " +
|
317
359
|
"filename=\"escape \\\"quotes\"\r\n" +
|
318
360
|
"Content-Type: application/octet-stream\r\n"
|
319
|
-
params["files"][:name].
|
320
|
-
params["files"][:tempfile].read.
|
361
|
+
params["files"][:name].must_equal "files"
|
362
|
+
params["files"][:tempfile].read.must_equal "contents"
|
321
363
|
end
|
322
364
|
|
323
|
-
|
365
|
+
it "parse filename with percent escaped quotes" do
|
324
366
|
env = Rack::MockRequest.env_for("/", multipart_fixture(:filename_with_percent_escaped_quotes))
|
325
367
|
params = Rack::Multipart.parse_multipart(env)
|
326
|
-
params["files"][:type].
|
327
|
-
params["files"][:filename].
|
328
|
-
params["files"][:head].
|
368
|
+
params["files"][:type].must_equal "application/octet-stream"
|
369
|
+
params["files"][:filename].must_equal "escape \"quotes"
|
370
|
+
params["files"][:head].must_equal "Content-Disposition: form-data; " +
|
329
371
|
"name=\"files\"; " +
|
330
372
|
"filename=\"escape %22quotes\"\r\n" +
|
331
373
|
"Content-Type: application/octet-stream\r\n"
|
332
|
-
params["files"][:name].
|
333
|
-
params["files"][:tempfile].read.
|
374
|
+
params["files"][:name].must_equal "files"
|
375
|
+
params["files"][:tempfile].read.must_equal "contents"
|
334
376
|
end
|
335
377
|
|
336
|
-
|
378
|
+
it "parse filename with unescaped quotes" do
|
337
379
|
env = Rack::MockRequest.env_for("/", multipart_fixture(:filename_with_unescaped_quotes))
|
338
380
|
params = Rack::Multipart.parse_multipart(env)
|
339
|
-
params["files"][:type].
|
340
|
-
params["files"][:filename].
|
341
|
-
params["files"][:head].
|
381
|
+
params["files"][:type].must_equal "application/octet-stream"
|
382
|
+
params["files"][:filename].must_equal "escape \"quotes"
|
383
|
+
params["files"][:head].must_equal "Content-Disposition: form-data; " +
|
342
384
|
"name=\"files\"; " +
|
343
385
|
"filename=\"escape \"quotes\"\r\n" +
|
344
386
|
"Content-Type: application/octet-stream\r\n"
|
345
|
-
params["files"][:name].
|
346
|
-
params["files"][:tempfile].read.
|
387
|
+
params["files"][:name].must_equal "files"
|
388
|
+
params["files"][:tempfile].read.must_equal "contents"
|
347
389
|
end
|
348
390
|
|
349
|
-
|
391
|
+
it "parse filename with escaped quotes and modification param" do
|
350
392
|
env = Rack::MockRequest.env_for("/", multipart_fixture(:filename_with_escaped_quotes_and_modification_param))
|
351
393
|
params = Rack::Multipart.parse_multipart(env)
|
352
|
-
params["files"][:type].
|
353
|
-
params["files"][:filename].
|
354
|
-
params["files"][:head].
|
394
|
+
params["files"][:type].must_equal "image/jpeg"
|
395
|
+
params["files"][:filename].must_equal "\"human\" genome.jpeg"
|
396
|
+
params["files"][:head].must_equal "Content-Type: image/jpeg\r\n" +
|
355
397
|
"Content-Disposition: attachment; " +
|
356
398
|
"name=\"files\"; " +
|
357
399
|
"filename=\"\"human\" genome.jpeg\"; " +
|
358
400
|
"modification-date=\"Wed, 12 Feb 1997 16:29:51 -0500\";\r\n" +
|
359
401
|
"Content-Description: a complete map of the human genome\r\n"
|
360
|
-
params["files"][:name].
|
361
|
-
params["files"][:tempfile].read.
|
402
|
+
params["files"][:name].must_equal "files"
|
403
|
+
params["files"][:tempfile].read.must_equal "contents"
|
362
404
|
end
|
363
405
|
|
364
|
-
|
406
|
+
it "parse filename with unescaped percentage characters" do
|
365
407
|
env = Rack::MockRequest.env_for("/", multipart_fixture(:filename_with_unescaped_percentages, "----WebKitFormBoundary2NHc7OhsgU68l3Al"))
|
366
408
|
params = Rack::Multipart.parse_multipart(env)
|
367
409
|
files = params["document"]["attachment"]
|
368
|
-
files[:type].
|
369
|
-
files[:filename].
|
370
|
-
files[:head].
|
410
|
+
files[:type].must_equal "image/jpeg"
|
411
|
+
files[:filename].must_equal "100% of a photo.jpeg"
|
412
|
+
files[:head].must_equal <<-MULTIPART
|
371
413
|
Content-Disposition: form-data; name="document[attachment]"; filename="100% of a photo.jpeg"\r
|
372
414
|
Content-Type: image/jpeg\r
|
373
415
|
MULTIPART
|
374
416
|
|
375
|
-
files[:name].
|
376
|
-
files[:tempfile].read.
|
417
|
+
files[:name].must_equal "document[attachment]"
|
418
|
+
files[:tempfile].read.must_equal "contents"
|
377
419
|
end
|
378
420
|
|
379
|
-
|
421
|
+
it "parse filename with unescaped percentage characters that look like partial hex escapes" do
|
380
422
|
env = Rack::MockRequest.env_for("/", multipart_fixture(:filename_with_unescaped_percentages2, "----WebKitFormBoundary2NHc7OhsgU68l3Al"))
|
381
423
|
params = Rack::Multipart.parse_multipart(env)
|
382
424
|
files = params["document"]["attachment"]
|
383
|
-
files[:type].
|
384
|
-
files[:filename].
|
385
|
-
files[:head].
|
425
|
+
files[:type].must_equal "image/jpeg"
|
426
|
+
files[:filename].must_equal "100%a"
|
427
|
+
files[:head].must_equal <<-MULTIPART
|
386
428
|
Content-Disposition: form-data; name="document[attachment]"; filename="100%a"\r
|
387
429
|
Content-Type: image/jpeg\r
|
388
430
|
MULTIPART
|
389
431
|
|
390
|
-
files[:name].
|
391
|
-
files[:tempfile].read.
|
432
|
+
files[:name].must_equal "document[attachment]"
|
433
|
+
files[:tempfile].read.must_equal "contents"
|
392
434
|
end
|
393
435
|
|
394
|
-
|
436
|
+
it "parse filename with unescaped percentage characters that look like partial hex escapes" do
|
395
437
|
env = Rack::MockRequest.env_for("/", multipart_fixture(:filename_with_unescaped_percentages3, "----WebKitFormBoundary2NHc7OhsgU68l3Al"))
|
396
438
|
params = Rack::Multipart.parse_multipart(env)
|
397
439
|
files = params["document"]["attachment"]
|
398
|
-
files[:type].
|
399
|
-
files[:filename].
|
400
|
-
files[:head].
|
440
|
+
files[:type].must_equal "image/jpeg"
|
441
|
+
files[:filename].must_equal "100%"
|
442
|
+
files[:head].must_equal <<-MULTIPART
|
401
443
|
Content-Disposition: form-data; name="document[attachment]"; filename="100%"\r
|
402
444
|
Content-Type: image/jpeg\r
|
403
445
|
MULTIPART
|
404
446
|
|
405
|
-
files[:name].
|
406
|
-
files[:tempfile].read.
|
447
|
+
files[:name].must_equal "document[attachment]"
|
448
|
+
files[:tempfile].read.must_equal "contents"
|
407
449
|
end
|
408
450
|
|
409
451
|
it "rewinds input after parsing upload" do
|
@@ -411,9 +453,9 @@ Content-Type: image/jpeg\r
|
|
411
453
|
input = options[:input]
|
412
454
|
env = Rack::MockRequest.env_for("/", options)
|
413
455
|
params = Rack::Multipart.parse_multipart(env)
|
414
|
-
params["submit-name"].
|
415
|
-
params["files"][:filename].
|
416
|
-
input.read.length.
|
456
|
+
params["submit-name"].must_equal "Larry"
|
457
|
+
params["files"][:filename].must_equal "file1.txt"
|
458
|
+
input.read.length.must_equal 307
|
417
459
|
end
|
418
460
|
|
419
461
|
it "builds multipart body" do
|
@@ -427,9 +469,9 @@ Content-Type: image/jpeg\r
|
|
427
469
|
}
|
428
470
|
env = Rack::MockRequest.env_for("/", options)
|
429
471
|
params = Rack::Multipart.parse_multipart(env)
|
430
|
-
params["submit-name"].
|
431
|
-
params["files"][:filename].
|
432
|
-
params["files"][:tempfile].read.
|
472
|
+
params["submit-name"].must_equal "Larry"
|
473
|
+
params["files"][:filename].must_equal "file1.txt"
|
474
|
+
params["files"][:tempfile].read.must_equal "contents"
|
433
475
|
end
|
434
476
|
|
435
477
|
it "builds nested multipart body" do
|
@@ -443,9 +485,9 @@ Content-Type: image/jpeg\r
|
|
443
485
|
}
|
444
486
|
env = Rack::MockRequest.env_for("/", options)
|
445
487
|
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.
|
488
|
+
params["people"][0]["submit-name"].must_equal "Larry"
|
489
|
+
params["people"][0]["files"][:filename].must_equal "file1.txt"
|
490
|
+
params["people"][0]["files"][:tempfile].read.must_equal "contents"
|
449
491
|
end
|
450
492
|
|
451
493
|
it "can parse fields that end at the end of the buffer" do
|
@@ -456,8 +498,8 @@ Content-Type: image/jpeg\r
|
|
456
498
|
"CONTENT_LENGTH" => input.size,
|
457
499
|
:input => input)
|
458
500
|
|
459
|
-
req.POST['file.path'].
|
460
|
-
req.POST['addresses'].
|
501
|
+
req.POST['file.path'].must_equal "/var/tmp/uploads/4/0001728414"
|
502
|
+
req.POST['addresses'].wont_equal nil
|
461
503
|
end
|
462
504
|
|
463
505
|
it "builds complete params with the chunk size of 16384 slicing exactly on boundary" do
|
@@ -474,54 +516,54 @@ Content-Type: image/jpeg\r
|
|
474
516
|
env = Rack::MockRequest.env_for("/", options)
|
475
517
|
params = Rack::Multipart.parse_multipart(env)
|
476
518
|
|
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"].
|
519
|
+
params.wont_equal nil
|
520
|
+
params.keys.must_include "AAAAAAAAAAAAAAAAAAA"
|
521
|
+
params["AAAAAAAAAAAAAAAAAAA"].keys.must_include "PLAPLAPLA_MEMMEMMEMM_ATTRATTRER"
|
522
|
+
params["AAAAAAAAAAAAAAAAAAA"]["PLAPLAPLA_MEMMEMMEMM_ATTRATTRER"].keys.must_include "new"
|
523
|
+
params["AAAAAAAAAAAAAAAAAAA"]["PLAPLAPLA_MEMMEMMEMM_ATTRATTRER"]["new"].keys.must_include "-2"
|
524
|
+
params["AAAAAAAAAAAAAAAAAAA"]["PLAPLAPLA_MEMMEMMEMM_ATTRATTRER"]["new"]["-2"].keys.must_include "ba_unit_id"
|
525
|
+
params["AAAAAAAAAAAAAAAAAAA"]["PLAPLAPLA_MEMMEMMEMM_ATTRATTRER"]["new"]["-2"]["ba_unit_id"].must_equal "1017"
|
484
526
|
ensure
|
485
527
|
Rack::Utils.multipart_part_limit = previous_limit
|
486
528
|
end
|
487
529
|
end
|
488
530
|
|
489
|
-
|
531
|
+
it "not reach a multi-part limit" do
|
490
532
|
begin
|
491
533
|
previous_limit = Rack::Utils.multipart_part_limit
|
492
534
|
Rack::Utils.multipart_part_limit = 4
|
493
535
|
|
494
536
|
env = Rack::MockRequest.env_for '/', multipart_fixture(:three_files_three_fields)
|
495
537
|
params = Rack::Multipart.parse_multipart(env)
|
496
|
-
params['reply'].
|
497
|
-
params['to'].
|
498
|
-
params['from'].
|
538
|
+
params['reply'].must_equal 'yes'
|
539
|
+
params['to'].must_equal 'people'
|
540
|
+
params['from'].must_equal 'others'
|
499
541
|
ensure
|
500
542
|
Rack::Utils.multipart_part_limit = previous_limit
|
501
543
|
end
|
502
544
|
end
|
503
545
|
|
504
|
-
|
546
|
+
it "reach a multipart limit" do
|
505
547
|
begin
|
506
548
|
previous_limit = Rack::Utils.multipart_part_limit
|
507
549
|
Rack::Utils.multipart_part_limit = 3
|
508
550
|
|
509
551
|
env = Rack::MockRequest.env_for '/', multipart_fixture(:three_files_three_fields)
|
510
|
-
lambda { Rack::Multipart.parse_multipart(env) }.
|
552
|
+
lambda { Rack::Multipart.parse_multipart(env) }.must_raise Rack::Multipart::MultipartPartLimitError
|
511
553
|
ensure
|
512
554
|
Rack::Utils.multipart_part_limit = previous_limit
|
513
555
|
end
|
514
556
|
end
|
515
557
|
|
516
|
-
|
558
|
+
it "return nil if no UploadedFiles were used" do
|
517
559
|
data = Rack::Multipart.build_multipart("people" => [{"submit-name" => "Larry", "files" => "contents"}])
|
518
|
-
data.
|
560
|
+
data.must_equal nil
|
519
561
|
end
|
520
562
|
|
521
|
-
|
522
|
-
lambda {
|
523
|
-
|
524
|
-
|
563
|
+
it "raise ArgumentError if params is not a Hash" do
|
564
|
+
lambda {
|
565
|
+
Rack::Multipart.build_multipart("foo=bar")
|
566
|
+
}.must_raise(ArgumentError).message.must_equal "value must be a Hash"
|
525
567
|
end
|
526
568
|
|
527
569
|
it "can parse fields with a content type" do
|
@@ -539,20 +581,20 @@ EOF
|
|
539
581
|
:input => StringIO.new(data)
|
540
582
|
}
|
541
583
|
env = Rack::MockRequest.env_for("/", options)
|
542
|
-
params = Rack::
|
584
|
+
params = Rack::Multipart.parse_multipart(env)
|
543
585
|
|
544
|
-
params.
|
586
|
+
params.must_equal "description"=>"Very very blue"
|
545
587
|
end
|
546
588
|
|
547
|
-
|
589
|
+
it "parse multipart upload with no content-length header" do
|
548
590
|
env = Rack::MockRequest.env_for '/', multipart_fixture(:webkit)
|
549
591
|
env['CONTENT_TYPE'] = "multipart/form-data; boundary=----WebKitFormBoundaryWLHCs9qmcJJoyjKR"
|
550
592
|
env.delete 'CONTENT_LENGTH'
|
551
593
|
params = Rack::Multipart.parse_multipart(env)
|
552
|
-
params['profile']['bio'].
|
594
|
+
params['profile']['bio'].must_include 'hello'
|
553
595
|
end
|
554
596
|
|
555
|
-
|
597
|
+
it "parse very long unquoted multipart file names" do
|
556
598
|
data = <<-EOF
|
557
599
|
--AaB03x\r
|
558
600
|
Content-Type: text/plain\r
|
@@ -568,17 +610,57 @@ contents\r
|
|
568
610
|
:input => StringIO.new(data)
|
569
611
|
}
|
570
612
|
env = Rack::MockRequest.env_for("/", options)
|
571
|
-
params = Rack::
|
613
|
+
params = Rack::Multipart.parse_multipart(env)
|
572
614
|
|
573
|
-
params["file"][:filename].
|
615
|
+
params["file"][:filename].must_equal 'long' * 100
|
574
616
|
end
|
575
617
|
|
576
|
-
|
618
|
+
it "parse unquoted parameter values at end of line" do
|
619
|
+
data = <<-EOF
|
620
|
+
--AaB03x\r
|
621
|
+
Content-Type: text/plain\r
|
622
|
+
Content-Disposition: attachment; name=inline\r
|
623
|
+
\r
|
624
|
+
true\r
|
625
|
+
--AaB03x--\r
|
626
|
+
EOF
|
627
|
+
|
628
|
+
options = {
|
629
|
+
"CONTENT_TYPE" => "multipart/form-data; boundary=AaB03x",
|
630
|
+
"CONTENT_LENGTH" => data.length.to_s,
|
631
|
+
:input => StringIO.new(data)
|
632
|
+
}
|
633
|
+
env = Rack::MockRequest.env_for("/", options)
|
634
|
+
params = Rack::Multipart.parse_multipart(env)
|
635
|
+
params["inline"].must_equal 'true'
|
636
|
+
end
|
637
|
+
|
638
|
+
it "parse quoted chars in name parameter" do
|
639
|
+
data = <<-EOF
|
640
|
+
--AaB03x\r
|
641
|
+
Content-Type: text/plain\r
|
642
|
+
Content-Disposition: attachment; name="quoted\\\\chars\\"in\rname"\r
|
643
|
+
\r
|
644
|
+
true\r
|
645
|
+
--AaB03x--\r
|
646
|
+
EOF
|
647
|
+
|
648
|
+
options = {
|
649
|
+
"CONTENT_TYPE" => "multipart/form-data; boundary=AaB03x",
|
650
|
+
"CONTENT_LENGTH" => data.length.to_s,
|
651
|
+
:input => StringIO.new(data)
|
652
|
+
}
|
653
|
+
env = Rack::MockRequest.env_for("/", options)
|
654
|
+
params = Rack::Multipart.parse_multipart(env)
|
655
|
+
params["quoted\\chars\"in\rname"].must_equal 'true'
|
656
|
+
end
|
657
|
+
|
658
|
+
it "support mixed case metadata" do
|
577
659
|
file = multipart_file(:text)
|
578
660
|
data = File.open(file, 'rb') { |io| io.read }
|
579
661
|
|
580
662
|
type = "Multipart/Form-Data; Boundary=AaB03x"
|
581
|
-
length = data.
|
663
|
+
length = data.bytesize
|
582
664
|
|
583
665
|
e = { "CONTENT_TYPE" => type,
|
584
666
|
"CONTENT_LENGTH" => length.to_s,
|
@@ -586,15 +668,49 @@ contents\r
|
|
586
668
|
|
587
669
|
env = Rack::MockRequest.env_for("/", e)
|
588
670
|
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].
|
671
|
+
params["submit-name"].must_equal "Larry"
|
672
|
+
params["submit-name-with-content"].must_equal "Berry"
|
673
|
+
params["files"][:type].must_equal "text/plain"
|
674
|
+
params["files"][:filename].must_equal "file1.txt"
|
675
|
+
params["files"][:head].must_equal "Content-Disposition: form-data; " +
|
594
676
|
"name=\"files\"; filename=\"file1.txt\"\r\n" +
|
595
677
|
"Content-Type: text/plain\r\n"
|
596
|
-
params["files"][:name].
|
597
|
-
params["files"][:tempfile].read.
|
678
|
+
params["files"][:name].must_equal "files"
|
679
|
+
params["files"][:tempfile].read.must_equal "contents"
|
598
680
|
end
|
599
681
|
|
682
|
+
it "fallback to content-type for name" do
|
683
|
+
rack_logo = File.read(multipart_file("rack-logo.png"))
|
684
|
+
|
685
|
+
data = <<-EOF
|
686
|
+
--AaB03x\r
|
687
|
+
Content-Type: text/plain\r
|
688
|
+
\r
|
689
|
+
some text\r
|
690
|
+
--AaB03x\r
|
691
|
+
\r
|
692
|
+
\r
|
693
|
+
some more text (I didn't specify Content-Type)\r
|
694
|
+
--AaB03x\r
|
695
|
+
Content-Type: image/png\r
|
696
|
+
\r
|
697
|
+
#{rack_logo}\r
|
698
|
+
--AaB03x--\r
|
699
|
+
EOF
|
700
|
+
|
701
|
+
options = {
|
702
|
+
"CONTENT_TYPE" => "multipart/related; boundary=AaB03x",
|
703
|
+
"CONTENT_LENGTH" => data.bytesize.to_s,
|
704
|
+
:input => StringIO.new(data)
|
705
|
+
}
|
706
|
+
env = Rack::MockRequest.env_for("/", options)
|
707
|
+
params = Rack::Multipart.parse_multipart(env)
|
708
|
+
|
709
|
+
params["text/plain"].must_equal ["some text", "some more text (I didn't specify Content-Type)"]
|
710
|
+
params["image/png"].length.must_equal 1
|
711
|
+
|
712
|
+
f = Tempfile.new("rack-logo")
|
713
|
+
f.write(params["image/png"][0])
|
714
|
+
f.length.must_equal 26473
|
715
|
+
end
|
600
716
|
end
|