rack 1.1.6 → 1.6.9

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 (212) hide show
  1. checksums.yaml +7 -0
  2. data/COPYING +1 -1
  3. data/HISTORY.md +375 -0
  4. data/KNOWN-ISSUES +23 -0
  5. data/README.rdoc +312 -0
  6. data/Rakefile +124 -0
  7. data/SPEC +125 -32
  8. data/contrib/rack.png +0 -0
  9. data/contrib/rack.svg +150 -0
  10. data/contrib/rack_logo.svg +1 -1
  11. data/contrib/rdoc.css +412 -0
  12. data/example/protectedlobster.rb +1 -1
  13. data/lib/rack/auth/abstract/handler.rb +4 -4
  14. data/lib/rack/auth/abstract/request.rb +7 -5
  15. data/lib/rack/auth/basic.rb +1 -1
  16. data/lib/rack/auth/digest/md5.rb +7 -3
  17. data/lib/rack/auth/digest/nonce.rb +1 -1
  18. data/lib/rack/auth/digest/params.rb +7 -9
  19. data/lib/rack/auth/digest/request.rb +10 -9
  20. data/lib/rack/backports/uri/common_18.rb +56 -0
  21. data/lib/rack/backports/uri/common_192.rb +52 -0
  22. data/lib/rack/backports/uri/common_193.rb +29 -0
  23. data/lib/rack/body_proxy.rb +39 -0
  24. data/lib/rack/builder.rb +106 -22
  25. data/lib/rack/cascade.rb +17 -6
  26. data/lib/rack/chunked.rb +44 -24
  27. data/lib/rack/commonlogger.rb +36 -13
  28. data/lib/rack/conditionalget.rb +49 -17
  29. data/lib/rack/config.rb +5 -0
  30. data/lib/rack/content_length.rb +14 -6
  31. data/lib/rack/content_type.rb +7 -1
  32. data/lib/rack/deflater.rb +73 -15
  33. data/lib/rack/directory.rb +18 -8
  34. data/lib/rack/etag.rb +59 -9
  35. data/lib/rack/file.rb +106 -44
  36. data/lib/rack/handler/cgi.rb +11 -11
  37. data/lib/rack/handler/fastcgi.rb +18 -6
  38. data/lib/rack/handler/lsws.rb +2 -4
  39. data/lib/rack/handler/mongrel.rb +22 -6
  40. data/lib/rack/handler/scgi.rb +16 -8
  41. data/lib/rack/handler/thin.rb +19 -4
  42. data/lib/rack/handler/webrick.rb +72 -19
  43. data/lib/rack/handler.rb +47 -14
  44. data/lib/rack/head.rb +10 -2
  45. data/lib/rack/lint.rb +260 -75
  46. data/lib/rack/lobster.rb +13 -8
  47. data/lib/rack/lock.rb +13 -3
  48. data/lib/rack/logger.rb +0 -2
  49. data/lib/rack/methodoverride.rb +27 -8
  50. data/lib/rack/mime.rb +625 -167
  51. data/lib/rack/mock.rb +78 -53
  52. data/lib/rack/multipart/generator.rb +93 -0
  53. data/lib/rack/multipart/parser.rb +253 -0
  54. data/lib/rack/multipart/uploaded_file.rb +34 -0
  55. data/lib/rack/multipart.rb +34 -0
  56. data/lib/rack/nulllogger.rb +21 -2
  57. data/lib/rack/recursive.rb +10 -5
  58. data/lib/rack/reloader.rb +3 -2
  59. data/lib/rack/request.rb +201 -74
  60. data/lib/rack/response.rb +41 -28
  61. data/lib/rack/rewindable_input.rb +15 -11
  62. data/lib/rack/runtime.rb +16 -3
  63. data/lib/rack/sendfile.rb +47 -29
  64. data/lib/rack/server.rb +223 -47
  65. data/lib/rack/session/abstract/id.rb +289 -30
  66. data/lib/rack/session/cookie.rb +133 -44
  67. data/lib/rack/session/memcache.rb +30 -56
  68. data/lib/rack/session/pool.rb +19 -43
  69. data/lib/rack/showexceptions.rb +53 -15
  70. data/lib/rack/showstatus.rb +14 -7
  71. data/lib/rack/static.rb +124 -12
  72. data/lib/rack/tempfile_reaper.rb +22 -0
  73. data/lib/rack/urlmap.rb +49 -15
  74. data/lib/rack/utils/okjson.rb +600 -0
  75. data/lib/rack/utils.rb +363 -361
  76. data/lib/rack.rb +17 -23
  77. data/rack.gemspec +11 -20
  78. data/test/builder/anything.rb +5 -0
  79. data/test/builder/comment.ru +4 -0
  80. data/test/builder/end.ru +5 -0
  81. data/test/builder/line.ru +1 -0
  82. data/test/builder/options.ru +2 -0
  83. data/test/cgi/assets/folder/test.js +1 -0
  84. data/test/cgi/assets/fonts/font.eot +1 -0
  85. data/test/cgi/assets/images/image.png +1 -0
  86. data/test/cgi/assets/index.html +1 -0
  87. data/test/cgi/assets/javascripts/app.js +1 -0
  88. data/test/cgi/assets/stylesheets/app.css +1 -0
  89. data/test/cgi/lighttpd.conf +26 -0
  90. data/test/cgi/rackup_stub.rb +6 -0
  91. data/test/cgi/sample_rackup.ru +5 -0
  92. data/test/cgi/test +9 -0
  93. data/test/cgi/test+directory/test+file +1 -0
  94. data/test/cgi/test.fcgi +8 -0
  95. data/test/cgi/test.ru +5 -0
  96. data/test/gemloader.rb +10 -0
  97. data/test/multipart/bad_robots +259 -0
  98. data/test/multipart/binary +0 -0
  99. data/test/multipart/content_type_and_no_filename +6 -0
  100. data/test/multipart/empty +10 -0
  101. data/test/multipart/fail_16384_nofile +814 -0
  102. data/test/multipart/file1.txt +1 -0
  103. data/test/multipart/filename_and_modification_param +7 -0
  104. data/test/multipart/filename_and_no_name +6 -0
  105. data/test/multipart/filename_with_escaped_quotes +6 -0
  106. data/test/multipart/filename_with_escaped_quotes_and_modification_param +7 -0
  107. data/test/multipart/filename_with_null_byte +7 -0
  108. data/test/multipart/filename_with_percent_escaped_quotes +6 -0
  109. data/test/multipart/filename_with_unescaped_percentages +6 -0
  110. data/test/multipart/filename_with_unescaped_percentages2 +6 -0
  111. data/test/multipart/filename_with_unescaped_percentages3 +6 -0
  112. data/test/multipart/filename_with_unescaped_quotes +6 -0
  113. data/test/multipart/ie +6 -0
  114. data/test/multipart/invalid_character +6 -0
  115. data/test/multipart/mixed_files +21 -0
  116. data/test/multipart/nested +10 -0
  117. data/test/multipart/none +9 -0
  118. data/test/multipart/semicolon +6 -0
  119. data/test/multipart/text +15 -0
  120. data/test/multipart/three_files_three_fields +31 -0
  121. data/test/multipart/webkit +32 -0
  122. data/test/rackup/config.ru +31 -0
  123. data/test/registering_handler/rack/handler/registering_myself.rb +8 -0
  124. data/test/{spec_rack_auth_basic.rb → spec_auth_basic.rb} +23 -15
  125. data/test/{spec_rack_auth_digest.rb → spec_auth_digest.rb} +56 -29
  126. data/test/spec_body_proxy.rb +85 -0
  127. data/test/spec_builder.rb +223 -0
  128. data/test/{spec_rack_cascade.rb → spec_cascade.rb} +28 -15
  129. data/test/{spec_rack_cgi.rb → spec_cgi.rb} +44 -31
  130. data/test/spec_chunked.rb +101 -0
  131. data/test/spec_commonlogger.rb +93 -0
  132. data/test/spec_conditionalget.rb +102 -0
  133. data/test/{spec_rack_config.rb → spec_config.rb} +6 -8
  134. data/test/spec_content_length.rb +85 -0
  135. data/test/spec_content_type.rb +45 -0
  136. data/test/spec_deflater.rb +339 -0
  137. data/test/{spec_rack_directory.rb → spec_directory.rb} +37 -10
  138. data/test/spec_etag.rb +107 -0
  139. data/test/{spec_rack_fastcgi.rb → spec_fastcgi.rb} +47 -29
  140. data/test/spec_file.rb +221 -0
  141. data/test/spec_handler.rb +72 -0
  142. data/test/spec_head.rb +45 -0
  143. data/test/{spec_rack_lint.rb → spec_lint.rb} +82 -60
  144. data/test/spec_lobster.rb +58 -0
  145. data/test/spec_lock.rb +164 -0
  146. data/test/spec_logger.rb +23 -0
  147. data/test/spec_methodoverride.rb +95 -0
  148. data/test/spec_mime.rb +51 -0
  149. data/test/{spec_rack_mock.rb → spec_mock.rb} +92 -38
  150. data/test/{spec_rack_mongrel.rb → spec_mongrel.rb} +46 -53
  151. data/test/spec_multipart.rb +600 -0
  152. data/test/spec_nulllogger.rb +20 -0
  153. data/test/spec_recursive.rb +72 -0
  154. data/test/spec_request.rb +1227 -0
  155. data/test/spec_response.rb +407 -0
  156. data/test/spec_rewindable_input.rb +118 -0
  157. data/test/spec_runtime.rb +49 -0
  158. data/test/spec_sendfile.rb +130 -0
  159. data/test/spec_server.rb +167 -0
  160. data/test/spec_session_abstract_id.rb +53 -0
  161. data/test/spec_session_cookie.rb +410 -0
  162. data/test/{spec_rack_session_memcache.rb → spec_session_memcache.rb} +119 -71
  163. data/test/{spec_rack_session_pool.rb → spec_session_pool.rb} +106 -69
  164. data/test/spec_showexceptions.rb +85 -0
  165. data/test/spec_showstatus.rb +103 -0
  166. data/test/spec_static.rb +145 -0
  167. data/test/spec_tempfile_reaper.rb +63 -0
  168. data/test/{spec_rack_thin.rb → spec_thin.rb} +35 -35
  169. data/test/{spec_rack_urlmap.rb → spec_urlmap.rb} +40 -19
  170. data/test/spec_utils.rb +647 -0
  171. data/test/spec_version.rb +17 -0
  172. data/test/spec_webrick.rb +184 -0
  173. data/test/static/another/index.html +1 -0
  174. data/test/static/index.html +1 -0
  175. data/test/testrequest.rb +78 -0
  176. data/test/unregistered_handler/rack/handler/unregistered.rb +7 -0
  177. data/test/unregistered_handler/rack/handler/unregistered_long_one.rb +7 -0
  178. metadata +220 -239
  179. data/RDOX +0 -0
  180. data/README +0 -592
  181. data/lib/rack/adapter/camping.rb +0 -22
  182. data/test/spec_auth.rb +0 -57
  183. data/test/spec_rack_builder.rb +0 -84
  184. data/test/spec_rack_camping.rb +0 -55
  185. data/test/spec_rack_chunked.rb +0 -62
  186. data/test/spec_rack_commonlogger.rb +0 -61
  187. data/test/spec_rack_conditionalget.rb +0 -41
  188. data/test/spec_rack_content_length.rb +0 -43
  189. data/test/spec_rack_content_type.rb +0 -30
  190. data/test/spec_rack_deflater.rb +0 -127
  191. data/test/spec_rack_etag.rb +0 -17
  192. data/test/spec_rack_file.rb +0 -75
  193. data/test/spec_rack_handler.rb +0 -43
  194. data/test/spec_rack_head.rb +0 -30
  195. data/test/spec_rack_lobster.rb +0 -45
  196. data/test/spec_rack_lock.rb +0 -38
  197. data/test/spec_rack_logger.rb +0 -21
  198. data/test/spec_rack_methodoverride.rb +0 -60
  199. data/test/spec_rack_nulllogger.rb +0 -13
  200. data/test/spec_rack_recursive.rb +0 -77
  201. data/test/spec_rack_request.rb +0 -594
  202. data/test/spec_rack_response.rb +0 -221
  203. data/test/spec_rack_rewindable_input.rb +0 -118
  204. data/test/spec_rack_runtime.rb +0 -35
  205. data/test/spec_rack_sendfile.rb +0 -86
  206. data/test/spec_rack_session_cookie.rb +0 -92
  207. data/test/spec_rack_showexceptions.rb +0 -21
  208. data/test/spec_rack_showstatus.rb +0 -72
  209. data/test/spec_rack_static.rb +0 -37
  210. data/test/spec_rack_utils.rb +0 -557
  211. data/test/spec_rack_webrick.rb +0 -130
  212. data/test/spec_rackup.rb +0 -164
