qoobaa-rack 1.0.0.1

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 (116) hide show
  1. data/COPYING +18 -0
  2. data/KNOWN-ISSUES +18 -0
  3. data/RDOX +0 -0
  4. data/README +353 -0
  5. data/Rakefile +164 -0
  6. data/SPEC +164 -0
  7. data/bin/rackup +176 -0
  8. data/contrib/rack_logo.svg +111 -0
  9. data/example/lobster.ru +4 -0
  10. data/example/protectedlobster.rb +14 -0
  11. data/example/protectedlobster.ru +8 -0
  12. data/lib/rack/adapter/camping.rb +22 -0
  13. data/lib/rack/auth/abstract/handler.rb +37 -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 +487 -0
  21. data/lib/rack/builder.rb +63 -0
  22. data/lib/rack/cascade.rb +41 -0
  23. data/lib/rack/chunked.rb +49 -0
  24. data/lib/rack/commonlogger.rb +52 -0
  25. data/lib/rack/conditionalget.rb +47 -0
  26. data/lib/rack/content_length.rb +29 -0
  27. data/lib/rack/content_type.rb +23 -0
  28. data/lib/rack/deflater.rb +96 -0
  29. data/lib/rack/directory.rb +153 -0
  30. data/lib/rack/file.rb +88 -0
  31. data/lib/rack/handler/cgi.rb +61 -0
  32. data/lib/rack/handler/evented_mongrel.rb +8 -0
  33. data/lib/rack/handler/fastcgi.rb +88 -0
  34. data/lib/rack/handler/lsws.rb +60 -0
  35. data/lib/rack/handler/mongrel.rb +87 -0
  36. data/lib/rack/handler/scgi.rb +62 -0
  37. data/lib/rack/handler/swiftiplied_mongrel.rb +8 -0
  38. data/lib/rack/handler/thin.rb +18 -0
  39. data/lib/rack/handler/webrick.rb +71 -0
  40. data/lib/rack/handler.rb +69 -0
  41. data/lib/rack/head.rb +19 -0
  42. data/lib/rack/lint.rb +546 -0
  43. data/lib/rack/lobster.rb +65 -0
  44. data/lib/rack/lock.rb +16 -0
  45. data/lib/rack/methodoverride.rb +27 -0
  46. data/lib/rack/mime.rb +204 -0
  47. data/lib/rack/mock.rb +187 -0
  48. data/lib/rack/recursive.rb +57 -0
  49. data/lib/rack/reloader.rb +107 -0
  50. data/lib/rack/request.rb +248 -0
  51. data/lib/rack/response.rb +183 -0
  52. data/lib/rack/rewindable_input.rb +100 -0
  53. data/lib/rack/session/abstract/id.rb +142 -0
  54. data/lib/rack/session/cookie.rb +91 -0
  55. data/lib/rack/session/memcache.rb +109 -0
  56. data/lib/rack/session/pool.rb +100 -0
  57. data/lib/rack/showexceptions.rb +349 -0
  58. data/lib/rack/showstatus.rb +106 -0
  59. data/lib/rack/static.rb +38 -0
  60. data/lib/rack/urlmap.rb +55 -0
  61. data/lib/rack/utils.rb +528 -0
  62. data/lib/rack.rb +90 -0
  63. data/rack.gemspec +60 -0
  64. data/test/cgi/lighttpd.conf +20 -0
  65. data/test/cgi/test +9 -0
  66. data/test/cgi/test.fcgi +8 -0
  67. data/test/cgi/test.ru +7 -0
  68. data/test/multipart/binary +0 -0
  69. data/test/multipart/empty +10 -0
  70. data/test/multipart/file1.txt +1 -0
  71. data/test/multipart/ie +6 -0
  72. data/test/multipart/nested +10 -0
  73. data/test/multipart/none +9 -0
  74. data/test/multipart/text +10 -0
  75. data/test/spec_rack_auth_basic.rb +73 -0
  76. data/test/spec_rack_auth_digest.rb +226 -0
  77. data/test/spec_rack_auth_openid.rb +84 -0
  78. data/test/spec_rack_builder.rb +84 -0
  79. data/test/spec_rack_camping.rb +51 -0
  80. data/test/spec_rack_cascade.rb +48 -0
  81. data/test/spec_rack_cgi.rb +89 -0
  82. data/test/spec_rack_chunked.rb +62 -0
  83. data/test/spec_rack_commonlogger.rb +61 -0
  84. data/test/spec_rack_conditionalget.rb +41 -0
  85. data/test/spec_rack_content_length.rb +43 -0
  86. data/test/spec_rack_content_type.rb +30 -0
  87. data/test/spec_rack_deflater.rb +127 -0
  88. data/test/spec_rack_directory.rb +61 -0
  89. data/test/spec_rack_fastcgi.rb +89 -0
  90. data/test/spec_rack_file.rb +75 -0
  91. data/test/spec_rack_handler.rb +43 -0
  92. data/test/spec_rack_head.rb +30 -0
  93. data/test/spec_rack_lint.rb +521 -0
  94. data/test/spec_rack_lobster.rb +45 -0
  95. data/test/spec_rack_lock.rb +38 -0
  96. data/test/spec_rack_methodoverride.rb +60 -0
  97. data/test/spec_rack_mock.rb +243 -0
  98. data/test/spec_rack_mongrel.rb +189 -0
  99. data/test/spec_rack_recursive.rb +77 -0
  100. data/test/spec_rack_request.rb +504 -0
  101. data/test/spec_rack_response.rb +218 -0
  102. data/test/spec_rack_rewindable_input.rb +118 -0
  103. data/test/spec_rack_session_cookie.rb +82 -0
  104. data/test/spec_rack_session_memcache.rb +250 -0
  105. data/test/spec_rack_session_pool.rb +172 -0
  106. data/test/spec_rack_showexceptions.rb +21 -0
  107. data/test/spec_rack_showstatus.rb +72 -0
  108. data/test/spec_rack_static.rb +37 -0
  109. data/test/spec_rack_thin.rb +91 -0
  110. data/test/spec_rack_urlmap.rb +185 -0
  111. data/test/spec_rack_utils.rb +467 -0
  112. data/test/spec_rack_webrick.rb +130 -0
  113. data/test/testrequest.rb +57 -0
  114. data/test/unregistered_handler/rack/handler/unregistered.rb +7 -0
  115. data/test/unregistered_handler/rack/handler/unregistered_long_one.rb +7 -0
  116. metadata +276 -0
