edgar-rack 1.2.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 (138) hide show
  1. data/COPYING +18 -0
  2. data/KNOWN-ISSUES +21 -0
  3. data/README +401 -0
  4. data/Rakefile +101 -0
  5. data/SPEC +171 -0
  6. data/bin/rackup +4 -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 +81 -0
  12. data/lib/rack/auth/abstract/handler.rb +37 -0
  13. data/lib/rack/auth/abstract/request.rb +43 -0
  14. data/lib/rack/auth/basic.rb +58 -0
  15. data/lib/rack/auth/digest/md5.rb +124 -0
  16. data/lib/rack/auth/digest/nonce.rb +51 -0
  17. data/lib/rack/auth/digest/params.rb +53 -0
  18. data/lib/rack/auth/digest/request.rb +40 -0
  19. data/lib/rack/builder.rb +80 -0
  20. data/lib/rack/cascade.rb +41 -0
  21. data/lib/rack/chunked.rb +52 -0
  22. data/lib/rack/commonlogger.rb +49 -0
  23. data/lib/rack/conditionalget.rb +63 -0
  24. data/lib/rack/config.rb +15 -0
  25. data/lib/rack/content_length.rb +29 -0
  26. data/lib/rack/content_type.rb +23 -0
  27. data/lib/rack/deflater.rb +96 -0
  28. data/lib/rack/directory.rb +157 -0
  29. data/lib/rack/etag.rb +59 -0
  30. data/lib/rack/file.rb +118 -0
  31. data/lib/rack/handler.rb +88 -0
  32. data/lib/rack/handler/cgi.rb +61 -0
  33. data/lib/rack/handler/evented_mongrel.rb +8 -0
  34. data/lib/rack/handler/fastcgi.rb +90 -0
  35. data/lib/rack/handler/lsws.rb +61 -0
  36. data/lib/rack/handler/mongrel.rb +90 -0
  37. data/lib/rack/handler/scgi.rb +59 -0
  38. data/lib/rack/handler/swiftiplied_mongrel.rb +8 -0
  39. data/lib/rack/handler/thin.rb +17 -0
  40. data/lib/rack/handler/webrick.rb +73 -0
  41. data/lib/rack/head.rb +19 -0
  42. data/lib/rack/lint.rb +567 -0
  43. data/lib/rack/lobster.rb +65 -0
  44. data/lib/rack/lock.rb +44 -0
  45. data/lib/rack/logger.rb +18 -0
  46. data/lib/rack/methodoverride.rb +27 -0
  47. data/lib/rack/mime.rb +210 -0
  48. data/lib/rack/mock.rb +185 -0
  49. data/lib/rack/nulllogger.rb +18 -0
  50. data/lib/rack/recursive.rb +61 -0
  51. data/lib/rack/reloader.rb +109 -0
  52. data/lib/rack/request.rb +307 -0
  53. data/lib/rack/response.rb +151 -0
  54. data/lib/rack/rewindable_input.rb +104 -0
  55. data/lib/rack/runtime.rb +27 -0
  56. data/lib/rack/sendfile.rb +139 -0
  57. data/lib/rack/server.rb +289 -0
  58. data/lib/rack/session/abstract/id.rb +348 -0
  59. data/lib/rack/session/cookie.rb +152 -0
  60. data/lib/rack/session/memcache.rb +93 -0
  61. data/lib/rack/session/pool.rb +79 -0
  62. data/lib/rack/showexceptions.rb +378 -0
  63. data/lib/rack/showstatus.rb +113 -0
  64. data/lib/rack/static.rb +53 -0
  65. data/lib/rack/urlmap.rb +55 -0
  66. data/lib/rack/utils.rb +698 -0
  67. data/rack.gemspec +39 -0
  68. data/test/cgi/lighttpd.conf +25 -0
  69. data/test/cgi/rackup_stub.rb +6 -0
  70. data/test/cgi/sample_rackup.ru +5 -0
  71. data/test/cgi/test +9 -0
  72. data/test/cgi/test.fcgi +8 -0
  73. data/test/cgi/test.ru +5 -0
  74. data/test/gemloader.rb +6 -0
  75. data/test/multipart/bad_robots +259 -0
  76. data/test/multipart/binary +0 -0
  77. data/test/multipart/empty +10 -0
  78. data/test/multipart/fail_16384_nofile +814 -0
  79. data/test/multipart/file1.txt +1 -0
  80. data/test/multipart/filename_and_modification_param +7 -0
  81. data/test/multipart/filename_with_escaped_quotes +6 -0
  82. data/test/multipart/filename_with_escaped_quotes_and_modification_param +7 -0
  83. data/test/multipart/filename_with_percent_escaped_quotes +6 -0
  84. data/test/multipart/filename_with_unescaped_quotes +6 -0
  85. data/test/multipart/ie +6 -0
  86. data/test/multipart/nested +10 -0
  87. data/test/multipart/none +9 -0
  88. data/test/multipart/semicolon +6 -0
  89. data/test/multipart/text +15 -0
  90. data/test/rackup/config.ru +31 -0
  91. data/test/spec_auth_basic.rb +70 -0
  92. data/test/spec_auth_digest.rb +241 -0
  93. data/test/spec_builder.rb +123 -0
  94. data/test/spec_cascade.rb +45 -0
  95. data/test/spec_cgi.rb +102 -0
  96. data/test/spec_chunked.rb +60 -0
  97. data/test/spec_commonlogger.rb +56 -0
  98. data/test/spec_conditionalget.rb +86 -0
  99. data/test/spec_config.rb +23 -0
  100. data/test/spec_content_length.rb +36 -0
  101. data/test/spec_content_type.rb +29 -0
  102. data/test/spec_deflater.rb +125 -0
  103. data/test/spec_directory.rb +57 -0
  104. data/test/spec_etag.rb +75 -0
  105. data/test/spec_fastcgi.rb +107 -0
  106. data/test/spec_file.rb +92 -0
  107. data/test/spec_handler.rb +49 -0
  108. data/test/spec_head.rb +30 -0
  109. data/test/spec_lint.rb +515 -0
  110. data/test/spec_lobster.rb +43 -0
  111. data/test/spec_lock.rb +142 -0
  112. data/test/spec_logger.rb +28 -0
  113. data/test/spec_methodoverride.rb +58 -0
  114. data/test/spec_mock.rb +241 -0
  115. data/test/spec_mongrel.rb +182 -0
  116. data/test/spec_nulllogger.rb +12 -0
  117. data/test/spec_recursive.rb +69 -0
  118. data/test/spec_request.rb +774 -0
  119. data/test/spec_response.rb +245 -0
  120. data/test/spec_rewindable_input.rb +118 -0
  121. data/test/spec_runtime.rb +39 -0
  122. data/test/spec_sendfile.rb +83 -0
  123. data/test/spec_server.rb +8 -0
  124. data/test/spec_session_abstract_id.rb +43 -0
  125. data/test/spec_session_cookie.rb +171 -0
  126. data/test/spec_session_memcache.rb +289 -0
  127. data/test/spec_session_pool.rb +200 -0
  128. data/test/spec_showexceptions.rb +87 -0
  129. data/test/spec_showstatus.rb +79 -0
  130. data/test/spec_static.rb +48 -0
  131. data/test/spec_thin.rb +86 -0
  132. data/test/spec_urlmap.rb +213 -0
  133. data/test/spec_utils.rb +678 -0
  134. data/test/spec_webrick.rb +141 -0
  135. data/test/testrequest.rb +78 -0
  136. data/test/unregistered_handler/rack/handler/unregistered.rb +7 -0
  137. data/test/unregistered_handler/rack/handler/unregistered_long_one.rb +7 -0
  138. metadata +329 -0
