spec_file.rb 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. require 'rack/file'
  2. require 'rack/mock'
  3. describe Rack::File do
  4. DOCROOT = File.expand_path(File.dirname(__FILE__)) unless defined? DOCROOT
  5. should "serve files" do
  6. res = Rack::MockRequest.new(Rack::Lint.new(Rack::File.new(DOCROOT))).
  7. get("/cgi/test")
  8. res.should.be.ok
  9. res.should =~ /ruby/
  10. end
  11. should "set Last-Modified header" do
  12. res = Rack::MockRequest.new(Rack::Lint.new(Rack::File.new(DOCROOT))).
  13. get("/cgi/test")
  14. path = File.join(DOCROOT, "/cgi/test")
  15. res.should.be.ok
  16. res["Last-Modified"].should.equal File.mtime(path).httpdate
  17. end
  18. should "return 304 if file isn't modified since last serve" do
  19. path = File.join(DOCROOT, "/cgi/test")
  20. res = Rack::MockRequest.new(Rack::Lint.new(Rack::File.new(DOCROOT))).
  21. get("/cgi/test", 'HTTP_IF_MODIFIED_SINCE' => File.mtime(path).httpdate)
  22. res.status.should.equal 304
  23. res.body.should.be.empty
  24. end
  25. should "return the file if it's modified since last serve" do
  26. path = File.join(DOCROOT, "/cgi/test")
  27. res = Rack::MockRequest.new(Rack::Lint.new(Rack::File.new(DOCROOT))).
  28. get("/cgi/test", 'HTTP_IF_MODIFIED_SINCE' => (File.mtime(path) - 100).httpdate)
  29. res.should.be.ok
  30. end
  31. should "serve files with URL encoded filenames" do
  32. res = Rack::MockRequest.new(Rack::Lint.new(Rack::File.new(DOCROOT))).
  33. get("/cgi/%74%65%73%74") # "/cgi/test"
  34. res.should.be.ok
  35. res.should =~ /ruby/
  36. end
  37. should "allow safe directory traversal" do
  38. req = Rack::MockRequest.new(Rack::Lint.new(Rack::File.new(DOCROOT)))
  39. res = req.get('/cgi/../cgi/test')
  40. res.should.be.successful
  41. res = req.get('.')
  42. res.should.be.not_found
  43. res = req.get("test/..")
  44. res.should.be.not_found
  45. end
  46. should "not allow unsafe directory traversal" do
  47. req = Rack::MockRequest.new(Rack::Lint.new(Rack::File.new(DOCROOT)))
  48. res = req.get("/../README")
  49. res.should.be.client_error
  50. res = req.get("../test")
  51. res.should.be.client_error
  52. res = req.get("..")
  53. res.should.be.client_error
  54. res.should.be.not_found
  55. end
  56. should "allow files with .. in their name" do
  57. req = Rack::MockRequest.new(Rack::Lint.new(Rack::File.new(DOCROOT)))
  58. res = req.get("/cgi/..test")
  59. res.should.be.not_found
  60. res = req.get("/cgi/test..")
  61. res.should.be.not_found
  62. res = req.get("/cgi../test..")
  63. res.should.be.not_found
  64. end
  65. should "not allow unsafe directory traversal with encoded periods" do
  66. res = Rack::MockRequest.new(Rack::Lint.new(Rack::File.new(DOCROOT))).
  67. get("/%2E%2E/README")
  68. res.should.be.client_error?
  69. res.should.be.not_found
  70. end
  71. should "allow safe directory traversal with encoded periods" do
  72. res = Rack::MockRequest.new(Rack::Lint.new(Rack::File.new(DOCROOT))).
  73. get("/cgi/%2E%2E/cgi/test")
  74. res.should.be.successful
  75. end
  76. should "404 if it can't find the file" do
  77. res = Rack::MockRequest.new(Rack::Lint.new(Rack::File.new(DOCROOT))).
  78. get("/cgi/blubb")
  79. res.should.be.not_found
  80. end
  81. should "detect SystemCallErrors" do
  82. res = Rack::MockRequest.new(Rack::Lint.new(Rack::File.new(DOCROOT))).
  83. get("/cgi")
  84. res.should.be.not_found
  85. end
  86. should "return bodies that respond to #to_path" do
  87. env = Rack::MockRequest.env_for("/cgi/test")
  88. status, _, body = Rack::File.new(DOCROOT).call(env)
  89. path = File.join(DOCROOT, "/cgi/test")
  90. status.should.equal 200
  91. body.should.respond_to :to_path
  92. body.to_path.should.equal path
  93. end
  94. should "return correct byte range in body" do
  95. env = Rack::MockRequest.env_for("/cgi/test")
  96. env["HTTP_RANGE"] = "bytes=22-33"
  97. res = Rack::MockResponse.new(*Rack::File.new(DOCROOT).call(env))
  98. res.status.should.equal 206
  99. res["Content-Length"].should.equal "12"
  100. res["Content-Range"].should.equal "bytes 22-33/193"
  101. res.body.should.equal "-*- ruby -*-"
  102. end
  103. should "return error for unsatisfiable byte range" do
  104. env = Rack::MockRequest.env_for("/cgi/test")
  105. env["HTTP_RANGE"] = "bytes=1234-5678"
  106. res = Rack::MockResponse.new(*Rack::File.new(DOCROOT).call(env))
  107. res.status.should.equal 416
  108. res["Content-Range"].should.equal "bytes */193"
  109. end
  110. should "support cache control options" do
  111. env = Rack::MockRequest.env_for("/cgi/test")
  112. status, heads, _ = Rack::File.new(DOCROOT, 'public, max-age=38').call(env)
  113. status.should.equal 200
  114. heads['Cache-Control'].should.equal 'public, max-age=38'
  115. end
  116. should "only support GET and HEAD requests" do
  117. req = Rack::MockRequest.new(Rack::Lint.new(Rack::File.new(DOCROOT)))
  118. forbidden = %w[post put delete]
  119. forbidden.each do |method|
  120. res = req.send(method, "/cgi/test")
  121. res.should.be.client_error
  122. res.should.be.method_not_allowed
  123. end
  124. allowed = %w[get head]
  125. allowed.each do |method|
  126. res = req.send(method, "/cgi/test")
  127. res.should.be.successful
  128. end
  129. end
  130. should "set Content-Length correctly for HEAD requests" do
  131. req = Rack::MockRequest.new(Rack::Lint.new(Rack::File.new(DOCROOT)))
  132. res = req.head "/cgi/test"
  133. res.should.be.successful
  134. res['Content-Length'].should.equal "193"
  135. end
  136. end