@@ -0,0 +1,43 @@
1
+ require 'test/spec'
2
+
3
+ require 'rack/handler'
4
+
5
+ class Rack::Handler::Lobster; end
6
+ class RockLobster; end
7
+
8
+ context "Rack::Handler" do
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 "handler that doesn't exist should raise a NameError" do
17
+ lambda {
18
+ Rack::Handler.get('boom')
19
+ }.should.raise(NameError)
20
+ end
21
+
22
+ specify "should get unregistered, but already required, handler by name" do
23
+ Rack::Handler.get('Lobster').should.equal Rack::Handler::Lobster
24
+ end
25
+
26
+ specify "should register custom handler" do
27
+ Rack::Handler.register('rock_lobster', 'RockLobster')
28
+ Rack::Handler.get('rock_lobster').should.equal RockLobster
29
+ end
30
+
31
+ specify "should not need registration for properly coded handlers even if not already required" do
32
+ begin
33
+ $:.push "test/unregistered_handler"
34
+ Rack::Handler.get('Unregistered').should.equal Rack::Handler::Unregistered
35
+ lambda {
36
+ Rack::Handler.get('UnRegistered')
37
+ }.should.raise(NameError)
38
+ Rack::Handler.get('UnregisteredLongOne').should.equal Rack::Handler::UnregisteredLongOne
39
+ ensure
40
+ $:.delete "test/unregistered_handler"
41
+ end
42
+ end
43
+ 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,521 @@
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("rack.session" => []))
71
+ }.should.raise(Rack::Lint::LintError).
72
+ message.should.equal("session [] must respond to store and []=")
73
+
74
+ lambda {
75
+ Rack::Lint.new(nil).call(env("REQUEST_METHOD" => "FUCKUP?"))
76
+ }.should.raise(Rack::Lint::LintError).
77
+ message.should.match(/REQUEST_METHOD/)
78
+
79
+ lambda {
80
+ Rack::Lint.new(nil).call(env("SCRIPT_NAME" => "howdy"))
81
+ }.should.raise(Rack::Lint::LintError).
82
+ message.should.match(/must start with/)
83
+
84
+ lambda {
85
+ Rack::Lint.new(nil).call(env("PATH_INFO" => "../foo"))
86
+ }.should.raise(Rack::Lint::LintError).
87
+ message.should.match(/must start with/)
88
+
89
+ lambda {
90
+ Rack::Lint.new(nil).call(env("CONTENT_LENGTH" => "xcii"))
91
+ }.should.raise(Rack::Lint::LintError).
92
+ message.should.match(/Invalid CONTENT_LENGTH/)
93
+
94
+ lambda {
95
+ e = env
96
+ e.delete("PATH_INFO")
97
+ e.delete("SCRIPT_NAME")
98
+ Rack::Lint.new(nil).call(e)
99
+ }.should.raise(Rack::Lint::LintError).
100
+ message.should.match(/One of .* must be set/)
101
+
102
+ lambda {
103
+ Rack::Lint.new(nil).call(env("SCRIPT_NAME" => "/"))
104
+ }.should.raise(Rack::Lint::LintError).
105
+ message.should.match(/cannot be .* make it ''/)
106
+ end
107
+
108
+ specify "notices input errors" do
109
+ lambda {
110
+ Rack::Lint.new(nil).call(env("rack.input" => ""))
111
+ }.should.raise(Rack::Lint::LintError).
112
+ message.should.match(/does not respond to #gets/)
113
+
114
+ lambda {
115
+ input = Object.new
116
+ def input.binmode?
117
+ false
118
+ end
119
+ Rack::Lint.new(nil).call(env("rack.input" => input))
120
+ }.should.raise(Rack::Lint::LintError).
121
+ message.should.match(/is not opened in binary mode/)
122
+
123
+ lambda {
124
+ input = Object.new
125
+ def input.external_encoding
126
+ result = Object.new
127
+ def result.name
128
+ "US-ASCII"
129
+ end
130
+ result
131
+ end
132
+ Rack::Lint.new(nil).call(env("rack.input" => input))
133
+ }.should.raise(Rack::Lint::LintError).
134
+ message.should.match(/does not have ASCII-8BIT as its external encoding/);puts RUBY_VERSION
135
+ end
136
+
137
+ specify "notices error errors" do
138
+ lambda {
139
+ Rack::Lint.new(nil).call(env("rack.errors" => ""))
140
+ }.should.raise(Rack::Lint::LintError).
141
+ message.should.match(/does not respond to #puts/)
142
+ end
143
+
144
+ specify "notices status errors" do
145
+ lambda {
146
+ Rack::Lint.new(lambda { |env|
147
+ ["cc", {}, ""]
148
+ }).call(env({}))
149
+ }.should.raise(Rack::Lint::LintError).
150
+ message.should.match(/must be >=100 seen as integer/)
151
+
152
+ lambda {
153
+ Rack::Lint.new(lambda { |env|
154
+ [42, {}, ""]
155
+ }).call(env({}))
156
+ }.should.raise(Rack::Lint::LintError).
157
+ message.should.match(/must be >=100 seen as integer/)
158
+ end
159
+
160
+ specify "notices header errors" do
161
+ lambda {
162
+ Rack::Lint.new(lambda { |env|
163
+ [200, Object.new, []]
164
+ }).call(env({}))
165
+ }.should.raise(Rack::Lint::LintError).
166
+ message.should.equal("headers object should respond to #each, but doesn't (got Object as headers)")
167
+
168
+ lambda {
169
+ Rack::Lint.new(lambda { |env|
170
+ [200, {true=>false}, []]
171
+ }).call(env({}))
172
+ }.should.raise(Rack::Lint::LintError).
173
+ message.should.equal("header key must be a string, was TrueClass")
174
+
175
+ lambda {
176
+ Rack::Lint.new(lambda { |env|
177
+ [200, {"Status" => "404"}, []]
178
+ }).call(env({}))
179
+ }.should.raise(Rack::Lint::LintError).
180
+ message.should.match(/must not contain Status/)
181
+
182
+ lambda {
183
+ Rack::Lint.new(lambda { |env|
184
+ [200, {"Content-Type:" => "text/plain"}, []]
185
+ }).call(env({}))
186
+ }.should.raise(Rack::Lint::LintError).
187
+ message.should.match(/must not contain :/)
188
+
189
+ lambda {
190
+ Rack::Lint.new(lambda { |env|
191
+ [200, {"Content-" => "text/plain"}, []]
192
+ }).call(env({}))
193
+ }.should.raise(Rack::Lint::LintError).
194
+ message.should.match(/must not end/)
195
+
196
+ lambda {
197
+ Rack::Lint.new(lambda { |env|
198
+ [200, {"..%%quark%%.." => "text/plain"}, []]
199
+ }).call(env({}))
200
+ }.should.raise(Rack::Lint::LintError).
201
+ message.should.equal("invalid header name: ..%%quark%%..")
202
+
203
+ lambda {
204
+ Rack::Lint.new(lambda { |env|
205
+ [200, {"Foo" => Object.new}, []]
206
+ }).call(env({}))
207
+ }.should.raise(Rack::Lint::LintError).
208
+ message.should.equal("a header value must be a String, but the value of 'Foo' is a Object")
209
+
210
+ lambda {
211
+ Rack::Lint.new(lambda { |env|
212
+ [200, {"Foo" => [1, 2, 3]}, []]
213
+ }).call(env({}))
214
+ }.should.raise(Rack::Lint::LintError).
215
+ message.should.equal("a header value must be a String, but the value of 'Foo' is a Array")
216
+
217
+
218
+ lambda {
219
+ Rack::Lint.new(lambda { |env|
220
+ [200, {"Foo-Bar" => "text\000plain"}, []]
221
+ }).call(env({}))
222
+ }.should.raise(Rack::Lint::LintError).
223
+ message.should.match(/invalid header/)
224
+
225
+ # line ends (010) should be allowed in header values.
226
+ lambda {
227
+ Rack::Lint.new(lambda { |env|
228
+ [200, {"Foo-Bar" => "one\ntwo\nthree", "Content-Length" => "0", "Content-Type" => "text/plain" }, []]
229
+ }).call(env({}))
230
+ }.should.not.raise(Rack::Lint::LintError)
231
+ end
232
+
233
+ specify "notices content-type errors" do
234
+ lambda {
235
+ Rack::Lint.new(lambda { |env|
236
+ [200, {"Content-length" => "0"}, []]
237
+ }).call(env({}))
238
+ }.should.raise(Rack::Lint::LintError).
239
+ message.should.match(/No Content-Type/)
240
+
241
+ [100, 101, 204, 304].each do |status|
242
+ lambda {
243
+ Rack::Lint.new(lambda { |env|
244
+ [status, {"Content-type" => "text/plain", "Content-length" => "0"}, []]
245
+ }).call(env({}))
246
+ }.should.raise(Rack::Lint::LintError).
247
+ message.should.match(/Content-Type header found/)
248
+ end
249
+ end
250
+
251
+ specify "notices content-length errors" do
252
+ [100, 101, 204, 304].each do |status|
253
+ lambda {
254
+ Rack::Lint.new(lambda { |env|
255
+ [status, {"Content-length" => "0"}, []]
256
+ }).call(env({}))
257
+ }.should.raise(Rack::Lint::LintError).
258
+ message.should.match(/Content-Length header found/)
259
+ end
260
+
261
+ lambda {
262
+ Rack::Lint.new(lambda { |env|
263
+ [200, {"Content-type" => "text/plain", "Content-Length" => "1"}, []]
264
+ }).call(env({}))
265
+ }.should.raise(Rack::Lint::LintError).
266
+ message.should.match(/Content-Length header was 1, but should be 0/)
267
+ end
268
+
269
+ specify "notices body errors" do
270
+ lambda {
271
+ status, header, body = Rack::Lint.new(lambda { |env|
272
+ [200, {"Content-type" => "text/plain","Content-length" => "3"}, [1,2,3]]
273
+ }).call(env({}))
274
+ body.each { |part| }
275
+ }.should.raise(Rack::Lint::LintError).
276
+ message.should.match(/yielded non-string/)
277
+ end
278
+
279
+ specify "notices input handling errors" do
280
+ lambda {
281
+ Rack::Lint.new(lambda { |env|
282
+ env["rack.input"].gets("\r\n")
283
+ [201, {"Content-type" => "text/plain", "Content-length" => "0"}, []]
284
+ }).call(env({}))
285
+ }.should.raise(Rack::Lint::LintError).
286
+ message.should.match(/gets called with arguments/)
287
+
288
+ lambda {
289
+ Rack::Lint.new(lambda { |env|
290
+ env["rack.input"].read(1, 2, 3)
291
+ [201, {"Content-type" => "text/plain", "Content-length" => "0"}, []]
292
+ }).call(env({}))
293
+ }.should.raise(Rack::Lint::LintError).
294
+ message.should.match(/read called with too many arguments/)
295
+
296
+ lambda {
297
+ Rack::Lint.new(lambda { |env|
298
+ env["rack.input"].read("foo")
299
+ [201, {"Content-type" => "text/plain", "Content-length" => "0"}, []]
300
+ }).call(env({}))
301
+ }.should.raise(Rack::Lint::LintError).
302
+ message.should.match(/read called with non-integer and non-nil length/)
303
+
304
+ lambda {
305
+ Rack::Lint.new(lambda { |env|
306
+ env["rack.input"].read(-1)
307
+ [201, {"Content-type" => "text/plain", "Content-length" => "0"}, []]
308
+ }).call(env({}))
309
+ }.should.raise(Rack::Lint::LintError).
310
+ message.should.match(/read called with a negative length/)
311
+
312
+ lambda {
313
+ Rack::Lint.new(lambda { |env|
314
+ env["rack.input"].read(nil, nil)
315
+ [201, {"Content-type" => "text/plain", "Content-length" => "0"}, []]
316
+ }).call(env({}))
317
+ }.should.raise(Rack::Lint::LintError).
318
+ message.should.match(/read called with non-String buffer/)
319
+
320
+ lambda {
321
+ Rack::Lint.new(lambda { |env|
322
+ env["rack.input"].read(nil, 1)
323
+ [201, {"Content-type" => "text/plain", "Content-length" => "0"}, []]
324
+ }).call(env({}))
325
+ }.should.raise(Rack::Lint::LintError).
326
+ message.should.match(/read called with non-String buffer/)
327
+
328
+ lambda {
329
+ Rack::Lint.new(lambda { |env|
330
+ env["rack.input"].rewind(0)
331
+ [201, {"Content-type" => "text/plain", "Content-length" => "0"}, []]
332
+ }).call(env({}))
333
+ }.should.raise(Rack::Lint::LintError).
334
+ message.should.match(/rewind called with arguments/)
335
+
336
+ weirdio = Object.new
337
+ class << weirdio
338
+ def gets
339
+ 42
340
+ end
341
+
342
+ def read
343
+ 23
344
+ end
345
+
346
+ def each
347
+ yield 23
348
+ yield 42
349
+ end
350
+
351
+ def rewind
352
+ raise Errno::ESPIPE, "Errno::ESPIPE"
353
+ end
354
+ end
355
+
356
+ eof_weirdio = Object.new
357
+ class << eof_weirdio
358
+ def gets
359
+ nil
360
+ end
361
+
362
+ def read(*args)
363
+ nil
364
+ end
365
+
366
+ def each
367
+ end
368
+
369
+ def rewind
370
+ end
371
+ end
372
+
373
+ lambda {
374
+ Rack::Lint.new(lambda { |env|
375
+ env["rack.input"].gets
376
+ [201, {"Content-type" => "text/plain", "Content-length" => "0"}, []]
377
+ }).call(env("rack.input" => weirdio))
378
+ }.should.raise(Rack::Lint::LintError).
379
+ message.should.match(/gets didn't return a String/)
380
+
381
+ lambda {
382
+ Rack::Lint.new(lambda { |env|
383
+ env["rack.input"].each { |x| }
384
+ [201, {"Content-type" => "text/plain", "Content-length" => "0"}, []]
385
+ }).call(env("rack.input" => weirdio))
386
+ }.should.raise(Rack::Lint::LintError).
387
+ message.should.match(/each didn't yield a String/)
388
+
389
+ lambda {
390
+ Rack::Lint.new(lambda { |env|
391
+ env["rack.input"].read
392
+ [201, {"Content-type" => "text/plain", "Content-length" => "0"}, []]
393
+ }).call(env("rack.input" => weirdio))
394
+ }.should.raise(Rack::Lint::LintError).
395
+ message.should.match(/read didn't return nil or a String/)
396
+
397
+ lambda {
398
+ Rack::Lint.new(lambda { |env|
399
+ env["rack.input"].read
400
+ [201, {"Content-type" => "text/plain", "Content-length" => "0"}, []]
401
+ }).call(env("rack.input" => eof_weirdio))
402
+ }.should.raise(Rack::Lint::LintError).
403
+ message.should.match(/read\(nil\) returned nil on EOF/)
404
+
405
+ lambda {
406
+ Rack::Lint.new(lambda { |env|
407
+ env["rack.input"].rewind
408
+ [201, {"Content-type" => "text/plain", "Content-length" => "0"}, []]
409
+ }).call(env("rack.input" => weirdio))
410
+ }.should.raise(Rack::Lint::LintError).
411
+ message.should.match(/rewind raised Errno::ESPIPE/)
412
+
413
+
414
+ lambda {
415
+ Rack::Lint.new(lambda { |env|
416
+ env["rack.input"].close
417
+ [201, {"Content-type" => "text/plain", "Content-length" => "0"}, []]
418
+ }).call(env({}))
419
+ }.should.raise(Rack::Lint::LintError).
420
+ message.should.match(/close must not be called/)
421
+ end
422
+
423
+ specify "notices error handling errors" do
424
+ lambda {
425
+ Rack::Lint.new(lambda { |env|
426
+ env["rack.errors"].write(42)
427
+ [201, {"Content-type" => "text/plain", "Content-length" => "0"}, []]
428
+ }).call(env({}))
429
+ }.should.raise(Rack::Lint::LintError).
430
+ message.should.match(/write not called with a String/)
431
+
432
+ lambda {
433
+ Rack::Lint.new(lambda { |env|
434
+ env["rack.errors"].close
435
+ [201, {"Content-type" => "text/plain", "Content-length" => "0"}, []]
436
+ }).call(env({}))
437
+ }.should.raise(Rack::Lint::LintError).
438
+ message.should.match(/close must not be called/)
439
+ end
440
+
441
+ specify "notices HEAD errors" do
442
+ lambda {
443
+ Rack::Lint.new(lambda { |env|
444
+ [200, {"Content-type" => "test/plain", "Content-length" => "3"}, []]
445
+ }).call(env({"REQUEST_METHOD" => "HEAD"}))
446
+ }.should.not.raise
447
+
448
+ lambda {
449
+ Rack::Lint.new(lambda { |env|
450
+ [200, {"Content-type" => "test/plain", "Content-length" => "3"}, ["foo"]]
451
+ }).call(env({"REQUEST_METHOD" => "HEAD"}))
452
+ }.should.raise(Rack::Lint::LintError).
453
+ message.should.match(/body was given for HEAD/)
454
+ end
455
+
456
+ specify "passes valid read calls" do
457
+ lambda {
458
+ Rack::Lint.new(lambda { |env|
459
+ env["rack.input"].read
460
+ [201, {"Content-type" => "text/plain", "Content-length" => "0"}, []]
461
+ }).call(env({"rack.input" => StringIO.new("hello world")}))
462
+ }.should.not.raise(Rack::Lint::LintError)
463
+
464
+ lambda {
465
+ Rack::Lint.new(lambda { |env|
466
+ env["rack.input"].read(0)
467
+ [201, {"Content-type" => "text/plain", "Content-length" => "0"}, []]
468
+ }).call(env({"rack.input" => StringIO.new("hello world")}))
469
+ }.should.not.raise(Rack::Lint::LintError)
470
+
471
+ lambda {
472
+ Rack::Lint.new(lambda { |env|
473
+ env["rack.input"].read(1)
474
+ [201, {"Content-type" => "text/plain", "Content-length" => "0"}, []]
475
+ }).call(env({"rack.input" => StringIO.new("hello world")}))
476
+ }.should.not.raise(Rack::Lint::LintError)
477
+
478
+ lambda {
479
+ Rack::Lint.new(lambda { |env|
480
+ env["rack.input"].read(nil)
481
+ [201, {"Content-type" => "text/plain", "Content-length" => "0"}, []]
482
+ }).call(env({"rack.input" => StringIO.new("hello world")}))
483
+ }.should.not.raise(Rack::Lint::LintError)
484
+
485
+ lambda {
486
+ Rack::Lint.new(lambda { |env|
487
+ env["rack.input"].read(nil, '')
488
+ [201, {"Content-type" => "text/plain", "Content-length" => "0"}, []]
489
+ }).call(env({"rack.input" => StringIO.new("hello world")}))
490
+ }.should.not.raise(Rack::Lint::LintError)
491
+
492
+ lambda {
493
+ Rack::Lint.new(lambda { |env|
494
+ env["rack.input"].read(1, '')
495
+ [201, {"Content-type" => "text/plain", "Content-length" => "0"}, []]
496
+ }).call(env({"rack.input" => StringIO.new("hello world")}))
497
+ }.should.not.raise(Rack::Lint::LintError)
498
+ end
499
+ end
500
+
501
+ context "Rack::Lint::InputWrapper" do
502
+ specify "delegates :size to underlying IO object" do
503
+ class IOMock
504
+ def size
505
+ 101
506
+ end
507
+ end
508
+
509
+ wrapper = Rack::Lint::InputWrapper.new(IOMock.new)
510
+ wrapper.size.should == 101
511
+ end
512
+
513
+ specify "delegates :rewind to underlying IO object" do
514
+ io = StringIO.new("123")
515
+ wrapper = Rack::Lint::InputWrapper.new(io)
516
+ wrapper.read.should.equal "123"
517
+ wrapper.read.should.equal ""
518
+ wrapper.rewind
519
+ wrapper.read.should.equal "123"
520
+ end
521
+ 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
@@ -0,0 +1,38 @@
1
+ require 'test/spec'
2
+
3
+ require 'rack/mock'
4
+ require 'rack/lock'
5
+
6
+ context "Rack::Lock" do
7
+ class Lock
8
+ attr_reader :synchronized
9
+
10
+ def initialize
11
+ @synchronized = false
12
+ end
13
+
14
+ def synchronize
15
+ @synchronized = true
16
+ yield
17
+ end
18
+ end
19
+
20
+ specify "should call synchronize on lock" do
21
+ lock = Lock.new
22
+ env = Rack::MockRequest.env_for("/")
23
+ app = Rack::Lock.new(lambda { |env| }, lock)
24
+ lock.synchronized.should.equal false
25
+ app.call(env)
26
+ lock.synchronized.should.equal true
27
+ end
28
+
29
+ specify "should set multithread flag to false" do
30
+ app = Rack::Lock.new(lambda { |env| env['rack.multithread'] })
31
+ app.call(Rack::MockRequest.env_for("/")).should.equal false
32
+ end
33
+
34
+ specify "should reset original multithread flag when exiting lock" do
35
+ app = Rack::Lock.new(lambda { |env| env })
36
+ app.call(Rack::MockRequest.env_for("/"))['rack.multithread'].should.equal true
37
+ end
38
+ end