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
data/test/spec_etag.rb ADDED
@@ -0,0 +1,107 @@
1
+ require 'rack/etag'
2
+ require 'rack/lint'
3
+ require 'rack/mock'
4
+ require 'time'
5
+
6
+ describe Rack::ETag do
7
+ def etag(app, *args)
8
+ Rack::Lint.new Rack::ETag.new(app, *args)
9
+ end
10
+
11
+ def request
12
+ Rack::MockRequest.env_for
13
+ end
14
+
15
+ def sendfile_body
16
+ res = ['Hello World']
17
+ def res.to_path ; "/tmp/hello.txt" ; end
18
+ res
19
+ end
20
+
21
+ should "set ETag if none is set if status is 200" do
22
+ app = lambda { |env| [200, {'Content-Type' => 'text/plain'}, ["Hello, World!"]] }
23
+ response = etag(app).call(request)
24
+ response[1]['ETag'].should.equal "W/\"65a8e27d8879283831b664bd8b7f0ad4\""
25
+ end
26
+
27
+ should "set ETag if none is set if status is 201" do
28
+ app = lambda { |env| [201, {'Content-Type' => 'text/plain'}, ["Hello, World!"]] }
29
+ response = etag(app).call(request)
30
+ response[1]['ETag'].should.equal "W/\"65a8e27d8879283831b664bd8b7f0ad4\""
31
+ end
32
+
33
+ should "set Cache-Control to 'max-age=0, private, must-revalidate' (default) if none is set" do
34
+ app = lambda { |env| [201, {'Content-Type' => 'text/plain'}, ["Hello, World!"]] }
35
+ response = etag(app).call(request)
36
+ response[1]['Cache-Control'].should.equal 'max-age=0, private, must-revalidate'
37
+ end
38
+
39
+ should "set Cache-Control to chosen one if none is set" do
40
+ app = lambda { |env| [201, {'Content-Type' => 'text/plain'}, ["Hello, World!"]] }
41
+ response = etag(app, nil, 'public').call(request)
42
+ response[1]['Cache-Control'].should.equal 'public'
43
+ end
44
+
45
+ should "set a given Cache-Control even if digest could not be calculated" do
46
+ app = lambda { |env| [200, {'Content-Type' => 'text/plain'}, []] }
47
+ response = etag(app, 'no-cache').call(request)
48
+ response[1]['Cache-Control'].should.equal 'no-cache'
49
+ end
50
+
51
+ should "not set Cache-Control if it is already set" do
52
+ app = lambda { |env| [201, {'Content-Type' => 'text/plain', 'Cache-Control' => 'public'}, ["Hello, World!"]] }
53
+ response = etag(app).call(request)
54
+ response[1]['Cache-Control'].should.equal 'public'
55
+ end
56
+
57
+ should "not set Cache-Control if directive isn't present" do
58
+ app = lambda { |env| [200, {'Content-Type' => 'text/plain'}, ["Hello, World!"]] }
59
+ response = etag(app, nil, nil).call(request)
60
+ response[1]['Cache-Control'].should.equal nil
61
+ end
62
+
63
+ should "not change ETag if it is already set" do
64
+ app = lambda { |env| [200, {'Content-Type' => 'text/plain', 'ETag' => '"abc"'}, ["Hello, World!"]] }
65
+ response = etag(app).call(request)
66
+ response[1]['ETag'].should.equal "\"abc\""
67
+ end
68
+
69
+ should "not set ETag if body is empty" do
70
+ app = lambda { |env| [200, {'Content-Type' => 'text/plain', 'Last-Modified' => Time.now.httpdate}, []] }
71
+ response = etag(app).call(request)
72
+ response[1]['ETag'].should.be.nil
73
+ end
74
+
75
+ should "not set ETag if Last-Modified is set" do
76
+ app = lambda { |env| [200, {'Content-Type' => 'text/plain', 'Last-Modified' => Time.now.httpdate}, ["Hello, World!"]] }
77
+ response = etag(app).call(request)
78
+ response[1]['ETag'].should.be.nil
79
+ end
80
+
81
+ should "not set ETag if a sendfile_body is given" do
82
+ app = lambda { |env| [200, {'Content-Type' => 'text/plain'}, sendfile_body] }
83
+ response = etag(app).call(request)
84
+ response[1]['ETag'].should.be.nil
85
+ end
86
+
87
+ should "not set ETag if a status is not 200 or 201" do
88
+ app = lambda { |env| [401, {'Content-Type' => 'text/plain'}, ['Access denied.']] }
89
+ response = etag(app).call(request)
90
+ response[1]['ETag'].should.be.nil
91
+ end
92
+
93
+ should "not set ETag if no-cache is given" do
94
+ app = lambda { |env| [200, {'Content-Type' => 'text/plain', 'Cache-Control' => 'no-cache, must-revalidate'}, ['Hello, World!']] }
95
+ response = etag(app).call(request)
96
+ response[1]['ETag'].should.be.nil
97
+ end
98
+
99
+ should "close the original body" do
100
+ body = StringIO.new
101
+ app = lambda { |env| [200, {}, body] }
102
+ response = etag(app).call(request)
103
+ body.should.not.be.closed
104
+ response[2].close
105
+ body.should.be.closed
106
+ end
107
+ end
@@ -1,48 +1,60 @@
1
- require 'test/spec'
2
- require 'testrequest'
1
+ begin
2
+ require File.expand_path('../testrequest', __FILE__)
3
+ require 'rack/handler/fastcgi'
3
4
 