@@ -0,0 +1,245 @@
1
+ require 'set'
2
+ require 'rack/response'
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 "Content-Type" => "text/html"
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 "Content-Type" => "text/html"
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 "text/html"
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 2109" 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 delete cookies" do
89
+ response = Rack::Response.new
90
+ response.set_cookie "foo", "bar"
91
+ response.set_cookie "foo2", "bar2"
92
+ response.delete_cookie "foo"
93
+ response["Set-Cookie"].should.equal [
94
+ "foo2=bar2",
95
+ "foo=; expires=Thu, 01-Jan-1970 00:00:00 GMT"
96
+ ].join("\n")
97
+ end
98
+
99
+ it "can delete cookies with the same name from multiple domains" do
100
+ response = Rack::Response.new
101
+ response.set_cookie "foo", {:value => "bar", :domain => "sample.example.com"}
102
+ response.set_cookie "foo", {:value => "bar", :domain => ".example.com"}
103
+ response["Set-Cookie"].should.equal ["foo=bar; domain=sample.example.com", "foo=bar; domain=.example.com"].join("\n")
104
+ response.delete_cookie "foo", :domain => ".example.com"
105
+ response["Set-Cookie"].should.equal ["foo=bar; domain=sample.example.com", "foo=; domain=.example.com; expires=Thu, 01-Jan-1970 00:00:00 GMT"].join("\n")
106
+ response.delete_cookie "foo", :domain => "sample.example.com"
107
+ response["Set-Cookie"].should.equal ["foo=; domain=.example.com; expires=Thu, 01-Jan-1970 00:00:00 GMT",
108
+ "foo=; domain=sample.example.com; expires=Thu, 01-Jan-1970 00:00:00 GMT"].join("\n")
109
+ end
110
+
111
+ it "can do redirects" do
112
+ response = Rack::Response.new
113
+ response.redirect "/foo"
114
+ status, header, body = response.finish
115
+
116
+ status.should.equal 302
117
+ header["Location"].should.equal "/foo"
118
+
119
+ response = Rack::Response.new
120
+ response.redirect "/foo", 307
121
+ status, header, body = response.finish
122
+
123
+ status.should.equal 307
124
+ end
125
+
126
+ it "has a useful constructor" do
127
+ r = Rack::Response.new("foo")
128
+ status, header, body = r.finish
129
+ str = ""; body.each { |part| str << part }
130
+ str.should.equal "foo"
131
+
132
+ r = Rack::Response.new(["foo", "bar"])
133
+ status, header, body = r.finish
134
+ str = ""; body.each { |part| str << part }
135
+ str.should.equal "foobar"
136
+
137
+ r = Rack::Response.new(["foo", "bar"].to_set)
138
+ r.write "foo"
139
+ status, header, body = r.finish
140
+ str = ""; body.each { |part| str << part }
141
+ str.should.equal "foobarfoo"
142
+
143
+ r = Rack::Response.new([], 500)
144
+ r.status.should.equal 500
145
+
146
+ r = Rack::Response.new([], "200 OK")
147
+ r.status.should.equal 200
148
+ end
149
+
150
+ it "has a constructor that can take a block" do
151
+ r = Rack::Response.new { |res|
152
+ res.status = 404
153
+ res.write "foo"
154
+ }
155
+ status, _, body = r.finish
156
+ str = ""; body.each { |part| str << part }
157
+ str.should.equal "foo"
158
+ status.should.equal 404
159
+ end
160
+
161
+ it "doesn't return invalid responses" do
162
+ r = Rack::Response.new(["foo", "bar"], 204)
163
+ _, header, body = r.finish
164
+ str = ""; body.each { |part| str << part }
165
+ str.should.be.empty
166
+ header["Content-Type"].should.equal nil
167
+
168
+ lambda {
169
+ Rack::Response.new(Object.new)
170
+ }.should.raise(TypeError).
171
+ message.should =~ /stringable or iterable required/
172
+ end
173
+
174
+ it "knows if it's empty" do
175
+ r = Rack::Response.new
176
+ r.should.be.empty
177
+ r.write "foo"
178
+ r.should.not.be.empty
179
+
180
+ r = Rack::Response.new
181
+ r.should.be.empty
182
+ r.finish
183
+ r.should.be.empty
184
+
185
+ r = Rack::Response.new
186
+ r.should.be.empty
187
+ r.finish { }
188
+ r.should.not.be.empty
189
+ end
190
+
191
+ should "provide access to the HTTP status" do
192
+ res = Rack::Response.new
193
+ res.status = 200
194
+ res.should.be.successful
195
+ res.should.be.ok
196
+
197
+ res.status = 404
198
+ res.should.not.be.successful
199
+ res.should.be.client_error
200
+ res.should.be.not_found
201
+
202
+ res.status = 501
203
+ res.should.not.be.successful
204
+ res.should.be.server_error
205
+
206
+ res.status = 307
207
+ res.should.be.redirect
208
+ end
209
+
210
+ should "provide access to the HTTP headers" do
211
+ res = Rack::Response.new
212
+ res["Content-Type"] = "text/yaml"
213
+
214
+ res.should.include "Content-Type"
215
+ res.headers["Content-Type"].should.equal "text/yaml"
216
+ res["Content-Type"].should.equal "text/yaml"
217
+ res.content_type.should.equal "text/yaml"
218
+ res.content_length.should.be.nil
219
+ res.location.should.be.nil
220
+ end
221
+
222
+ it "does not add or change Content-Length when #finish()ing" do
223
+ res = Rack::Response.new
224
+ res.status = 200
225
+ res.finish
226
+ res.headers["Content-Length"].should.be.nil
227
+
228
+ res = Rack::Response.new
229
+ res.status = 200
230
+ res.headers["Content-Length"] = "10"
231
+ res.finish
232
+ res.headers["Content-Length"].should.equal "10"
233
+ end
234
+
235
+ it "updates Content-Length when body appended to using #write" do
236
+ res = Rack::Response.new
237
+ res.status = 200
238
+ res.headers["Content-Length"].should.be.nil
239
+ res.write "Hi"
240
+ res.headers["Content-Length"].should.equal "2"
241
+ res.write " there"
242
+ res.headers["Content-Length"].should.equal "8"
243
+ end
244
+
245
+ 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
@@ -0,0 +1,39 @@
1
+ require 'rack/runtime'
2
+
3
+ describe Rack::Runtime do
4
+ it "sets X-Runtime is none is set" do
5
+ app = lambda { |env| [200, {'Content-Type' => 'text/plain'}, "Hello, World!"] }
6
+ response = Rack::Runtime.new(app).call({})
7
+ response[1]['X-Runtime'].should =~ /[\d\.]+/
8
+ end
9
+
10
+ it "doesn't set the X-Runtime if it is already set" do
11
+ app = lambda { |env| [200, {'Content-Type' => 'text/plain', "X-Runtime" => "foobar"}, "Hello, World!"] }
12
+ response = Rack::Runtime.new(app).call({})
13
+ response[1]['X-Runtime'].should == "foobar"
14
+ end
15
+
16
+ should "allow a suffix to be set" do
17
+ app = lambda { |env| [200, {'Content-Type' => 'text/plain'}, "Hello, World!"] }
18
+ response = Rack::Runtime.new(app, "Test").call({})
19
+ response[1]['X-Runtime-Test'].should =~ /[\d\.]+/
20
+ end
21
+
22
+ should "allow multiple timers to be set" do
23
+ app = lambda { |env| sleep 0.1; [200, {'Content-Type' => 'text/plain'}, "Hello, World!"] }
24
+ runtime = Rack::Runtime.new(app, "App")
25
+
26
+ # wrap many times to guarantee a measurable difference
27
+ 100.times do |i|
28
+ runtime = Rack::Runtime.new(runtime, i.to_s)
29
+ end
30
+ runtime = Rack::Runtime.new(runtime, "All")
31
+
32
+ response = runtime.call({})
33
+
34
+ response[1]['X-Runtime-App'].should =~ /[\d\.]+/
35
+ response[1]['X-Runtime-All'].should =~ /[\d\.]+/
36
+
37
+ Float(response[1]['X-Runtime-All']).should > Float(response[1]['X-Runtime-App'])
38
+ end
39
+ end
@@ -0,0 +1,83 @@
1
+ require 'rack/sendfile'
2
+ require 'rack/mock'
3
+
4
+ describe Rack::File do
5
+ should "respond to #to_path" do
6
+ Rack::File.new(Dir.pwd).should.respond_to :to_path
7
+ end
8
+ end
9
+
10
+ describe Rack::Sendfile do
11
+ def sendfile_body
12
+ res = ['Hello World']
13
+ def res.to_path ; "/tmp/hello.txt" ; end
14
+ res
15
+ end
16
+
17
+ def simple_app(body=sendfile_body)
18
+ lambda { |env| [200, {'Content-Type' => 'text/plain'}, body] }
19
+ end
20
+
21
+ def sendfile_app(body=sendfile_body)
22
+ Rack::Sendfile.new(simple_app(body))
23
+ end
24
+
25
+ @request = Rack::MockRequest.new(sendfile_app)
26
+
27
+ def request(headers={})
28
+ yield @request.get('/', headers)
29
+ end
30
+
31
+ it "does nothing when no X-Sendfile-Type header present" do
32
+ request do |response|
33
+ response.should.be.ok
34
+ response.body.should.equal 'Hello World'
35
+ response.headers.should.not.include 'X-Sendfile'
36
+ end
37
+ end
38
+
39
+ it "sets X-Sendfile response header and discards body" do
40
+ request 'HTTP_X_SENDFILE_TYPE' => 'X-Sendfile' do |response|
41
+ response.should.be.ok
42
+ response.body.should.be.empty
43
+ response.headers['X-Sendfile'].should.equal '/tmp/hello.txt'
44
+ end
45
+ end
46
+
47
+ it "sets X-Lighttpd-Send-File response header and discards body" do
48
+ request 'HTTP_X_SENDFILE_TYPE' => 'X-Lighttpd-Send-File' do |response|
49
+ response.should.be.ok
50
+ response.body.should.be.empty
51
+ response.headers['X-Lighttpd-Send-File'].should.equal '/tmp/hello.txt'
52
+ end
53
+ end
54
+
55
+ it "sets X-Accel-Redirect response header and discards body" do
56
+ headers = {
57
+ 'HTTP_X_SENDFILE_TYPE' => 'X-Accel-Redirect',
58
+ 'HTTP_X_ACCEL_MAPPING' => '/tmp/=/foo/bar/'
59
+ }
60
+ request headers do |response|
61
+ response.should.be.ok
62
+ response.body.should.be.empty
63
+ response.headers['X-Accel-Redirect'].should.equal '/foo/bar/hello.txt'
64
+ end
65
+ end
66
+
67
+ it 'writes to rack.error when no X-Accel-Mapping is specified' do
68
+ request 'HTTP_X_SENDFILE_TYPE' => 'X-Accel-Redirect' do |response|
69
+ response.should.be.ok
70
+ response.body.should.equal 'Hello World'
71
+ response.headers.should.not.include 'X-Accel-Redirect'
72
+ response.errors.should.include 'X-Accel-Mapping'
73
+ end
74
+ end
75
+
76
+ it 'does nothing when body does not respond to #to_path' do
77
+ @request = Rack::MockRequest.new(sendfile_app(['Not a file...']))
78
+ request 'HTTP_X_SENDFILE_TYPE' => 'X-Sendfile' do |response|
79
+ response.body.should.equal 'Not a file...'
80
+ response.headers.should.not.include 'X-Sendfile'
81
+ end
82
+ end
83
+ end