rack 2.0.9.3 → 3.0.0

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 (201) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +808 -0
  3. data/CONTRIBUTING.md +142 -0
  4. data/{COPYING → MIT-LICENSE} +4 -2
  5. data/README.md +293 -0
  6. data/SPEC.rdoc +340 -0
  7. data/lib/rack/auth/abstract/handler.rb +6 -2
  8. data/lib/rack/auth/abstract/request.rb +4 -2
  9. data/lib/rack/auth/basic.rb +7 -4
  10. data/lib/rack/auth/digest/md5.rb +1 -129
  11. data/lib/rack/auth/digest/nonce.rb +1 -51
  12. data/lib/rack/auth/digest/params.rb +1 -52
  13. data/lib/rack/auth/digest/request.rb +1 -41
  14. data/lib/rack/auth/digest.rb +256 -0
  15. data/lib/rack/body_proxy.rb +18 -15
  16. data/lib/rack/builder.rb +151 -40
  17. data/lib/rack/cascade.rb +30 -12
  18. data/lib/rack/chunked.rb +74 -23
  19. data/lib/rack/common_logger.rb +49 -36
  20. data/lib/rack/conditional_get.rb +33 -26
  21. data/lib/rack/config.rb +2 -0
  22. data/lib/rack/constants.rb +63 -0
  23. data/lib/rack/content_length.rb +13 -16
  24. data/lib/rack/content_type.rb +12 -8
  25. data/lib/rack/deflater.rb +84 -45
  26. data/lib/rack/directory.rb +90 -64
  27. data/lib/rack/etag.rb +17 -23
  28. data/lib/rack/events.rb +23 -20
  29. data/lib/rack/file.rb +5 -172
  30. data/lib/rack/files.rb +216 -0
  31. data/lib/rack/head.rb +10 -9
  32. data/lib/rack/headers.rb +154 -0
  33. data/lib/rack/lint.rb +786 -645
  34. data/lib/rack/lock.rb +4 -6
  35. data/lib/rack/logger.rb +4 -0
  36. data/lib/rack/media_type.rb +10 -5
  37. data/lib/rack/method_override.rb +8 -2
  38. data/lib/rack/mime.rb +17 -1
  39. data/lib/rack/mock.rb +2 -195
  40. data/lib/rack/mock_request.rb +166 -0
  41. data/lib/rack/mock_response.rb +126 -0
  42. data/lib/rack/multipart/generator.rb +21 -15
  43. data/lib/rack/multipart/parser.rb +161 -118
  44. data/lib/rack/multipart/uploaded_file.rb +19 -7
  45. data/lib/rack/multipart.rb +23 -41
  46. data/lib/rack/null_logger.rb +11 -0
  47. data/lib/rack/query_parser.rb +126 -65
  48. data/lib/rack/recursive.rb +9 -5
  49. data/lib/rack/reloader.rb +6 -4
  50. data/lib/rack/request.rb +331 -74
  51. data/lib/rack/response.rb +223 -70
  52. data/lib/rack/rewindable_input.rb +28 -8
  53. data/lib/rack/runtime.rb +11 -8
  54. data/lib/rack/sendfile.rb +42 -33
  55. data/lib/rack/show_exceptions.rb +35 -18
  56. data/lib/rack/show_status.rb +25 -15
  57. data/lib/rack/static.rb +30 -18
  58. data/lib/rack/tempfile_reaper.rb +16 -5
  59. data/lib/rack/urlmap.rb +14 -6
  60. data/lib/rack/utils.rb +268 -260
  61. data/lib/rack/version.rb +34 -0
  62. data/lib/rack.rb +15 -92
  63. metadata +44 -207
  64. data/HISTORY.md +0 -520
  65. data/README.rdoc +0 -316
  66. data/Rakefile +0 -116
  67. data/SPEC +0 -263
  68. data/bin/rackup +0 -4
  69. data/contrib/rack.png +0 -0
  70. data/contrib/rack.svg +0 -150
  71. data/contrib/rack_logo.svg +0 -164
  72. data/contrib/rdoc.css +0 -412
  73. data/example/lobster.ru +0 -4
  74. data/example/protectedlobster.rb +0 -14
  75. data/example/protectedlobster.ru +0 -8
  76. data/lib/rack/handler/cgi.rb +0 -60
  77. data/lib/rack/handler/fastcgi.rb +0 -100
  78. data/lib/rack/handler/lsws.rb +0 -61
  79. data/lib/rack/handler/scgi.rb +0 -70
  80. data/lib/rack/handler/thin.rb +0 -36
  81. data/lib/rack/handler/webrick.rb +0 -120
  82. data/lib/rack/handler.rb +0 -99
  83. data/lib/rack/lobster.rb +0 -70
  84. data/lib/rack/server.rb +0 -395
  85. data/lib/rack/session/abstract/id.rb +0 -510
  86. data/lib/rack/session/cookie.rb +0 -204
  87. data/lib/rack/session/memcache.rb +0 -99
  88. data/lib/rack/session/pool.rb +0 -83
  89. data/rack.gemspec +0 -34
  90. data/test/builder/an_underscore_app.rb +0 -5
  91. data/test/builder/anything.rb +0 -5
  92. data/test/builder/comment.ru +0 -4
  93. data/test/builder/end.ru +0 -5
  94. data/test/builder/line.ru +0 -1
  95. data/test/builder/options.ru +0 -2
  96. data/test/cgi/assets/folder/test.js +0 -1
  97. data/test/cgi/assets/fonts/font.eot +0 -1
  98. data/test/cgi/assets/images/image.png +0 -1
  99. data/test/cgi/assets/index.html +0 -1
  100. data/test/cgi/assets/javascripts/app.js +0 -1
  101. data/test/cgi/assets/stylesheets/app.css +0 -1
  102. data/test/cgi/lighttpd.conf +0 -26
  103. data/test/cgi/rackup_stub.rb +0 -6
  104. data/test/cgi/sample_rackup.ru +0 -5
  105. data/test/cgi/test +0 -9
  106. data/test/cgi/test+directory/test+file +0 -1
  107. data/test/cgi/test.fcgi +0 -9
  108. data/test/cgi/test.gz +0 -0
  109. data/test/cgi/test.ru +0 -5
  110. data/test/gemloader.rb +0 -10
  111. data/test/helper.rb +0 -34
  112. data/test/multipart/bad_robots +0 -259
  113. data/test/multipart/binary +0 -0
  114. data/test/multipart/content_type_and_no_filename +0 -6
  115. data/test/multipart/empty +0 -10
  116. data/test/multipart/fail_16384_nofile +0 -814
  117. data/test/multipart/file1.txt +0 -1
  118. data/test/multipart/filename_and_modification_param +0 -7
  119. data/test/multipart/filename_and_no_name +0 -6
  120. data/test/multipart/filename_with_encoded_words +0 -7
  121. data/test/multipart/filename_with_escaped_quotes +0 -6
  122. data/test/multipart/filename_with_escaped_quotes_and_modification_param +0 -7
  123. data/test/multipart/filename_with_null_byte +0 -7
  124. data/test/multipart/filename_with_percent_escaped_quotes +0 -6
  125. data/test/multipart/filename_with_single_quote +0 -7
  126. data/test/multipart/filename_with_unescaped_percentages +0 -6
  127. data/test/multipart/filename_with_unescaped_percentages2 +0 -6
  128. data/test/multipart/filename_with_unescaped_percentages3 +0 -6
  129. data/test/multipart/filename_with_unescaped_quotes +0 -6
  130. data/test/multipart/ie +0 -6
  131. data/test/multipart/invalid_character +0 -6
  132. data/test/multipart/mixed_files +0 -21
  133. data/test/multipart/nested +0 -10
  134. data/test/multipart/none +0 -9
  135. data/test/multipart/quoted +0 -15
  136. data/test/multipart/rack-logo.png +0 -0
  137. data/test/multipart/semicolon +0 -6
  138. data/test/multipart/text +0 -15
  139. data/test/multipart/three_files_three_fields +0 -31
  140. data/test/multipart/unity3d_wwwform +0 -11
  141. data/test/multipart/webkit +0 -32
  142. data/test/rackup/config.ru +0 -31
  143. data/test/registering_handler/rack/handler/registering_myself.rb +0 -8
  144. data/test/spec_auth_basic.rb +0 -89
  145. data/test/spec_auth_digest.rb +0 -260
  146. data/test/spec_body_proxy.rb +0 -85
  147. data/test/spec_builder.rb +0 -233
  148. data/test/spec_cascade.rb +0 -63
  149. data/test/spec_cgi.rb +0 -84
  150. data/test/spec_chunked.rb +0 -103
  151. data/test/spec_common_logger.rb +0 -107
  152. data/test/spec_conditional_get.rb +0 -103
  153. data/test/spec_config.rb +0 -23
  154. data/test/spec_content_length.rb +0 -86
  155. data/test/spec_content_type.rb +0 -46
  156. data/test/spec_deflater.rb +0 -375
  157. data/test/spec_directory.rb +0 -148
  158. data/test/spec_etag.rb +0 -108
  159. data/test/spec_events.rb +0 -133
  160. data/test/spec_fastcgi.rb +0 -85
  161. data/test/spec_file.rb +0 -264
  162. data/test/spec_handler.rb +0 -57
  163. data/test/spec_head.rb +0 -46
  164. data/test/spec_lint.rb +0 -520
  165. data/test/spec_lobster.rb +0 -59
  166. data/test/spec_lock.rb +0 -204
  167. data/test/spec_logger.rb +0 -24
  168. data/test/spec_media_type.rb +0 -42
  169. data/test/spec_method_override.rb +0 -110
  170. data/test/spec_mime.rb +0 -51
  171. data/test/spec_mock.rb +0 -359
  172. data/test/spec_multipart.rb +0 -721
  173. data/test/spec_null_logger.rb +0 -21
  174. data/test/spec_recursive.rb +0 -75
  175. data/test/spec_request.rb +0 -1423
  176. data/test/spec_response.rb +0 -528
  177. data/test/spec_rewindable_input.rb +0 -128
  178. data/test/spec_runtime.rb +0 -50
  179. data/test/spec_sendfile.rb +0 -125
  180. data/test/spec_server.rb +0 -193
  181. data/test/spec_session_abstract_id.rb +0 -31
  182. data/test/spec_session_abstract_session_hash.rb +0 -45
  183. data/test/spec_session_cookie.rb +0 -442
  184. data/test/spec_session_memcache.rb +0 -357
  185. data/test/spec_session_persisted_secure_secure_session_hash.rb +0 -73
  186. data/test/spec_session_pool.rb +0 -247
  187. data/test/spec_show_exceptions.rb +0 -93
  188. data/test/spec_show_status.rb +0 -104
  189. data/test/spec_static.rb +0 -184
  190. data/test/spec_tempfile_reaper.rb +0 -64
  191. data/test/spec_thin.rb +0 -96
  192. data/test/spec_urlmap.rb +0 -237
  193. data/test/spec_utils.rb +0 -742
  194. data/test/spec_version.rb +0 -11
  195. data/test/spec_webrick.rb +0 -206
  196. data/test/static/another/index.html +0 -1
  197. data/test/static/foo.html +0 -1
  198. data/test/static/index.html +0 -1
  199. data/test/testrequest.rb +0 -78
  200. data/test/unregistered_handler/rack/handler/unregistered.rb +0 -7
  201. data/test/unregistered_handler/rack/handler/unregistered_long_one.rb +0 -7
