rack 2.0.8 → 2.2.2
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/CHANGELOG.md +690 -0
- data/CONTRIBUTING.md +136 -0
- data/{COPYING → MIT-LICENSE} +4 -2
- data/README.rdoc +152 -148
- data/Rakefile +37 -23
- data/{SPEC → SPEC.rdoc} +29 -5
- data/bin/rackup +1 -0
- data/example/lobster.ru +2 -0
- data/example/protectedlobster.rb +3 -1
- data/example/protectedlobster.ru +2 -0
- data/lib/rack.rb +67 -73
- data/lib/rack/auth/abstract/handler.rb +3 -1
- data/lib/rack/auth/abstract/request.rb +1 -1
- data/lib/rack/auth/basic.rb +7 -4
- data/lib/rack/auth/digest/md5.rb +13 -11
- data/lib/rack/auth/digest/nonce.rb +6 -3
- data/lib/rack/auth/digest/params.rb +4 -2
- data/lib/rack/auth/digest/request.rb +5 -3
- data/lib/rack/body_proxy.rb +15 -14
- data/lib/rack/builder.rb +116 -23
- data/lib/rack/cascade.rb +28 -12
- data/lib/rack/chunked.rb +68 -20
- data/lib/rack/common_logger.rb +33 -25
- data/lib/rack/conditional_get.rb +20 -16
- data/lib/rack/config.rb +2 -0
- data/lib/rack/content_length.rb +8 -7
- data/lib/rack/content_type.rb +5 -4
- data/lib/rack/core_ext/regexp.rb +14 -0
- data/lib/rack/deflater.rb +59 -34
- data/lib/rack/directory.rb +84 -64
- data/lib/rack/etag.rb +5 -4
- data/lib/rack/events.rb +19 -20
- data/lib/rack/file.rb +4 -173
- data/lib/rack/files.rb +218 -0
- data/lib/rack/handler.rb +7 -2
- data/lib/rack/handler/cgi.rb +2 -3
- data/lib/rack/handler/fastcgi.rb +4 -4
- data/lib/rack/handler/lsws.rb +3 -3
- data/lib/rack/handler/scgi.rb +9 -8
- data/lib/rack/handler/thin.rb +3 -3
- data/lib/rack/handler/webrick.rb +15 -6
- data/lib/rack/head.rb +1 -1
- data/lib/rack/lint.rb +71 -25
- data/lib/rack/lobster.rb +10 -10
- data/lib/rack/lock.rb +2 -1
- data/lib/rack/logger.rb +2 -0
- data/lib/rack/media_type.rb +10 -5
- data/lib/rack/method_override.rb +4 -2
- data/lib/rack/mime.rb +9 -1
- data/lib/rack/mock.rb +97 -20
- data/lib/rack/multipart.rb +6 -4
- data/lib/rack/multipart/generator.rb +17 -13
- data/lib/rack/multipart/parser.rb +54 -56
- data/lib/rack/multipart/uploaded_file.rb +15 -7
- data/lib/rack/null_logger.rb +2 -0
- data/lib/rack/query_parser.rb +53 -28
- data/lib/rack/recursive.rb +7 -5
- data/lib/rack/reloader.rb +8 -4
- data/lib/rack/request.rb +220 -61
- data/lib/rack/response.rb +127 -44
- data/lib/rack/rewindable_input.rb +4 -3
- data/lib/rack/runtime.rb +6 -4
- data/lib/rack/sendfile.rb +13 -9
- data/lib/rack/server.rb +95 -24
- data/lib/rack/session/abstract/id.rb +36 -23
- data/lib/rack/session/cookie.rb +11 -12
- data/lib/rack/session/memcache.rb +4 -93
- data/lib/rack/session/pool.rb +5 -3
- data/lib/rack/show_exceptions.rb +21 -17
- data/lib/rack/show_status.rb +9 -9
- data/lib/rack/static.rb +23 -11
- data/lib/rack/tempfile_reaper.rb +1 -1
- data/lib/rack/urlmap.rb +12 -6
- data/lib/rack/utils.rb +98 -109
- data/lib/rack/version.rb +29 -0
- data/rack.gemspec +40 -28
- metadata +36 -177
- data/HISTORY.md +0 -505
- data/test/builder/an_underscore_app.rb +0 -5
- data/test/builder/anything.rb +0 -5
- data/test/builder/comment.ru +0 -4
- data/test/builder/end.ru +0 -5
- data/test/builder/line.ru +0 -1
- data/test/builder/options.ru +0 -2
- data/test/cgi/assets/folder/test.js +0 -1
- data/test/cgi/assets/fonts/font.eot +0 -1
- data/test/cgi/assets/images/image.png +0 -1
- data/test/cgi/assets/index.html +0 -1
- data/test/cgi/assets/javascripts/app.js +0 -1
- data/test/cgi/assets/stylesheets/app.css +0 -1
- data/test/cgi/lighttpd.conf +0 -26
- data/test/cgi/rackup_stub.rb +0 -6
- data/test/cgi/sample_rackup.ru +0 -5
- data/test/cgi/test +0 -9
- data/test/cgi/test+directory/test+file +0 -1
- data/test/cgi/test.fcgi +0 -9
- data/test/cgi/test.gz +0 -0
- data/test/cgi/test.ru +0 -5
- data/test/gemloader.rb +0 -10
- data/test/helper.rb +0 -34
- data/test/multipart/bad_robots +0 -259
- data/test/multipart/binary +0 -0
- data/test/multipart/content_type_and_no_filename +0 -6
- data/test/multipart/empty +0 -10
- data/test/multipart/fail_16384_nofile +0 -814
- data/test/multipart/file1.txt +0 -1
- data/test/multipart/filename_and_modification_param +0 -7
- data/test/multipart/filename_and_no_name +0 -6
- data/test/multipart/filename_with_encoded_words +0 -7
- data/test/multipart/filename_with_escaped_quotes +0 -6
- data/test/multipart/filename_with_escaped_quotes_and_modification_param +0 -7
- data/test/multipart/filename_with_null_byte +0 -7
- data/test/multipart/filename_with_percent_escaped_quotes +0 -6
- data/test/multipart/filename_with_single_quote +0 -7
- data/test/multipart/filename_with_unescaped_percentages +0 -6
- data/test/multipart/filename_with_unescaped_percentages2 +0 -6
- data/test/multipart/filename_with_unescaped_percentages3 +0 -6
- data/test/multipart/filename_with_unescaped_quotes +0 -6
- data/test/multipart/ie +0 -6
- data/test/multipart/invalid_character +0 -6
- data/test/multipart/mixed_files +0 -21
- data/test/multipart/nested +0 -10
- data/test/multipart/none +0 -9
- data/test/multipart/quoted +0 -15
- data/test/multipart/rack-logo.png +0 -0
- data/test/multipart/semicolon +0 -6
- data/test/multipart/text +0 -15
- data/test/multipart/three_files_three_fields +0 -31
- data/test/multipart/unity3d_wwwform +0 -11
- data/test/multipart/webkit +0 -32
- data/test/rackup/config.ru +0 -31
- data/test/registering_handler/rack/handler/registering_myself.rb +0 -8
- data/test/spec_auth_basic.rb +0 -89
- data/test/spec_auth_digest.rb +0 -260
- data/test/spec_body_proxy.rb +0 -85
- data/test/spec_builder.rb +0 -233
- data/test/spec_cascade.rb +0 -63
- data/test/spec_cgi.rb +0 -84
- data/test/spec_chunked.rb +0 -103
- data/test/spec_common_logger.rb +0 -95
- data/test/spec_conditional_get.rb +0 -103
- data/test/spec_config.rb +0 -23
- data/test/spec_content_length.rb +0 -86
- data/test/spec_content_type.rb +0 -46
- data/test/spec_deflater.rb +0 -375
- data/test/spec_directory.rb +0 -148
- data/test/spec_etag.rb +0 -108
- data/test/spec_events.rb +0 -133
- data/test/spec_fastcgi.rb +0 -85
- data/test/spec_file.rb +0 -264
- data/test/spec_handler.rb +0 -57
- data/test/spec_head.rb +0 -46
- data/test/spec_lint.rb +0 -515
- data/test/spec_lobster.rb +0 -59
- data/test/spec_lock.rb +0 -204
- data/test/spec_logger.rb +0 -24
- data/test/spec_media_type.rb +0 -42
- data/test/spec_method_override.rb +0 -110
- data/test/spec_mime.rb +0 -51
- data/test/spec_mock.rb +0 -359
- data/test/spec_multipart.rb +0 -722
- data/test/spec_null_logger.rb +0 -21
- data/test/spec_recursive.rb +0 -75
- data/test/spec_request.rb +0 -1407
- data/test/spec_response.rb +0 -510
- data/test/spec_rewindable_input.rb +0 -128
- data/test/spec_runtime.rb +0 -50
- data/test/spec_sendfile.rb +0 -125
- data/test/spec_server.rb +0 -193
- data/test/spec_session_abstract_id.rb +0 -31
- data/test/spec_session_abstract_session_hash.rb +0 -45
- data/test/spec_session_cookie.rb +0 -442
- data/test/spec_session_memcache.rb +0 -357
- data/test/spec_session_pool.rb +0 -247
- data/test/spec_show_exceptions.rb +0 -93
- data/test/spec_show_status.rb +0 -104
- data/test/spec_static.rb +0 -184
- data/test/spec_tempfile_reaper.rb +0 -64
- data/test/spec_thin.rb +0 -96
- data/test/spec_urlmap.rb +0 -237
- data/test/spec_utils.rb +0 -742
- data/test/spec_version.rb +0 -11
- data/test/spec_webrick.rb +0 -206
- data/test/static/another/index.html +0 -1
- data/test/static/foo.html +0 -1
- data/test/static/index.html +0 -1
- data/test/testrequest.rb +0 -78
- data/test/unregistered_handler/rack/handler/unregistered.rb +0 -7
- data/test/unregistered_handler/rack/handler/unregistered_long_one.rb +0 -7
data/test/spec_mock.rb
DELETED
@@ -1,359 +0,0 @@
|
|
1
|
-
require 'minitest/autorun'
|
2
|
-
require 'yaml'
|
3
|
-
require 'rack/lint'
|
4
|
-
require 'rack/mock'
|
5
|
-
require 'stringio'
|
6
|
-
|
7
|
-
app = Rack::Lint.new(lambda { |env|
|
8
|
-
req = Rack::Request.new(env)
|
9
|
-
|
10
|
-
env["mock.postdata"] = env["rack.input"].read
|
11
|
-
if req.GET["error"]
|
12
|
-
env["rack.errors"].puts req.GET["error"]
|
13
|
-
env["rack.errors"].flush
|
14
|
-
end
|
15
|
-
|
16
|
-
body = req.head? ? "" : env.to_yaml
|
17
|
-
Rack::Response.new(body,
|
18
|
-
req.GET["status"] || 200,
|
19
|
-
"Content-Type" => "text/yaml").finish
|
20
|
-
})
|
21
|
-
|
22
|
-
describe Rack::MockRequest do
|
23
|
-
it "return a MockResponse" do
|
24
|
-
res = Rack::MockRequest.new(app).get("")
|
25
|
-
res.must_be_kind_of Rack::MockResponse
|
26
|
-
end
|
27
|
-
|
28
|
-
it "be able to only return the environment" do
|
29
|
-
env = Rack::MockRequest.env_for("")
|
30
|
-
env.must_be_kind_of Hash
|
31
|
-
env.must_include "rack.version"
|
32
|
-
end
|
33
|
-
|
34
|
-
it "return an environment with a path" do
|
35
|
-
env = Rack::MockRequest.env_for("http://www.example.com/parse?location[]=1&location[]=2&age_group[]=2")
|
36
|
-
env["QUERY_STRING"].must_equal "location[]=1&location[]=2&age_group[]=2"
|
37
|
-
env["PATH_INFO"].must_equal "/parse"
|
38
|
-
env.must_be_kind_of Hash
|
39
|
-
env.must_include "rack.version"
|
40
|
-
end
|
41
|
-
|
42
|
-
it "provide sensible defaults" do
|
43
|
-
res = Rack::MockRequest.new(app).request
|
44
|
-
|
45
|
-
env = YAML.load(res.body)
|
46
|
-
env["REQUEST_METHOD"].must_equal "GET"
|
47
|
-
env["SERVER_NAME"].must_equal "example.org"
|
48
|
-
env["SERVER_PORT"].must_equal "80"
|
49
|
-
env["QUERY_STRING"].must_equal ""
|
50
|
-
env["PATH_INFO"].must_equal "/"
|
51
|
-
env["SCRIPT_NAME"].must_equal ""
|
52
|
-
env["rack.url_scheme"].must_equal "http"
|
53
|
-
env["mock.postdata"].must_be :empty?
|
54
|
-
end
|
55
|
-
|
56
|
-
it "allow GET/POST/PUT/DELETE/HEAD" do
|
57
|
-
res = Rack::MockRequest.new(app).get("", :input => "foo")
|
58
|
-
env = YAML.load(res.body)
|
59
|
-
env["REQUEST_METHOD"].must_equal "GET"
|
60
|
-
|
61
|
-
res = Rack::MockRequest.new(app).post("", :input => "foo")
|
62
|
-
env = YAML.load(res.body)
|
63
|
-
env["REQUEST_METHOD"].must_equal "POST"
|
64
|
-
|
65
|
-
res = Rack::MockRequest.new(app).put("", :input => "foo")
|
66
|
-
env = YAML.load(res.body)
|
67
|
-
env["REQUEST_METHOD"].must_equal "PUT"
|
68
|
-
|
69
|
-
res = Rack::MockRequest.new(app).patch("", :input => "foo")
|
70
|
-
env = YAML.load(res.body)
|
71
|
-
env["REQUEST_METHOD"].must_equal "PATCH"
|
72
|
-
|
73
|
-
res = Rack::MockRequest.new(app).delete("", :input => "foo")
|
74
|
-
env = YAML.load(res.body)
|
75
|
-
env["REQUEST_METHOD"].must_equal "DELETE"
|
76
|
-
|
77
|
-
Rack::MockRequest.env_for("/", :method => "HEAD")["REQUEST_METHOD"]
|
78
|
-
.must_equal "HEAD"
|
79
|
-
|
80
|
-
Rack::MockRequest.env_for("/", :method => "OPTIONS")["REQUEST_METHOD"]
|
81
|
-
.must_equal "OPTIONS"
|
82
|
-
end
|
83
|
-
|
84
|
-
it "set content length" do
|
85
|
-
env = Rack::MockRequest.env_for("/", :input => "foo")
|
86
|
-
env["CONTENT_LENGTH"].must_equal "3"
|
87
|
-
end
|
88
|
-
|
89
|
-
it "allow posting" do
|
90
|
-
res = Rack::MockRequest.new(app).get("", :input => "foo")
|
91
|
-
env = YAML.load(res.body)
|
92
|
-
env["mock.postdata"].must_equal "foo"
|
93
|
-
|
94
|
-
res = Rack::MockRequest.new(app).post("", :input => StringIO.new("foo"))
|
95
|
-
env = YAML.load(res.body)
|
96
|
-
env["mock.postdata"].must_equal "foo"
|
97
|
-
end
|
98
|
-
|
99
|
-
it "use all parts of an URL" do
|
100
|
-
res = Rack::MockRequest.new(app).
|
101
|
-
get("https://bla.example.org:9292/meh/foo?bar")
|
102
|
-
res.must_be_kind_of Rack::MockResponse
|
103
|
-
|
104
|
-
env = YAML.load(res.body)
|
105
|
-
env["REQUEST_METHOD"].must_equal "GET"
|
106
|
-
env["SERVER_NAME"].must_equal "bla.example.org"
|
107
|
-
env["SERVER_PORT"].must_equal "9292"
|
108
|
-
env["QUERY_STRING"].must_equal "bar"
|
109
|
-
env["PATH_INFO"].must_equal "/meh/foo"
|
110
|
-
env["rack.url_scheme"].must_equal "https"
|
111
|
-
end
|
112
|
-
|
113
|
-
it "set SSL port and HTTP flag on when using https" do
|
114
|
-
res = Rack::MockRequest.new(app).
|
115
|
-
get("https://example.org/foo")
|
116
|
-
res.must_be_kind_of Rack::MockResponse
|
117
|
-
|
118
|
-
env = YAML.load(res.body)
|
119
|
-
env["REQUEST_METHOD"].must_equal "GET"
|
120
|
-
env["SERVER_NAME"].must_equal "example.org"
|
121
|
-
env["SERVER_PORT"].must_equal "443"
|
122
|
-
env["QUERY_STRING"].must_equal ""
|
123
|
-
env["PATH_INFO"].must_equal "/foo"
|
124
|
-
env["rack.url_scheme"].must_equal "https"
|
125
|
-
env["HTTPS"].must_equal "on"
|
126
|
-
end
|
127
|
-
|
128
|
-
it "prepend slash to uri path" do
|
129
|
-
res = Rack::MockRequest.new(app).
|
130
|
-
get("foo")
|
131
|
-
res.must_be_kind_of Rack::MockResponse
|
132
|
-
|
133
|
-
env = YAML.load(res.body)
|
134
|
-
env["REQUEST_METHOD"].must_equal "GET"
|
135
|
-
env["SERVER_NAME"].must_equal "example.org"
|
136
|
-
env["SERVER_PORT"].must_equal "80"
|
137
|
-
env["QUERY_STRING"].must_equal ""
|
138
|
-
env["PATH_INFO"].must_equal "/foo"
|
139
|
-
env["rack.url_scheme"].must_equal "http"
|
140
|
-
end
|
141
|
-
|
142
|
-
it "properly convert method name to an uppercase string" do
|
143
|
-
res = Rack::MockRequest.new(app).request(:get)
|
144
|
-
env = YAML.load(res.body)
|
145
|
-
env["REQUEST_METHOD"].must_equal "GET"
|
146
|
-
end
|
147
|
-
|
148
|
-
it "accept params and build query string for GET requests" do
|
149
|
-
res = Rack::MockRequest.new(app).get("/foo?baz=2", :params => {:foo => {:bar => "1"}})
|
150
|
-
env = YAML.load(res.body)
|
151
|
-
env["REQUEST_METHOD"].must_equal "GET"
|
152
|
-
env["QUERY_STRING"].must_include "baz=2"
|
153
|
-
env["QUERY_STRING"].must_include "foo[bar]=1"
|
154
|
-
env["PATH_INFO"].must_equal "/foo"
|
155
|
-
env["mock.postdata"].must_equal ""
|
156
|
-
end
|
157
|
-
|
158
|
-
it "accept raw input in params for GET requests" do
|
159
|
-
res = Rack::MockRequest.new(app).get("/foo?baz=2", :params => "foo[bar]=1")
|
160
|
-
env = YAML.load(res.body)
|
161
|
-
env["REQUEST_METHOD"].must_equal "GET"
|
162
|
-
env["QUERY_STRING"].must_include "baz=2"
|
163
|
-
env["QUERY_STRING"].must_include "foo[bar]=1"
|
164
|
-
env["PATH_INFO"].must_equal "/foo"
|
165
|
-
env["mock.postdata"].must_equal ""
|
166
|
-
end
|
167
|
-
|
168
|
-
it "accept params and build url encoded params for POST requests" do
|
169
|
-
res = Rack::MockRequest.new(app).post("/foo", :params => {:foo => {:bar => "1"}})
|
170
|
-
env = YAML.load(res.body)
|
171
|
-
env["REQUEST_METHOD"].must_equal "POST"
|
172
|
-
env["QUERY_STRING"].must_equal ""
|
173
|
-
env["PATH_INFO"].must_equal "/foo"
|
174
|
-
env["CONTENT_TYPE"].must_equal "application/x-www-form-urlencoded"
|
175
|
-
env["mock.postdata"].must_equal "foo[bar]=1"
|
176
|
-
end
|
177
|
-
|
178
|
-
it "accept raw input in params for POST requests" do
|
179
|
-
res = Rack::MockRequest.new(app).post("/foo", :params => "foo[bar]=1")
|
180
|
-
env = YAML.load(res.body)
|
181
|
-
env["REQUEST_METHOD"].must_equal "POST"
|
182
|
-
env["QUERY_STRING"].must_equal ""
|
183
|
-
env["PATH_INFO"].must_equal "/foo"
|
184
|
-
env["CONTENT_TYPE"].must_equal "application/x-www-form-urlencoded"
|
185
|
-
env["mock.postdata"].must_equal "foo[bar]=1"
|
186
|
-
end
|
187
|
-
|
188
|
-
it "accept params and build multipart encoded params for POST requests" do
|
189
|
-
files = Rack::Multipart::UploadedFile.new(File.join(File.dirname(__FILE__), "multipart", "file1.txt"))
|
190
|
-
res = Rack::MockRequest.new(app).post("/foo", :params => { "submit-name" => "Larry", "files" => files })
|
191
|
-
env = YAML.load(res.body)
|
192
|
-
env["REQUEST_METHOD"].must_equal "POST"
|
193
|
-
env["QUERY_STRING"].must_equal ""
|
194
|
-
env["PATH_INFO"].must_equal "/foo"
|
195
|
-
env["CONTENT_TYPE"].must_equal "multipart/form-data; boundary=AaB03x"
|
196
|
-
# The gsub accounts for differences in YAMLs affect on the data.
|
197
|
-
env["mock.postdata"].gsub("\r", "").length.must_equal 206
|
198
|
-
end
|
199
|
-
|
200
|
-
it "behave valid according to the Rack spec" do
|
201
|
-
url = "https://bla.example.org:9292/meh/foo?bar"
|
202
|
-
Rack::MockRequest.new(app).get(url, :lint => true).
|
203
|
-
must_be_kind_of Rack::MockResponse
|
204
|
-
end
|
205
|
-
|
206
|
-
it "call close on the original body object" do
|
207
|
-
called = false
|
208
|
-
body = Rack::BodyProxy.new(['hi']) { called = true }
|
209
|
-
capp = proc { |e| [200, {'Content-Type' => 'text/plain'}, body] }
|
210
|
-
called.must_equal false
|
211
|
-
Rack::MockRequest.new(capp).get('/', :lint => true)
|
212
|
-
called.must_equal true
|
213
|
-
end
|
214
|
-
|
215
|
-
it "defaults encoding to ASCII 8BIT" do
|
216
|
-
req = Rack::MockRequest.env_for("/foo")
|
217
|
-
|
218
|
-
keys = [
|
219
|
-
Rack::REQUEST_METHOD,
|
220
|
-
Rack::SERVER_NAME,
|
221
|
-
Rack::SERVER_PORT,
|
222
|
-
Rack::QUERY_STRING,
|
223
|
-
Rack::PATH_INFO,
|
224
|
-
Rack::HTTPS,
|
225
|
-
Rack::RACK_URL_SCHEME
|
226
|
-
]
|
227
|
-
keys.each do |k|
|
228
|
-
assert_equal Encoding::ASCII_8BIT, req[k].encoding
|
229
|
-
end
|
230
|
-
end
|
231
|
-
end
|
232
|
-
|
233
|
-
describe Rack::MockResponse do
|
234
|
-
it "provide access to the HTTP status" do
|
235
|
-
res = Rack::MockRequest.new(app).get("")
|
236
|
-
res.must_be :successful?
|
237
|
-
res.must_be :ok?
|
238
|
-
|
239
|
-
res = Rack::MockRequest.new(app).get("/?status=404")
|
240
|
-
res.wont_be :successful?
|
241
|
-
res.must_be :client_error?
|
242
|
-
res.must_be :not_found?
|
243
|
-
|
244
|
-
res = Rack::MockRequest.new(app).get("/?status=501")
|
245
|
-
res.wont_be :successful?
|
246
|
-
res.must_be :server_error?
|
247
|
-
|
248
|
-
res = Rack::MockRequest.new(app).get("/?status=307")
|
249
|
-
res.must_be :redirect?
|
250
|
-
|
251
|
-
res = Rack::MockRequest.new(app).get("/?status=201", :lint => true)
|
252
|
-
res.must_be :empty?
|
253
|
-
end
|
254
|
-
|
255
|
-
it "provide access to the HTTP headers" do
|
256
|
-
res = Rack::MockRequest.new(app).get("")
|
257
|
-
res.must_include "Content-Type"
|
258
|
-
res.headers["Content-Type"].must_equal "text/yaml"
|
259
|
-
res.original_headers["Content-Type"].must_equal "text/yaml"
|
260
|
-
res["Content-Type"].must_equal "text/yaml"
|
261
|
-
res.content_type.must_equal "text/yaml"
|
262
|
-
res.content_length.wont_equal 0
|
263
|
-
res.location.must_be_nil
|
264
|
-
end
|
265
|
-
|
266
|
-
it "provide access to the HTTP body" do
|
267
|
-
res = Rack::MockRequest.new(app).get("")
|
268
|
-
res.body.must_match(/rack/)
|
269
|
-
assert_match(res, /rack/)
|
270
|
-
end
|
271
|
-
|
272
|
-
it "provide access to the Rack errors" do
|
273
|
-
res = Rack::MockRequest.new(app).get("/?error=foo", :lint => true)
|
274
|
-
res.must_be :ok?
|
275
|
-
res.errors.wont_be :empty?
|
276
|
-
res.errors.must_include "foo"
|
277
|
-
end
|
278
|
-
|
279
|
-
it "allow calling body.close afterwards" do
|
280
|
-
# this is exactly what rack-test does
|
281
|
-
body = StringIO.new("hi")
|
282
|
-
res = Rack::MockResponse.new(200, {}, body)
|
283
|
-
body.close if body.respond_to?(:close)
|
284
|
-
res.body.must_equal 'hi'
|
285
|
-
end
|
286
|
-
|
287
|
-
it "optionally make Rack errors fatal" do
|
288
|
-
lambda {
|
289
|
-
Rack::MockRequest.new(app).get("/?error=foo", :fatal => true)
|
290
|
-
}.must_raise Rack::MockRequest::FatalWarning
|
291
|
-
end
|
292
|
-
end
|
293
|
-
|
294
|
-
describe Rack::MockResponse, 'headers' do
|
295
|
-
before do
|
296
|
-
@res = Rack::MockRequest.new(app).get('')
|
297
|
-
@res.set_header 'FOO', '1'
|
298
|
-
end
|
299
|
-
|
300
|
-
it 'has_header?' do
|
301
|
-
lambda { @res.has_header? nil }.must_raise NoMethodError
|
302
|
-
|
303
|
-
@res.has_header?('FOO').must_equal true
|
304
|
-
@res.has_header?('Foo').must_equal true
|
305
|
-
end
|
306
|
-
|
307
|
-
it 'get_header' do
|
308
|
-
lambda { @res.get_header nil }.must_raise NoMethodError
|
309
|
-
|
310
|
-
@res.get_header('FOO').must_equal '1'
|
311
|
-
@res.get_header('Foo').must_equal '1'
|
312
|
-
end
|
313
|
-
|
314
|
-
it 'set_header' do
|
315
|
-
lambda { @res.set_header nil, '1' }.must_raise NoMethodError
|
316
|
-
|
317
|
-
@res.set_header('FOO', '2').must_equal '2'
|
318
|
-
@res.get_header('FOO').must_equal '2'
|
319
|
-
|
320
|
-
@res.set_header('Foo', '3').must_equal '3'
|
321
|
-
@res.get_header('Foo').must_equal '3'
|
322
|
-
@res.get_header('FOO').must_equal '3'
|
323
|
-
|
324
|
-
@res.set_header('FOO', nil).must_be_nil
|
325
|
-
@res.get_header('FOO').must_be_nil
|
326
|
-
@res.has_header?('FOO').must_equal true
|
327
|
-
end
|
328
|
-
|
329
|
-
it 'add_header' do
|
330
|
-
lambda { @res.add_header nil, '1' }.must_raise NoMethodError
|
331
|
-
|
332
|
-
# Sets header on first addition
|
333
|
-
@res.add_header('FOO', '1').must_equal '1,1'
|
334
|
-
@res.get_header('FOO').must_equal '1,1'
|
335
|
-
|
336
|
-
# Ignores nil additions
|
337
|
-
@res.add_header('FOO', nil).must_equal '1,1'
|
338
|
-
@res.get_header('FOO').must_equal '1,1'
|
339
|
-
|
340
|
-
# Converts additions to strings
|
341
|
-
@res.add_header('FOO', 2).must_equal '1,1,2'
|
342
|
-
@res.get_header('FOO').must_equal '1,1,2'
|
343
|
-
|
344
|
-
# Respects underlying case-sensitivity
|
345
|
-
@res.add_header('Foo', 'yep').must_equal '1,1,2,yep'
|
346
|
-
@res.get_header('Foo').must_equal '1,1,2,yep'
|
347
|
-
@res.get_header('FOO').must_equal '1,1,2,yep'
|
348
|
-
end
|
349
|
-
|
350
|
-
it 'delete_header' do
|
351
|
-
lambda { @res.delete_header nil }.must_raise NoMethodError
|
352
|
-
|
353
|
-
@res.delete_header('FOO').must_equal '1'
|
354
|
-
@res.has_header?('FOO').must_equal false
|
355
|
-
|
356
|
-
@res.has_header?('Foo').must_equal false
|
357
|
-
@res.delete_header('Foo').must_be_nil
|
358
|
-
end
|
359
|
-
end
|
data/test/spec_multipart.rb
DELETED
@@ -1,722 +0,0 @@
|
|
1
|
-
# coding: utf-8
|
2
|
-
|
3
|
-
require 'minitest/autorun'
|
4
|
-
require 'rack'
|
5
|
-
require 'rack/multipart'
|
6
|
-
require 'rack/multipart/parser'
|
7
|
-
require 'rack/utils'
|
8
|
-
require 'rack/mock'
|
9
|
-
|
10
|
-
describe Rack::Multipart do
|
11
|
-
def multipart_fixture(name, boundary = "AaB03x")
|
12
|
-
file = multipart_file(name)
|
13
|
-
data = File.open(file, 'rb') { |io| io.read }
|
14
|
-
|
15
|
-
type = %(multipart/form-data; boundary=#{boundary})
|
16
|
-
length = data.bytesize
|
17
|
-
|
18
|
-
{ "CONTENT_TYPE" => type,
|
19
|
-
"CONTENT_LENGTH" => length.to_s,
|
20
|
-
:input => StringIO.new(data) }
|
21
|
-
end
|
22
|
-
|
23
|
-
def multipart_file(name)
|
24
|
-
File.join(File.dirname(__FILE__), "multipart", name.to_s)
|
25
|
-
end
|
26
|
-
|
27
|
-
it "return nil if content type is not multipart" do
|
28
|
-
env = Rack::MockRequest.env_for("/",
|
29
|
-
"CONTENT_TYPE" => 'application/x-www-form-urlencoded')
|
30
|
-
Rack::Multipart.parse_multipart(env).must_be_nil
|
31
|
-
end
|
32
|
-
|
33
|
-
it "parse multipart content when content type present but filename is not" do
|
34
|
-
env = Rack::MockRequest.env_for("/", multipart_fixture(:content_type_and_no_filename))
|
35
|
-
params = Rack::Multipart.parse_multipart(env)
|
36
|
-
params["text"].must_equal "contents"
|
37
|
-
end
|
38
|
-
|
39
|
-
it "set US_ASCII encoding based on charset" do
|
40
|
-
env = Rack::MockRequest.env_for("/", multipart_fixture(:content_type_and_no_filename))
|
41
|
-
params = Rack::Multipart.parse_multipart(env)
|
42
|
-
params["text"].encoding.must_equal Encoding::US_ASCII
|
43
|
-
|
44
|
-
# I'm not 100% sure if making the param name encoding match the
|
45
|
-
# Content-Type charset is the right thing to do. We should revisit this.
|
46
|
-
params.keys.each do |key|
|
47
|
-
key.encoding.must_equal Encoding::US_ASCII
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
it "set BINARY encoding on things without content type" do
|
52
|
-
env = Rack::MockRequest.env_for("/", multipart_fixture(:none))
|
53
|
-
params = Rack::Multipart.parse_multipart(env)
|
54
|
-
params["submit-name"].encoding.must_equal Encoding::UTF_8
|
55
|
-
end
|
56
|
-
|
57
|
-
it "set UTF8 encoding on names of things without content type" do
|
58
|
-
env = Rack::MockRequest.env_for("/", multipart_fixture(:none))
|
59
|
-
params = Rack::Multipart.parse_multipart(env)
|
60
|
-
params.keys.each do |key|
|
61
|
-
key.encoding.must_equal Encoding::UTF_8
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
|
-
it "default text to UTF8" do
|
66
|
-
env = Rack::MockRequest.env_for("/", multipart_fixture(:text))
|
67
|
-
params = Rack::Multipart.parse_multipart(env)
|
68
|
-
params['submit-name'].encoding.must_equal Encoding::UTF_8
|
69
|
-
params['submit-name-with-content'].encoding.must_equal Encoding::UTF_8
|
70
|
-
params.keys.each do |key|
|
71
|
-
key.encoding.must_equal Encoding::UTF_8
|
72
|
-
end
|
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
|
80
|
-
end
|
81
|
-
|
82
|
-
it "raise RangeError if the key space is exhausted" do
|
83
|
-
env = Rack::MockRequest.env_for("/", multipart_fixture(:content_type_and_no_filename))
|
84
|
-
|
85
|
-
old, Rack::Utils.key_space_limit = Rack::Utils.key_space_limit, 1
|
86
|
-
begin
|
87
|
-
lambda { Rack::Multipart.parse_multipart(env) }.must_raise(RangeError)
|
88
|
-
ensure
|
89
|
-
Rack::Utils.key_space_limit = old
|
90
|
-
end
|
91
|
-
end
|
92
|
-
|
93
|
-
it "parse multipart form webkit style" do
|
94
|
-
env = Rack::MockRequest.env_for '/', multipart_fixture(:webkit)
|
95
|
-
env['CONTENT_TYPE'] = "multipart/form-data; boundary=----WebKitFormBoundaryWLHCs9qmcJJoyjKR"
|
96
|
-
params = Rack::Multipart.parse_multipart(env)
|
97
|
-
params['profile']['bio'].must_include 'hello'
|
98
|
-
params['profile'].keys.must_include 'public_email'
|
99
|
-
end
|
100
|
-
|
101
|
-
it "reject insanely long boundaries" do
|
102
|
-
# using a pipe since a tempfile can use up too much space
|
103
|
-
rd, wr = IO.pipe
|
104
|
-
|
105
|
-
# we only call rewind once at start, so make sure it succeeds
|
106
|
-
# and doesn't hit ESPIPE
|
107
|
-
def rd.rewind; end
|
108
|
-
wr.sync = true
|
109
|
-
|
110
|
-
# mock out length to make this pipe look like a Tempfile
|
111
|
-
def rd.length
|
112
|
-
1024 * 1024 * 8
|
113
|
-
end
|
114
|
-
|
115
|
-
# write to a pipe in a background thread, this will write a lot
|
116
|
-
# unless Rack (properly) shuts down the read end
|
117
|
-
thr = Thread.new do
|
118
|
-
begin
|
119
|
-
wr.write("--AaB03x")
|
120
|
-
|
121
|
-
# make the initial boundary a few gigs long
|
122
|
-
longer = "0123456789" * 1024 * 1024
|
123
|
-
(1024 * 1024).times { wr.write(longer) }
|
124
|
-
|
125
|
-
wr.write("\r\n")
|
126
|
-
wr.write('Content-Disposition: form-data; name="a"; filename="a.txt"')
|
127
|
-
wr.write("\r\n")
|
128
|
-
wr.write("Content-Type: text/plain\r\n")
|
129
|
-
wr.write("\r\na")
|
130
|
-
wr.write("--AaB03x--\r\n")
|
131
|
-
wr.close
|
132
|
-
rescue => err # this is EPIPE if Rack shuts us down
|
133
|
-
err
|
134
|
-
end
|
135
|
-
end
|
136
|
-
|
137
|
-
fixture = {
|
138
|
-
"CONTENT_TYPE" => "multipart/form-data; boundary=AaB03x",
|
139
|
-
"CONTENT_LENGTH" => rd.length.to_s,
|
140
|
-
:input => rd,
|
141
|
-
}
|
142
|
-
|
143
|
-
env = Rack::MockRequest.env_for '/', fixture
|
144
|
-
lambda {
|
145
|
-
Rack::Multipart.parse_multipart(env)
|
146
|
-
}.must_raise EOFError
|
147
|
-
rd.close
|
148
|
-
|
149
|
-
err = thr.value
|
150
|
-
err.must_be_instance_of Errno::EPIPE
|
151
|
-
wr.close
|
152
|
-
end
|
153
|
-
|
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
|
163
|
-
env = Rack::MockRequest.env_for("/", multipart_fixture(:text))
|
164
|
-
params = Rack::Multipart.parse_multipart(env)
|
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; " +
|
170
|
-
"name=\"files\"; filename=\"file1.txt\"\r\n" +
|
171
|
-
"Content-Type: text/plain\r\n"
|
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"
|
187
|
-
end
|
188
|
-
|
189
|
-
it "preserve extension in the created tempfile" do
|
190
|
-
env = Rack::MockRequest.env_for("/", multipart_fixture(:text))
|
191
|
-
params = Rack::Multipart.parse_multipart(env)
|
192
|
-
File.extname(params["files"][:tempfile].path).must_equal ".txt"
|
193
|
-
end
|
194
|
-
|
195
|
-
it "parse multipart upload with text file with no name field" do
|
196
|
-
env = Rack::MockRequest.env_for("/", multipart_fixture(:filename_and_no_name))
|
197
|
-
params = Rack::Multipart.parse_multipart(env)
|
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; " +
|
201
|
-
"filename=\"file1.txt\"\r\n" +
|
202
|
-
"Content-Type: text/plain\r\n"
|
203
|
-
params["file1.txt"][:name].must_equal "file1.txt"
|
204
|
-
params["file1.txt"][:tempfile].read.must_equal "contents"
|
205
|
-
end
|
206
|
-
|
207
|
-
it "parse multipart upload file using custom tempfile class" do
|
208
|
-
env = Rack::MockRequest.env_for("/", multipart_fixture(:text))
|
209
|
-
my_tempfile = ""
|
210
|
-
env['rack.multipart.tempfile_factory'] = lambda { |filename, content_type| my_tempfile }
|
211
|
-
params = Rack::Multipart.parse_multipart(env)
|
212
|
-
params["files"][:tempfile].object_id.must_equal my_tempfile.object_id
|
213
|
-
my_tempfile.must_equal "contents"
|
214
|
-
end
|
215
|
-
|
216
|
-
it "parse multipart upload with nested parameters" do
|
217
|
-
env = Rack::MockRequest.env_for("/", multipart_fixture(:nested))
|
218
|
-
params = Rack::Multipart.parse_multipart(env)
|
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; " +
|
223
|
-
"name=\"foo[files]\"; filename=\"file1.txt\"\r\n" +
|
224
|
-
"Content-Type: text/plain\r\n"
|
225
|
-
params["foo"]["files"][:name].must_equal "foo[files]"
|
226
|
-
params["foo"]["files"][:tempfile].read.must_equal "contents"
|
227
|
-
end
|
228
|
-
|
229
|
-
it "parse multipart upload with binary file" do
|
230
|
-
env = Rack::MockRequest.env_for("/", multipart_fixture(:binary))
|
231
|
-
params = Rack::Multipart.parse_multipart(env)
|
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; " +
|
237
|
-
"name=\"files\"; filename=\"rack-logo.png\"\r\n" +
|
238
|
-
"Content-Type: image/png\r\n"
|
239
|
-
params["files"][:name].must_equal "files"
|
240
|
-
params["files"][:tempfile].read.length.must_equal 26473
|
241
|
-
end
|
242
|
-
|
243
|
-
it "parse multipart upload with empty file" do
|
244
|
-
env = Rack::MockRequest.env_for("/", multipart_fixture(:empty))
|
245
|
-
params = Rack::Multipart.parse_multipart(env)
|
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; " +
|
250
|
-
"name=\"files\"; filename=\"file1.txt\"\r\n" +
|
251
|
-
"Content-Type: text/plain\r\n"
|
252
|
-
params["files"][:name].must_equal "files"
|
253
|
-
params["files"][:tempfile].read.must_equal ""
|
254
|
-
end
|
255
|
-
|
256
|
-
it "parse multipart upload with filename with semicolons" do
|
257
|
-
env = Rack::MockRequest.env_for("/", multipart_fixture(:semicolon))
|
258
|
-
params = Rack::Multipart.parse_multipart(env)
|
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; " +
|
262
|
-
"name=\"files\"; filename=\"fi;le1.txt\"\r\n" +
|
263
|
-
"Content-Type: text/plain\r\n"
|
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"
|
280
|
-
end
|
281
|
-
|
282
|
-
it "parse multipart upload with filename with invalid characters" do
|
283
|
-
env = Rack::MockRequest.env_for("/", multipart_fixture(:invalid_character))
|
284
|
-
params = Rack::Multipart.parse_multipart(env)
|
285
|
-
params["files"][:type].must_equal "text/plain"
|
286
|
-
params["files"][:filename].must_match(/invalid/)
|
287
|
-
head = "Content-Disposition: form-data; " +
|
288
|
-
"name=\"files\"; filename=\"invalid\xC3.txt\"\r\n" +
|
289
|
-
"Content-Type: text/plain\r\n"
|
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"
|
294
|
-
end
|
295
|
-
|
296
|
-
it "parse multipart form with an encoded word filename" do
|
297
|
-
env = Rack::MockRequest.env_for '/', multipart_fixture(:filename_with_encoded_words)
|
298
|
-
params = Rack::Multipart.parse_multipart(env)
|
299
|
-
params["files"][:filename].must_equal "файл"
|
300
|
-
end
|
301
|
-
|
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"
|
306
|
-
end
|
307
|
-
|
308
|
-
it "parse multipart form with a null byte in the filename" do
|
309
|
-
env = Rack::MockRequest.env_for '/', multipart_fixture(:filename_with_null_byte)
|
310
|
-
params = Rack::Multipart.parse_multipart(env)
|
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"
|
320
|
-
end
|
321
|
-
|
322
|
-
it "parse multipart/mixed" do
|
323
|
-
env = Rack::MockRequest.env_for("/", multipart_fixture(:mixed_files))
|
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
|
328
|
-
end
|
329
|
-
|
330
|
-
it "parse IE multipart upload and clean up filename" do
|
331
|
-
env = Rack::MockRequest.env_for("/", multipart_fixture(:ie))
|
332
|
-
params = Rack::Multipart.parse_multipart(env)
|
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; " +
|
336
|
-
"name=\"files\"; " +
|
337
|
-
'filename="C:\Documents and Settings\Administrator\Desktop\file1.txt"' +
|
338
|
-
"\r\nContent-Type: text/plain\r\n"
|
339
|
-
params["files"][:name].must_equal "files"
|
340
|
-
params["files"][:tempfile].read.must_equal "contents"
|
341
|
-
end
|
342
|
-
|
343
|
-
it "parse filename and modification param" do
|
344
|
-
env = Rack::MockRequest.env_for("/", multipart_fixture(:filename_and_modification_param))
|
345
|
-
params = Rack::Multipart.parse_multipart(env)
|
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" +
|
349
|
-
"Content-Disposition: attachment; " +
|
350
|
-
"name=\"files\"; " +
|
351
|
-
"filename=genome.jpeg; " +
|
352
|
-
"modification-date=\"Wed, 12 Feb 1997 16:29:51 -0500\";\r\n" +
|
353
|
-
"Content-Description: a complete map of the human genome\r\n"
|
354
|
-
params["files"][:name].must_equal "files"
|
355
|
-
params["files"][:tempfile].read.must_equal "contents"
|
356
|
-
end
|
357
|
-
|
358
|
-
it "parse filename with escaped quotes" do
|
359
|
-
env = Rack::MockRequest.env_for("/", multipart_fixture(:filename_with_escaped_quotes))
|
360
|
-
params = Rack::Multipart.parse_multipart(env)
|
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; " +
|
364
|
-
"name=\"files\"; " +
|
365
|
-
"filename=\"escape \\\"quotes\"\r\n" +
|
366
|
-
"Content-Type: application/octet-stream\r\n"
|
367
|
-
params["files"][:name].must_equal "files"
|
368
|
-
params["files"][:tempfile].read.must_equal "contents"
|
369
|
-
end
|
370
|
-
|
371
|
-
it "parse filename with percent escaped quotes" do
|
372
|
-
env = Rack::MockRequest.env_for("/", multipart_fixture(:filename_with_percent_escaped_quotes))
|
373
|
-
params = Rack::Multipart.parse_multipart(env)
|
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; " +
|
377
|
-
"name=\"files\"; " +
|
378
|
-
"filename=\"escape %22quotes\"\r\n" +
|
379
|
-
"Content-Type: application/octet-stream\r\n"
|
380
|
-
params["files"][:name].must_equal "files"
|
381
|
-
params["files"][:tempfile].read.must_equal "contents"
|
382
|
-
end
|
383
|
-
|
384
|
-
it "parse filename with unescaped quotes" do
|
385
|
-
env = Rack::MockRequest.env_for("/", multipart_fixture(:filename_with_unescaped_quotes))
|
386
|
-
params = Rack::Multipart.parse_multipart(env)
|
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; " +
|
390
|
-
"name=\"files\"; " +
|
391
|
-
"filename=\"escape \"quotes\"\r\n" +
|
392
|
-
"Content-Type: application/octet-stream\r\n"
|
393
|
-
params["files"][:name].must_equal "files"
|
394
|
-
params["files"][:tempfile].read.must_equal "contents"
|
395
|
-
end
|
396
|
-
|
397
|
-
it "parse filename with escaped quotes and modification param" do
|
398
|
-
env = Rack::MockRequest.env_for("/", multipart_fixture(:filename_with_escaped_quotes_and_modification_param))
|
399
|
-
params = Rack::Multipart.parse_multipart(env)
|
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" +
|
403
|
-
"Content-Disposition: attachment; " +
|
404
|
-
"name=\"files\"; " +
|
405
|
-
"filename=\"\"human\" genome.jpeg\"; " +
|
406
|
-
"modification-date=\"Wed, 12 Feb 1997 16:29:51 -0500\";\r\n" +
|
407
|
-
"Content-Description: a complete map of the human genome\r\n"
|
408
|
-
params["files"][:name].must_equal "files"
|
409
|
-
params["files"][:tempfile].read.must_equal "contents"
|
410
|
-
end
|
411
|
-
|
412
|
-
it "parse filename with unescaped percentage characters" do
|
413
|
-
env = Rack::MockRequest.env_for("/", multipart_fixture(:filename_with_unescaped_percentages, "----WebKitFormBoundary2NHc7OhsgU68l3Al"))
|
414
|
-
params = Rack::Multipart.parse_multipart(env)
|
415
|
-
files = params["document"]["attachment"]
|
416
|
-
files[:type].must_equal "image/jpeg"
|
417
|
-
files[:filename].must_equal "100% of a photo.jpeg"
|
418
|
-
files[:head].must_equal <<-MULTIPART
|
419
|
-
Content-Disposition: form-data; name="document[attachment]"; filename="100% of a photo.jpeg"\r
|
420
|
-
Content-Type: image/jpeg\r
|
421
|
-
MULTIPART
|
422
|
-
|
423
|
-
files[:name].must_equal "document[attachment]"
|
424
|
-
files[:tempfile].read.must_equal "contents"
|
425
|
-
end
|
426
|
-
|
427
|
-
it "parse filename with unescaped percentage characters that look like partial hex escapes" do
|
428
|
-
env = Rack::MockRequest.env_for("/", multipart_fixture(:filename_with_unescaped_percentages2, "----WebKitFormBoundary2NHc7OhsgU68l3Al"))
|
429
|
-
params = Rack::Multipart.parse_multipart(env)
|
430
|
-
files = params["document"]["attachment"]
|
431
|
-
files[:type].must_equal "image/jpeg"
|
432
|
-
files[:filename].must_equal "100%a"
|
433
|
-
files[:head].must_equal <<-MULTIPART
|
434
|
-
Content-Disposition: form-data; name="document[attachment]"; filename="100%a"\r
|
435
|
-
Content-Type: image/jpeg\r
|
436
|
-
MULTIPART
|
437
|
-
|
438
|
-
files[:name].must_equal "document[attachment]"
|
439
|
-
files[:tempfile].read.must_equal "contents"
|
440
|
-
end
|
441
|
-
|
442
|
-
it "parse filename with unescaped percentage characters that look like partial hex escapes" do
|
443
|
-
env = Rack::MockRequest.env_for("/", multipart_fixture(:filename_with_unescaped_percentages3, "----WebKitFormBoundary2NHc7OhsgU68l3Al"))
|
444
|
-
params = Rack::Multipart.parse_multipart(env)
|
445
|
-
files = params["document"]["attachment"]
|
446
|
-
files[:type].must_equal "image/jpeg"
|
447
|
-
files[:filename].must_equal "100%"
|
448
|
-
files[:head].must_equal <<-MULTIPART
|
449
|
-
Content-Disposition: form-data; name="document[attachment]"; filename="100%"\r
|
450
|
-
Content-Type: image/jpeg\r
|
451
|
-
MULTIPART
|
452
|
-
|
453
|
-
files[:name].must_equal "document[attachment]"
|
454
|
-
files[:tempfile].read.must_equal "contents"
|
455
|
-
end
|
456
|
-
|
457
|
-
it "rewinds input after parsing upload" do
|
458
|
-
options = multipart_fixture(:text)
|
459
|
-
input = options[:input]
|
460
|
-
env = Rack::MockRequest.env_for("/", options)
|
461
|
-
params = Rack::Multipart.parse_multipart(env)
|
462
|
-
params["submit-name"].must_equal "Larry"
|
463
|
-
params["files"][:filename].must_equal "file1.txt"
|
464
|
-
input.read.length.must_equal 307
|
465
|
-
end
|
466
|
-
|
467
|
-
it "builds multipart body" do
|
468
|
-
files = Rack::Multipart::UploadedFile.new(multipart_file("file1.txt"))
|
469
|
-
data = Rack::Multipart.build_multipart("submit-name" => "Larry", "files" => files)
|
470
|
-
|
471
|
-
options = {
|
472
|
-
"CONTENT_TYPE" => "multipart/form-data; boundary=AaB03x",
|
473
|
-
"CONTENT_LENGTH" => data.length.to_s,
|
474
|
-
:input => StringIO.new(data)
|
475
|
-
}
|
476
|
-
env = Rack::MockRequest.env_for("/", options)
|
477
|
-
params = Rack::Multipart.parse_multipart(env)
|
478
|
-
params["submit-name"].must_equal "Larry"
|
479
|
-
params["files"][:filename].must_equal "file1.txt"
|
480
|
-
params["files"][:tempfile].read.must_equal "contents"
|
481
|
-
end
|
482
|
-
|
483
|
-
it "builds nested multipart body" do
|
484
|
-
files = Rack::Multipart::UploadedFile.new(multipart_file("file1.txt"))
|
485
|
-
data = Rack::Multipart.build_multipart("people" => [{"submit-name" => "Larry", "files" => files}])
|
486
|
-
|
487
|
-
options = {
|
488
|
-
"CONTENT_TYPE" => "multipart/form-data; boundary=AaB03x",
|
489
|
-
"CONTENT_LENGTH" => data.length.to_s,
|
490
|
-
:input => StringIO.new(data)
|
491
|
-
}
|
492
|
-
env = Rack::MockRequest.env_for("/", options)
|
493
|
-
params = Rack::Multipart.parse_multipart(env)
|
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"
|
497
|
-
end
|
498
|
-
|
499
|
-
it "can parse fields that end at the end of the buffer" do
|
500
|
-
input = File.read(multipart_file("bad_robots"))
|
501
|
-
|
502
|
-
req = Rack::Request.new Rack::MockRequest.env_for("/",
|
503
|
-
"CONTENT_TYPE" => "multipart/form-data, boundary=1yy3laWhgX31qpiHinh67wJXqKalukEUTvqTzmon",
|
504
|
-
"CONTENT_LENGTH" => input.size,
|
505
|
-
:input => input)
|
506
|
-
|
507
|
-
req.POST['file.path'].must_equal "/var/tmp/uploads/4/0001728414"
|
508
|
-
req.POST['addresses'].wont_equal nil
|
509
|
-
end
|
510
|
-
|
511
|
-
it "builds complete params with the chunk size of 16384 slicing exactly on boundary" do
|
512
|
-
begin
|
513
|
-
previous_limit = Rack::Utils.multipart_part_limit
|
514
|
-
Rack::Utils.multipart_part_limit = 256
|
515
|
-
|
516
|
-
data = File.open(multipart_file("fail_16384_nofile"), 'rb') { |f| f.read }.gsub(/\n/, "\r\n")
|
517
|
-
options = {
|
518
|
-
"CONTENT_TYPE" => "multipart/form-data; boundary=----WebKitFormBoundaryWsY0GnpbI5U7ztzo",
|
519
|
-
"CONTENT_LENGTH" => data.length.to_s,
|
520
|
-
:input => StringIO.new(data)
|
521
|
-
}
|
522
|
-
env = Rack::MockRequest.env_for("/", options)
|
523
|
-
params = Rack::Multipart.parse_multipart(env)
|
524
|
-
|
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"
|
532
|
-
ensure
|
533
|
-
Rack::Utils.multipart_part_limit = previous_limit
|
534
|
-
end
|
535
|
-
end
|
536
|
-
|
537
|
-
it "not reach a multi-part limit" do
|
538
|
-
begin
|
539
|
-
previous_limit = Rack::Utils.multipart_part_limit
|
540
|
-
Rack::Utils.multipart_part_limit = 4
|
541
|
-
|
542
|
-
env = Rack::MockRequest.env_for '/', multipart_fixture(:three_files_three_fields)
|
543
|
-
params = Rack::Multipart.parse_multipart(env)
|
544
|
-
params['reply'].must_equal 'yes'
|
545
|
-
params['to'].must_equal 'people'
|
546
|
-
params['from'].must_equal 'others'
|
547
|
-
ensure
|
548
|
-
Rack::Utils.multipart_part_limit = previous_limit
|
549
|
-
end
|
550
|
-
end
|
551
|
-
|
552
|
-
it "reach a multipart limit" do
|
553
|
-
begin
|
554
|
-
previous_limit = Rack::Utils.multipart_part_limit
|
555
|
-
Rack::Utils.multipart_part_limit = 3
|
556
|
-
|
557
|
-
env = Rack::MockRequest.env_for '/', multipart_fixture(:three_files_three_fields)
|
558
|
-
lambda { Rack::Multipart.parse_multipart(env) }.must_raise Rack::Multipart::MultipartPartLimitError
|
559
|
-
ensure
|
560
|
-
Rack::Utils.multipart_part_limit = previous_limit
|
561
|
-
end
|
562
|
-
end
|
563
|
-
|
564
|
-
it "return nil if no UploadedFiles were used" do
|
565
|
-
data = Rack::Multipart.build_multipart("people" => [{"submit-name" => "Larry", "files" => "contents"}])
|
566
|
-
data.must_be_nil
|
567
|
-
end
|
568
|
-
|
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"
|
573
|
-
end
|
574
|
-
|
575
|
-
it "can parse fields with a content type" do
|
576
|
-
data = <<-EOF
|
577
|
-
--1yy3laWhgX31qpiHinh67wJXqKalukEUTvqTzmon\r
|
578
|
-
Content-Disposition: form-data; name="description"\r
|
579
|
-
Content-Type: text/plain"\r
|
580
|
-
\r
|
581
|
-
Very very blue\r
|
582
|
-
--1yy3laWhgX31qpiHinh67wJXqKalukEUTvqTzmon--\r
|
583
|
-
EOF
|
584
|
-
options = {
|
585
|
-
"CONTENT_TYPE" => "multipart/form-data; boundary=1yy3laWhgX31qpiHinh67wJXqKalukEUTvqTzmon",
|
586
|
-
"CONTENT_LENGTH" => data.length.to_s,
|
587
|
-
:input => StringIO.new(data)
|
588
|
-
}
|
589
|
-
env = Rack::MockRequest.env_for("/", options)
|
590
|
-
params = Rack::Multipart.parse_multipart(env)
|
591
|
-
|
592
|
-
params.must_equal "description"=>"Very very blue"
|
593
|
-
end
|
594
|
-
|
595
|
-
it "parse multipart upload with no content-length header" do
|
596
|
-
env = Rack::MockRequest.env_for '/', multipart_fixture(:webkit)
|
597
|
-
env['CONTENT_TYPE'] = "multipart/form-data; boundary=----WebKitFormBoundaryWLHCs9qmcJJoyjKR"
|
598
|
-
env.delete 'CONTENT_LENGTH'
|
599
|
-
params = Rack::Multipart.parse_multipart(env)
|
600
|
-
params['profile']['bio'].must_include 'hello'
|
601
|
-
end
|
602
|
-
|
603
|
-
it "parse very long unquoted multipart file names" do
|
604
|
-
data = <<-EOF
|
605
|
-
--AaB03x\r
|
606
|
-
Content-Type: text/plain\r
|
607
|
-
Content-Disposition: attachment; name=file; filename=#{'long' * 100}\r
|
608
|
-
\r
|
609
|
-
contents\r
|
610
|
-
--AaB03x--\r
|
611
|
-
EOF
|
612
|
-
|
613
|
-
options = {
|
614
|
-
"CONTENT_TYPE" => "multipart/form-data; boundary=AaB03x",
|
615
|
-
"CONTENT_LENGTH" => data.length.to_s,
|
616
|
-
:input => StringIO.new(data)
|
617
|
-
}
|
618
|
-
env = Rack::MockRequest.env_for("/", options)
|
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
|
643
|
-
|
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'
|
662
|
-
end
|
663
|
-
|
664
|
-
it "support mixed case metadata" do
|
665
|
-
file = multipart_file(:text)
|
666
|
-
data = File.open(file, 'rb') { |io| io.read }
|
667
|
-
|
668
|
-
type = "Multipart/Form-Data; Boundary=AaB03x"
|
669
|
-
length = data.bytesize
|
670
|
-
|
671
|
-
e = { "CONTENT_TYPE" => type,
|
672
|
-
"CONTENT_LENGTH" => length.to_s,
|
673
|
-
:input => StringIO.new(data) }
|
674
|
-
|
675
|
-
env = Rack::MockRequest.env_for("/", e)
|
676
|
-
params = Rack::Multipart.parse_multipart(env)
|
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; " +
|
682
|
-
"name=\"files\"; filename=\"file1.txt\"\r\n" +
|
683
|
-
"Content-Type: text/plain\r\n"
|
684
|
-
params["files"][:name].must_equal "files"
|
685
|
-
params["files"][:tempfile].read.must_equal "contents"
|
686
|
-
end
|
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
|
722
|
-
end
|