@@ -0,0 +1,223 @@
1
+ require 'rack/builder'
2
+ require 'rack/lint'
3
+ require 'rack/mock'
4
+ require 'rack/showexceptions'
5
+ require 'rack/urlmap'
6
+
7
+ class NothingMiddleware
8
+ def initialize(app)
9
+ @app = app
10
+ end
11
+ def call(env)
12
+ @@env = env
13
+ response = @app.call(env)
14
+ response
15
+ end
16
+ def self.env
17
+ @@env
18
+ end
19
+ end
20
+
21
+ describe Rack::Builder do
22
+ def builder(&block)
23
+ Rack::Lint.new Rack::Builder.new(&block)
24
+ end
25
+
26
+ def builder_to_app(&block)
27
+ Rack::Lint.new Rack::Builder.new(&block).to_app
28
+ end
29
+
30
+ it "supports mapping" do
31
+ app = builder_to_app do
32
+ map '/' do |outer_env|
33
+ run lambda { |inner_env| [200, {"Content-Type" => "text/plain"}, ['root']] }
34
+ end
35
+ map '/sub' do
36
+ run lambda { |inner_env| [200, {"Content-Type" => "text/plain"}, ['sub']] }
37
+ end
38
+ end
39
+ Rack::MockRequest.new(app).get("/").body.to_s.should.equal 'root'
40
+ Rack::MockRequest.new(app).get("/sub").body.to_s.should.equal 'sub'
41
+ end
42
+
43
+ it "doesn't dupe env even when mapping" do
44
+ app = builder_to_app do
45
+ use NothingMiddleware
46
+ map '/' do |outer_env|
47
+ run lambda { |inner_env|
48
+ inner_env['new_key'] = 'new_value'
49
+ [200, {"Content-Type" => "text/plain"}, ['root']]
50
+ }
51
+ end
52
+ end
53
+ Rack::MockRequest.new(app).get("/").body.to_s.should.equal 'root'
54
+ NothingMiddleware.env['new_key'].should.equal 'new_value'
55
+ end
56
+
57
+ it "chains apps by default" do
58
+ app = builder_to_app do
59
+ use Rack::ShowExceptions
60
+ run lambda { |env| raise "bzzzt" }
61
+ end
62
+
63
+ Rack::MockRequest.new(app).get("/").should.be.server_error
64
+ Rack::MockRequest.new(app).get("/").should.be.server_error
65
+ Rack::MockRequest.new(app).get("/").should.be.server_error
66
+ end
67
+
68
+ it "has implicit #to_app" do
69
+ app = builder do
70
+ use Rack::ShowExceptions
71
+ run lambda { |env| raise "bzzzt" }
72
+ end
73
+
74
+ Rack::MockRequest.new(app).get("/").should.be.server_error
75
+ Rack::MockRequest.new(app).get("/").should.be.server_error
76
+ Rack::MockRequest.new(app).get("/").should.be.server_error
77
+ end
78
+
79
+ it "supports blocks on use" do
80
+ app = builder do
81
+ use Rack::ShowExceptions
82
+ use Rack::Auth::Basic do |username, password|
83
+ 'secret' == password
84
+ end
85
+
86
+ run lambda { |env| [200, {"Content-Type" => "text/plain"}, ['Hi Boss']] }
87
+ end
88
+
89
+ response = Rack::MockRequest.new(app).get("/")
90
+ response.should.be.client_error
91
+ response.status.should.equal 401
92
+
93
+ # with auth...
94
+ response = Rack::MockRequest.new(app).get("/",
95
+ 'HTTP_AUTHORIZATION' => 'Basic ' + ["joe:secret"].pack("m*"))
96
+ response.status.should.equal 200
97
+ response.body.to_s.should.equal 'Hi Boss'
98
+ end
99
+
100
+ it "has explicit #to_app" do
101
+ app = builder do
102
+ use Rack::ShowExceptions
103
+ run lambda { |env| raise "bzzzt" }
104
+ end
105
+
106
+ Rack::MockRequest.new(app).get("/").should.be.server_error
107
+ Rack::MockRequest.new(app).get("/").should.be.server_error
108
+ Rack::MockRequest.new(app).get("/").should.be.server_error
109
+ end
110
+
111
+ it "can mix map and run for endpoints" do
112
+ app = builder do
113
+ map '/sub' do
114
+ run lambda { |inner_env| [200, {"Content-Type" => "text/plain"}, ['sub']] }
115
+ end
116
+ run lambda { |inner_env| [200, {"Content-Type" => "text/plain"}, ['root']] }
117
+ end
118
+
119
+ Rack::MockRequest.new(app).get("/").body.to_s.should.equal 'root'
120
+ Rack::MockRequest.new(app).get("/sub").body.to_s.should.equal 'sub'
121
+ end
122
+
123
+ it "accepts middleware-only map blocks" do
124
+ app = builder do
125
+ map('/foo') { use Rack::ShowExceptions }
126
+ run lambda { |env| raise "bzzzt" }
127
+ end
128
+
129
+ proc { Rack::MockRequest.new(app).get("/") }.should.raise(RuntimeError)
130
+ Rack::MockRequest.new(app).get("/foo").should.be.server_error
131
+ end
132
+
133
+ it "yields the generated app to a block for warmup" do
134
+ warmed_up_app = nil
135
+
136
+ app = Rack::Builder.new do
137
+ warmup { |a| warmed_up_app = a }
138
+ run lambda { |env| [200, {}, []] }
139
+ end.to_app
140
+
141
+ warmed_up_app.should.equal app
142
+ end
143
+
144
+ should "initialize apps once" do
145
+ app = builder do
146
+ class AppClass
147
+ def initialize
148
+ @called = 0
149
+ end
150
+ def call(env)
151
+ raise "bzzzt" if @called > 0
152
+ @called += 1
153
+ [200, {'Content-Type' => 'text/plain'}, ['OK']]
154
+ end
155
+ end
156
+
157
+ use Rack::ShowExceptions
158
+ run AppClass.new
159
+ end
160
+
161
+ Rack::MockRequest.new(app).get("/").status.should.equal 200
162
+ Rack::MockRequest.new(app).get("/").should.be.server_error
163
+ end
164
+
165
+ it "allows use after run" do
166
+ app = builder do
167
+ run lambda { |env| raise "bzzzt" }
168
+ use Rack::ShowExceptions
169
+ end
170
+
171
+ Rack::MockRequest.new(app).get("/").should.be.server_error
172
+ Rack::MockRequest.new(app).get("/").should.be.server_error
173
+ Rack::MockRequest.new(app).get("/").should.be.server_error
174
+ end
175
+
176
+ it 'complains about a missing run' do
177
+ proc do
178
+ Rack::Lint.new Rack::Builder.app { use Rack::ShowExceptions }
179
+ end.should.raise(RuntimeError)
180
+ end
181
+
182
+ describe "parse_file" do
183
+ def config_file(name)
184
+ File.join(File.dirname(__FILE__), 'builder', name)
185
+ end
186
+
187
+ it "parses commented options" do
188
+ app, options = Rack::Builder.parse_file config_file('options.ru')
189
+ options[:debug].should.be.true
190
+ Rack::MockRequest.new(app).get("/").body.to_s.should.equal 'OK'
191
+ end
192
+
193
+ it "removes __END__ before evaluating app" do
194
+ app, _ = Rack::Builder.parse_file config_file('end.ru')
195
+ Rack::MockRequest.new(app).get("/").body.to_s.should.equal 'OK'
196
+ end
197
+
198
+ it "supports multi-line comments" do
199
+ lambda {
200
+ Rack::Builder.parse_file config_file('comment.ru')
201
+ }.should.not.raise(SyntaxError)
202
+ end
203
+
204
+ it "requires anything not ending in .ru" do
205
+ $: << File.dirname(__FILE__)
206
+ app, * = Rack::Builder.parse_file 'builder/anything'
207
+ Rack::MockRequest.new(app).get("/").body.to_s.should.equal 'OK'
208
+ $:.pop
209
+ end
210
+
211
+ it "sets __LINE__ correctly" do
212
+ app, _ = Rack::Builder.parse_file config_file('line.ru')
213
+ Rack::MockRequest.new(app).get("/").body.to_s.should.equal '1'
214
+ end
215
+ end
216
+
217
+ describe 'new_from_string' do
218
+ it "builds a rack app from string" do
219
+ app, = Rack::Builder.new_from_string "run lambda{|env| [200, {'Content-Type' => 'text/plane'}, ['OK']] }"
220
+ Rack::MockRequest.new(app).get("/").body.to_s.should.equal 'OK'
221
+ end
222
+ end
223
+ end
@@ -1,12 +1,14 @@
1
- require 'test/spec'
2
-
3
1
  require 'rack/cascade'
