lack 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
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