lack 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. checksums.yaml +7 -0
  2. data/bin/rackup +5 -0
  3. data/lib/rack.rb +26 -0
  4. data/lib/rack/body_proxy.rb +39 -0
  5. data/lib/rack/builder.rb +166 -0
  6. data/lib/rack/handler.rb +63 -0
  7. data/lib/rack/handler/webrick.rb +120 -0
  8. data/lib/rack/mime.rb +661 -0
  9. data/lib/rack/mock.rb +198 -0
  10. data/lib/rack/multipart.rb +31 -0
  11. data/lib/rack/multipart/generator.rb +93 -0
  12. data/lib/rack/multipart/parser.rb +239 -0
  13. data/lib/rack/multipart/uploaded_file.rb +34 -0
  14. data/lib/rack/request.rb +394 -0
  15. data/lib/rack/response.rb +160 -0
  16. data/lib/rack/server.rb +258 -0
  17. data/lib/rack/server/options.rb +121 -0
  18. data/lib/rack/utils.rb +653 -0
  19. data/lib/rack/version.rb +3 -0
  20. data/spec/spec_helper.rb +1 -0
  21. data/test/builder/anything.rb +5 -0
  22. data/test/builder/comment.ru +4 -0
  23. data/test/builder/end.ru +5 -0
  24. data/test/builder/line.ru +1 -0
  25. data/test/builder/options.ru +2 -0
  26. data/test/multipart/bad_robots +259 -0
  27. data/test/multipart/binary +0 -0
  28. data/test/multipart/content_type_and_no_filename +6 -0
  29. data/test/multipart/empty +10 -0
  30. data/test/multipart/fail_16384_nofile +814 -0
  31. data/test/multipart/file1.txt +1 -0
  32. data/test/multipart/filename_and_modification_param +7 -0
  33. data/test/multipart/filename_and_no_name +6 -0
  34. data/test/multipart/filename_with_escaped_quotes +6 -0
  35. data/test/multipart/filename_with_escaped_quotes_and_modification_param +7 -0
  36. data/test/multipart/filename_with_percent_escaped_quotes +6 -0
  37. data/test/multipart/filename_with_unescaped_percentages +6 -0
  38. data/test/multipart/filename_with_unescaped_percentages2 +6 -0
  39. data/test/multipart/filename_with_unescaped_percentages3 +6 -0
  40. data/test/multipart/filename_with_unescaped_quotes +6 -0
  41. data/test/multipart/ie +6 -0
  42. data/test/multipart/invalid_character +6 -0
  43. data/test/multipart/mixed_files +21 -0
  44. data/test/multipart/nested +10 -0
  45. data/test/multipart/none +9 -0
  46. data/test/multipart/semicolon +6 -0
  47. data/test/multipart/text +15 -0
  48. data/test/multipart/webkit +32 -0
  49. data/test/rackup/config.ru +31 -0
  50. data/test/registering_handler/rack/handler/registering_myself.rb +8 -0
  51. data/test/spec_body_proxy.rb +69 -0
  52. data/test/spec_builder.rb +223 -0
  53. data/test/spec_chunked.rb +101 -0
  54. data/test/spec_file.rb +221 -0
  55. data/test/spec_handler.rb +59 -0
  56. data/test/spec_head.rb +45 -0
  57. data/test/spec_lint.rb +522 -0
  58. data/test/spec_mime.rb +51 -0
  59. data/test/spec_mock.rb +277 -0
  60. data/test/spec_multipart.rb +547 -0
  61. data/test/spec_recursive.rb +72 -0
  62. data/test/spec_request.rb +1199 -0
  63. data/test/spec_response.rb +343 -0
  64. data/test/spec_rewindable_input.rb +118 -0
  65. data/test/spec_sendfile.rb +130 -0
  66. data/test/spec_server.rb +167 -0
  67. data/test/spec_utils.rb +635 -0
  68. data/test/spec_webrick.rb +184 -0
  69. data/test/testrequest.rb +78 -0
  70. data/test/unregistered_handler/rack/handler/unregistered.rb +7 -0
  71. data/test/unregistered_handler/rack/handler/unregistered_long_one.rb +7 -0
  72. metadata +240 -0