4
- require 'rack/mock'
5
-
6
- require 'rack/urlmap'
7
2
  require 'rack/file'
3
+ require 'rack/lint'
4
+ require 'rack/urlmap'
5
+ require 'rack/mock'
8
6
 
9
- context "Rack::Cascade" do
7
+ describe Rack::Cascade do
8
+ def cascade(*args)
9
+ Rack::Lint.new Rack::Cascade.new(*args)
10
+ end
11
+
10
12
  docroot = File.expand_path(File.dirname(__FILE__))
11
13
  app1 = Rack::File.new(docroot)
12
14
 
@@ -15,24 +17,27 @@ context "Rack::Cascade" do
15
17
  app3 = Rack::URLMap.new("/foo" => lambda { |env|
16
18
  [200, { "Content-Type" => "text/plain"}, [""]]})
17
19
 
18
- specify "should dispatch onward on 404 by default" do
19
- cascade = Rack::Cascade.new([app1, app2, app3])
20
+ should "dispatch onward on 404 and 405 by default" do
21
+ cascade = cascade([app1, app2, app3])
20
22
  Rack::MockRequest.new(cascade).get("/cgi/test").should.be.ok
21
23
  Rack::MockRequest.new(cascade).get("/foo").should.be.ok
22
24
  Rack::MockRequest.new(cascade).get("/toobad").should.be.not_found
