lack 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. checksums.yaml +7 -0
  2. data/bin/rackup +5 -0
  3. data/lib/rack.rb +26 -0
  4. data/lib/rack/body_proxy.rb +39 -0
  5. data/lib/rack/builder.rb +166 -0
  6. data/lib/rack/handler.rb +63 -0
  7. data/lib/rack/handler/webrick.rb +120 -0
  8. data/lib/rack/mime.rb +661 -0
  9. data/lib/rack/mock.rb +198 -0
  10. data/lib/rack/multipart.rb +31 -0
  11. data/lib/rack/multipart/generator.rb +93 -0
  12. data/lib/rack/multipart/parser.rb +239 -0
  13. data/lib/rack/multipart/uploaded_file.rb +34 -0
  14. data/lib/rack/request.rb +394 -0
  15. data/lib/rack/response.rb +160 -0
  16. data/lib/rack/server.rb +258 -0
  17. data/lib/rack/server/options.rb +121 -0
  18. data/lib/rack/utils.rb +653 -0
  19. data/lib/rack/version.rb +3 -0
  20. data/spec/spec_helper.rb +1 -0
  21. data/test/builder/anything.rb +5 -0
  22. data/test/builder/comment.ru +4 -0
  23. data/test/builder/end.ru +5 -0
  24. data/test/builder/line.ru +1 -0
  25. data/test/builder/options.ru +2 -0
  26. data/test/multipart/bad_robots +259 -0
  27. data/test/multipart/binary +0 -0
  28. data/test/multipart/content_type_and_no_filename +6 -0
  29. data/test/multipart/empty +10 -0
  30. data/test/multipart/fail_16384_nofile +814 -0
  31. data/test/multipart/file1.txt +1 -0
  32. data/test/multipart/filename_and_modification_param +7 -0
  33. data/test/multipart/filename_and_no_name +6 -0
  34. data/test/multipart/filename_with_escaped_quotes +6 -0
  35. data/test/multipart/filename_with_escaped_quotes_and_modification_param +7 -0
  36. data/test/multipart/filename_with_percent_escaped_quotes +6 -0
  37. data/test/multipart/filename_with_unescaped_percentages +6 -0
  38. data/test/multipart/filename_with_unescaped_percentages2 +6 -0
  39. data/test/multipart/filename_with_unescaped_percentages3 +6 -0
  40. data/test/multipart/filename_with_unescaped_quotes +6 -0
  41. data/test/multipart/ie +6 -0
  42. data/test/multipart/invalid_character +6 -0
  43. data/test/multipart/mixed_files +21 -0
  44. data/test/multipart/nested +10 -0
  45. data/test/multipart/none +9 -0
  46. data/test/multipart/semicolon +6 -0
  47. data/test/multipart/text +15 -0
  48. data/test/multipart/webkit +32 -0
  49. data/test/rackup/config.ru +31 -0
  50. data/test/registering_handler/rack/handler/registering_myself.rb +8 -0
  51. data/test/spec_body_proxy.rb +69 -0
  52. data/test/spec_builder.rb +223 -0
  53. data/test/spec_chunked.rb +101 -0
  54. data/test/spec_file.rb +221 -0
  55. data/test/spec_handler.rb +59 -0
  56. data/test/spec_head.rb +45 -0
  57. data/test/spec_lint.rb +522 -0
  58. data/test/spec_mime.rb +51 -0
  59. data/test/spec_mock.rb +277 -0
  60. data/test/spec_multipart.rb +547 -0
  61. data/test/spec_recursive.rb +72 -0
  62. data/test/spec_request.rb +1199 -0
  63. data/test/spec_response.rb +343 -0
  64. data/test/spec_rewindable_input.rb +118 -0
  65. data/test/spec_sendfile.rb +130 -0
  66. data/test/spec_server.rb +167 -0
  67. data/test/spec_utils.rb +635 -0
  68. data/test/spec_webrick.rb +184 -0
  69. data/test/testrequest.rb +78 -0
  70. data/test/unregistered_handler/rack/handler/unregistered.rb +7 -0
  71. data/test/unregistered_handler/rack/handler/unregistered_long_one.rb +7 -0
  72. metadata +240 -0