@@ -0,0 +1,72 @@
1
+ require 'rack/lint'
2
+ require 'rack/recursive'
3
+ require 'rack/mock'
4
+
5
+ describe Rack::Recursive do
6
+ @app1 = lambda { |env|
7
+ res = Rack::Response.new
8
+ res["X-Path-Info"] = env["PATH_INFO"]
9
+ res["X-Query-String"] = env["QUERY_STRING"]
10
+ res.finish do |inner_res|
11
+ inner_res.write "App1"
12
+ end
13
+ }
14
+
15
+ @app2 = lambda { |env|
16
+ Rack::Response.new.finish do |res|
17
+ res.write "App2"
18
+ _, _, body = env['rack.recursive.include'].call(env, "/app1")
19
+ body.each { |b|
20
+ res.write b
21
+ }
22
+ end
23
+ }
24
+
25
+ @app3 = lambda { |env|
26
+ raise Rack::ForwardRequest.new("/app1")
27
+ }
28
+
29
+ @app4 = lambda { |env|
30
+ raise Rack::ForwardRequest.new("http://example.org/app1/quux?meh")
31
+ }
32
+
33
+ def recursive(map)
34
+ Rack::Lint.new Rack::Recursive.new(Rack::URLMap.new(map))
35
+ end
36
+
37
+ should "allow for subrequests" do
38
+ res = Rack::MockRequest.new(recursive("/app1" => @app1,
39
+ "/app2" => @app2)).
40
+ get("/app2")
41
+
42
+ res.should.be.ok
43
+ res.body.should.equal "App2App1"
44
+ end
45
+
46
+ should "raise error on requests not below the app" do
47
+ app = Rack::URLMap.new("/app1" => @app1,
48
+ "/app" => recursive("/1" => @app1,
49
+ "/2" => @app2))
50
+
51
+ lambda {
52
+ Rack::MockRequest.new(app).get("/app/2")
53
+ }.should.raise(ArgumentError).
54
+ message.should =~ /can only include below/
55
+ end
56
+
57
+ should "support forwarding" do
58
+ app = recursive("/app1" => @app1,
59
+ "/app3" => @app3,
60
+ "/app4" => @app4)
61
+
62
+ res = Rack::MockRequest.new(app).get("/app3")
63
+ res.should.be.ok
64
+ res.body.should.equal "App1"
65
+
66
+ res = Rack::MockRequest.new(app).get("/app4")
67
+ res.should.be.ok
68
+ res.body.should.equal "App1"
69
+ res["X-Path-Info"].should.equal "/quux"
70
+ res["X-Query-String"].should.equal "meh"
71
+ end
72
+ end
@@ -0,0 +1,1199 @@
1
+ require 'stringio'
2
+ require 'cgi'
3
+ require 'rack/request'
4
+ require 'rack/mock'
5
+
6
+ describe Rack::Request do
7
+ should "wrap the rack variables" do
8
+ req = Rack::Request.new(Rack::MockRequest.env_for("http://example.com:8080/"))
9
+
10
+ req.body.should.respond_to? :gets
11
+ req.scheme.should.equal "http"
12
+ req.request_method.should.equal "GET"
13
+
14
+ req.should.be.get
15
+ req.should.not.be.post
16
+ req.should.not.be.put
17
+ req.should.not.be.delete
18
+ req.should.not.be.head
19
+ req.should.not.be.patch
20
+
21
+ req.script_name.should.equal ""
22
+ req.path_info.should.equal "/"
23
+ req.query_string.should.equal ""
24
+
25
+ req.host.should.equal "example.com"
26
+ req.port.should.equal 8080
27
+
28
+ req.content_length.should.equal "0"
29
+ req.content_type.should.be.nil
30
+ end
31
+
32
+ should "figure out the correct host" do
33
+ req = Rack::Request.new \
34
+ Rack::MockRequest.env_for("/", "HTTP_HOST" => "www2.example.org")
35
+ req.host.should.equal "www2.example.org"
36
+
37
+ req = Rack::Request.new \
38
+ Rack::MockRequest.env_for("/", "SERVER_NAME" => "example.org", "SERVER_PORT" => "9292")
39
+ req.host.should.equal "example.org"
40
+
41
+ req = Rack::Request.new \
42
+ Rack::MockRequest.env_for("/", "HTTP_HOST" => "localhost:81", "HTTP_X_FORWARDED_HOST" => "example.org:9292")
43
+ req.host.should.equal "example.org"
44
+
45
+ env = Rack::MockRequest.env_for("/", "SERVER_ADDR" => "192.168.1.1", "SERVER_PORT" => "9292")
46
+ env.delete("SERVER_NAME")
47
+ req = Rack::Request.new(env)
48
+ req.host.should.equal "192.168.1.1"
49
+
50
+ env = Rack::MockRequest.env_for("/")
51
+ env.delete("SERVER_NAME")
52
+ req = Rack::Request.new(env)
53
+ req.host.should.equal ""
54
+ end
55
+
56
+ should "figure out the correct port" do
57
+ req = Rack::Request.new \
58
+ Rack::MockRequest.env_for("/", "HTTP_HOST" => "www2.example.org")
59
+ req.port.should.equal 80
60
+
61
+ req = Rack::Request.new \
62
+ Rack::MockRequest.env_for("/", "HTTP_HOST" => "www2.example.org:81")
63
+ req.port.should.equal 81
64
+
65
+ req = Rack::Request.new \
66
+ Rack::MockRequest.env_for("/", "SERVER_NAME" => "example.org", "SERVER_PORT" => "9292")
67
+ req.port.should.equal 9292
68
+
69
+ req = Rack::Request.new \
70
+ Rack::MockRequest.env_for("/", "HTTP_HOST" => "localhost:81", "HTTP_X_FORWARDED_HOST" => "example.org:9292")
71
+ req.port.should.equal 9292
72
+
73
+ req = Rack::Request.new \
74
+ Rack::MockRequest.env_for("/", "HTTP_HOST" => "localhost:81", "HTTP_X_FORWARDED_HOST" => "example.org")
75
+ req.port.should.equal 80
76
+
77
+ req = Rack::Request.new \
78
+ Rack::MockRequest.env_for("/", "HTTP_HOST" => "localhost:81", "HTTP_X_FORWARDED_HOST" => "example.org", "HTTP_X_FORWARDED_SSL" => "on")
79
+ req.port.should.equal 443
80
+
81
+ req = Rack::Request.new \
82
+ Rack::MockRequest.env_for("/", "HTTP_HOST" => "localhost:81", "HTTP_X_FORWARDED_HOST" => "example.org", "HTTP_X_FORWARDED_PROTO" => "https")
83
+ req.port.should.equal 443
84
+
85
+ req = Rack::Request.new \
86
+ Rack::MockRequest.env_for("/", "HTTP_HOST" => "localhost:81", "HTTP_X_FORWARDED_HOST" => "example.org", "HTTP_X_FORWARDED_PORT" => "9393")
87
+ req.port.should.equal 9393
88
+
89
+ req = Rack::Request.new \
90
+ Rack::MockRequest.env_for("/", "HTTP_HOST" => "localhost:81", "HTTP_X_FORWARDED_HOST" => "example.org:9393", "SERVER_PORT" => "80")
91
+ req.port.should.equal 9393
92
+
93
+ req = Rack::Request.new \
94
+ Rack::MockRequest.env_for("/", "HTTP_HOST" => "localhost:81", "HTTP_X_FORWARDED_HOST" => "example.org", "SERVER_PORT" => "9393")
95
+ req.port.should.equal 80
96
+
97
+ req = Rack::Request.new \
98
+ Rack::MockRequest.env_for("/", "HTTP_HOST" => "localhost", "HTTP_X_FORWARDED_PROTO" => "https", "SERVER_PORT" => "80")
99
+ req.port.should.equal 443
100
+
101
+ req = Rack::Request.new \
102
+ Rack::MockRequest.env_for("/", "HTTP_HOST" => "localhost", "HTTP_X_FORWARDED_PROTO" => "https,https", "SERVER_PORT" => "80")
103
+ req.port.should.equal 443
104
+ end
105
+
106
+ should "figure out the correct host with port" do
107
+ req = Rack::Request.new \
108
+ Rack::MockRequest.env_for("/", "HTTP_HOST" => "www2.example.org")
109
+ req.host_with_port.should.equal "www2.example.org"
110
+
111
+ req = Rack::Request.new \
112
+ Rack::MockRequest.env_for("/", "HTTP_HOST" => "localhost:81")
113
+ req.host_with_port.should.equal "localhost:81"
114
+
115
+ req = Rack::Request.new \
116
+ Rack::MockRequest.env_for("/", "SERVER_NAME" => "example.org", "SERVER_PORT" => "9292")
117
+ req.host_with_port.should.equal "example.org:9292"
118
+
119
+ req = Rack::Request.new \
120
+ Rack::MockRequest.env_for("/", "HTTP_HOST" => "localhost:81", "HTTP_X_FORWARDED_HOST" => "example.org:9292")
121
+ req.host_with_port.should.equal "example.org:9292"
122
+
123
+ req = Rack::Request.new \
124
+ Rack::MockRequest.env_for("/", "HTTP_HOST" => "localhost:81", "HTTP_X_FORWARDED_HOST" => "example.org", "SERVER_PORT" => "9393")
125
+ req.host_with_port.should.equal "example.org"
126
+ end
127
+
128
+ should "parse the query string" do
129
+ req = Rack::Request.new(Rack::MockRequest.env_for("/?foo=bar&quux=bla"))
130
+ req.query_string.should.equal "foo=bar&quux=bla"
131
+ req.GET.should.equal "foo" => "bar", "quux" => "bla"
132
+ req.POST.should.be.empty
133
+ req.params.should.equal "foo" => "bar", "quux" => "bla"
134
+ end
135
+
136
+ should "not truncate query strings containing semi-colons #543" do
137
+ req = Rack::Request.new(Rack::MockRequest.env_for("/?foo=bar&quux=b;la"))
138
+ req.query_string.should.equal "foo=bar&quux=b;la"
139
+ req.GET.should.equal "foo" => "bar", "quux" => "b;la"
140
+ req.POST.should.be.empty
141
+ req.params.should.equal "foo" => "bar", "quux" => "b;la"
142
+ end
143
+
144
+ should "limit the keys from the GET query string" do
145
+ env = Rack::MockRequest.env_for("/?foo=bar")
146
+
147
+ old, Rack::Utils.key_space_limit = Rack::Utils.key_space_limit, 1
148
+ begin
149
+ req = Rack::Request.new(env)
150
+ lambda { req.GET }.should.raise(RangeError)
151
+ ensure
152
+ Rack::Utils.key_space_limit = old
153
+ end
154
+ end
155
+
156
+ should "limit the key size per nested params hash" do
157
+ nested_query = Rack::MockRequest.env_for("/?foo%5Bbar%5D%5Bbaz%5D%5Bqux%5D=1")
158
+ plain_query = Rack::MockRequest.env_for("/?foo_bar__baz__qux_=1")
159
+
160
+ old, Rack::Utils.key_space_limit = Rack::Utils.key_space_limit, 3
161
+ begin
162
+ lambda { Rack::Request.new(nested_query).GET }.should.not.raise(RangeError)
163
+ lambda { Rack::Request.new(plain_query).GET }.should.raise(RangeError)
164
+ ensure
165
+ Rack::Utils.key_space_limit = old
166
+ end
167
+ end
168
+
169
+ should "not unify GET and POST when calling params" do
170
+ mr = Rack::MockRequest.env_for("/?foo=quux",
171
+ "REQUEST_METHOD" => 'POST',
172
+ :input => "foo=bar&quux=bla"
173
+ )
174
+ req = Rack::Request.new mr
175
+
176
+ req.params
177
+
178
+ req.GET.should.equal "foo" => "quux"
179
+ req.POST.should.equal "foo" => "bar", "quux" => "bla"
180
+ req.params.should.equal req.GET.merge(req.POST)
181
+ end
182
+
183
+ should "raise if input params has invalid %-encoding" do
184
+ mr = Rack::MockRequest.env_for("/?foo=quux",
185
+ "REQUEST_METHOD" => 'POST',
186
+ :input => "a%=1"
187
+ )
188
+ req = Rack::Request.new mr
189
+
190
+ lambda { req.POST }.
191
+ should.raise(Rack::Utils::InvalidParameterError).
192
+ message.should.equal "invalid %-encoding (a%)"
193
+ end
194
+
195
+ should "raise if rack.input is missing" do
196
+ req = Rack::Request.new({})
197
+ lambda { req.POST }.should.raise(RuntimeError)
198
+ end
199
+
200
+ should "parse POST data when method is POST and no Content-Type given" do
201
+ req = Rack::Request.new \
202
+ Rack::MockRequest.env_for("/?foo=quux",
203
+ "REQUEST_METHOD" => 'POST',
204
+ :input => "foo=bar&quux=bla")
205
+ req.content_type.should.be.nil
206
+ req.media_type.should.be.nil
207
+ req.query_string.should.equal "foo=quux"
208
+ req.GET.should.equal "foo" => "quux"
209
+ req.POST.should.equal "foo" => "bar", "quux" => "bla"
210
+ req.params.should.equal "foo" => "bar", "quux" => "bla"
211
+ end
212
+
213
+ should "limit the keys from the POST form data" do
214
+ env = Rack::MockRequest.env_for("",
215
+ "REQUEST_METHOD" => 'POST',
216
+ :input => "foo=bar&quux=bla")
217
+
218
+ old, Rack::Utils.key_space_limit = Rack::Utils.key_space_limit, 1
219
+ begin
220
+ req = Rack::Request.new(env)
221
+ lambda { req.POST }.should.raise(RangeError)
222
+ ensure
223
+ Rack::Utils.key_space_limit = old
224
+ end
225
+ end
226
+
227
+ should "parse POST data with explicit content type regardless of method" do
228
+ req = Rack::Request.new \
229
+ Rack::MockRequest.env_for("/",
230
+ "CONTENT_TYPE" => 'application/x-www-form-urlencoded;foo=bar',
231
+ :input => "foo=bar&quux=bla")
232
+ req.content_type.should.equal 'application/x-www-form-urlencoded;foo=bar'
233
+ req.media_type.should.equal 'application/x-www-form-urlencoded'
234
+ req.media_type_params['foo'].should.equal 'bar'
235
+ req.POST.should.equal "foo" => "bar", "quux" => "bla"
236
+ req.params.should.equal "foo" => "bar", "quux" => "bla"
237
+ end
238
+
239
+ should "not parse POST data when media type is not form-data" do
240
+ req = Rack::Request.new \
241
+ Rack::MockRequest.env_for("/?foo=quux",
242
+ "REQUEST_METHOD" => 'POST',
243
+ "CONTENT_TYPE" => 'text/plain;charset=utf-8',
244
+ :input => "foo=bar&quux=bla")
245
+ req.content_type.should.equal 'text/plain;charset=utf-8'
246
+ req.media_type.should.equal 'text/plain'
247
+ req.media_type_params['charset'].should.equal 'utf-8'
248
+ req.POST.should.be.empty
249
+ req.params.should.equal "foo" => "quux"
250
+ req.body.read.should.equal "foo=bar&quux=bla"
251
+ end
252
+
253
+ should "parse POST data on PUT when media type is form-data" do
254
+ req = Rack::Request.new \
255
+ Rack::MockRequest.env_for("/?foo=quux",
256
+ "REQUEST_METHOD" => 'PUT',
257
+ "CONTENT_TYPE" => 'application/x-www-form-urlencoded',
258
+ :input => "foo=bar&quux=bla")
259
+ req.POST.should.equal "foo" => "bar", "quux" => "bla"
260
+ req.body.read.should.equal "foo=bar&quux=bla"
261
+ end
262
+
263
+ should "rewind input after parsing POST data" do
264
+ input = StringIO.new("foo=bar&quux=bla")
265
+ req = Rack::Request.new \
266
+ Rack::MockRequest.env_for("/",
267
+ "CONTENT_TYPE" => 'application/x-www-form-urlencoded;foo=bar',
268
+ :input => input)
269
+ req.params.should.equal "foo" => "bar", "quux" => "bla"
270
+ input.read.should.equal "foo=bar&quux=bla"
271
+ end
272
+
273
+ should "clean up Safari's ajax POST body" do
274
+ req = Rack::Request.new \
275
+ Rack::MockRequest.env_for("/",
276
+ 'REQUEST_METHOD' => 'POST', :input => "foo=bar&quux=bla\0")
277
+ req.POST.should.equal "foo" => "bar", "quux" => "bla"
278
+ end
279
+
280
+ should "get value by key from params with #[]" do
281
+ req = Rack::Request.new \
282
+ Rack::MockRequest.env_for("?foo=quux")
283
+ req['foo'].should.equal 'quux'
284
+ req[:foo].should.equal 'quux'
285
+ end
286
+
287
+ should "set value to key on params with #[]=" do
288
+ req = Rack::Request.new \
289
+ Rack::MockRequest.env_for("?foo=duh")
290
+ req['foo'].should.equal 'duh'
291
+ req[:foo].should.equal 'duh'
292
+ req.params.should.equal 'foo' => 'duh'
293
+
294
+ req['foo'] = 'bar'
295
+ req.params.should.equal 'foo' => 'bar'
296
+ req['foo'].should.equal 'bar'
297
+ req[:foo].should.equal 'bar'
298
+
299
+ req[:foo] = 'jaz'
300
+ req.params.should.equal 'foo' => 'jaz'
301
+ req['foo'].should.equal 'jaz'
302
+ req[:foo].should.equal 'jaz'
303
+ end
304
+
305
+ should "return values for the keys in the order given from values_at" do
306
+ req = Rack::Request.new \
307
+ Rack::MockRequest.env_for("?foo=baz&wun=der&bar=ful")
308
+ req.values_at('foo').should.equal ['baz']
309
+ req.values_at('foo', 'wun').should.equal ['baz', 'der']
310
+ req.values_at('bar', 'foo', 'wun').should.equal ['ful', 'baz', 'der']
311
+ end
312
+
313
+ should "extract referrer correctly" do
314
+ req = Rack::Request.new \
315
+ Rack::MockRequest.env_for("/", "HTTP_REFERER" => "/some/path")
316
+ req.referer.should.equal "/some/path"
317
+
318
+ req = Rack::Request.new \
319
+ Rack::MockRequest.env_for("/")
320
+ req.referer.should.equal nil
321
+ end
322
+
323
+ should "extract user agent correctly" do
324
+ req = Rack::Request.new \
325
+ Rack::MockRequest.env_for("/", "HTTP_USER_AGENT" => "Mozilla/4.0 (compatible)")
326
+ req.user_agent.should.equal "Mozilla/4.0 (compatible)"
327
+
328
+ req = Rack::Request.new \
329
+ Rack::MockRequest.env_for("/")
330
+ req.user_agent.should.equal nil
331
+ end
332
+
333
+ should "treat missing content type as nil" do
334
+ req = Rack::Request.new \
335
+ Rack::MockRequest.env_for("/")
336
+ req.content_type.should.equal nil
337
+ end
338
+
339
+ should "treat empty content type as nil" do
340
+ req = Rack::Request.new \
341
+ Rack::MockRequest.env_for("/", "CONTENT_TYPE" => "")
342
+ req.content_type.should.equal nil
343
+ end
344
+
345
+ should "return nil media type for empty content type" do
346
+ req = Rack::Request.new \
347
+ Rack::MockRequest.env_for("/", "CONTENT_TYPE" => "")
348
+ req.media_type.should.equal nil
349
+ end
350
+
351
+ should "cache, but invalidates the cache" do
352
+ req = Rack::Request.new \
353
+ Rack::MockRequest.env_for("/?foo=quux",
354
+ "CONTENT_TYPE" => "application/x-www-form-urlencoded",
355
+ :input => "foo=bar&quux=bla")
356
+ req.GET.should.equal "foo" => "quux"
357
+ req.GET.should.equal "foo" => "quux"
358
+ req.env["QUERY_STRING"] = "bla=foo"
359
+ req.GET.should.equal "bla" => "foo"
360
+ req.GET.should.equal "bla" => "foo"
361
+
362
+ req.POST.should.equal "foo" => "bar", "quux" => "bla"
363
+ req.POST.should.equal "foo" => "bar", "quux" => "bla"
364
+ req.env["rack.input"] = StringIO.new("foo=bla&quux=bar")
365
+ req.POST.should.equal "foo" => "bla", "quux" => "bar"
366
+ req.POST.should.equal "foo" => "bla", "quux" => "bar"
367
+ end
368
+
369
+ should "figure out if called via XHR" do
370
+ req = Rack::Request.new(Rack::MockRequest.env_for(""))
371
+ req.should.not.be.xhr
372
+
373
+ req = Rack::Request.new \
374
+ Rack::MockRequest.env_for("", "HTTP_X_REQUESTED_WITH" => "XMLHttpRequest")
375
+ req.should.be.xhr
376
+ end
377
+
378
+ should "ssl detection" do
379
+ request = Rack::Request.new(Rack::MockRequest.env_for("/"))
380
+ request.scheme.should.equal "http"
381
+ request.should.not.be.ssl?
382
+
383
+ request = Rack::Request.new(Rack::MockRequest.env_for("/", 'HTTPS' => 'on'))
384
+ request.scheme.should.equal "https"
385
+ request.should.be.ssl?
386
+
387
+ request = Rack::Request.new(Rack::MockRequest.env_for("/", 'rack.url_scheme' => 'https'))
388
+ request.scheme.should.equal "https"
389
+ request.should.be.ssl?
390
+
391
+ request = Rack::Request.new(Rack::MockRequest.env_for("/", 'HTTP_HOST' => 'www.example.org:8080'))
392
+ request.scheme.should.equal "http"
393
+ request.should.not.be.ssl?
394
+
395
+ request = Rack::Request.new(Rack::MockRequest.env_for("/", 'HTTP_HOST' => 'www.example.org:8443', 'HTTPS' => 'on'))
396
+ request.scheme.should.equal "https"
397
+ request.should.be.ssl?
398
+
399
+ request = Rack::Request.new(Rack::MockRequest.env_for("/", 'HTTP_HOST' => 'www.example.org:8443', 'HTTP_X_FORWARDED_SSL' => 'on'))
400
+ request.scheme.should.equal "https"
401
+ request.should.be.ssl?
402
+
403
+ request = Rack::Request.new(Rack::MockRequest.env_for("/", 'HTTP_X_FORWARDED_SCHEME' => 'https'))
404
+ request.scheme.should.equal "https"
405
+ request.should.be.ssl?
406
+
407
+ request = Rack::Request.new(Rack::MockRequest.env_for("/", 'HTTP_X_FORWARDED_PROTO' => 'https'))
408
+ request.scheme.should.equal "https"
409
+ request.should.be.ssl?
410
+
411
+ request = Rack::Request.new(Rack::MockRequest.env_for("/", 'HTTP_X_FORWARDED_PROTO' => 'https, http, http'))
412
+ request.scheme.should.equal "https"
413
+ request.should.be.ssl?
414
+ end
415
+
416
+ should "parse cookies" do
417
+ req = Rack::Request.new \
418
+ Rack::MockRequest.env_for("", "HTTP_COOKIE" => "foo=bar;quux=h&m")
419
+ req.cookies.should.equal "foo" => "bar", "quux" => "h&m"
420
+ req.cookies.should.equal "foo" => "bar", "quux" => "h&m"
421
+ req.env.delete("HTTP_COOKIE")
422
+ req.cookies.should.equal({})
423
+ end
424
+
425
+ should "always return the same hash object" do
426
+ req = Rack::Request.new \
427
+ Rack::MockRequest.env_for("", "HTTP_COOKIE" => "foo=bar;quux=h&m")
428
+ hash = req.cookies
429
+ req.env.delete("HTTP_COOKIE")
430
+ req.cookies.should.equal(hash)
431
+ req.env["HTTP_COOKIE"] = "zoo=m"
432
+ req.cookies.should.equal(hash)
433
+ end
434
+
435
+ should "modify the cookies hash in place" do
436
+ req = Rack::Request.new(Rack::MockRequest.env_for(""))
437
+ req.cookies.should.equal({})
438
+ req.cookies['foo'] = 'bar'
439
+ req.cookies.should.equal 'foo' => 'bar'
440
+ end
441
+
442
+ should "not modify the params hash in place" do
443
+ e = Rack::MockRequest.env_for("")
444
+ req1 = Rack::Request.new(e)
445
+ req1.params.should.equal({})
446
+ req1.params['foo'] = 'bar'
447
+ req1.params.should.equal 'foo' => 'bar'
448
+ req2 = Rack::Request.new(e)
449
+ req2.params.should.equal({})
450
+ end
451
+
452
+ should "modify params hash if param is in GET" do
453
+ e = Rack::MockRequest.env_for("?foo=duh")
454
+ req1 = Rack::Request.new(e)
455
+ req1.params.should.equal 'foo' => 'duh'
456
+ req1.update_param 'foo', 'bar'
457
+ req1.params.should.equal 'foo' => 'bar'
458
+ req2 = Rack::Request.new(e)
459
+ req2.params.should.equal 'foo' => 'bar'
460
+ end
461
+
462
+ should "modify params hash if param is in POST" do
463
+ e = Rack::MockRequest.env_for("", "REQUEST_METHOD" => 'POST', :input => 'foo=duh')
464
+ req1 = Rack::Request.new(e)
465
+ req1.params.should.equal 'foo' => 'duh'
466
+ req1.update_param 'foo', 'bar'
467
+ req1.params.should.equal 'foo' => 'bar'
468
+ req2 = Rack::Request.new(e)
469
+ req2.params.should.equal 'foo' => 'bar'
470
+ end
471
+
472
+ should "modify params hash, even if param didn't exist before" do
473
+ e = Rack::MockRequest.env_for("")
474
+ req1 = Rack::Request.new(e)
475
+ req1.params.should.equal({})
476
+ req1.update_param 'foo', 'bar'
477
+ req1.params.should.equal 'foo' => 'bar'
478
+ req2 = Rack::Request.new(e)
479
+ req2.params.should.equal 'foo' => 'bar'
480
+ end
481
+
482
+ should "modify params hash by changing only GET" do
483
+ e = Rack::MockRequest.env_for("?foo=duhget")
484
+ req = Rack::Request.new(e)
485
+ req.GET.should.equal 'foo' => 'duhget'
486
+ req.POST.should.equal({})
487
+ req.update_param 'foo', 'bar'
488
+ req.GET.should.equal 'foo' => 'bar'
489
+ req.POST.should.equal({})
490
+ end
491
+
492
+ should "modify params hash by changing only POST" do
493
+ e = Rack::MockRequest.env_for("", "REQUEST_METHOD" => 'POST', :input => "foo=duhpost")
494
+ req = Rack::Request.new(e)
495
+ req.GET.should.equal({})
496
+ req.POST.should.equal 'foo' => 'duhpost'
497
+ req.update_param 'foo', 'bar'
498
+ req.GET.should.equal({})
499
+ req.POST.should.equal 'foo' => 'bar'
500
+ end
501
+
502
+ should "modify params hash, even if param is defined in both POST and GET" do
503
+ e = Rack::MockRequest.env_for("?foo=duhget", "REQUEST_METHOD" => 'POST', :input => "foo=duhpost")
504
+ req1 = Rack::Request.new(e)
505
+ req1.GET.should.equal 'foo' => 'duhget'
506
+ req1.POST.should.equal 'foo' => 'duhpost'
507
+ req1.params.should.equal 'foo' => 'duhpost'
508
+ req1.update_param 'foo', 'bar'
509
+ req1.GET.should.equal 'foo' => 'bar'
510
+ req1.POST.should.equal 'foo' => 'bar'
511
+ req1.params.should.equal 'foo' => 'bar'
512
+ req2 = Rack::Request.new(e)
513
+ req2.GET.should.equal 'foo' => 'bar'
514
+ req2.POST.should.equal 'foo' => 'bar'
515
+ req2.params.should.equal 'foo' => 'bar'
516
+ req2.params.should.equal 'foo' => 'bar'
517
+ end
518
+
519
+ should "allow deleting from params hash if param is in GET" do
520
+ e = Rack::MockRequest.env_for("?foo=bar")
521
+ req1 = Rack::Request.new(e)
522
+ req1.params.should.equal 'foo' => 'bar'
523
+ req1.delete_param('foo').should.equal 'bar'
524
+ req1.params.should.equal({})
525
+ req2 = Rack::Request.new(e)
526
+ req2.params.should.equal({})
527
+ end
528
+
529
+ should "allow deleting from params hash if param is in POST" do
530
+ e = Rack::MockRequest.env_for("", "REQUEST_METHOD" => 'POST', :input => 'foo=bar')
531
+ req1 = Rack::Request.new(e)
532
+ req1.params.should.equal 'foo' => 'bar'
533
+ req1.delete_param('foo').should.equal 'bar'
534
+ req1.params.should.equal({})
535
+ req2 = Rack::Request.new(e)
536
+ req2.params.should.equal({})
537
+ end
538
+
539
+ should "pass through non-uri escaped cookies as-is" do
540
+ req = Rack::Request.new Rack::MockRequest.env_for("", "HTTP_COOKIE" => "foo=%")
541
+ req.cookies["foo"].should == "%"
542
+ end
543
+
544
+ should "parse cookies according to RFC 2109" do
545
+ req = Rack::Request.new \
546
+ Rack::MockRequest.env_for('', 'HTTP_COOKIE' => 'foo=bar;foo=car')
547
+ req.cookies.should.equal 'foo' => 'bar'
548
+ end
549
+
550
+ should 'parse cookies with quotes' do
551
+ req = Rack::Request.new Rack::MockRequest.env_for('', {
552
+ 'HTTP_COOKIE' => '$Version="1"; Customer="WILE_E_COYOTE"; $Path="/acme"; Part_Number="Rocket_Launcher_0001"; $Path="/acme"'
553
+ })
554
+ req.cookies.should.equal({
555
+ '$Version' => '"1"',
556
+ 'Customer' => '"WILE_E_COYOTE"',
557
+ '$Path' => '"/acme"',
558
+ 'Part_Number' => '"Rocket_Launcher_0001"',
559
+ })
560
+ end
561
+
562
+ should "provide setters" do
563
+ req = Rack::Request.new(e=Rack::MockRequest.env_for(""))
564
+ req.script_name.should.equal ""
565
+ req.script_name = "/foo"
566
+ req.script_name.should.equal "/foo"
567
+ e["SCRIPT_NAME"].should.equal "/foo"
568
+
569
+ req.path_info.should.equal "/"
570
+ req.path_info = "/foo"
571
+ req.path_info.should.equal "/foo"
572
+ e["PATH_INFO"].should.equal "/foo"
573
+ end
574
+
575
+ should "provide the original env" do
576
+ req = Rack::Request.new(e = Rack::MockRequest.env_for(""))
577
+ req.env.should == e
578
+ end
579
+
580
+ should "restore the base URL" do
581
+ Rack::Request.new(Rack::MockRequest.env_for("")).base_url.
582
+ should.equal "http://example.org"
583
+ Rack::Request.new(Rack::MockRequest.env_for("", "SCRIPT_NAME" => "/foo")).base_url.
584
+ should.equal "http://example.org"
585
+ end
586
+
587
+ should "restore the URL" do
588
+ Rack::Request.new(Rack::MockRequest.env_for("")).url.
589
+ should.equal "http://example.org/"
590
+ Rack::Request.new(Rack::MockRequest.env_for("", "SCRIPT_NAME" => "/foo")).url.
591
+ should.equal "http://example.org/foo/"
592
+ Rack::Request.new(Rack::MockRequest.env_for("/foo")).url.
593
+ should.equal "http://example.org/foo"
594
+ Rack::Request.new(Rack::MockRequest.env_for("?foo")).url.
595
+ should.equal "http://example.org/?foo"
596
+ Rack::Request.new(Rack::MockRequest.env_for("http://example.org:8080/")).url.
597
+ should.equal "http://example.org:8080/"
598
+ Rack::Request.new(Rack::MockRequest.env_for("https://example.org/")).url.
599
+ should.equal "https://example.org/"
600
+ Rack::Request.new(Rack::MockRequest.env_for("coffee://example.org/")).url.
601
+ should.equal "coffee://example.org/"
602
+ Rack::Request.new(Rack::MockRequest.env_for("coffee://example.org:443/")).url.
603
+ should.equal "coffee://example.org:443/"
604
+ Rack::Request.new(Rack::MockRequest.env_for("https://example.com:8080/foo?foo")).url.
605
+ should.equal "https://example.com:8080/foo?foo"
606
+ end
607
+
608
+ should "restore the full path" do
609
+ Rack::Request.new(Rack::MockRequest.env_for("")).fullpath.
610
+ should.equal "/"
611
+ Rack::Request.new(Rack::MockRequest.env_for("", "SCRIPT_NAME" => "/foo")).fullpath.
612
+ should.equal "/foo/"
613
+ Rack::Request.new(Rack::MockRequest.env_for("/foo")).fullpath.
614
+ should.equal "/foo"
615
+ Rack::Request.new(Rack::MockRequest.env_for("?foo")).fullpath.
616
+ should.equal "/?foo"
617
+ Rack::Request.new(Rack::MockRequest.env_for("http://example.org:8080/")).fullpath.
618
+ should.equal "/"
619
+ Rack::Request.new(Rack::MockRequest.env_for("https://example.org/")).fullpath.
620
+ should.equal "/"
621
+
622
+ Rack::Request.new(Rack::MockRequest.env_for("https://example.com:8080/foo?foo")).fullpath.
623
+ should.equal "/foo?foo"
624
+ end
625
+
626
+ should "handle multiple media type parameters" do
627
+ req = Rack::Request.new \
628
+ Rack::MockRequest.env_for("/",
629
+ "CONTENT_TYPE" => 'text/plain; foo=BAR,baz=bizzle dizzle;BLING=bam;blong="boo";zump="zoo\"o";weird=lol"')
630
+ req.should.not.be.form_data
631
+ req.media_type_params.should.include 'foo'
632
+ req.media_type_params['foo'].should.equal 'BAR'
633
+ req.media_type_params.should.include 'baz'
634
+ req.media_type_params['baz'].should.equal 'bizzle dizzle'
635
+ req.media_type_params.should.not.include 'BLING'
636
+ req.media_type_params.should.include 'bling'
637
+ req.media_type_params['bling'].should.equal 'bam'
638
+ req.media_type_params['blong'].should.equal 'boo'
639
+ req.media_type_params['zump'].should.equal 'zoo\"o'
640
+ req.media_type_params['weird'].should.equal 'lol"'
641
+ end
642
+
643
+ should "parse with junk before boundry" do
644
+ # Adapted from RFC 1867.
645
+ input = <<EOF
646
+ blah blah\r
647
+ \r
648
+ --AaB03x\r
649
+ content-disposition: form-data; name="reply"\r
650
+ \r
651
+ yes\r
652
+ --AaB03x\r
653
+ content-disposition: form-data; name="fileupload"; filename="dj.jpg"\r
654
+ Content-Type: image/jpeg\r
655
+ Content-Transfer-Encoding: base64\r
656
+ \r
657
+ /9j/4AAQSkZJRgABAQAAAQABAAD//gA+Q1JFQVRPUjogZ2QtanBlZyB2MS4wICh1c2luZyBJSkcg\r
658
+ --AaB03x--\r
659
+ EOF
660
+ req = Rack::Request.new Rack::MockRequest.env_for("/",
661
+ "CONTENT_TYPE" => "multipart/form-data, boundary=AaB03x",
662
+ "CONTENT_LENGTH" => input.size,
663
+ :input => input)
664
+
665
+ req.POST.should.include "fileupload"
666
+ req.POST.should.include "reply"
667
+
668
+ req.should.be.form_data
669
+ req.content_length.should.equal input.size
670
+ req.media_type.should.equal 'multipart/form-data'
671
+ req.media_type_params.should.include 'boundary'
672
+ req.media_type_params['boundary'].should.equal 'AaB03x'
673
+
674
+ req.POST["reply"].should.equal "yes"
675
+
676
+ f = req.POST["fileupload"]
677
+ f.should.be.kind_of Hash
678
+ f[:type].should.equal "image/jpeg"
679
+ f[:filename].should.equal "dj.jpg"
680
+ f.should.include :tempfile
681
+ f[:tempfile].size.should.equal 76
682
+ end
683
+
684
+ should "not infinite loop with a malformed HTTP request" do
685
+ # Adapted from RFC 1867.
686
+ input = <<EOF
687
+ --AaB03x
688
+ content-disposition: form-data; name="reply"
689
+
690
+ yes
691
+ --AaB03x
692
+ content-disposition: form-data; name="fileupload"; filename="dj.jpg"
693
+ Content-Type: image/jpeg
694
+ Content-Transfer-Encoding: base64
695
+
696
+ /9j/4AAQSkZJRgABAQAAAQABAAD//gA+Q1JFQVRPUjogZ2QtanBlZyB2MS4wICh1c2luZyBJSkcg
697
+ --AaB03x--
698
+ EOF
699
+ req = Rack::Request.new Rack::MockRequest.env_for("/",
700
+ "CONTENT_TYPE" => "multipart/form-data, boundary=AaB03x",
701
+ "CONTENT_LENGTH" => input.size,
702
+ :input => input)
703
+
704
+ lambda{req.POST}.should.raise(EOFError)
705
+ end
706
+
707
+
708
+ should "parse multipart form data" do
709
+ # Adapted from RFC 1867.
710
+ input = <<EOF
711
+ --AaB03x\r
712
+ content-disposition: form-data; name="reply"\r
713
+ \r
714
+ yes\r
715
+ --AaB03x\r
716
+ content-disposition: form-data; name="fileupload"; filename="dj.jpg"\r
717
+ Content-Type: image/jpeg\r
718
+ Content-Transfer-Encoding: base64\r
719
+ \r
720
+ /9j/4AAQSkZJRgABAQAAAQABAAD//gA+Q1JFQVRPUjogZ2QtanBlZyB2MS4wICh1c2luZyBJSkcg\r
721
+ --AaB03x--\r
722
+ EOF
723
+ req = Rack::Request.new Rack::MockRequest.env_for("/",
724
+ "CONTENT_TYPE" => "multipart/form-data, boundary=AaB03x",
725
+ "CONTENT_LENGTH" => input.size,
726
+ :input => input)
727
+
728
+ req.POST.should.include "fileupload"
729
+ req.POST.should.include "reply"
730
+
731
+ req.should.be.form_data
732
+ req.content_length.should.equal input.size
733
+ req.media_type.should.equal 'multipart/form-data'
734
+ req.media_type_params.should.include 'boundary'
735
+ req.media_type_params['boundary'].should.equal 'AaB03x'
736
+
737
+ req.POST["reply"].should.equal "yes"
738
+
739
+ f = req.POST["fileupload"]
740
+ f.should.be.kind_of Hash
741
+ f[:type].should.equal "image/jpeg"
742
+ f[:filename].should.equal "dj.jpg"
743
+ f.should.include :tempfile
744
+ f[:tempfile].size.should.equal 76
745
+ end
746
+
747
+ should "parse big multipart form data" do
748
+ input = <<EOF
749
+ --AaB03x\r
750
+ content-disposition: form-data; name="huge"; filename="huge"\r
751
+ \r
752
+ #{"x"*32768}\r
753
+ --AaB03x\r
754
+ content-disposition: form-data; name="mean"; filename="mean"\r
755
+ \r
756
+ --AaB03xha\r
757
+ --AaB03x--\r
758
+ EOF
759
+ req = Rack::Request.new Rack::MockRequest.env_for("/",
760
+ "CONTENT_TYPE" => "multipart/form-data, boundary=AaB03x",
761
+ "CONTENT_LENGTH" => input.size,
762
+ :input => input)
763
+
764
+ req.POST["huge"][:tempfile].size.should.equal 32768
765
+ req.POST["mean"][:tempfile].size.should.equal 10
766
+ req.POST["mean"][:tempfile].read.should.equal "--AaB03xha"
767
+ end
768
+
769
+ should "record tempfiles from multipart form data in env[rack.tempfiles]" do
770
+ input = <<EOF
771
+ --AaB03x\r
772
+ content-disposition: form-data; name="fileupload"; filename="foo.jpg"\r
773
+ Content-Type: image/jpeg\r
774
+ Content-Transfer-Encoding: base64\r
775
+ \r
776
+ /9j/4AAQSkZJRgABAQAAAQABAAD//gA+Q1JFQVRPUjogZ2QtanBlZyB2MS4wICh1c2luZyBJSkcg\r
777
+ --AaB03x\r
778
+ content-disposition: form-data; name="fileupload"; filename="bar.jpg"\r
779
+ Content-Type: image/jpeg\r
780
+ Content-Transfer-Encoding: base64\r
781
+ \r
782
+ /9j/4AAQSkZJRgABAQAAAQABAAD//gA+Q1JFQVRPUjogZ2QtanBlZyB2MS4wICh1c2luZyBJSkcg\r
783
+ --AaB03x--\r
784
+ EOF
785
+ env = Rack::MockRequest.env_for("/",
786
+ "CONTENT_TYPE" => "multipart/form-data, boundary=AaB03x",
787
+ "CONTENT_LENGTH" => input.size,
788
+ :input => input)
789
+ req = Rack::Request.new(env)
790
+ req.params
791
+ env['rack.tempfiles'].size.should.equal(2)
792
+ end
793
+
794
+ should "detect invalid multipart form data" do
795
+ input = <<EOF
796
+ --AaB03x\r
797
+ content-disposition: form-data; name="huge"; filename="huge"\r
798
+ EOF
799
+ req = Rack::Request.new Rack::MockRequest.env_for("/",
800
+ "CONTENT_TYPE" => "multipart/form-data, boundary=AaB03x",
801
+ "CONTENT_LENGTH" => input.size,
802
+ :input => input)
803
+
804
+ lambda { req.POST }.should.raise(EOFError)
805
+
806
+ input = <<EOF
807
+ --AaB03x\r
808
+ content-disposition: form-data; name="huge"; filename="huge"\r
809
+ \r
810
+ foo\r
811
+ EOF
812
+ req = Rack::Request.new Rack::MockRequest.env_for("/",
813
+ "CONTENT_TYPE" => "multipart/form-data, boundary=AaB03x",
814
+ "CONTENT_LENGTH" => input.size,
815
+ :input => input)
816
+
817
+ lambda { req.POST }.should.raise(EOFError)
818
+
819
+ input = <<EOF
820
+ --AaB03x\r
821
+ content-disposition: form-data; name="huge"; filename="huge"\r
822
+ \r
823
+ foo\r
824
+ EOF
825
+ req = Rack::Request.new Rack::MockRequest.env_for("/",
826
+ "CONTENT_TYPE" => "multipart/form-data, boundary=AaB03x",
827
+ "CONTENT_LENGTH" => input.size,
828
+ :input => input)
829
+
830
+ lambda { req.POST }.should.raise(EOFError)
831
+ end
832
+
833
+ should "consistently raise EOFError on bad multipart form data" do
834
+ input = <<EOF
835
+ --AaB03x\r
836
+ content-disposition: form-data; name="huge"; filename="huge"\r
837
+ EOF
838
+ req = Rack::Request.new Rack::MockRequest.env_for("/",
839
+ "CONTENT_TYPE" => "multipart/form-data, boundary=AaB03x",
840
+ "CONTENT_LENGTH" => input.size,
841
+ :input => input)
842
+
843
+ lambda { req.POST }.should.raise(EOFError)
844
+ lambda { req.POST }.should.raise(EOFError)
845
+ end
846
+
847
+ should "correctly parse the part name from Content-Id header" do
848
+ input = <<EOF
849
+ --AaB03x\r
850
+ Content-Type: text/xml; charset=utf-8\r
851
+ Content-Id: <soap-start>\r
852
+ Content-Transfer-Encoding: 7bit\r
853
+ \r
854
+ foo\r
855
+ --AaB03x--\r
856
+ EOF
857
+ req = Rack::Request.new Rack::MockRequest.env_for("/",
858
+ "CONTENT_TYPE" => "multipart/related, boundary=AaB03x",
859
+ "CONTENT_LENGTH" => input.size,
860
+ :input => input)
861
+
862
+ req.params.keys.should.equal ["<soap-start>"]
863
+ end
864
+
865
+ should "not try to interpret binary as utf8" do
866
+ if /regexp/.respond_to?(:kcode) # < 1.9
867
+ begin
868
+ original_kcode = $KCODE
869
+ $KCODE='UTF8'
870
+
871
+ input = <<EOF
872
+ --AaB03x\r
873
+ content-disposition: form-data; name="fileupload"; filename="junk.a"\r
874
+ content-type: application/octet-stream\r
875
+ \r
876
+ #{[0x36,0xCF,0x0A,0xF8].pack('c*')}\r
877
+ --AaB03x--\r
878
+ EOF
879
+
880
+ req = Rack::Request.new Rack::MockRequest.env_for("/",
881
+ "CONTENT_TYPE" => "multipart/form-data, boundary=AaB03x",
882
+ "CONTENT_LENGTH" => input.size,
883
+ :input => input)
884
+
885
+ lambda{req.POST}.should.not.raise(EOFError)
886
+ req.POST["fileupload"][:tempfile].size.should.equal 4
887
+ ensure
888
+ $KCODE = original_kcode
889
+ end
890
+ else # >= 1.9
891
+ input = <<EOF
892
+ --AaB03x\r
893
+ content-disposition: form-data; name="fileupload"; filename="junk.a"\r
894
+ content-type: application/octet-stream\r
895
+ \r
896
+ #{[0x36,0xCF,0x0A,0xF8].pack('c*')}\r
897
+ --AaB03x--\r
898
+ EOF
899
+
900
+ req = Rack::Request.new Rack::MockRequest.env_for("/",
901
+ "CONTENT_TYPE" => "multipart/form-data, boundary=AaB03x",
902
+ "CONTENT_LENGTH" => input.size,
903
+ :input => input)
904
+
905
+ lambda{req.POST}.should.not.raise(EOFError)
906
+ req.POST["fileupload"][:tempfile].size.should.equal 4
907
+ end
908
+ end
909
+
910
+ should "work around buggy 1.8.* Tempfile equality" do
911
+ input = <<EOF
912
+ --AaB03x\r
913
+ content-disposition: form-data; name="huge"; filename="huge"\r
914
+ \r
915
+ foo\r
916
+ --AaB03x--
917
+ EOF
918
+
919
+ rack_input = Tempfile.new("rackspec")
920
+ rack_input.write(input)
921
+ rack_input.rewind
922
+
923
+ req = Rack::Request.new Rack::MockRequest.env_for("/",
924
+ "CONTENT_TYPE" => "multipart/form-data, boundary=AaB03x",
925
+ "CONTENT_LENGTH" => input.size,
926
+ :input => rack_input)
927
+
928
+ lambda{ req.POST }.should.not.raise
929
+ lambda{ req.POST }.should.not.raise("input re-processed!")
930
+ end
931
+
932
+ should "use form_hash when form_input is a Tempfile" do
933
+ input = "{foo: 'bar'}"
934
+
935
+ rack_input = Tempfile.new("rackspec")
936
+ rack_input.write(input)
937
+ rack_input.rewind
938
+
939
+ req = Rack::Request.new Rack::MockRequest.env_for("/",
940
+ "rack.request.form_hash" => {'foo' => 'bar'},
941
+ "rack.request.form_input" => rack_input,
942
+ :input => rack_input)
943
+
944
+ req.POST.should.equal(req.env['rack.request.form_hash'])
945
+ end
946
+
947
+ should "conform to the Rack spec" do
948
+ app = lambda { |env|
949
+ content = Rack::Request.new(env).POST["file"].inspect
950
+ size = content.respond_to?(:bytesize) ? content.bytesize : content.size
951
+ [200, {"Content-Type" => "text/html", "Content-Length" => size.to_s}, [content]]
952
+ }
953
+
954
+ input = <<EOF
955
+ --AaB03x\r
956
+ content-disposition: form-data; name="reply"\r
957
+ \r
958
+ yes\r
959
+ --AaB03x\r
960
+ content-disposition: form-data; name="fileupload"; filename="dj.jpg"\r
961
+ Content-Type: image/jpeg\r
962
+ Content-Transfer-Encoding: base64\r
963
+ \r
964
+ /9j/4AAQSkZJRgABAQAAAQABAAD//gA+Q1JFQVRPUjogZ2QtanBlZyB2MS4wICh1c2luZyBJSkcg\r
965
+ --AaB03x--\r
966
+ EOF
967
+ input.force_encoding("ASCII-8BIT") if input.respond_to? :force_encoding
968
+ res = Rack::MockRequest.new(Rack::Lint.new(app)).get "/",
969
+ "CONTENT_TYPE" => "multipart/form-data, boundary=AaB03x",
970
+ "CONTENT_LENGTH" => input.size.to_s, "rack.input" => StringIO.new(input)
971
+
972
+ res.should.be.ok
973
+ end
974
+
975
+ should "parse Accept-Encoding correctly" do
976
+ parser = lambda do |x|
977
+ Rack::Request.new(Rack::MockRequest.env_for("", "HTTP_ACCEPT_ENCODING" => x)).accept_encoding
978
+ end
979
+
980
+ parser.call(nil).should.equal([])
981
+
982
+ parser.call("compress, gzip").should.equal([["compress", 1.0], ["gzip", 1.0]])
983
+ parser.call("").should.equal([])
984
+ parser.call("*").should.equal([["*", 1.0]])
985
+ parser.call("compress;q=0.5, gzip;q=1.0").should.equal([["compress", 0.5], ["gzip", 1.0]])
986
+ parser.call("gzip;q=1.0, identity; q=0.5, *;q=0").should.equal([["gzip", 1.0], ["identity", 0.5], ["*", 0] ])
987
+
988
+ parser.call("gzip ; q=0.9").should.equal([["gzip", 0.9]])
989
+ parser.call("gzip ; deflate").should.equal([["gzip", 1.0]])
990
+ end
991
+
992
+ should "parse Accept-Language correctly" do
993
+ parser = lambda do |x|
994
+ Rack::Request.new(Rack::MockRequest.env_for("", "HTTP_ACCEPT_LANGUAGE" => x)).accept_language
995
+ end
996
+
997
+ parser.call(nil).should.equal([])
998
+
999
+ parser.call("fr, en").should.equal([["fr", 1.0], ["en", 1.0]])
1000
+ parser.call("").should.equal([])
1001
+ parser.call("*").should.equal([["*", 1.0]])
1002
+ parser.call("fr;q=0.5, en;q=1.0").should.equal([["fr", 0.5], ["en", 1.0]])
1003
+ parser.call("fr;q=1.0, en; q=0.5, *;q=0").should.equal([["fr", 1.0], ["en", 0.5], ["*", 0] ])
1004
+
1005
+ parser.call("fr ; q=0.9").should.equal([["fr", 0.9]])
1006
+ parser.call("fr").should.equal([["fr", 1.0]])
1007
+ end
1008
+
1009
+ ip_app = lambda { |env|
1010
+ request = Rack::Request.new(env)
1011
+ response = Rack::Response.new
1012
+ response.write request.ip
1013
+ response.finish
1014
+ }
1015
+
1016
+ should 'provide ip information' do
1017
+ mock = Rack::MockRequest.new(Rack::Lint.new(ip_app))
1018
+
1019
+ res = mock.get '/', 'REMOTE_ADDR' => '1.2.3.4'
1020
+ res.body.should.equal '1.2.3.4'
1021
+
1022
+ res = mock.get '/', 'REMOTE_ADDR' => 'fe80::202:b3ff:fe1e:8329'
1023
+ res.body.should.equal 'fe80::202:b3ff:fe1e:8329'
1024
+
1025
+ res = mock.get '/', 'REMOTE_ADDR' => '1.2.3.4,3.4.5.6'
1026
+ res.body.should.equal '1.2.3.4'
1027
+ end
1028
+
1029
+ should 'deals with proxies' do
1030
+ mock = Rack::MockRequest.new(Rack::Lint.new(ip_app))
1031
+
1032
+ res = mock.get '/',
1033
+ 'REMOTE_ADDR' => '1.2.3.4',
1034
+ 'HTTP_X_FORWARDED_FOR' => '3.4.5.6'
1035
+ res.body.should.equal '1.2.3.4'
1036
+
1037
+ res = mock.get '/',
1038
+ 'REMOTE_ADDR' => '1.2.3.4',
1039
+ 'HTTP_X_FORWARDED_FOR' => 'unknown'
1040
+ res.body.should.equal '1.2.3.4'
1041
+
1042
+ res = mock.get '/',
1043
+ 'REMOTE_ADDR' => '127.0.0.1',
1044
+ 'HTTP_X_FORWARDED_FOR' => '3.4.5.6'
1045
+ res.body.should.equal '3.4.5.6'
1046
+
1047
+ res = mock.get '/', 'HTTP_X_FORWARDED_FOR' => 'unknown,3.4.5.6'
1048
+ res.body.should.equal '3.4.5.6'
1049
+
1050
+ res = mock.get '/', 'HTTP_X_FORWARDED_FOR' => '192.168.0.1,3.4.5.6'
1051
+ res.body.should.equal '3.4.5.6'
1052
+
1053
+ res = mock.get '/', 'HTTP_X_FORWARDED_FOR' => '10.0.0.1,3.4.5.6'
1054
+ res.body.should.equal '3.4.5.6'
1055
+
1056
+ res = mock.get '/', 'HTTP_X_FORWARDED_FOR' => '10.0.0.1, 10.0.0.1, 3.4.5.6'
1057
+ res.body.should.equal '3.4.5.6'
1058
+
1059
+ res = mock.get '/', 'HTTP_X_FORWARDED_FOR' => '127.0.0.1, 3.4.5.6'
1060
+ res.body.should.equal '3.4.5.6'
1061
+
1062
+ res = mock.get '/', 'HTTP_X_FORWARDED_FOR' => 'unknown,192.168.0.1'
1063
+ res.body.should.equal 'unknown'
1064
+
1065
+ res = mock.get '/', 'HTTP_X_FORWARDED_FOR' => 'other,unknown,192.168.0.1'
1066
+ res.body.should.equal 'unknown'
1067
+
1068
+ res = mock.get '/', 'HTTP_X_FORWARDED_FOR' => 'unknown,localhost,192.168.0.1'
1069
+ res.body.should.equal 'unknown'
1070
+
1071
+ res = mock.get '/', 'HTTP_X_FORWARDED_FOR' => '9.9.9.9, 3.4.5.6, 10.0.0.1, 172.31.4.4'
1072
+ res.body.should.equal '3.4.5.6'
1073
+
1074
+ res = mock.get '/', 'HTTP_X_FORWARDED_FOR' => '::1,2620:0:1c00:0:812c:9583:754b:ca11'
1075
+ res.body.should.equal '2620:0:1c00:0:812c:9583:754b:ca11'
1076
+
1077
+ res = mock.get '/', 'HTTP_X_FORWARDED_FOR' => '2620:0:1c00:0:812c:9583:754b:ca11,::1'
1078
+ res.body.should.equal '2620:0:1c00:0:812c:9583:754b:ca11'
1079
+
1080
+ res = mock.get '/', 'HTTP_X_FORWARDED_FOR' => 'fd5b:982e:9130:247f:0000:0000:0000:0000,2620:0:1c00:0:812c:9583:754b:ca11'
1081
+ res.body.should.equal '2620:0:1c00:0:812c:9583:754b:ca11'
1082
+
1083
+ res = mock.get '/', 'HTTP_X_FORWARDED_FOR' => '2620:0:1c00:0:812c:9583:754b:ca11,fd5b:982e:9130:247f:0000:0000:0000:0000'
1084
+ res.body.should.equal '2620:0:1c00:0:812c:9583:754b:ca11'
1085
+
1086
+ res = mock.get '/',
1087
+ 'HTTP_X_FORWARDED_FOR' => '1.1.1.1, 127.0.0.1',
1088
+ 'HTTP_CLIENT_IP' => '1.1.1.1'
1089
+ res.body.should.equal '1.1.1.1'
1090
+
1091
+ res = mock.get '/', 'HTTP_X_FORWARDED_FOR' => '8.8.8.8, 9.9.9.9'
1092
+ res.body.should.equal '9.9.9.9'
1093
+
1094
+ res = mock.get '/', 'HTTP_X_FORWARDED_FOR' => '8.8.8.8, fe80::202:b3ff:fe1e:8329'
1095
+ res.body.should.equal 'fe80::202:b3ff:fe1e:8329'
1096
+
1097
+ # Unix Sockets
1098
+ res = mock.get '/',
1099
+ 'REMOTE_ADDR' => 'unix',
1100
+ 'HTTP_X_FORWARDED_FOR' => '3.4.5.6'
1101
+ res.body.should.equal '3.4.5.6'
1102
+
1103
+ res = mock.get '/',
1104
+ 'REMOTE_ADDR' => 'unix:/tmp/foo',
1105
+ 'HTTP_X_FORWARDED_FOR' => '3.4.5.6'
1106
+ res.body.should.equal '3.4.5.6'
1107
+ end
1108
+
1109
+ should "not allow IP spoofing via Client-IP and X-Forwarded-For headers" do
1110
+ mock = Rack::MockRequest.new(Rack::Lint.new(ip_app))
1111
+
1112
+ # IP Spoofing attempt:
1113
+ # Client sends X-Forwarded-For: 6.6.6.6
1114
+ # Client-IP: 6.6.6.6
1115
+ # Load balancer adds X-Forwarded-For: 2.2.2.3, 192.168.0.7
1116
+ # App receives: X-Forwarded-For: 6.6.6.6
1117
+ # X-Forwarded-For: 2.2.2.3, 192.168.0.7
1118
+ # Client-IP: 6.6.6.6
1119
+ # Rack env: HTTP_X_FORWARDED_FOR: '6.6.6.6, 2.2.2.3, 192.168.0.7'
1120
+ # HTTP_CLIENT_IP: '6.6.6.6'
1121
+ res = mock.get '/',
1122
+ 'HTTP_X_FORWARDED_FOR' => '6.6.6.6, 2.2.2.3, 192.168.0.7',
1123
+ 'HTTP_CLIENT_IP' => '6.6.6.6'
1124
+ res.body.should.equal '2.2.2.3'
1125
+ end
1126
+
1127
+ should "regard local addresses as proxies" do
1128
+ req = Rack::Request.new(Rack::MockRequest.env_for("/"))
1129
+ req.trusted_proxy?('127.0.0.1').should.equal 0
1130
+ req.trusted_proxy?('10.0.0.1').should.equal 0
1131
+ req.trusted_proxy?('172.16.0.1').should.equal 0
1132
+ req.trusted_proxy?('172.20.0.1').should.equal 0
1133
+ req.trusted_proxy?('172.30.0.1').should.equal 0
1134
+ req.trusted_proxy?('172.31.0.1').should.equal 0
1135
+ req.trusted_proxy?('192.168.0.1').should.equal 0
1136
+ req.trusted_proxy?('::1').should.equal 0
1137
+ req.trusted_proxy?('fd00::').should.equal 0
1138
+ req.trusted_proxy?('localhost').should.equal 0
1139
+ req.trusted_proxy?('unix').should.equal 0
1140
+ req.trusted_proxy?('unix:/tmp/sock').should.equal 0
1141
+
1142
+ req.trusted_proxy?("unix.example.org").should.equal nil
1143
+ req.trusted_proxy?("example.org\n127.0.0.1").should.equal nil
1144
+ req.trusted_proxy?("127.0.0.1\nexample.org").should.equal nil
1145
+ req.trusted_proxy?("11.0.0.1").should.equal nil
1146
+ req.trusted_proxy?("172.15.0.1").should.equal nil
1147
+ req.trusted_proxy?("172.32.0.1").should.equal nil
1148
+ req.trusted_proxy?("2001:470:1f0b:18f8::1").should.equal nil
1149
+ end
1150
+
1151
+ class MyRequest < Rack::Request
1152
+ def params
1153
+ {:foo => "bar"}
1154
+ end
1155
+ end
1156
+
1157
+ should "allow subclass request to be instantiated after parent request" do
1158
+ env = Rack::MockRequest.env_for("/?foo=bar")
1159
+
1160
+ req1 = Rack::Request.new(env)
1161
+ req1.GET.should.equal "foo" => "bar"
1162
+ req1.params.should.equal "foo" => "bar"
1163
+
1164
+ req2 = MyRequest.new(env)
1165
+ req2.GET.should.equal "foo" => "bar"
1166
+ req2.params.should.equal :foo => "bar"
1167
+ end
1168
+
1169
+ should "allow parent request to be instantiated after subclass request" do
1170
+ env = Rack::MockRequest.env_for("/?foo=bar")
1171
+
1172
+ req1 = MyRequest.new(env)
1173
+ req1.GET.should.equal "foo" => "bar"
1174
+ req1.params.should.equal :foo => "bar"
1175
+
1176
+ req2 = Rack::Request.new(env)
1177
+ req2.GET.should.equal "foo" => "bar"
1178
+ req2.params.should.equal "foo" => "bar"
1179
+ end
1180
+
1181
+ should "raise TypeError every time if request parameters are broken" do
1182
+ broken_query = Rack::MockRequest.env_for("/?foo%5B%5D=0&foo%5Bbar%5D=1")
1183
+ req = Rack::Request.new(broken_query)
1184
+ lambda{req.GET}.should.raise(TypeError)
1185
+ lambda{req.params}.should.raise(TypeError)
1186
+ end
1187
+
1188
+ (0x20...0x7E).collect { |a|
1189
+ b = a.chr
1190
+ c = CGI.escape(b)
1191
+ should "not strip '#{a}' => '#{c}' => '#{b}' escaped character from parameters when accessed as string" do
1192
+ url = "/?foo=#{c}bar#{c}"
1193
+ env = Rack::MockRequest.env_for(url)
1194
+ req2 = Rack::Request.new(env)
1195
+ req2.GET.should.equal "foo" => "#{b}bar#{b}"
1196
+ req2.params.should.equal "foo" => "#{b}bar#{b}"
1197
+ end
1198
+ }
1199
+ end