@@ -1,721 +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 escaped quotes and modification param" do
385
- env = Rack::MockRequest.env_for("/", multipart_fixture(:filename_with_escaped_quotes_and_modification_param))
386
- params = Rack::Multipart.parse_multipart(env)
387
- params["files"][:type].must_equal "image/jpeg"
388
- params["files"][:filename].must_equal "\"human\" genome.jpeg"
389
- params["files"][:head].must_equal "Content-Type: image/jpeg\r\n" +
390
- "Content-Disposition: attachment; " +
391
- "name=\"files\"; " +
392
- "filename=\"\\\"human\\\" genome.jpeg\"; " +
393
- "modification-date=\"Wed, 12 Feb 1997 16:29:51 -0500\";\r\n" +
394
- "Content-Description: a complete map of the human genome\r\n"
395
- params["files"][:name].must_equal "files"
396
- params["files"][:tempfile].read.must_equal "contents"
397
- end
398
-
399
- it "parse filename with unescaped percentage characters" do
400
- env = Rack::MockRequest.env_for("/", multipart_fixture(:filename_with_unescaped_percentages, "----WebKitFormBoundary2NHc7OhsgU68l3Al"))
401
- params = Rack::Multipart.parse_multipart(env)
402
- files = params["document"]["attachment"]
403
- files[:type].must_equal "image/jpeg"
404
- files[:filename].must_equal "100% of a photo.jpeg"
405
- files[:head].must_equal <<-MULTIPART
406
- Content-Disposition: form-data; name="document[attachment]"; filename="100% of a photo.jpeg"\r
407
- Content-Type: image/jpeg\r
408
- MULTIPART
409
-
410
- files[:name].must_equal "document[attachment]"
411
- files[:tempfile].read.must_equal "contents"
412
- end
413
-
414
- it "parse filename with unescaped percentage characters that look like partial hex escapes" do
415
- env = Rack::MockRequest.env_for("/", multipart_fixture(:filename_with_unescaped_percentages2, "----WebKitFormBoundary2NHc7OhsgU68l3Al"))
416
- params = Rack::Multipart.parse_multipart(env)
417
- files = params["document"]["attachment"]
418
- files[:type].must_equal "image/jpeg"
419
- files[:filename].must_equal "100%a"
420
- files[:head].must_equal <<-MULTIPART
421
- Content-Disposition: form-data; name="document[attachment]"; filename="100%a"\r
422
- Content-Type: image/jpeg\r
423
- MULTIPART
424
-
425
- files[:name].must_equal "document[attachment]"
426
- files[:tempfile].read.must_equal "contents"
427
- end
428
-
429
- it "parse filename with unescaped percentage characters that look like partial hex escapes" do
430
- env = Rack::MockRequest.env_for("/", multipart_fixture(:filename_with_unescaped_percentages3, "----WebKitFormBoundary2NHc7OhsgU68l3Al"))
431
- params = Rack::Multipart.parse_multipart(env)
432
- files = params["document"]["attachment"]
433
- files[:type].must_equal "image/jpeg"
434
- files[:filename].must_equal "100%"
435
- files[:head].must_equal <<-MULTIPART
436
- Content-Disposition: form-data; name="document[attachment]"; filename="100%"\r
437
- Content-Type: image/jpeg\r
438
- MULTIPART
439
-
440
- files[:name].must_equal "document[attachment]"
441
- files[:tempfile].read.must_equal "contents"
442
- end
443
-
444
- it "rewinds input after parsing upload" do
445
- options = multipart_fixture(:text)
446
- input = options[:input]
447
- env = Rack::MockRequest.env_for("/", options)
448
- params = Rack::Multipart.parse_multipart(env)
449
- params["submit-name"].must_equal "Larry"
450
- params["files"][:filename].must_equal "file1.txt"
451
- input.read.length.must_equal 307
452
- end
453
-
454
- it "builds multipart body" do
455
- files = Rack::Multipart::UploadedFile.new(multipart_file("file1.txt"))
456
- data = Rack::Multipart.build_multipart("submit-name" => "Larry", "files" => files)
457
-
458
- options = {
459
- "CONTENT_TYPE" => "multipart/form-data; boundary=AaB03x",
460
- "CONTENT_LENGTH" => data.length.to_s,
461
- :input => StringIO.new(data)
462
- }
463
- env = Rack::MockRequest.env_for("/", options)
464
- params = Rack::Multipart.parse_multipart(env)
465
- params["submit-name"].must_equal "Larry"
466
- params["files"][:filename].must_equal "file1.txt"
467
- params["files"][:tempfile].read.must_equal "contents"
468
- end
469
-
470
- it "builds nested multipart body" do
471
- files = Rack::Multipart::UploadedFile.new(multipart_file("file1.txt"))
472
- data = Rack::Multipart.build_multipart("people" => [{"submit-name" => "Larry", "files" => files}])
473
-
474
- options = {
475
- "CONTENT_TYPE" => "multipart/form-data; boundary=AaB03x",
476
- "CONTENT_LENGTH" => data.length.to_s,
477
- :input => StringIO.new(data)
478
- }
479
- env = Rack::MockRequest.env_for("/", options)
480
- params = Rack::Multipart.parse_multipart(env)
481
- params["people"][0]["submit-name"].must_equal "Larry"
482
- params["people"][0]["files"][:filename].must_equal "file1.txt"
483
- params["people"][0]["files"][:tempfile].read.must_equal "contents"
484
- end
485
-
486
- it "can parse fields that end at the end of the buffer" do
487
- input = File.read(multipart_file("bad_robots"))
488
-
489
- req = Rack::Request.new Rack::MockRequest.env_for("/",
490
- "CONTENT_TYPE" => "multipart/form-data, boundary=1yy3laWhgX31qpiHinh67wJXqKalukEUTvqTzmon",
491
- "CONTENT_LENGTH" => input.size,
492
- :input => input)
493
-
494
- req.POST['file.path'].must_equal "/var/tmp/uploads/4/0001728414"
495
- req.POST['addresses'].wont_equal nil
496
- end
497
-
498
- it "builds complete params with the chunk size of 16384 slicing exactly on boundary" do
499
- begin
500
- previous_limit = Rack::Utils.multipart_part_limit
501
- Rack::Utils.multipart_part_limit = 256
502
-
503
- data = File.open(multipart_file("fail_16384_nofile"), 'rb') { |f| f.read }.gsub(/\n/, "\r\n")
504
- options = {
505
- "CONTENT_TYPE" => "multipart/form-data; boundary=----WebKitFormBoundaryWsY0GnpbI5U7ztzo",
506
- "CONTENT_LENGTH" => data.length.to_s,
507
- :input => StringIO.new(data)
508
- }
509
- env = Rack::MockRequest.env_for("/", options)
510
- params = Rack::Multipart.parse_multipart(env)
511
-
512
- params.wont_equal nil
513
- params.keys.must_include "AAAAAAAAAAAAAAAAAAA"
514
- params["AAAAAAAAAAAAAAAAAAA"].keys.must_include "PLAPLAPLA_MEMMEMMEMM_ATTRATTRER"
515
- params["AAAAAAAAAAAAAAAAAAA"]["PLAPLAPLA_MEMMEMMEMM_ATTRATTRER"].keys.must_include "new"
516
- params["AAAAAAAAAAAAAAAAAAA"]["PLAPLAPLA_MEMMEMMEMM_ATTRATTRER"]["new"].keys.must_include "-2"
517
- params["AAAAAAAAAAAAAAAAAAA"]["PLAPLAPLA_MEMMEMMEMM_ATTRATTRER"]["new"]["-2"].keys.must_include "ba_unit_id"
518
- params["AAAAAAAAAAAAAAAAAAA"]["PLAPLAPLA_MEMMEMMEMM_ATTRATTRER"]["new"]["-2"]["ba_unit_id"].must_equal "1017"
519
- ensure
520
- Rack::Utils.multipart_part_limit = previous_limit
521
- end
522
- end
523
-
524
- it "not reach a multi-part limit" do
525
- begin
526
- previous_limit = Rack::Utils.multipart_part_limit
527
- Rack::Utils.multipart_part_limit = 4
528
-
529
- env = Rack::MockRequest.env_for '/', multipart_fixture(:three_files_three_fields)
530
- params = Rack::Multipart.parse_multipart(env)
531
- params['reply'].must_equal 'yes'
532
- params['to'].must_equal 'people'
533
- params['from'].must_equal 'others'
534
- ensure
535
- Rack::Utils.multipart_part_limit = previous_limit
536
- end
537
- end
538
-
539
- it "reach a multipart limit" do
540
- begin
541
- previous_limit = Rack::Utils.multipart_part_limit
542
- Rack::Utils.multipart_part_limit = 3
543
-
544
- env = Rack::MockRequest.env_for '/', multipart_fixture(:three_files_three_fields)
545
- lambda { Rack::Multipart.parse_multipart(env) }.must_raise Rack::Multipart::MultipartPartLimitError
546
- ensure
547
- Rack::Utils.multipart_part_limit = previous_limit
548
- end
549
- end
550
-
551
- it "reach a multipart total limit" do
552
- begin
553
- previous_limit = Rack::Utils.multipart_total_part_limit
554
- Rack::Utils.multipart_total_part_limit = 5
555
-
556
- env = Rack::MockRequest.env_for '/', multipart_fixture(:three_files_three_fields)
557
- lambda { Rack::Multipart.parse_multipart(env) }.must_raise Rack::Multipart::MultipartTotalPartLimitError
558
- ensure
559
- Rack::Utils.multipart_total_part_limit = previous_limit
560
- end
561
- end
562
-
563
- it "return nil if no UploadedFiles were used" do
564
- data = Rack::Multipart.build_multipart("people" => [{"submit-name" => "Larry", "files" => "contents"}])
565
- data.must_be_nil
566
- end
567
-
568
- it "raise ArgumentError if params is not a Hash" do
569
- lambda {
570
- Rack::Multipart.build_multipart("foo=bar")
571
- }.must_raise(ArgumentError).message.must_equal "value must be a Hash"
572
- end
573
-
574
- it "can parse fields with a content type" do
575
- data = <<-EOF
576
- --1yy3laWhgX31qpiHinh67wJXqKalukEUTvqTzmon\r
577
- Content-Disposition: form-data; name="description"\r
578
- Content-Type: text/plain"\r
579
- \r
580
- Very very blue\r
581
- --1yy3laWhgX31qpiHinh67wJXqKalukEUTvqTzmon--\r
582
- EOF
583
- options = {
584
- "CONTENT_TYPE" => "multipart/form-data; boundary=1yy3laWhgX31qpiHinh67wJXqKalukEUTvqTzmon",
585
- "CONTENT_LENGTH" => data.length.to_s,
586
- :input => StringIO.new(data)
587
- }
588
- env = Rack::MockRequest.env_for("/", options)
589
- params = Rack::Multipart.parse_multipart(env)
590
-
591
- params.must_equal "description"=>"Very very blue"
592
- end
593
-
594
- it "parse multipart upload with no content-length header" do
595
- env = Rack::MockRequest.env_for '/', multipart_fixture(:webkit)
596
- env['CONTENT_TYPE'] = "multipart/form-data; boundary=----WebKitFormBoundaryWLHCs9qmcJJoyjKR"
597
- env.delete 'CONTENT_LENGTH'
598
- params = Rack::Multipart.parse_multipart(env)
599
- params['profile']['bio'].must_include 'hello'
600
- end
601
-
602
- it "parse very long unquoted multipart file names" do
603
- data = <<-EOF
604
- --AaB03x\r
605
- Content-Type: text/plain\r
606
- Content-Disposition: attachment; name=file; filename=#{'long' * 100}\r
607
- \r
608
- contents\r
609
- --AaB03x--\r
610
- EOF
611
-
612
- options = {
613
- "CONTENT_TYPE" => "multipart/form-data; boundary=AaB03x",
614
- "CONTENT_LENGTH" => data.length.to_s,
615
- :input => StringIO.new(data)
616
- }
617
- env = Rack::MockRequest.env_for("/", options)
618
- params = Rack::Multipart.parse_multipart(env)
619
-
620
- params["file"][:filename].must_equal 'long' * 100
621
- end
622
-
623
- it "parse unquoted parameter values at end of line" do
624
- data = <<-EOF
625
- --AaB03x\r
626
- Content-Type: text/plain\r
627
- Content-Disposition: attachment; name=inline\r
628
- \r
629
- true\r
630
- --AaB03x--\r
631
- EOF
632
-
633
- options = {
634
- "CONTENT_TYPE" => "multipart/form-data; boundary=AaB03x",
635
- "CONTENT_LENGTH" => data.length.to_s,
636
- :input => StringIO.new(data)
637
- }
638
- env = Rack::MockRequest.env_for("/", options)
639
- params = Rack::Multipart.parse_multipart(env)
640
- params["inline"].must_equal 'true'
641
- end
642
-
643
- it "parse quoted chars in name parameter" do
644
- data = <<-EOF
645
- --AaB03x\r
646
- Content-Type: text/plain\r
647
- Content-Disposition: attachment; name="quoted\\\\chars\\"in\rname"\r
648
- \r
649
- true\r
650
- --AaB03x--\r
651
- EOF
652
-
653
- options = {
654
- "CONTENT_TYPE" => "multipart/form-data; boundary=AaB03x",
655
- "CONTENT_LENGTH" => data.length.to_s,
656
- :input => StringIO.new(data)
657
- }
658
- env = Rack::MockRequest.env_for("/", options)
659
- params = Rack::Multipart.parse_multipart(env)
660
- params["quoted\\chars\"in\rname"].must_equal 'true'
661
- end
662
-
663
- it "support mixed case metadata" do
664
- file = multipart_file(:text)
665
- data = File.open(file, 'rb') { |io| io.read }
666
-
667
- type = "Multipart/Form-Data; Boundary=AaB03x"
668
- length = data.bytesize
669
-
670
- e = { "CONTENT_TYPE" => type,
671
- "CONTENT_LENGTH" => length.to_s,
672
- :input => StringIO.new(data) }
673
-
674
- env = Rack::MockRequest.env_for("/", e)
675
- params = Rack::Multipart.parse_multipart(env)
676
- params["submit-name"].must_equal "Larry"
677
- params["submit-name-with-content"].must_equal "Berry"
678
- params["files"][:type].must_equal "text/plain"
679
- params["files"][:filename].must_equal "file1.txt"
680
- params["files"][:head].must_equal "Content-Disposition: form-data; " +
681
- "name=\"files\"; filename=\"file1.txt\"\r\n" +
682
- "Content-Type: text/plain\r\n"
683
- params["files"][:name].must_equal "files"
684
- params["files"][:tempfile].read.must_equal "contents"
685
- end
686
-
687
- it "fallback to content-type for name" do
688
- rack_logo = File.read(multipart_file("rack-logo.png"))
689
-
690
- data = <<-EOF
691
- --AaB03x\r
692
- Content-Type: text/plain\r
693
- \r
694
- some text\r
695
- --AaB03x\r
696
- \r
697
- \r
698
- some more text (I didn't specify Content-Type)\r
699
- --AaB03x\r
700
- Content-Type: image/png\r
701
- \r
702
- #{rack_logo}\r
703
- --AaB03x--\r
704
- EOF
705
-
706
- options = {
707
- "CONTENT_TYPE" => "multipart/related; boundary=AaB03x",
708
- "CONTENT_LENGTH" => data.bytesize.to_s,
709
- :input => StringIO.new(data)
710
- }
711
- env = Rack::MockRequest.env_for("/", options)
712
- params = Rack::Multipart.parse_multipart(env)
713
-
714
- params["text/plain"].must_equal ["some text", "some more text (I didn't specify Content-Type)"]
715
- params["image/png"].length.must_equal 1
716
-
717
- f = Tempfile.new("rack-logo")
718
- f.write(params["image/png"][0])
719
- f.length.must_equal 26473
720
- end
721
- end