23
- Rack::MockRequest.new(cascade).get("/cgi/../bla").should.be.forbidden
25
+ Rack::MockRequest.new(cascade).get("/cgi/../..").should.be.client_error
26
+
27
+ # Put is not allowed by Rack::File so it'll 405.
28
+ Rack::MockRequest.new(cascade).put("/foo").should.be.ok
24
29
  end
25
30
 
26
- specify "should dispatch onward on whatever is passed" do
27
- cascade = Rack::Cascade.new([app1, app2, app3], [404, 403])
31
+ should "dispatch onward on whatever is passed" do
32
+ cascade = cascade([app1, app2, app3], [404, 403])
28
33
  Rack::MockRequest.new(cascade).get("/cgi/../bla").should.be.not_found
29
34
  end
30
35
 
31
- specify "should return 404 if empty" do
32
- Rack::MockRequest.new(Rack::Cascade.new([])).get('/').should.be.not_found
36
+ should "return 404 if empty" do
37
+ Rack::MockRequest.new(cascade([])).get('/').should.be.not_found
33
38
  end
34
39
 
35
- specify "should append new app" do
40
+ should "append new app" do
36
41
  cascade = Rack::Cascade.new([], [404, 403])
37
42
  Rack::MockRequest.new(cascade).get('/').should.be.not_found
