rack 2.0.6 → 2.2.3

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of rack might be problematic. Click here for more details.

Files changed (190) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +694 -0
  3. data/CONTRIBUTING.md +136 -0
  4. data/{COPYING → MIT-LICENSE} +4 -2
  5. data/README.rdoc +152 -148
  6. data/Rakefile +37 -23
  7. data/{SPEC → SPEC.rdoc} +38 -10
  8. data/bin/rackup +1 -0
  9. data/example/lobster.ru +2 -0
  10. data/example/protectedlobster.rb +3 -1
  11. data/example/protectedlobster.ru +2 -0
  12. data/lib/rack.rb +67 -73
  13. data/lib/rack/auth/abstract/handler.rb +3 -1
  14. data/lib/rack/auth/abstract/request.rb +1 -1
  15. data/lib/rack/auth/basic.rb +7 -4
  16. data/lib/rack/auth/digest/md5.rb +13 -11
  17. data/lib/rack/auth/digest/nonce.rb +6 -3
  18. data/lib/rack/auth/digest/params.rb +4 -2
  19. data/lib/rack/auth/digest/request.rb +5 -3
  20. data/lib/rack/body_proxy.rb +15 -14
  21. data/lib/rack/builder.rb +116 -23
  22. data/lib/rack/cascade.rb +28 -12
  23. data/lib/rack/chunked.rb +68 -20
  24. data/lib/rack/common_logger.rb +33 -25
  25. data/lib/rack/conditional_get.rb +20 -16
  26. data/lib/rack/config.rb +2 -0
  27. data/lib/rack/content_length.rb +8 -7
  28. data/lib/rack/content_type.rb +5 -4
  29. data/lib/rack/core_ext/regexp.rb +14 -0
  30. data/lib/rack/deflater.rb +59 -34
  31. data/lib/rack/directory.rb +84 -64
  32. data/lib/rack/etag.rb +5 -4
  33. data/lib/rack/events.rb +19 -20
  34. data/lib/rack/file.rb +4 -173
  35. data/lib/rack/files.rb +218 -0
  36. data/lib/rack/handler.rb +7 -2
  37. data/lib/rack/handler/cgi.rb +2 -3
  38. data/lib/rack/handler/fastcgi.rb +4 -4
  39. data/lib/rack/handler/lsws.rb +3 -3
  40. data/lib/rack/handler/scgi.rb +9 -8
  41. data/lib/rack/handler/thin.rb +3 -3
  42. data/lib/rack/handler/webrick.rb +15 -6
  43. data/lib/rack/head.rb +1 -1
  44. data/lib/rack/lint.rb +71 -25
  45. data/lib/rack/lobster.rb +10 -10
  46. data/lib/rack/lock.rb +2 -1
  47. data/lib/rack/logger.rb +2 -0
  48. data/lib/rack/media_type.rb +10 -5
  49. data/lib/rack/method_override.rb +4 -2
  50. data/lib/rack/mime.rb +9 -1
  51. data/lib/rack/mock.rb +97 -20
  52. data/lib/rack/multipart.rb +6 -4
  53. data/lib/rack/multipart/generator.rb +17 -13
  54. data/lib/rack/multipart/parser.rb +57 -62
  55. data/lib/rack/multipart/uploaded_file.rb +15 -7
  56. data/lib/rack/null_logger.rb +2 -0
  57. data/lib/rack/query_parser.rb +53 -28
  58. data/lib/rack/recursive.rb +7 -5
  59. data/lib/rack/reloader.rb +8 -4
  60. data/lib/rack/request.rb +220 -61
  61. data/lib/rack/response.rb +127 -44
  62. data/lib/rack/rewindable_input.rb +4 -3
  63. data/lib/rack/runtime.rb +6 -4
  64. data/lib/rack/sendfile.rb +13 -9
  65. data/lib/rack/server.rb +95 -24
  66. data/lib/rack/session/abstract/id.rb +100 -22
  67. data/lib/rack/session/cookie.rb +22 -14
  68. data/lib/rack/session/memcache.rb +4 -87
  69. data/lib/rack/session/pool.rb +18 -9
  70. data/lib/rack/show_exceptions.rb +21 -17
  71. data/lib/rack/show_status.rb +9 -9
  72. data/lib/rack/static.rb +23 -11
  73. data/lib/rack/tempfile_reaper.rb +1 -1
  74. data/lib/rack/urlmap.rb +12 -6
  75. data/lib/rack/utils.rb +102 -109
  76. data/lib/rack/version.rb +29 -0
  77. data/rack.gemspec +40 -28
  78. metadata +39 -181
  79. data/HISTORY.md +0 -505
  80. data/test/builder/an_underscore_app.rb +0 -5
  81. data/test/builder/anything.rb +0 -5
  82. data/test/builder/comment.ru +0 -4
  83. data/test/builder/end.ru +0 -5
  84. data/test/builder/line.ru +0 -1
  85. data/test/builder/options.ru +0 -2
  86. data/test/cgi/assets/folder/test.js +0 -1
  87. data/test/cgi/assets/fonts/font.eot +0 -1
  88. data/test/cgi/assets/images/image.png +0 -1
  89. data/test/cgi/assets/index.html +0 -1
  90. data/test/cgi/assets/javascripts/app.js +0 -1
  91. data/test/cgi/assets/stylesheets/app.css +0 -1
  92. data/test/cgi/lighttpd.conf +0 -26
  93. data/test/cgi/rackup_stub.rb +0 -6
  94. data/test/cgi/sample_rackup.ru +0 -5
  95. data/test/cgi/test +0 -9
  96. data/test/cgi/test+directory/test+file +0 -1
  97. data/test/cgi/test.fcgi +0 -9
  98. data/test/cgi/test.gz +0 -0
  99. data/test/cgi/test.ru +0 -5
  100. data/test/gemloader.rb +0 -10
  101. data/test/helper.rb +0 -34
  102. data/test/multipart/bad_robots +0 -259
  103. data/test/multipart/binary +0 -0
  104. data/test/multipart/content_type_and_no_filename +0 -6
  105. data/test/multipart/empty +0 -10
  106. data/test/multipart/fail_16384_nofile +0 -814
  107. data/test/multipart/file1.txt +0 -1
  108. data/test/multipart/filename_and_modification_param +0 -7
  109. data/test/multipart/filename_and_no_name +0 -6
  110. data/test/multipart/filename_with_encoded_words +0 -7
  111. data/test/multipart/filename_with_escaped_quotes +0 -6
  112. data/test/multipart/filename_with_escaped_quotes_and_modification_param +0 -7
  113. data/test/multipart/filename_with_null_byte +0 -7
  114. data/test/multipart/filename_with_percent_escaped_quotes +0 -6
  115. data/test/multipart/filename_with_single_quote +0 -7
  116. data/test/multipart/filename_with_unescaped_percentages +0 -6
  117. data/test/multipart/filename_with_unescaped_percentages2 +0 -6
  118. data/test/multipart/filename_with_unescaped_percentages3 +0 -6
  119. data/test/multipart/filename_with_unescaped_quotes +0 -6
  120. data/test/multipart/ie +0 -6
  121. data/test/multipart/invalid_character +0 -6
  122. data/test/multipart/mixed_files +0 -21
  123. data/test/multipart/nested +0 -10
  124. data/test/multipart/none +0 -9
  125. data/test/multipart/quoted +0 -15
  126. data/test/multipart/rack-logo.png +0 -0
  127. data/test/multipart/semicolon +0 -6
  128. data/test/multipart/text +0 -15
  129. data/test/multipart/three_files_three_fields +0 -31
  130. data/test/multipart/unity3d_wwwform +0 -11
  131. data/test/multipart/webkit +0 -32
  132. data/test/rackup/config.ru +0 -31
  133. data/test/registering_handler/rack/handler/registering_myself.rb +0 -8
  134. data/test/spec_auth_basic.rb +0 -89
  135. data/test/spec_auth_digest.rb +0 -260
  136. data/test/spec_body_proxy.rb +0 -85
  137. data/test/spec_builder.rb +0 -233
  138. data/test/spec_cascade.rb +0 -63
  139. data/test/spec_cgi.rb +0 -84
  140. data/test/spec_chunked.rb +0 -103
  141. data/test/spec_common_logger.rb +0 -95
  142. data/test/spec_conditional_get.rb +0 -103
  143. data/test/spec_config.rb +0 -23
  144. data/test/spec_content_length.rb +0 -86
  145. data/test/spec_content_type.rb +0 -46
  146. data/test/spec_deflater.rb +0 -375
  147. data/test/spec_directory.rb +0 -148
  148. data/test/spec_etag.rb +0 -108
  149. data/test/spec_events.rb +0 -133
  150. data/test/spec_fastcgi.rb +0 -85
  151. data/test/spec_file.rb +0 -264
  152. data/test/spec_handler.rb +0 -57
  153. data/test/spec_head.rb +0 -46
  154. data/test/spec_lint.rb +0 -515
  155. data/test/spec_lobster.rb +0 -59
  156. data/test/spec_lock.rb +0 -204
  157. data/test/spec_logger.rb +0 -24
  158. data/test/spec_media_type.rb +0 -42
  159. data/test/spec_method_override.rb +0 -110
  160. data/test/spec_mime.rb +0 -51
  161. data/test/spec_mock.rb +0 -359
  162. data/test/spec_multipart.rb +0 -722
  163. data/test/spec_null_logger.rb +0 -21
  164. data/test/spec_recursive.rb +0 -75
  165. data/test/spec_request.rb +0 -1398
  166. data/test/spec_response.rb +0 -510
  167. data/test/spec_rewindable_input.rb +0 -128
  168. data/test/spec_runtime.rb +0 -50
  169. data/test/spec_sendfile.rb +0 -125
  170. data/test/spec_server.rb +0 -193
  171. data/test/spec_session_abstract_id.rb +0 -31
  172. data/test/spec_session_abstract_session_hash.rb +0 -45
  173. data/test/spec_session_cookie.rb +0 -442
  174. data/test/spec_session_memcache.rb +0 -320
  175. data/test/spec_session_pool.rb +0 -210
  176. data/test/spec_show_exceptions.rb +0 -93
  177. data/test/spec_show_status.rb +0 -104
  178. data/test/spec_static.rb +0 -184
  179. data/test/spec_tempfile_reaper.rb +0 -64
  180. data/test/spec_thin.rb +0 -96
  181. data/test/spec_urlmap.rb +0 -237
  182. data/test/spec_utils.rb +0 -742
  183. data/test/spec_version.rb +0 -11
  184. data/test/spec_webrick.rb +0 -206
  185. data/test/static/another/index.html +0 -1
  186. data/test/static/foo.html +0 -1
  187. data/test/static/index.html +0 -1
  188. data/test/testrequest.rb +0 -78
  189. data/test/unregistered_handler/rack/handler/unregistered.rb +0 -7
  190. data/test/unregistered_handler/rack/handler/unregistered_long_one.rb +0 -7
@@ -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
@@ -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