kastner-rack 0.3.171

Sign up to get free protection for your applications and to get access to all the features.
Files changed (94) hide show
  1. data/AUTHORS +8 -0
  2. data/COPYING +18 -0
  3. data/KNOWN-ISSUES +18 -0
  4. data/README +273 -0
  5. data/Rakefile +185 -0
  6. data/bin/rackup +172 -0
  7. data/contrib/rack_logo.svg +111 -0
  8. data/example/lobster.ru +4 -0
  9. data/example/protectedlobster.rb +14 -0
  10. data/example/protectedlobster.ru +8 -0
  11. data/lib/rack.rb +85 -0
  12. data/lib/rack/adapter/camping.rb +22 -0
  13. data/lib/rack/auth/abstract/handler.rb +28 -0
  14. data/lib/rack/auth/abstract/request.rb +37 -0
  15. data/lib/rack/auth/basic.rb +58 -0
  16. data/lib/rack/auth/digest/md5.rb +124 -0
  17. data/lib/rack/auth/digest/nonce.rb +51 -0
  18. data/lib/rack/auth/digest/params.rb +55 -0
  19. data/lib/rack/auth/digest/request.rb +40 -0
  20. data/lib/rack/auth/openid.rb +437 -0
  21. data/lib/rack/builder.rb +67 -0
  22. data/lib/rack/cascade.rb +36 -0
  23. data/lib/rack/commonlogger.rb +61 -0
  24. data/lib/rack/conditionalget.rb +42 -0
  25. data/lib/rack/deflater.rb +63 -0
  26. data/lib/rack/directory.rb +149 -0
  27. data/lib/rack/file.rb +84 -0
  28. data/lib/rack/handler.rb +46 -0
  29. data/lib/rack/handler/cgi.rb +57 -0
  30. data/lib/rack/handler/evented_mongrel.rb +8 -0
  31. data/lib/rack/handler/fastcgi.rb +86 -0
  32. data/lib/rack/handler/lsws.rb +52 -0
  33. data/lib/rack/handler/mongrel.rb +78 -0
  34. data/lib/rack/handler/scgi.rb +57 -0
  35. data/lib/rack/handler/swiftiplied_mongrel.rb +8 -0
  36. data/lib/rack/handler/webrick.rb +61 -0
  37. data/lib/rack/head.rb +19 -0
  38. data/lib/rack/lint.rb +463 -0
  39. data/lib/rack/lobster.rb +65 -0
  40. data/lib/rack/methodoverride.rb +21 -0
  41. data/lib/rack/mime.rb +204 -0
  42. data/lib/rack/mock.rb +160 -0
  43. data/lib/rack/recursive.rb +57 -0
  44. data/lib/rack/reloader.rb +64 -0
  45. data/lib/rack/request.rb +217 -0
  46. data/lib/rack/response.rb +171 -0
  47. data/lib/rack/session/abstract/id.rb +140 -0
  48. data/lib/rack/session/cookie.rb +89 -0
  49. data/lib/rack/session/memcache.rb +97 -0
  50. data/lib/rack/session/pool.rb +73 -0
  51. data/lib/rack/showexceptions.rb +348 -0
  52. data/lib/rack/showstatus.rb +105 -0
  53. data/lib/rack/static.rb +38 -0
  54. data/lib/rack/urlmap.rb +48 -0
  55. data/lib/rack/utils.rb +318 -0
  56. data/rack.gemspec +31 -0
  57. data/test/cgi/lighttpd.conf +20 -0
  58. data/test/cgi/test +9 -0
  59. data/test/cgi/test.fcgi +8 -0
  60. data/test/cgi/test.ru +7 -0
  61. data/test/spec_rack_auth_basic.rb +69 -0
  62. data/test/spec_rack_auth_digest.rb +169 -0
  63. data/test/spec_rack_auth_openid.rb +137 -0
  64. data/test/spec_rack_builder.rb +84 -0
  65. data/test/spec_rack_camping.rb +51 -0
  66. data/test/spec_rack_cascade.rb +50 -0
  67. data/test/spec_rack_cgi.rb +89 -0
  68. data/test/spec_rack_commonlogger.rb +32 -0
  69. data/test/spec_rack_conditionalget.rb +41 -0
  70. data/test/spec_rack_deflater.rb +70 -0
  71. data/test/spec_rack_directory.rb +56 -0
  72. data/test/spec_rack_fastcgi.rb +89 -0
  73. data/test/spec_rack_file.rb +57 -0
  74. data/test/spec_rack_handler.rb +24 -0
  75. data/test/spec_rack_head.rb +30 -0
  76. data/test/spec_rack_lint.rb +371 -0
  77. data/test/spec_rack_lobster.rb +45 -0
  78. data/test/spec_rack_methodoverride.rb +31 -0
  79. data/test/spec_rack_mock.rb +152 -0
  80. data/test/spec_rack_mongrel.rb +170 -0
  81. data/test/spec_rack_recursive.rb +77 -0
  82. data/test/spec_rack_request.rb +426 -0
  83. data/test/spec_rack_response.rb +173 -0
  84. data/test/spec_rack_session_cookie.rb +78 -0
  85. data/test/spec_rack_session_memcache.rb +132 -0
  86. data/test/spec_rack_session_pool.rb +84 -0
  87. data/test/spec_rack_showexceptions.rb +21 -0
  88. data/test/spec_rack_showstatus.rb +72 -0
  89. data/test/spec_rack_static.rb +37 -0
  90. data/test/spec_rack_urlmap.rb +175 -0
  91. data/test/spec_rack_utils.rb +174 -0
  92. data/test/spec_rack_webrick.rb +123 -0
  93. data/test/testrequest.rb +45 -0
  94. metadata +177 -0