38
43
  cascade << app2
@@ -40,9 +45,17 @@ context "Rack::Cascade" do
40
45
  Rack::MockRequest.new(cascade).get('/cgi/../bla').should.be.not_found
41
46
  cascade << app1
42
47
  Rack::MockRequest.new(cascade).get('/cgi/test').should.be.ok
43
- Rack::MockRequest.new(cascade).get('/cgi/../bla').should.be.forbidden
48
+ Rack::MockRequest.new(cascade).get('/cgi/../..').should.be.client_error
44
49
  Rack::MockRequest.new(cascade).get('/foo').should.be.not_found
45
50
  cascade << app3
46
51
  Rack::MockRequest.new(cascade).get('/foo').should.be.ok
47
52
  end
53
+
54
+ should "close the body on cascade" do
55
+ body = StringIO.new
56
+ closer = lambda { |env| [404, {}, body] }
57
+ cascade = Rack::Cascade.new([closer, app3], [404])
58
+ Rack::MockRequest.new(cascade).get("/foo").should.be.ok
59
+ body.should.be.closed
60
+ end
48
61
  end
@@ -1,53 +1,60 @@
1
- require 'test/spec'
2
- require 'testrequest'
1
+ begin
2
+ require File.expand_path('../testrequest', __FILE__)
3
+ require 'rack/handler/cgi'
3
4
 