4
- context "Rack::Handler::FastCGI" do
5
- include TestRequest::Helpers
5
+ describe Rack::Handler::FastCGI 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.fcgi")
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 "respond via rackup server" do
35
+ GET("/sample_rackup.ru")
36
+ status.should.equal 200
37
+ end
38
+
39
+ should "be a lighttpd" do
28
40
  GET("/test.fcgi")
29
- status.should.be 200
41
+ status.should.equal 200
30
42
  response["SERVER_SOFTWARE"].should =~ /lighttpd/
31
43
  response["HTTP_VERSION"].should.equal "HTTP/1.1"
32
44
  response["SERVER_PROTOCOL"].should.equal "HTTP/1.1"
33
45
  response["SERVER_PORT"].should.equal @port.to_s
34
- response["SERVER_NAME"].should =~ @host
46
+ response["SERVER_NAME"].should.equal @host
35
47
  end
36
48
 
37
- specify "should have rack headers" do
49
+ should "have rack headers" do
38
50
  GET("/test.fcgi")
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 false
51
+ response["rack.version"].should.equal [1,3]
52
+ response["rack.multithread"].should.be.false
53
+ response["rack.multiprocess"].should.be.true
54
+ response["rack.run_once"].should.be.false
43
55
  end
44
56
 
45
- specify "should have CGI headers on GET" do
57
+ should "have CGI headers on GET" do
46
58
  GET("/test.fcgi")
47
59
  response["REQUEST_METHOD"].should.equal "GET"
48
60
  response["SCRIPT_NAME"].should.equal "/test.fcgi"
@@ -59,7 +71,7 @@ context "Rack::Handler::FastCGI" do
59
71
  response["QUERY_STRING"].should.equal "quux=1"
60
72
  end
61
73
 
62
- specify "should have CGI headers on POST" do
74
+ should "have CGI headers on POST" do
63
75
  POST("/test.fcgi", {"rack-form-data" => "23"}, {'X-test-header' => '42'})
64
76
  status.should.equal 200
65
77
  response["REQUEST_METHOD"].should.equal "POST"
@@ -70,20 +82,26 @@ context "Rack::Handler::FastCGI" do
70
82
  response["test.postdata"].should.equal "rack-form-data=23"
71
83
  end
72
84
 
73
- specify "should support HTTP auth" do
85
+ should "support HTTP auth" do
74
86
  GET("/test.fcgi", {:user => "ruth", :passwd => "secret"})
75
87
  response["HTTP_AUTHORIZATION"].should.equal "Basic cnV0aDpzZWNyZXQ="
76
88
  end
77
89
 
78
- specify "should set status" do
90
+ should "set status" do
79
91
  GET("/test.fcgi?secret")
80
92
  status.should.equal 403
81
93
  response["rack.url_scheme"].should.equal "http"
82
94
  end
83
95
 
84
96
  # Keep this last.
85
- specify "shutdown" do
97
+ should "shutdown" do
86
98
  Process.kill 15, $pid
87
99
  Process.wait($pid).should.equal $pid
88
100
  end
89
101
  end
