rack 0.1.0

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

Potentially problematic release.


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

Files changed (54) hide show
  1. data/AUTHORS +3 -0
  2. data/COPYING +18 -0
  3. data/KNOWN-ISSUES +18 -0
  4. data/RDOX +144 -0
  5. data/README +154 -0
  6. data/Rakefile +174 -0
  7. data/SPEC +132 -0
  8. data/bin/rackup +148 -0
  9. data/contrib/rack_logo.svg +111 -0
  10. data/example/lobster.ru +4 -0
  11. data/lib/rack.rb +67 -0
  12. data/lib/rack/adapter/camping.rb +16 -0
  13. data/lib/rack/adapter/rails.rb +65 -0
  14. data/lib/rack/builder.rb +52 -0
  15. data/lib/rack/cascade.rb +26 -0
  16. data/lib/rack/commonlogger.rb +56 -0
  17. data/lib/rack/file.rb +108 -0
  18. data/lib/rack/handler/cgi.rb +57 -0
  19. data/lib/rack/handler/fastcgi.rb +81 -0
  20. data/lib/rack/handler/mongrel.rb +57 -0
  21. data/lib/rack/handler/webrick.rb +56 -0
  22. data/lib/rack/lint.rb +394 -0
  23. data/lib/rack/lobster.rb +65 -0
  24. data/lib/rack/mock.rb +183 -0
  25. data/lib/rack/recursive.rb +57 -0
  26. data/lib/rack/reloader.rb +64 -0
  27. data/lib/rack/request.rb +112 -0
  28. data/lib/rack/response.rb +114 -0
  29. data/lib/rack/showexceptions.rb +344 -0
  30. data/lib/rack/urlmap.rb +50 -0
  31. data/lib/rack/utils.rb +176 -0
  32. data/test/cgi/lighttpd.conf +20 -0
  33. data/test/cgi/test +9 -0
  34. data/test/cgi/test.fcgi +9 -0
  35. data/test/cgi/test.ru +7 -0
  36. data/test/spec_rack_camping.rb +44 -0
  37. data/test/spec_rack_cascade.rb +35 -0
  38. data/test/spec_rack_cgi.rb +82 -0
  39. data/test/spec_rack_commonlogger.rb +32 -0
  40. data/test/spec_rack_fastcgi.rb +82 -0
  41. data/test/spec_rack_file.rb +32 -0
  42. data/test/spec_rack_lint.rb +317 -0
  43. data/test/spec_rack_lobster.rb +45 -0
  44. data/test/spec_rack_mock.rb +150 -0
  45. data/test/spec_rack_mongrel.rb +87 -0
  46. data/test/spec_rack_recursive.rb +77 -0
  47. data/test/spec_rack_request.rb +219 -0
  48. data/test/spec_rack_response.rb +110 -0
  49. data/test/spec_rack_showexceptions.rb +21 -0
  50. data/test/spec_rack_urlmap.rb +140 -0
  51. data/test/spec_rack_utils.rb +57 -0
  52. data/test/spec_rack_webrick.rb +89 -0
  53. data/test/testrequest.rb +43 -0
  54. metadata +117 -0