4
- context "Rack::Handler::CGI" do
5
- include TestRequest::Helpers
5
+ describe Rack::Handler::CGI do
6
+ extend TestRequest::Helpers
6
7
 
7
- setup do
8
- @host = '0.0.0.0'
9
- @port = 9203
8
+ @host = '127.0.0.1'
9
+ @port = 9203
10
+
11
+ if `which lighttpd` && !$?.success?
12
+ raise "lighttpd not found"
10
13
  end
11
14
 
12
15
  # Keep this first.
13
- specify "startup" do
14
- $pid = fork {
15
- Dir.chdir(File.join(File.dirname(__FILE__), "..", "test", "cgi"))
16
+ $pid = fork {
17
+ ENV['RACK_ENV'] = 'deployment'
18
+ ENV['RUBYLIB'] = [
19
+ File.expand_path('../../lib', __FILE__),
20
+ ENV['RUBYLIB'],
21
+ ].compact.join(':')
22
+
23
+ Dir.chdir(File.expand_path("../cgi", __FILE__)) do
16
24
  exec "lighttpd -D -f lighttpd.conf"
17
- }
18
- end
25
+ end
26
+ }
19
27
 
20
- specify "should respond" do
28
+ should "respond" do
21
29
  sleep 1
22
- lambda {
23
- GET("/test")
24
- }.should.not.raise
30
+ GET("/test")
31
+ response.should.not.be.nil
25
32
  end
26
33
 
27
- specify "should be a lighttpd" do
34
+ should "be a lighttpd" do
28
35
  GET("/test")
29
- status.should.be 200
36
+ status.should.equal 200
30
37
  response["SERVER_SOFTWARE"].should =~ /lighttpd/
31
38
  response["HTTP_VERSION"].should.equal "HTTP/1.1"
32
39
  response["SERVER_PROTOCOL"].should.equal "HTTP/1.1"
33
40
  response["SERVER_PORT"].should.equal @port.to_s
34
- response["SERVER_NAME"].should =~ @host
41
+ response["SERVER_NAME"].should.equal @host
35
42
  end
36
43
 
37
- specify "should have rack headers" do
44
+ should "have rack headers" do
38
45
  GET("/test")
39
- response["rack.version"].should.equal [1,1]
40
- response["rack.multithread"].should.be false
41
- response["rack.multiprocess"].should.be true
42
- response["rack.run_once"].should.be true
46
+ response["rack.version"].should.equal([1,3])
47
+ response["rack.multithread"].should.be.false
48
+ response["rack.multiprocess"].should.be.true
49
+ response["rack.run_once"].should.be.true
43
50
  end
44
51
 
45
- specify "should have CGI headers on GET" do
52
+ should "have CGI headers on GET" do
46
53
  GET("/test")
47
54
  response["REQUEST_METHOD"].should.equal "GET"
48
55
  response["SCRIPT_NAME"].should.equal "/test"
49
56
  response["REQUEST_PATH"].should.equal "/"
50
- response["PATH_INFO"].should.be nil
57
+ response["PATH_INFO"].should.be.nil
51
58
  response["QUERY_STRING"].should.equal ""
52
59
  response["test.postdata"].should.equal ""
53
60
 
@@ -59,7 +66,7 @@ context "Rack::Handler::CGI" do
59
66
  response["QUERY_STRING"].should.equal "quux=1"
60
67
  end
61
68
 
62
- specify "should have CGI headers on POST" do
69
+ should "have CGI headers on POST" do
63
70
  POST("/test", {"rack-form-data" => "23"}, {'X-test-header' => '42'})
64
71
  status.should.equal 200
65
72
  response["REQUEST_METHOD"].should.equal "POST"
@@ -70,20 +77,26 @@ context "Rack::Handler::CGI" do
70
77
  response["test.postdata"].should.equal "rack-form-data=23"
71
78
  end
72
79
 
