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,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