@@ -0,0 +1,89 @@
1
+ require 'test/spec'
2
+ require 'testrequest'
3
+
4
+ context "Rack::Handler::FastCGI" do
5
+ include TestRequest::Helpers
6
+
7
+ setup do
8
+ @host = '0.0.0.0'
9
+ @port = 9203
10
+ end
11
+
12
+ # Keep this first.
13
+ specify "startup" do
14
+ $pid = fork {
15
+ Dir.chdir(File.join(File.dirname(__FILE__), "..", "test", "cgi"))
16
+ exec "lighttpd -D -f lighttpd.conf"
17
+ }
18
+ end
19
+
20
+ specify "should respond" do
21
+ sleep 1
22
+ lambda {
23
+ GET("/test.fcgi")
24
+ }.should.not.raise
25
+ end
26
+
27
+ specify "should be a lighttpd" do
28
+ GET("/test.fcgi")
29
+ status.should.be 200
30
+ response["SERVER_SOFTWARE"].should =~ /lighttpd/
31
+ response["HTTP_VERSION"].should.equal "HTTP/1.1"
32
+ response["SERVER_PROTOCOL"].should.equal "HTTP/1.1"
33
+ response["SERVER_PORT"].should.equal @port.to_s
34
+ response["SERVER_NAME"].should =~ @host
35
+ end
36
+
37
+ specify "should have rack headers" do
38
+ GET("/test.fcgi")
39
+ response["rack.version"].should.equal [0,1]
40
+ response["rack.multithread"].should.be false
41
+ response["rack.multiprocess"].should.be true
42
+ response["rack.run_once"].should.be false
43
+ end
44
+
45
+ specify "should have CGI headers on GET" do
46
+ GET("/test.fcgi")
47
+ response["REQUEST_METHOD"].should.equal "GET"
48
+ response["SCRIPT_NAME"].should.equal "/test.fcgi"
49
+ response["REQUEST_PATH"].should.equal "/"
50
+ response["PATH_INFO"].should.be.nil
51
+ response["QUERY_STRING"].should.equal ""
52
+ response["test.postdata"].should.equal ""
53
+
54
+ GET("/test.fcgi/foo?quux=1")
55
+ response["REQUEST_METHOD"].should.equal "GET"
56
+ response["SCRIPT_NAME"].should.equal "/test.fcgi"
57
+ response["REQUEST_PATH"].should.equal "/"
58
+ response["PATH_INFO"].should.equal "/foo"
59
+ response["QUERY_STRING"].should.equal "quux=1"
60
+ end
61
+
62
+ specify "should have CGI headers on POST" do
63
+ POST("/test.fcgi", {"rack-form-data" => "23"}, {'X-test-header' => '42'})
64
+ status.should.equal 200
65
+ response["REQUEST_METHOD"].should.equal "POST"
66
+ response["SCRIPT_NAME"].should.equal "/test.fcgi"
67
+ response["REQUEST_PATH"].should.equal "/"
68
+ response["QUERY_STRING"].should.equal ""
69
+ response["HTTP_X_TEST_HEADER"].should.equal "42"
70
+ response["test.postdata"].should.equal "rack-form-data=23"
71
+ end
72
+
73
+ specify "should support HTTP auth" do
74
+ GET("/test.fcgi", {:user => "ruth", :passwd => "secret"})
75
+ response["HTTP_AUTHORIZATION"].should.equal "Basic cnV0aDpzZWNyZXQ="
76
+ end
77
+
78
+ specify "should set status" do
79
+ GET("/test.fcgi?secret")
80
+ status.should.equal 403
81
+ response["rack.url_scheme"].should.equal "http"
82
+ end
83
+
84
+ # Keep this last.
85
+ specify "shutdown" do
86
+ Process.kill 15, $pid
87
+ Process.wait($pid).should.equal $pid
88
+ end
89
+ end
@@ -0,0 +1,57 @@
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 "sets Last-Modified header" do
20
+ res = Rack::MockRequest.new(Rack::Lint.new(Rack::File.new(DOCROOT))).
21
+ get("/cgi/test")
22
+
23
+ path = File.join(DOCROOT, "/cgi/test")
24
+
25
+ res.should.be.ok
26
+ res["Last-Modified"].should.equal File.mtime(path).httpdate
27
+ end
28
+
29
+ specify "serves files with URL encoded filenames" do
30
+ res = Rack::MockRequest.new(Rack::Lint.new(Rack::File.new(DOCROOT))).
31
+ get("/cgi/%74%65%73%74") # "/cgi/test"
32
+
33
+ res.should.be.ok
34
+ res.should =~ /ruby/
35
+ end
36
+
37
+ specify "does not allow directory traversal" do
38
+ res = Rack::MockRequest.new(Rack::Lint.new(Rack::File.new(DOCROOT))).
39
+ get("/cgi/../test")
40
+
41
+ res.should.be.forbidden
42
+ end
43
+
44
+ specify "404s if it can't find the file" do
45
+ res = Rack::MockRequest.new(Rack::Lint.new(Rack::File.new(DOCROOT))).
46
+ get("/cgi/blubb")
47
+
48
+ res.should.be.not_found
49
+ end
50
+
51
+ specify "detects SystemCallErrors" do
52
+ res = Rack::MockRequest.new(Rack::Lint.new(Rack::File.new(DOCROOT))).
53
+ get("/cgi")
54
+
55
+ res.should.be.not_found
56
+ end
57
+ end
@@ -0,0 +1,24 @@
1
+ require 'test/spec'
2
+
3
+ require 'rack/handler'
4
+
5
+ context "Rack::Handler" do
6
+ class Rack::Handler::Lobster; end
7
+ class RockLobster; end
8
+
9
+ specify "has registered default handlers" do
10
+ Rack::Handler.get('cgi').should.equal Rack::Handler::CGI
11
+ Rack::Handler.get('fastcgi').should.equal Rack::Handler::FastCGI
12
+ Rack::Handler.get('mongrel').should.equal Rack::Handler::Mongrel
13
+ Rack::Handler.get('webrick').should.equal Rack::Handler::WEBrick
14
+ end
15
+
16
+ specify "should get unregistered handler by name" do
17
+ Rack::Handler.get('lobster').should.equal Rack::Handler::Lobster
18
+ end
19
+
20
+ specify "should register custom handler" do
21
+ Rack::Handler.register('rock_lobster', 'RockLobster')
22
+ Rack::Handler.get('rock_lobster').should.equal RockLobster
23
+ end
24
+ end
@@ -0,0 +1,30 @@
1
+ require 'rack/head'
2
+ require 'rack/mock'
3
+
4
+ context "Rack::Head" do
5
+ def test_response(headers = {})
6
+ app = lambda { |env| [200, {"Content-type" => "test/plain", "Content-length" => "3"}, ["foo"]] }
7
+ request = Rack::MockRequest.env_for("/", headers)
8
+ response = Rack::Head.new(app).call(request)
9
+
10
+ return response
11
+ end
12
+
13
+ specify "passes GET, POST, PUT, DELETE, OPTIONS, TRACE requests" do
14
+ %w[GET POST PUT DELETE OPTIONS TRACE].each do |type|
15
+ resp = test_response("REQUEST_METHOD" => type)
16
+
17
+ resp[0].should.equal(200)
18
+ resp[1].should.equal({"Content-type" => "test/plain", "Content-length" => "3"})
19
+ resp[2].should.equal(["foo"])
20
+ end
21
+ end
22
+
23
+ specify "removes body from HEAD requests" do
24
+ resp = test_response("REQUEST_METHOD" => "HEAD")
25
+
26
+ resp[0].should.equal(200)
27
+ resp[1].should.equal({"Content-type" => "test/plain", "Content-length" => "3"})
28
+ resp[2].should.equal([])
29
+ end
30
+ end
@@ -0,0 +1,371 @@
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", "Content-length" => "3"}, "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/)
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.equal("headers object should respond to #each, but doesn't (got Object as headers)")
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.equal("header key must be a string, was TrueClass")
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.equal("invalid header name: ..%%quark%%..")
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.equal("header values must respond to #each, but the value of 'Foo' doesn't (is Object)")
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.equal("header values must consist of Strings, but 'Foo' also contains a Fixnum")
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, {"Content-length" => "0"}, ""]
203
+ }).call(env({}))
204
+ }.should.raise(Rack::Lint::LintError).
205
+ message.should.match(/No Content-Type/)
206
+
207
+ [100, 101, 204, 304].each do |status|
208
+ lambda {
209
+ Rack::Lint.new(lambda { |env|
210
+ [status, {"Content-type" => "text/plain", "Content-length" => "0"}, ""]
211
+ }).call(env({}))
212
+ }.should.raise(Rack::Lint::LintError).
213
+ message.should.match(/Content-Type header found/)
214
+ end
215
+ end
216
+
217
+ specify "notices content-length errors" do
218
+ lambda {
219
+ Rack::Lint.new(lambda { |env|
220
+ [200, {"Content-type" => "text/plain"}, ""]
221
+ }).call(env({}))
222
+ }.should.raise(Rack::Lint::LintError).
223
+ message.should.match(/No Content-Length/)
224
+
225
+ [100, 101, 204, 304].each do |status|
226
+ lambda {
227
+ Rack::Lint.new(lambda { |env|
228
+ [status, {"Content-length" => "0"}, ""]
229
+ }).call(env({}))
230
+ }.should.raise(Rack::Lint::LintError).
231
+ message.should.match(/Content-Length header found/)
232
+ end
233
+
234
+ lambda {
235
+ Rack::Lint.new(lambda { |env|
236
+ [200, {"Content-type" => "text/plain", "Content-Length" => "0", "Transfer-Encoding" => "chunked"}, ""]
237
+ }).call(env({}))
238
+ }.should.raise(Rack::Lint::LintError).
239
+ message.should.match(/Content-Length header should not be used/)
240
+
241
+ lambda {
242
+ Rack::Lint.new(lambda { |env|
243
+ [200, {"Content-type" => "text/plain", "Content-Length" => "1"}, ""]
244
+ }).call(env({}))
245
+ }.should.raise(Rack::Lint::LintError).
246
+ message.should.match(/Content-Length header was 1, but should be 0/)
247
+ end
248
+
249
+ specify "notices body errors" do
250
+ lambda {
251
+ status, header, body = Rack::Lint.new(lambda { |env|
252
+ [200, {"Content-type" => "text/plain","Content-length" => "3"}, [1,2,3]]
253
+ }).call(env({}))
254
+ body.each { |part| }
255
+ }.should.raise(Rack::Lint::LintError).
256
+ message.should.match(/yielded non-string/)
257
+ end
258
+
259
+ specify "notices input handling errors" do
260
+ lambda {
261
+ Rack::Lint.new(lambda { |env|
262
+ env["rack.input"].gets("\r\n")
263
+ [201, {"Content-type" => "text/plain", "Content-length" => "0"}, ""]
264
+ }).call(env({}))
265
+ }.should.raise(Rack::Lint::LintError).
266
+ message.should.match(/gets called with arguments/)
267
+
268
+ lambda {
269
+ Rack::Lint.new(lambda { |env|
270
+ env["rack.input"].read("foo")
271
+ [201, {"Content-type" => "text/plain", "Content-length" => "0"}, ""]
272
+ }).call(env({}))
273
+ }.should.raise(Rack::Lint::LintError).
274
+ message.should.match(/read called with non-integer argument/)
275
+
276
+ weirdio = Object.new
277
+ class << weirdio
278
+ def gets
279
+ 42
280
+ end
281
+
282
+ def read
283
+ 23
284
+ end
285
+
286
+ def each
287
+ yield 23
288
+ yield 42
289
+ end
290
+ end
291
+
292
+ lambda {
293
+ Rack::Lint.new(lambda { |env|
294
+ env["rack.input"].gets
295
+ [201, {"Content-type" => "text/plain", "Content-length" => "0"}, ""]
296
+ }).call(env("rack.input" => weirdio))
297
+ }.should.raise(Rack::Lint::LintError).
298
+ message.should.match(/gets didn't return a String/)
299
+
300
+ lambda {
301
+ Rack::Lint.new(lambda { |env|
302
+ env["rack.input"].each { |x| }
303
+ [201, {"Content-type" => "text/plain", "Content-length" => "0"}, ""]
304
+ }).call(env("rack.input" => weirdio))
305
+ }.should.raise(Rack::Lint::LintError).
306
+ message.should.match(/each didn't yield a String/)
307
+
308
+ lambda {
309
+ Rack::Lint.new(lambda { |env|
310
+ env["rack.input"].read
311
+ [201, {"Content-type" => "text/plain", "Content-length" => "0"}, ""]
312
+ }).call(env("rack.input" => weirdio))
313
+ }.should.raise(Rack::Lint::LintError).
314
+ message.should.match(/read didn't return a String/)
315
+
316
+
317
+ lambda {
318
+ Rack::Lint.new(lambda { |env|
319
+ env["rack.input"].close
320
+ [201, {"Content-type" => "text/plain", "Content-length" => "0"}, ""]
321
+ }).call(env({}))
322
+ }.should.raise(Rack::Lint::LintError).
323
+ message.should.match(/close must not be called/)
324
+ end
325
+
326
+ specify "notices error handling errors" do
327
+ lambda {
328
+ Rack::Lint.new(lambda { |env|
329
+ env["rack.errors"].write(42)
330
+ [201, {"Content-type" => "text/plain", "Content-length" => "0"}, ""]
331
+ }).call(env({}))
332
+ }.should.raise(Rack::Lint::LintError).
333
+ message.should.match(/write not called with a String/)
334
+
335
+ lambda {
336
+ Rack::Lint.new(lambda { |env|
337
+ env["rack.errors"].close
338
+ [201, {"Content-type" => "text/plain", "Content-length" => "0"}, ""]
339
+ }).call(env({}))
340
+ }.should.raise(Rack::Lint::LintError).
341
+ message.should.match(/close must not be called/)
342
+ end
343
+
344
+ specify "notices HEAD errors" do
345
+ lambda {
346
+ Rack::Lint.new(lambda { |env|
347
+ [200, {"Content-type" => "test/plain", "Content-length" => "3"}, []]
348
+ }).call(env({"REQUEST_METHOD" => "HEAD"}))
349
+ }.should.not.raise
350
+
351
+ lambda {
352
+ Rack::Lint.new(lambda { |env|
353
+ [200, {"Content-type" => "test/plain", "Content-length" => "3"}, "foo"]
354
+ }).call(env({"REQUEST_METHOD" => "HEAD"}))
355
+ }.should.raise(Rack::Lint::LintError).
356
+ message.should.match(/body was given for HEAD/)
357
+ end
358
+ end
359
+
360
+ context "Rack::Lint::InputWrapper" do
361
+ specify "delegates :size to underlying IO object" do
362
+ class IOMock
363
+ def size
364
+ 101
365
+ end
366
+ end
367
+
368
+ wrapper = Rack::Lint::InputWrapper.new(IOMock.new)
369
+ wrapper.size.should == 101
370
+ end
371
+ end