73
- specify "should support HTTP auth" do
80
+ should "support HTTP auth" do
74
81
  GET("/test", {:user => "ruth", :passwd => "secret"})
75
82
  response["HTTP_AUTHORIZATION"].should.equal "Basic cnV0aDpzZWNyZXQ="
76
83
  end
77
84
 
78
- specify "should set status" do
85
+ should "set status" do
79
86
  GET("/test?secret")
80
87
  status.should.equal 403
81
88
  response["rack.url_scheme"].should.equal "http"
82
89
  end
83
90
 
84
91
  # Keep this last.
85
- specify "shutdown" do
92
+ should "shutdown" do
86
93
  Process.kill 15, $pid
87
- Process.wait($pid).should.equal $pid
94
+ Process.wait($pid).should == $pid
88
95
  end
89
96
  end
97
+
98
+ rescue RuntimeError
99
+ $stderr.puts "Skipping Rack::Handler::CGI tests (lighttpd is required). Install lighttpd and try again."
100
+ rescue NotImplementedError
101
+ $stderr.puts "Your Ruby implemenation or platform does not support fork. Skipping Rack::Handler::CGI tests."
102
+ end
@@ -0,0 +1,101 @@
1
+ require 'rack/chunked'
2
+ require 'rack/lint'
3
+ require 'rack/mock'
4
+
5
+ describe Rack::Chunked do
6
+ def chunked(app)
7
+ proc do |env|
8
+ app = Rack::Chunked.new(app)
9
+ response = Rack::Lint.new(app).call(env)
10
+ # we want to use body like an array, but it only has #each
11
+ response[2] = response[2].to_enum.to_a
12
+ response
13
+ end
14
+ end
15
+
16
+ before do
17
+ @env = Rack::MockRequest.
18
+ env_for('/', 'HTTP_VERSION' => '1.1', 'REQUEST_METHOD' => 'GET')
19
+ end
20
+
21
+ should 'chunk responses with no Content-Length' do
22
+ app = lambda { |env| [200, {"Content-Type" => "text/plain"}, ['Hello', ' ', 'World!']] }
23
+ response = Rack::MockResponse.new(*chunked(app).call(@env))
24
+ response.headers.should.not.include 'Content-Length'
25
+ response.headers['Transfer-Encoding'].should.equal 'chunked'
26
+ response.body.should.equal "5\r\nHello\r\n1\r\n \r\n6\r\nWorld!\r\n0\r\n\r\n"
27
+ end
28
+
29
+ should 'chunks empty bodies properly' do
30
+ app = lambda { |env| [200, {"Content-Type" => "text/plain"}, []] }
31
+ response = Rack::MockResponse.new(*chunked(app).call(@env))
32
+ response.headers.should.not.include 'Content-Length'
33
+ response.headers['Transfer-Encoding'].should.equal 'chunked'
34
+ response.body.should.equal "0\r\n\r\n"
35
+ end
36
+
37
+ should 'chunks encoded bodies properly' do
38
+ body = ["\uFFFEHello", " ", "World"].map {|t| t.encode("UTF-16LE") }
39
+ app = lambda { |env| [200, {"Content-Type" => "text/plain"}, body] }
40
+ response = Rack::MockResponse.new(*chunked(app).call(@env))
41
+ response.headers.should.not.include 'Content-Length'
42
+ response.headers['Transfer-Encoding'].should.equal 'chunked'
43
+ response.body.encoding.to_s.should.equal "ASCII-8BIT"
44
+ response.body.should.equal "c\r\n\xFE\xFFH\x00e\x00l\x00l\x00o\x00\r\n2\r\n \x00\r\na\r\nW\x00o\x00r\x00l\x00d\x00\r\n0\r\n\r\n".force_encoding("BINARY")
45
+ end if RUBY_VERSION >= "1.9"
46
+
47
+ should 'not modify response when Content-Length header present' do
48
+ app = lambda { |env|
49
+ [200, {"Content-Type" => "text/plain", 'Content-Length'=>'12'}, ['Hello', ' ', 'World!']]
50
+ }
51
+ status, headers, body = chunked(app).call(@env)
52
+ status.should.equal 200
53
+ headers.should.not.include 'Transfer-Encoding'
54
+ headers.should.include 'Content-Length'
55
+ body.join.should.equal 'Hello World!'
56
+ end
57
+
58
+ should 'not modify response when client is HTTP/1.0' do
59
+ app = lambda { |env| [200, {"Content-Type" => "text/plain"}, ['Hello', ' ', 'World!']] }
60
+ @env['HTTP_VERSION'] = 'HTTP/1.0'
61
+ status, headers, body = chunked(app).call(@env)
62
+ status.should.equal 200
63
+ headers.should.not.include 'Transfer-Encoding'
64
+ body.join.should.equal 'Hello World!'
65
+ end
66
+
67
+ should 'not modify response when client is ancient, pre-HTTP/1.0' do
68
+ app = lambda { |env| [200, {"Content-Type" => "text/plain"}, ['Hello', ' ', 'World!']] }
69
+ check = lambda do
70
+ status, headers, body = chunked(app).call(@env.dup)
71
+ status.should.equal 200
72
+ headers.should.not.include 'Transfer-Encoding'
73
+ body.join.should.equal 'Hello World!'
74
+ end
75
+
76
+ @env.delete('HTTP_VERSION') # unicorn will do this on pre-HTTP/1.0 requests
77
+ check.call
78
+
79
+ @env['HTTP_VERSION'] = 'HTTP/0.9' # not sure if this happens in practice
80
+ check.call
81
+ end
82
+
83
+ should 'not modify response when Transfer-Encoding header already present' do
84
+ app = lambda { |env|
85
+ [200, {"Content-Type" => "text/plain", 'Transfer-Encoding' => 'identity'}, ['Hello', ' ', 'World!']]
86
+ }
87
+ status, headers, body = chunked(app).call(@env)
88
+ status.should.equal 200
89
+ headers['Transfer-Encoding'].should.equal 'identity'
90
+ body.join.should.equal 'Hello World!'
91
+ end
92
+
93
+ [100, 204, 205, 304].each do |status_code|
94
+ should "not modify response when status code is #{status_code}" do
95
+ app = lambda { |env| [status_code, {}, []] }
96
+ status, headers, _ = chunked(app).call(@env)
97
+ status.should.equal status_code
98
+ headers.should.not.include 'Transfer-Encoding'
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,93 @@
1
+ require 'rack/commonlogger'
2
+ require 'rack/lint'
3
+ require 'rack/mock'
4
+
5
+ require 'logger'
6
+
7
+ describe Rack::CommonLogger do
8
+ obj = 'foobar'
9
+ length = obj.size
10
+
11
+ app = Rack::Lint.new lambda { |env|
12
+ [200,
13
+ {"Content-Type" => "text/html", "Content-Length" => length.to_s},
14
+ [obj]]}
15
+ app_without_length = Rack::Lint.new lambda { |env|
16
+ [200,
17
+ {"Content-Type" => "text/html"},
18
+ []]}
19
+ app_with_zero_length = Rack::Lint.new lambda { |env|
20
+ [200,
21
+ {"Content-Type" => "text/html", "Content-Length" => "0"},
22
+ []]}
23
+
24
+ should "log to rack.errors by default" do
25
+ res = Rack::MockRequest.new(Rack::CommonLogger.new(app)).get("/")
26
+
27
+ res.errors.should.not.be.empty
28
+ res.errors.should =~ /"GET \/ " 200 #{length} /
29
+ end
30
+
31
+ should "log to anything with +write+" do
32
+ log = StringIO.new
33
+ Rack::MockRequest.new(Rack::CommonLogger.new(app, log)).get("/")
34
+
35
+ log.string.should =~ /"GET \/ " 200 #{length} /
36
+ end
37
+
38
+ should "work with standartd library logger" do
39
+ logdev = StringIO.new
40
+ log = Logger.new(logdev)
41
+ Rack::MockRequest.new(Rack::CommonLogger.new(app, log)).get("/")
42
+
43
+ logdev.string.should =~ /"GET \/ " 200 #{length} /
44
+ end
45
+
46
+ should "log - content length if header is missing" do
47
+ res = Rack::MockRequest.new(Rack::CommonLogger.new(app_without_length)).get("/")
48
+
49
+ res.errors.should.not.be.empty
50
+ res.errors.should =~ /"GET \/ " 200 - /
51
+ end
52
+
53
+ should "log - content length if header is zero" do
54
+ res = Rack::MockRequest.new(Rack::CommonLogger.new(app_with_zero_length)).get("/")
55
+
56
+ res.errors.should.not.be.empty
57
+ res.errors.should =~ /"GET \/ " 200 - /
58
+ end
59
+
60
+ def with_mock_time(t = 0)
61
+ mc = class <<Time; self; end
62
+ mc.send :alias_method, :old_now, :now
63
+ mc.send :define_method, :now do
64
+ at(t)
65
+ end
66
+ yield
67
+ ensure
68
+ mc.send :alias_method, :now, :old_now
69
+ end
70
+
71
+ should "log in common log format" do
72
+ log = StringIO.new
73
+ with_mock_time do
74
+ Rack::MockRequest.new(Rack::CommonLogger.new(app, log)).get("/")
75
+ end
76
+
77
+ md = /- - - \[([^\]]+)\] "(\w+) \/ " (\d{3}) \d+ ([\d\.]+)/.match(log.string)
78
+ md.should.not.equal nil
79
+ time, method, status, duration = *md.captures
80
+ time.should.equal Time.at(0).strftime("%d/%b/%Y:%H:%M:%S %z")
81
+ method.should.equal "GET"
82
+ status.should.equal "200"
83
+ (0..1).should.include?(duration.to_f)
84
+ end
85
+
86
+ def length
87
+ 123
88
+ end
89
+
90
+ def self.obj
91
+ "hello world"
92
+ end
93
+ end