@@ -0,0 +1,343 @@
1
+ require 'rack/response'
2
+ require 'stringio'
3
+
4
+ describe Rack::Response do
5
+ should "have sensible default values" do
6
+ response = Rack::Response.new
7
+ status, header, body = response.finish
8
+ status.should.equal 200
9
+ header.should.equal({})
10
+ body.each { |part|
11
+ part.should.equal ""
12
+ }
13
+
14
+ response = Rack::Response.new
15
+ status, header, body = *response
16
+ status.should.equal 200
17
+ header.should.equal({})
18
+ body.each { |part|
19
+ part.should.equal ""
20
+ }
21
+ end
22
+
23
+ it "can be written to" do
24
+ response = Rack::Response.new
25
+
26
+ _, _, body = response.finish do
27
+ response.write "foo"
28
+ response.write "bar"
29
+ response.write "baz"
30
+ end
31
+
32
+ parts = []
33
+ body.each { |part| parts << part }
34
+
35
+ parts.should.equal ["foo", "bar", "baz"]
36
+ end
37
+
38
+ it "can set and read headers" do
39
+ response = Rack::Response.new
40
+ response["Content-Type"].should.equal nil
41
+ response["Content-Type"] = "text/plain"
42
+ response["Content-Type"].should.equal "text/plain"
43
+ end
44
+
45
+ it "can override the initial Content-Type with a different case" do
46
+ response = Rack::Response.new("", 200, "content-type" => "text/plain")
47
+ response["Content-Type"].should.equal "text/plain"
48
+ end
49
+
50
+ it "can set cookies" do
51
+ response = Rack::Response.new
52
+
53
+ response.set_cookie "foo", "bar"
54
+ response["Set-Cookie"].should.equal "foo=bar"
55
+ response.set_cookie "foo2", "bar2"
56
+ response["Set-Cookie"].should.equal ["foo=bar", "foo2=bar2"].join("\n")
57
+ response.set_cookie "foo3", "bar3"
58
+ response["Set-Cookie"].should.equal ["foo=bar", "foo2=bar2", "foo3=bar3"].join("\n")
59
+ end
60
+
61
+ it "can set cookies with the same name for multiple domains" do
62
+ response = Rack::Response.new
63
+ response.set_cookie "foo", {:value => "bar", :domain => "sample.example.com"}
64
+ response.set_cookie "foo", {:value => "bar", :domain => ".example.com"}
65
+ response["Set-Cookie"].should.equal ["foo=bar; domain=sample.example.com", "foo=bar; domain=.example.com"].join("\n")
66
+ end
67
+
68
+ it "formats the Cookie expiration date accordingly to RFC 6265" do
69
+ response = Rack::Response.new
70
+
71
+ response.set_cookie "foo", {:value => "bar", :expires => Time.now+10}
72
+ response["Set-Cookie"].should.match(
73
+ /expires=..., \d\d ... \d\d\d\d \d\d:\d\d:\d\d .../)
74
+ end
75
+
76
+ it "can set secure cookies" do
77
+ response = Rack::Response.new
78
+ response.set_cookie "foo", {:value => "bar", :secure => true}
79
+ response["Set-Cookie"].should.equal "foo=bar; secure"
80
+ end
81
+
82
+ it "can set http only cookies" do
83
+ response = Rack::Response.new
84
+ response.set_cookie "foo", {:value => "bar", :httponly => true}
85
+ response["Set-Cookie"].should.equal "foo=bar; HttpOnly"
86
+ end
87
+
88
+ it "can set http only cookies with :http_only" do
89
+ response = Rack::Response.new
90
+ response.set_cookie "foo", {:value => "bar", :http_only => true}
91
+ response["Set-Cookie"].should.equal "foo=bar; HttpOnly"
92
+ end
93
+
94
+ it "can set prefers :httponly for http only cookie setting when :httponly and :http_only provided" do
95
+ response = Rack::Response.new
96
+ response.set_cookie "foo", {:value => "bar", :httponly => false, :http_only => true}
97
+ response["Set-Cookie"].should.equal "foo=bar"
98
+ end
99
+
100
+ it "can delete cookies" do
101
+ response = Rack::Response.new
102
+ response.set_cookie "foo", "bar"
103
+ response.set_cookie "foo2", "bar2"
104
+ response.delete_cookie "foo"
105
+ response["Set-Cookie"].should.equal [
106
+ "foo2=bar2",
107
+ "foo=; max-age=0; expires=Thu, 01 Jan 1970 00:00:00 -0000"
108
+ ].join("\n")
109
+ end
110
+
111
+ it "can delete cookies with the same name from multiple domains" do
112
+ response = Rack::Response.new
113
+ response.set_cookie "foo", {:value => "bar", :domain => "sample.example.com"}
114
+ response.set_cookie "foo", {:value => "bar", :domain => ".example.com"}
115
+ response["Set-Cookie"].should.equal ["foo=bar; domain=sample.example.com", "foo=bar; domain=.example.com"].join("\n")
116
+ response.delete_cookie "foo", :domain => ".example.com"
117
+ response["Set-Cookie"].should.equal ["foo=bar; domain=sample.example.com", "foo=; domain=.example.com; max-age=0; expires=Thu, 01 Jan 1970 00:00:00 -0000"].join("\n")
118
+ response.delete_cookie "foo", :domain => "sample.example.com"
119
+ response["Set-Cookie"].should.equal ["foo=; domain=.example.com; max-age=0; expires=Thu, 01 Jan 1970 00:00:00 -0000",
120
+ "foo=; domain=sample.example.com; max-age=0; expires=Thu, 01 Jan 1970 00:00:00 -0000"].join("\n")
121
+ end
122
+
123
+ it "can delete cookies with the same name with different paths" do
124
+ response = Rack::Response.new
125
+ response.set_cookie "foo", {:value => "bar", :path => "/"}
126
+ response.set_cookie "foo", {:value => "bar", :path => "/path"}
127
+ response["Set-Cookie"].should.equal ["foo=bar; path=/",
128
+ "foo=bar; path=/path"].join("\n")
129
+
130
+ response.delete_cookie "foo", :path => "/path"
131
+ response["Set-Cookie"].should.equal ["foo=bar; path=/",
132
+ "foo=; path=/path; max-age=0; expires=Thu, 01 Jan 1970 00:00:00 -0000"].join("\n")
133
+ end
134
+
135
+ it "can do redirects" do
136
+ response = Rack::Response.new
137
+ response.redirect "/foo"
138
+ status, header, body = response.finish
139
+ status.should.equal 302
140
+ header["Location"].should.equal "/foo"
141
+
142
+ response = Rack::Response.new
143
+ response.redirect "/foo", 307
144
+ status, header, body = response.finish
145
+
146
+ status.should.equal 307
147
+ end
148
+
149
+ it "has a useful constructor" do
150
+ r = Rack::Response.new("foo")
151
+ status, header, body = r.finish
152
+ str = ""; body.each { |part| str << part }
153
+ str.should.equal "foo"
154
+
155
+ r = Rack::Response.new(["foo", "bar"])
156
+ status, header, body = r.finish
157
+ str = ""; body.each { |part| str << part }
158
+ str.should.equal "foobar"
159
+
160
+ object_with_each = Object.new
161
+ def object_with_each.each
162
+ yield "foo"
163
+ yield "bar"
164
+ end
165
+ r = Rack::Response.new(object_with_each)
166
+ r.write "foo"
167
+ status, header, body = r.finish
168
+ str = ""; body.each { |part| str << part }
169
+ str.should.equal "foobarfoo"
170
+
171
+ r = Rack::Response.new([], 500)
172
+ r.status.should.equal 500
173
+
174
+ r = Rack::Response.new([], "200 OK")
175
+ r.status.should.equal 200
176
+ end
177
+
178
+ it "has a constructor that can take a block" do
179
+ r = Rack::Response.new { |res|
180
+ res.status = 404
181
+ res.write "foo"
182
+ }
183
+ status, _, body = r.finish
184
+ str = ""; body.each { |part| str << part }
185
+ str.should.equal "foo"
186
+ status.should.equal 404
187
+ end
188
+
189
+ it "doesn't return invalid responses" do
190
+ r = Rack::Response.new(["foo", "bar"], 204)
191
+ _, header, body = r.finish
192
+ str = ""; body.each { |part| str << part }
193
+ str.should.be.empty
194
+ header["Content-Type"].should.equal nil
195
+ header['Content-Length'].should.equal nil
196
+
197
+ lambda {
198
+ Rack::Response.new(Object.new)
199
+ }.should.raise(TypeError).
200
+ message.should =~ /stringable or iterable required/
201
+ end
202
+
203
+ it "knows if it's empty" do
204
+ r = Rack::Response.new
205
+ r.should.be.empty
206
+ r.write "foo"
207
+ r.should.not.be.empty
208
+
209
+ r = Rack::Response.new
210
+ r.should.be.empty
211
+ r.finish
212
+ r.should.be.empty
213
+
214
+ r = Rack::Response.new
215
+ r.should.be.empty
216
+ r.finish { }
217
+ r.should.not.be.empty
218
+ end
219
+
220
+ should "provide access to the HTTP status" do
221
+ res = Rack::Response.new
222
+ res.status = 200
223
+ res.should.be.successful
224
+ res.should.be.ok
225
+
226
+ res.status = 201
227
+ res.should.be.successful
228
+ res.should.be.created
229
+
230
+ res.status = 202
231
+ res.should.be.successful
232
+ res.should.be.accepted
233
+
234
+ res.status = 400
235
+ res.should.not.be.successful
236
+ res.should.be.client_error
237
+ res.should.be.bad_request
238
+
239
+ res.status = 401
240
+ res.should.not.be.successful
241
+ res.should.be.client_error
242
+ res.should.be.unauthorized
243
+
244
+ res.status = 404
245
+ res.should.not.be.successful
246
+ res.should.be.client_error
247
+ res.should.be.not_found
248
+
249
+ res.status = 405
250
+ res.should.not.be.successful
251
+ res.should.be.client_error
252
+ res.should.be.method_not_allowed
253
+
254
+ res.status = 418
255
+ res.should.not.be.successful
256
+ res.should.be.client_error
257
+ res.should.be.i_m_a_teapot
258
+
259
+ res.status = 422
260
+ res.should.not.be.successful
261
+ res.should.be.client_error
262
+ res.should.be.unprocessable
263
+
264
+ res.status = 501
265
+ res.should.not.be.successful
266
+ res.should.be.server_error
267
+
268
+ res.status = 307
269
+ res.should.be.redirect
270
+ end
271
+
272
+ should "provide access to the HTTP headers" do
273
+ res = Rack::Response.new
274
+ res["Content-Type"] = "text/yaml"
275
+
276
+ res.should.include "Content-Type"
277
+ res.headers["Content-Type"].should.equal "text/yaml"
278
+ res["Content-Type"].should.equal "text/yaml"
279
+ res.content_type.should.equal "text/yaml"
280
+ res.content_length.should.be.nil
281
+ res.location.should.be.nil
282
+ end
283
+
284
+ it "does not add or change Content-Length when #finish()ing" do
285
+ res = Rack::Response.new
286
+ res.status = 200
287
+ res.finish
288
+ res.headers["Content-Length"].should.be.nil
289
+
290
+ res = Rack::Response.new
291
+ res.status = 200
292
+ res.headers["Content-Length"] = "10"
293
+ res.finish
294
+ res.headers["Content-Length"].should.equal "10"
295
+ end
296
+
297
+ it "updates Content-Length when body appended to using #write" do
298
+ res = Rack::Response.new
299
+ res.status = 200
300
+ res.headers["Content-Length"].should.be.nil
301
+ res.write "Hi"
302
+ res.headers["Content-Length"].should.equal "2"
303
+ res.write " there"
304
+ res.headers["Content-Length"].should.equal "8"
305
+ end
306
+
307
+ it "calls close on #body" do
308
+ res = Rack::Response.new
309
+ res.body = StringIO.new
310
+ res.close
311
+ res.body.should.be.closed
312
+ end
313
+
314
+ it "calls close on #body when 204, 205, or 304" do
315
+ res = Rack::Response.new
316
+ res.body = StringIO.new
317
+ res.finish
318
+ res.body.should.not.be.closed
319
+
320
+ res.status = 204
321
+ _, _, b = res.finish
322
+ res.body.should.be.closed
323
+ b.should.not.equal res.body
324
+
325
+ res.body = StringIO.new
326
+ res.status = 205
327
+ _, _, b = res.finish
328
+ res.body.should.be.closed
329
+ b.should.not.equal res.body
330
+
331
+ res.body = StringIO.new
332
+ res.status = 304
333
+ _, _, b = res.finish
334
+ res.body.should.be.closed
335
+ b.should.not.equal res.body
336
+ end
337
+
338
+ it "wraps the body from #to_ary to prevent infinite loops" do
339
+ res = Rack::Response.new
340
+ res.finish.last.should.not.respond_to?(:to_ary)
341
+ lambda { res.finish.last.to_ary }.should.raise(NoMethodError)
342
+ end
343
+ end
@@ -0,0 +1,118 @@
1
+ require 'stringio'
2
+ require 'rack/rewindable_input'
3
+
4
+ shared "a rewindable IO object" do
5
+ before do
6
+ @rio = Rack::RewindableInput.new(@io)
7
+ end
8
+
9
+ should "be able to handle to read()" do
10
+ @rio.read.should.equal "hello world"
11
+ end
12
+
13
+ should "be able to handle to read(nil)" do
14
+ @rio.read(nil).should.equal "hello world"
15
+ end
16
+
17
+ should "be able to handle to read(length)" do
18
+ @rio.read(1).should.equal "h"
19
+ end
20
+
21
+ should "be able to handle to read(length, buffer)" do
22
+ buffer = ""
23
+ result = @rio.read(1, buffer)
24
+ result.should.equal "h"
25
+ result.object_id.should.equal buffer.object_id
26
+ end
27
+
28
+ should "be able to handle to read(nil, buffer)" do
29
+ buffer = ""
30
+ result = @rio.read(nil, buffer)
31
+ result.should.equal "hello world"
32
+ result.object_id.should.equal buffer.object_id
33
+ end
34
+
35
+ should "rewind to the beginning when #rewind is called" do
36
+ @rio.read(1)
37
+ @rio.rewind
38
+ @rio.read.should.equal "hello world"
39
+ end
40
+
41
+ should "be able to handle gets" do
42
+ @rio.gets.should == "hello world"
43
+ end
44
+
45
+ should "be able to handle each" do
46
+ array = []
47
+ @rio.each do |data|
48
+ array << data
49
+ end
50
+ array.should.equal(["hello world"])
51
+ end
52
+
53
+ should "not buffer into a Tempfile if no data has been read yet" do
54
+ @rio.instance_variable_get(:@rewindable_io).should.be.nil
55
+ end
56
+
57
+ should "buffer into a Tempfile when data has been consumed for the first time" do
58
+ @rio.read(1)
59
+ tempfile = @rio.instance_variable_get(:@rewindable_io)
60
+ tempfile.should.not.be.nil
61
+ @rio.read(1)
62
+ tempfile2 = @rio.instance_variable_get(:@rewindable_io)
63
+ tempfile2.path.should == tempfile.path
64
+ end
65
+
66
+ should "close the underlying tempfile upon calling #close" do
67
+ @rio.read(1)
68
+ tempfile = @rio.instance_variable_get(:@rewindable_io)
69
+ @rio.close
70
+ tempfile.should.be.closed
71
+ end
72
+
73
+ should "be possible to call #close when no data has been buffered yet" do
74
+ lambda{ @rio.close }.should.not.raise
75
+ end
76
+
77
+ should "be possible to call #close multiple times" do
78
+ lambda{
79
+ @rio.close
80
+ @rio.close
81
+ }.should.not.raise
82
+ end
83
+
84
+ @rio.close
85
+ @rio = nil
86
+ end
87
+
88
+ describe Rack::RewindableInput do
89
+ describe "given an IO object that is already rewindable" do
90
+ before do
91
+ @io = StringIO.new("hello world")
92
+ end
93
+
94
+ behaves_like "a rewindable IO object"
95
+ end
96
+
97
+ describe "given an IO object that is not rewindable" do
98
+ before do
99
+ @io = StringIO.new("hello world")
100
+ @io.instance_eval do
101
+ undef :rewind
102
+ end
103
+ end
104
+
105
+ behaves_like "a rewindable IO object"
106
+ end
107
+
108
+ describe "given an IO object whose rewind method raises Errno::ESPIPE" do
109
+ before do
110
+ @io = StringIO.new("hello world")
111
+ def @io.rewind
112
+ raise Errno::ESPIPE, "You can't rewind this!"
113
+ end
114
+ end
115
+
116
+ behaves_like "a rewindable IO object"
117
+ end
118
+ end