102
+
103
+ rescue RuntimeError
104
+ $stderr.puts "Skipping Rack::Handler::FastCGI tests (lighttpd is required). Install lighttpd and try again."
105
+ rescue LoadError
106
+ $stderr.puts "Skipping Rack::Handler::FastCGI tests (FCGI is required). `gem install fcgi` and try again."
107
+ end
data/test/spec_file.rb ADDED
@@ -0,0 +1,221 @@
1
+ require 'rack/file'
2
+ require 'rack/lint'
3
+ require 'rack/mock'
4
+
5
+ describe Rack::File do
6
+ DOCROOT = File.expand_path(File.dirname(__FILE__)) unless defined? DOCROOT
7
+
8
+ def file(*args)
9
+ Rack::Lint.new Rack::File.new(*args)
10
+ end
11
+
12
+ should "serve files" do
13
+ res = Rack::MockRequest.new(file(DOCROOT)).get("/cgi/test")
14
+
15
+ res.should.be.ok
16
+ res.should =~ /ruby/
17
+ end
18
+
19
+ should "set Last-Modified header" do
20
+ res = Rack::MockRequest.new(file(DOCROOT)).get("/cgi/test")
21
+
22
+ path = File.join(DOCROOT, "/cgi/test")
23
+
24
+ res.should.be.ok
25
+ res["Last-Modified"].should.equal File.mtime(path).httpdate
26
+ end
27
+
28
+ should "return 304 if file isn't modified since last serve" do
29
+ path = File.join(DOCROOT, "/cgi/test")
30
+ res = Rack::MockRequest.new(file(DOCROOT)).
31
+ get("/cgi/test", 'HTTP_IF_MODIFIED_SINCE' => File.mtime(path).httpdate)
32
+
33
+ res.status.should.equal 304
34
+ res.body.should.be.empty
35
+ end
36
+
37
+ should "return the file if it's modified since last serve" do
38
+ path = File.join(DOCROOT, "/cgi/test")
39
+ res = Rack::MockRequest.new(file(DOCROOT)).
40
+ get("/cgi/test", 'HTTP_IF_MODIFIED_SINCE' => (File.mtime(path) - 100).httpdate)
41
+
42
+ res.should.be.ok
43
+ end
44
+
45
+ should "serve files with URL encoded filenames" do
46
+ res = Rack::MockRequest.new(file(DOCROOT)).get("/cgi/%74%65%73%74") # "/cgi/test"
47
+
48
+ res.should.be.ok
49
+ res.should =~ /ruby/
50
+ end
51
+
52
+ should "allow safe directory traversal" do
53
+ req = Rack::MockRequest.new(file(DOCROOT))
54
+
55
+ res = req.get('/cgi/../cgi/test')
56
+ res.should.be.successful
57
+
58
+ res = req.get('.')
59
+ res.should.be.not_found
60
+
61
+ res = req.get("test/..")
62
+ res.should.be.not_found
63
+ end
64
+
65
+ should "not allow unsafe directory traversal" do
66
+ req = Rack::MockRequest.new(file(DOCROOT))
67
+
68
+ res = req.get("/../README.rdoc")
69
+ res.should.be.client_error
70
+
71
+ res = req.get("../test/spec_file.rb")
72
+ res.should.be.client_error
73
+
74
+ res = req.get("../README.rdoc")
75
+ res.should.be.client_error
76
+
77
+ res.should.be.not_found
78
+ end
79
+
80
+ should "allow files with .. in their name" do
81
+ req = Rack::MockRequest.new(file(DOCROOT))
82
+ res = req.get("/cgi/..test")
83
+ res.should.be.not_found
84
+
85
+ res = req.get("/cgi/test..")
86
+ res.should.be.not_found
87
+
88
+ res = req.get("/cgi../test..")
89
+ res.should.be.not_found
90
+ end
91
+
92
+ should "not allow unsafe directory traversal with encoded periods" do
93
+ res = Rack::MockRequest.new(file(DOCROOT)).get("/%2E%2E/README")
94
+
95
+ res.should.be.client_error?
96
+ res.should.be.not_found
97
+ end
98
+
99
+ should "allow safe directory traversal with encoded periods" do
100
+ res = Rack::MockRequest.new(file(DOCROOT)).get("/cgi/%2E%2E/cgi/test")
101
+
102
+ res.should.be.successful
103
+ end
104
+
105
+ should "404 if it can't find the file" do
106
+ res = Rack::MockRequest.new(file(DOCROOT)).get("/cgi/blubb")
107
+
108
+ res.should.be.not_found
109
+ end
110
+
111
+ should "detect SystemCallErrors" do
112
+ res = Rack::MockRequest.new(file(DOCROOT)).get("/cgi")
113
+
114
+ res.should.be.not_found
115
+ end
116
+
117
+ should "return bodies that respond to #to_path" do
118
+ env = Rack::MockRequest.env_for("/cgi/test")
119
+ status, _, body = Rack::File.new(DOCROOT).call(env)
120
+
121
+ path = File.join(DOCROOT, "/cgi/test")
122
+
123
+ status.should.equal 200
124
+ body.should.respond_to :to_path
125
+ body.to_path.should.equal path
126
+ end
127
+
128
+ should "return correct byte range in body" do
129
+ env = Rack::MockRequest.env_for("/cgi/test")
130
+ env["HTTP_RANGE"] = "bytes=22-33"
131
+ res = Rack::MockResponse.new(*file(DOCROOT).call(env))
132
+
133
+ res.status.should.equal 206
134
+ res["Content-Length"].should.equal "12"
135
+ res["Content-Range"].should.equal "bytes 22-33/193"
136
+ res.body.should.equal "-*- ruby -*-"
137
+ end
138
+
139
+ should "return error for unsatisfiable byte range" do
140
+ env = Rack::MockRequest.env_for("/cgi/test")
141
+ env["HTTP_RANGE"] = "bytes=1234-5678"
142
+ res = Rack::MockResponse.new(*file(DOCROOT).call(env))
143
+
144
+ res.status.should.equal 416
145
+ res["Content-Range"].should.equal "bytes */193"
146
+ end
147
+
148
+ should "support custom http headers" do
149
+ env = Rack::MockRequest.env_for("/cgi/test")
150
+ status, heads, _ = file(DOCROOT, 'Cache-Control' => 'public, max-age=38',
151
+ 'Access-Control-Allow-Origin' => '*').call(env)
152
+
153
+ status.should.equal 200
154
+ heads['Cache-Control'].should.equal 'public, max-age=38'
155
+ heads['Access-Control-Allow-Origin'].should.equal '*'
156
+ end
157
+
158
+ should "support not add custom http headers if none are supplied" do
159
+ env = Rack::MockRequest.env_for("/cgi/test")
160
+ status, heads, _ = file(DOCROOT).call(env)
161
+
162
+ status.should.equal 200
163
+ heads['Cache-Control'].should.equal nil
164
+ heads['Access-Control-Allow-Origin'].should.equal nil
165
+ end
166
+
167
+ should "only support GET, HEAD, and OPTIONS requests" do
168
+ req = Rack::MockRequest.new(file(DOCROOT))
169
+
170
+ forbidden = %w[post put patch delete]
171
+ forbidden.each do |method|
172
+ res = req.send(method, "/cgi/test")
173
+ res.should.be.client_error
174
+ res.should.be.method_not_allowed
175
+ res.headers['Allow'].split(/, */).sort.should == %w(GET HEAD OPTIONS)
176
+ end
177
+
178
+ allowed = %w[get head options]
179
+ allowed.each do |method|
180
+ res = req.send(method, "/cgi/test")
181
+ res.should.be.successful
182
+ end
183
+ end
184
+
185
+ should "set Allow correctly for OPTIONS requests" do
186
+ req = Rack::MockRequest.new(file(DOCROOT))
187
+ res = req.options('/cgi/test')
188
+ res.should.be.successful
189
+ res.headers['Allow'].should.not.equal nil
190
+ res.headers['Allow'].split(/, */).sort.should == %w(GET HEAD OPTIONS)
191
+ end
192
+
193
+ should "set Content-Length correctly for HEAD requests" do
194
+ req = Rack::MockRequest.new(Rack::Lint.new(Rack::File.new(DOCROOT)))
195
+ res = req.head "/cgi/test"
196
+ res.should.be.successful
197
+ res['Content-Length'].should.equal "193"
198
+ end
199
+
200
+ should "default to a mime type of text/plain" do
201
+ req = Rack::MockRequest.new(Rack::Lint.new(Rack::File.new(DOCROOT)))
202
+ res = req.get "/cgi/test"
203
+ res.should.be.successful
204
+ res['Content-Type'].should.equal "text/plain"
205
+ end
206
+
207
+ should "allow the default mime type to be set" do
208
+ req = Rack::MockRequest.new(Rack::Lint.new(Rack::File.new(DOCROOT, nil, 'application/octet-stream')))
209
+ res = req.get "/cgi/test"
210
+ res.should.be.successful
211
+ res['Content-Type'].should.equal "application/octet-stream"
212
+ end
213
+
214
+ should "not set Content-Type if the mime type is not set" do
215
+ req = Rack::MockRequest.new(Rack::Lint.new(Rack::File.new(DOCROOT, nil, nil)))
216
+ res = req.get "/cgi/test"
217
+ res.should.be.successful
218
+ res['Content-Type'].should.equal nil
219
+ end
220
+
221
+ end
@@ -0,0 +1,72 @@
1
+ require 'rack/handler'
2
+
3
+ class Rack::Handler::Lobster; end
4
+ class RockLobster; end
5
+
6
+ describe Rack::Handler do
7
+ it "has registered default handlers" do
8
+ Rack::Handler.get('cgi').should.equal Rack::Handler::CGI
9
+ Rack::Handler.get('webrick').should.equal Rack::Handler::WEBrick
10
+
11
+ begin
12
+ Rack::Handler.get('fastcgi').should.equal Rack::Handler::FastCGI
13
+ rescue LoadError
14
+ end
15
+
16
+ begin
17
+ Rack::Handler.get('mongrel').should.equal Rack::Handler::Mongrel
18
+ rescue LoadError
19
+ end
20
+ end
21
+
22
+ should "raise LoadError if handler doesn't exist" do
23
+ lambda {
24
+ Rack::Handler.get('boom')
25
+ }.should.raise(LoadError)
26
+ end
27
+
28
+ should "raise LoadError if handler isn't nested under Rack::Handler" do
29
+ # Feature-detect whether Ruby can do non-inherited const lookups.
30
+ # If it can't, then Rack::Handler may lookup non-handler toplevel
31
+ # constants, so the best we can do is no-op here and not test it.
32
+ begin
33
+ Rack::Handler._const_get('Object', false)
34
+ rescue NameError
35
+ lambda {
36
+ Rack::Handler.get('Object')
37
+ }.should.raise(LoadError)
38
+ end
39
+ end
40
+
41
+ should "get unregistered, but already required, handler by name" do
42
+ Rack::Handler.get('Lobster').should.equal Rack::Handler::Lobster
43
+ end
44
+
45
+ should "register custom handler" do
46
+ Rack::Handler.register('rock_lobster', 'RockLobster')
47
+ Rack::Handler.get('rock_lobster').should.equal RockLobster
48
+ end
49
+
50
+ should "not need registration for properly coded handlers even if not already required" do
51
+ begin
52
+ $LOAD_PATH.push File.expand_path('../unregistered_handler', __FILE__)
53
+ Rack::Handler.get('Unregistered').should.equal Rack::Handler::Unregistered
54
+ lambda {
55
+ Rack::Handler.get('UnRegistered')
56
+ }.should.raise LoadError
57
+ Rack::Handler.get('UnregisteredLongOne').should.equal Rack::Handler::UnregisteredLongOne
58
+ ensure
59
+ $LOAD_PATH.delete File.expand_path('../unregistered_handler', __FILE__)
60
+ end
61
+ end
62
+
63
+ should "allow autoloaded handlers to be registered properly while being loaded" do
64
+ path = File.expand_path('../registering_handler', __FILE__)
65
+ begin
66
+ $LOAD_PATH.push path
67
+ Rack::Handler.get('registering_myself').should.equal Rack::Handler::RegisteringMyself
68
+ ensure
69
+ $LOAD_PATH.delete path
70
+ end
71
+ end
72
+ end
data/test/spec_head.rb ADDED
@@ -0,0 +1,45 @@
1
+ require 'rack/head'
2
+ require 'rack/lint'
3
+ require 'rack/mock'
4
+
5
+ describe Rack::Head do
6
+
7
+ def test_response(headers = {})
8
+ body = StringIO.new "foo"
9
+ app = lambda do |env|
10
+ [200, {"Content-type" => "test/plain", "Content-length" => "3"}, body]
11
+ end
12
+ request = Rack::MockRequest.env_for("/", headers)
13
+ response = Rack::Lint.new(Rack::Head.new(app)).call(request)
14
+
15
+ return response, body
16
+ end
17
+
18
+ should "pass GET, POST, PUT, DELETE, OPTIONS, TRACE requests" do
19
+ %w[GET POST PUT DELETE OPTIONS TRACE].each do |type|
20
+ resp, _ = test_response("REQUEST_METHOD" => type)
21
+
22
+ resp[0].should.equal(200)
23
+ resp[1].should.equal({"Content-type" => "test/plain", "Content-length" => "3"})
24
+ resp[2].to_enum.to_a.should.equal(["foo"])
25
+ end
26
+ end
27
+
28
+ should "remove body from HEAD requests" do
29
+ resp, _ = test_response("REQUEST_METHOD" => "HEAD")
30
+
31
+ resp[0].should.equal(200)
32
+ resp[1].should.equal({"Content-type" => "test/plain", "Content-length" => "3"})
33
+ resp[2].to_enum.to_a.should.equal([])
34
+ end
35
+
36
+ should "close the body when it is removed" do
37
+ resp, body = test_response("REQUEST_METHOD" => "HEAD")
38
+ resp[0].should.equal(200)
39
+ resp[1].should.equal({"Content-type" => "test/plain", "Content-length" => "3"})
40
+ resp[2].to_enum.to_a.should.equal([])
41
+ body.should.not.be.closed
42
+ resp[2].close
43
+ body.should.be.closed
44
+ end
45
+ end