@@ -0,0 +1,82 @@
1
+ require 'test/spec'
2
+ require 'testrequest'
3
+
4
+ pid = fork {
5
+ exec "cd #{File.join(File.dirname(__FILE__), 'cgi')} && lighttpd -D -f lighttpd.conf"
6
+ }
7
+
8
+ at_exit {
9
+ Process.kill 15, pid
10
+ }
11
+
12
+ context "Rack::Handler::FastCGI" do
13
+ include TestRequest::Helpers
14
+
15
+ setup do
16
+ @host = '0.0.0.0'
17
+ @port = 9203
18
+ end
19
+
20
+ specify "should respond" do
21
+ lambda {
22
+ GET("/test.fcgi")
23
+ }.should.not.raise
24
+ end
25
+
26
+ specify "should be a lighttpd" do
27
+ GET("/test.fcgi")
28
+ status.should.be 200
29
+ response["SERVER_SOFTWARE"].should =~ /lighttpd/
30
+ response["HTTP_VERSION"].should.equal "HTTP/1.1"
31
+ response["SERVER_PROTOCOL"].should.equal "HTTP/1.1"
32
+ response["SERVER_PORT"].should.equal "9203"
33
+ response["SERVER_NAME"].should =~ "0.0.0.0"
34
+ end
35
+
36
+ specify "should have rack headers" do
37
+ GET("/test.fcgi")
38
+ response["rack.version"].should.equal [0,1]
39
+ response["rack.multithread"].should.be false
40
+ response["rack.multiprocess"].should.be true
41
+ response["rack.run_once"].should.be false
42
+ end
43
+
44
+ specify "should have CGI headers on GET" do
45
+ GET("/test.fcgi")
46
+ response["REQUEST_METHOD"].should.equal "GET"
47
+ response["SCRIPT_NAME"].should.equal "/test.fcgi"
48
+ response["REQUEST_PATH"].should.equal "/"
49
+ response["PATH_INFO"].should.be.nil
50
+ response["QUERY_STRING"].should.equal ""
51
+ response["test.postdata"].should.equal ""
52
+
53
+ GET("/test.fcgi/foo?quux=1")
54
+ response["REQUEST_METHOD"].should.equal "GET"
55
+ response["SCRIPT_NAME"].should.equal "/test.fcgi"
56
+ response["REQUEST_PATH"].should.equal "/"
57
+ response["PATH_INFO"].should.equal "/foo"
58
+ response["QUERY_STRING"].should.equal "quux=1"
59
+ end
60
+
61
+ specify "should have CGI headers on POST" do
62
+ POST("/test.fcgi", {"rack-form-data" => "23"}, {'X-test-header' => '42'})
63
+ status.should.equal 200
64
+ response["REQUEST_METHOD"].should.equal "POST"
65
+ response["SCRIPT_NAME"].should.equal "/test.fcgi"
66
+ response["REQUEST_PATH"].should.equal "/"
67
+ response["QUERY_STRING"].should.equal ""
68
+ response["HTTP_X_TEST_HEADER"].should.equal "42"
69
+ response["test.postdata"].should.equal "rack-form-data=23"
70
+ end
71
+
72
+ specify "should support HTTP auth" do
73
+ GET("/test.fcgi", {:user => "ruth", :passwd => "secret"})
74
+ response["HTTP_AUTHORIZATION"].should.equal "Basic cnV0aDpzZWNyZXQ="
75
+ end
76
+
77
+ specify "should set status" do
78
+ GET("/test.fcgi?secret")
79
+ status.should.equal 403
80
+ response["rack.url_scheme"].should.equal "http"
81
+ end
82
+ end
@@ -0,0 +1,32 @@
1
+ require 'test/spec'
2
+
3
+ require 'rack/file'
4
+ require 'rack/lint'
5
+
6
+ require 'rack/mock'
7
+
8
+ context "Rack::File" do
9
+ DOCROOT = File.expand_path(File.dirname(__FILE__))
10
+
11
+ specify "serves files" do
12
+ res = Rack::MockRequest.new(Rack::Lint.new(Rack::File.new(DOCROOT))).
13
+ get("/cgi/test")
14
+
15
+ res.should.be.ok
16
+ res.should =~ /ruby/
17
+ end
18
+
19
+ specify "does not allow directory traversal" do
20
+ res = Rack::MockRequest.new(Rack::Lint.new(Rack::File.new(DOCROOT))).
21
+ get("/cgi/../test")
22
+
23
+ res.should.be.forbidden
24
+ end
25
+
26
+ specify "404s if it can't find the file" do
27
+ res = Rack::MockRequest.new(Rack::Lint.new(Rack::File.new(DOCROOT))).
28
+ get("/cgi/blubb")
29
+
30
+ res.should.be.not_found
31
+ end
32
+ end
@@ -0,0 +1,317 @@
1
+ require 'test/spec'
2
+ require 'stringio'
3
+
4
+ require 'rack/lint'
5
+ require 'rack/mock'
6
+
7
+ context "Rack::Lint" do
8
+ def env(*args)
9
+ Rack::MockRequest.env_for("/", *args)
10
+ end
11
+
12
+ specify "passes valid request" do
13
+ lambda {
14
+ Rack::Lint.new(lambda { |env|
15
+ [200, {"Content-type" => "test/plain"}, "foo"]
16
+ }).call(env({}))
17
+ }.should.not.raise
18
+ end
19
+
20
+ specify "notices fatal errors" do
21
+ lambda { Rack::Lint.new(nil).call }.should.raise(Rack::Lint::LintError).
22
+ message.should.match(/No env given/)
23
+ end
24
+
25
+ specify "notices environment errors" do
26
+ lambda { Rack::Lint.new(nil).call 5 }.should.raise(Rack::Lint::LintError).
27
+ message.should.match(/not a Hash/)
28
+
29
+ lambda {
30
+ e = env
31
+ e.delete("REQUEST_METHOD")
32
+ Rack::Lint.new(nil).call(e)
33
+ }.should.raise(Rack::Lint::LintError).
34
+ message.should.match(/missing required key REQUEST_METHOD/)
35
+
36
+ lambda {
37
+ e = env
38
+ e.delete("SERVER_NAME")
39
+ Rack::Lint.new(nil).call(e)
40
+ }.should.raise(Rack::Lint::LintError).
41
+ message.should.match(/missing required key SERVER_NAME/)
42
+
43
+
44
+ lambda {
45
+ Rack::Lint.new(nil).call(env("HTTP_CONTENT_TYPE" => "text/plain"))
46
+ }.should.raise(Rack::Lint::LintError).
47
+ message.should.match(/contains HTTP_CONTENT_TYPE/)
48
+
49
+ lambda {
50
+ Rack::Lint.new(nil).call(env("HTTP_CONTENT_LENGTH" => "42"))
51
+ }.should.raise(Rack::Lint::LintError).
52
+ message.should.match(/contains HTTP_CONTENT_LENGTH/)
53
+
54
+ lambda {
55
+ Rack::Lint.new(nil).call(env("FOO" => Object.new))
56
+ }.should.raise(Rack::Lint::LintError).
57
+ message.should.match(/non-string value/)
58
+
59
+ lambda {
60
+ Rack::Lint.new(nil).call(env("rack.version" => "0.2"))
61
+ }.should.raise(Rack::Lint::LintError).
62
+ message.should.match(/must be an Array/)
63
+
64
+ lambda {
65
+ Rack::Lint.new(nil).call(env("rack.url_scheme" => "gopher"))
66
+ }.should.raise(Rack::Lint::LintError).
67
+ message.should.match(/url_scheme unknown/)
68
+
69
+ lambda {
70
+ Rack::Lint.new(nil).call(env("REQUEST_METHOD" => "FUCKUP"))
71
+ }.should.raise(Rack::Lint::LintError).
72
+ message.should.match(/REQUEST_METHOD unknown/)
73
+
74
+ lambda {
75
+ Rack::Lint.new(nil).call(env("SCRIPT_NAME" => "howdy"))
76
+ }.should.raise(Rack::Lint::LintError).
77
+ message.should.match(/must start with/)
78
+
79
+ lambda {
80
+ Rack::Lint.new(nil).call(env("PATH_INFO" => "../foo"))
81
+ }.should.raise(Rack::Lint::LintError).
82
+ message.should.match(/must start with/)
83
+
84
+ lambda {
85
+ Rack::Lint.new(nil).call(env("CONTENT_LENGTH" => "xcii"))
86
+ }.should.raise(Rack::Lint::LintError).
87
+ message.should.match(/Invalid CONTENT_LENGTH/)
88
+
89
+ lambda {
90
+ e = env
91
+ e.delete("PATH_INFO")
92
+ e.delete("SCRIPT_NAME")
93
+ Rack::Lint.new(nil).call(e)
94
+ }.should.raise(Rack::Lint::LintError).
95
+ message.should.match(/One of .* must be set/)
96
+
97
+ lambda {
98
+ Rack::Lint.new(nil).call(env("SCRIPT_NAME" => "/"))
99
+ }.should.raise(Rack::Lint::LintError).
100
+ message.should.match(/cannot be .* make it ''/)
101
+ end
102
+
103
+ specify "notices input errors" do
104
+ lambda {
105
+ Rack::Lint.new(nil).call(env("rack.input" => ""))
106
+ }.should.raise(Rack::Lint::LintError).
107
+ message.should.match(/does not respond to #gets/)
108
+ end
109
+
110
+ specify "notices error errors" do
111
+ lambda {
112
+ Rack::Lint.new(nil).call(env("rack.errors" => ""))
113
+ }.should.raise(Rack::Lint::LintError).
114
+ message.should.match(/does not respond to #puts/)
115
+ end
116
+
117
+ specify "notices status errors" do
118
+ lambda {
119
+ Rack::Lint.new(lambda { |env|
120
+ ["cc", {}, ""]
121
+ }).call(env({}))
122
+ }.should.raise(Rack::Lint::LintError).
123
+ message.should.match(/must be >100 seen as integer/)
124
+
125
+ lambda {
126
+ Rack::Lint.new(lambda { |env|
127
+ [42, {}, ""]
128
+ }).call(env({}))
129
+ }.should.raise(Rack::Lint::LintError).
130
+ message.should.match(/must be >100 seen as integer/)
131
+ end
132
+
133
+ specify "notices header errors" do
134
+ lambda {
135
+ Rack::Lint.new(lambda { |env|
136
+ [200, Object.new, ""]
137
+ }).call(env({}))
138
+ }.should.raise(Rack::Lint::LintError).
139
+ message.should.match(/should respond to #each/)
140
+
141
+ lambda {
142
+ Rack::Lint.new(lambda { |env|
143
+ [200, {true=>false}, ""]
144
+ }).call(env({}))
145
+ }.should.raise(Rack::Lint::LintError).
146
+ message.should.match(/header key must be a string/)
147
+
148
+ lambda {
149
+ Rack::Lint.new(lambda { |env|
150
+ [200, {"Status" => "404"}, ""]
151
+ }).call(env({}))
152
+ }.should.raise(Rack::Lint::LintError).
153
+ message.should.match(/must not contain Status/)
154
+
155
+ lambda {
156
+ Rack::Lint.new(lambda { |env|
157
+ [200, {"Content-Type:" => "text/plain"}, ""]
158
+ }).call(env({}))
159
+ }.should.raise(Rack::Lint::LintError).
160
+ message.should.match(/must not contain :/)
161
+
162
+ lambda {
163
+ Rack::Lint.new(lambda { |env|
164
+ [200, {"Content-" => "text/plain"}, ""]
165
+ }).call(env({}))
166
+ }.should.raise(Rack::Lint::LintError).
167
+ message.should.match(/must not end/)
168
+
169
+ lambda {
170
+ Rack::Lint.new(lambda { |env|
171
+ [200, {"..%%quark%%.." => "text/plain"}, ""]
172
+ }).call(env({}))
173
+ }.should.raise(Rack::Lint::LintError).
174
+ message.should.match(/invalid header/)
175
+
176
+ lambda {
177
+ Rack::Lint.new(lambda { |env|
178
+ [200, {"Foo" => Object.new}, ""]
179
+ }).call(env({}))
180
+ }.should.raise(Rack::Lint::LintError).
181
+ message.should.match(/must respond to #each/)
182
+
183
+ lambda {
184
+ Rack::Lint.new(lambda { |env|
185
+ [200, {"Foo" => [1,2,3]}, ""]
186
+ }).call(env({}))
187
+ }.should.raise(Rack::Lint::LintError).
188
+ message.should.match(/must consist of Strings/)
189
+
190
+
191
+ lambda {
192
+ Rack::Lint.new(lambda { |env|
193
+ [200, {"Foo-Bar" => "text\000plain"}, ""]
194
+ }).call(env({}))
195
+ }.should.raise(Rack::Lint::LintError).
196
+ message.should.match(/invalid header/)
197
+ end
198
+
199
+ specify "notices content-type errors" do
200
+ lambda {
201
+ Rack::Lint.new(lambda { |env|
202
+ [200, {}, ""]
203
+ }).call(env({}))
204
+ }.should.raise(Rack::Lint::LintError).
205
+ message.should.match(/No Content-Type/)
206
+
207
+ lambda {
208
+ Rack::Lint.new(lambda { |env|
209
+ [201, {"Content-Type" => "text/plain"}, ""]
210
+ }).call(env({}))
211
+ }.should.raise(Rack::Lint::LintError).
212
+ message.should.match(/Content-Type header found/)
213
+
214
+ lambda {
215
+ Rack::Lint.new(lambda { |env|
216
+ [201, {"Content-type" => "text/plain"}, ""]
217
+ }).call(env({}))
218
+ }.should.raise(Rack::Lint::LintError).
219
+ message.should.match(/Content-Type header found/)
220
+ end
221
+
222
+ specify "notices body errors" do
223
+ lambda {
224
+ status, header, body = Rack::Lint.new(lambda { |env|
225
+ [200, {"Content-type" => "text/plain"}, [1,2,3]]
226
+ }).call(env({}))
227
+ body.each { |part| }
228
+ }.should.raise(Rack::Lint::LintError).
229
+ message.should.match(/yielded non-string/)
230
+ end
231
+
232
+ specify "notices input handling errors" do
233
+ lambda {
234
+ Rack::Lint.new(lambda { |env|
235
+ env["rack.input"].gets("\r\n")
236
+ [201, {"Content-type" => "text/plain"}, ""]
237
+ }).call(env({}))
238
+ }.should.raise(Rack::Lint::LintError).
239
+ message.should.match(/gets called with arguments/)
240
+
241
+ lambda {
242
+ Rack::Lint.new(lambda { |env|
243
+ env["rack.input"].read("foo")
244
+ [201, {"Content-type" => "text/plain"}, ""]
245
+ }).call(env({}))
246
+ }.should.raise(Rack::Lint::LintError).
247
+ message.should.match(/read called with too many arguments/)
248
+
249
+ weirdio = Object.new
250
+ class << weirdio
251
+ def gets
252
+ 42
253
+ end
254
+
255
+ def read
256
+ 23
257
+ end
258
+
259
+ def each
260
+ yield 23
261
+ yield 42
262
+ end
263
+ end
264
+
265
+ lambda {
266
+ Rack::Lint.new(lambda { |env|
267
+ env["rack.input"].gets
268
+ [201, {"Content-type" => "text/plain"}, ""]
269
+ }).call(env("rack.input" => weirdio))
270
+ }.should.raise(Rack::Lint::LintError).
271
+ message.should.match(/gets didn't return a String/)
272
+
273
+ lambda {
274
+ Rack::Lint.new(lambda { |env|
275
+ env["rack.input"].each { |x| }
276
+ [201, {"Content-type" => "text/plain"}, ""]
277
+ }).call(env("rack.input" => weirdio))
278
+ }.should.raise(Rack::Lint::LintError).
279
+ message.should.match(/each didn't yield a String/)
280
+
281
+ lambda {
282
+ Rack::Lint.new(lambda { |env|
283
+ env["rack.input"].read
284
+ [201, {"Content-type" => "text/plain"}, ""]
285
+ }).call(env("rack.input" => weirdio))
286
+ }.should.raise(Rack::Lint::LintError).
287
+ message.should.match(/read didn't return a String/)
288
+
289
+
290
+ lambda {
291
+ Rack::Lint.new(lambda { |env|
292
+ env["rack.input"].close
293
+ [201, {"Content-type" => "text/plain"}, ""]
294
+ }).call(env({}))
295
+ }.should.raise(Rack::Lint::LintError).
296
+ message.should.match(/close must not be called/)
297
+ end
298
+
299
+ specify "notices error handling errors" do
300
+ lambda {
301
+ Rack::Lint.new(lambda { |env|
302
+ env["rack.errors"].write(42)
303
+ [201, {"Content-type" => "text/plain"}, ""]
304
+ }).call(env({}))
305
+ }.should.raise(Rack::Lint::LintError).
306
+ message.should.match(/write not called with a String/)
307
+
308
+ lambda {
309
+ Rack::Lint.new(lambda { |env|
310
+ env["rack.errors"].close
311
+ [201, {"Content-type" => "text/plain"}, ""]
312
+ }).call(env({}))
313
+ }.should.raise(Rack::Lint::LintError).
314
+ message.should.match(/close must not be called/)
315
+ end
316
+
317
+ end
@@ -0,0 +1,45 @@
1
+ require 'test/spec'
2
+
3
+ require 'rack/lobster'
4
+ require 'rack/mock'
5
+
6
+ context "Rack::Lobster::LambdaLobster" do
7
+ specify "should be a single lambda" do
8
+ Rack::Lobster::LambdaLobster.should.be.kind_of Proc
9
+ end
10
+
11
+ specify "should look like a lobster" do
12
+ res = Rack::MockRequest.new(Rack::Lobster::LambdaLobster).get("/")
13
+ res.should.be.ok
14
+ res.body.should.include "(,(,,(,,,("
15
+ res.body.should.include "?flip"
16
+ end
17
+
18
+ specify "should be flippable" do
19
+ res = Rack::MockRequest.new(Rack::Lobster::LambdaLobster).get("/?flip")
20
+ res.should.be.ok
21
+ res.body.should.include "(,,,(,,(,("
22
+ end
23
+ end
24
+
25
+ context "Rack::Lobster" do
26
+ specify "should look like a lobster" do
27
+ res = Rack::MockRequest.new(Rack::Lobster.new).get("/")
28
+ res.should.be.ok
29
+ res.body.should.include "(,(,,(,,,("
30
+ res.body.should.include "?flip"
31
+ res.body.should.include "crash"
32
+ end
33
+
34
+ specify "should be flippable" do
35
+ res = Rack::MockRequest.new(Rack::Lobster.new).get("/?flip=left")
36
+ res.should.be.ok
37
+ res.body.should.include "(,,,(,,(,("
38
+ end
39
+
40
+ specify "should provide crashing for testing purposes" do
41
+ lambda {
42
+ Rack::MockRequest.new(Rack::Lobster.new).get("/?flip=crash")
43
+ }.should.raise
44
+ end
45
+ end