Skip to content
This repository
Fetching contributors…

Octocat-spinner-32-eaf2f5

Cannot retrieve contributors at this time

file 193 lines (142 sloc) 5.306 kb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192
require 'rack/file'
require 'rack/lint'
require 'rack/mock'

describe Rack::File do
  DOCROOT = File.expand_path(File.dirname(__FILE__)) unless defined? DOCROOT

  def file(*args)
    Rack::Lint.new Rack::File.new(*args)
  end

  should "serve files" do
    res = Rack::MockRequest.new(file(DOCROOT)).get("/cgi/test")

    res.should.be.ok
    res.should =~ /ruby/
  end

  should "set Last-Modified header" do
    res = Rack::MockRequest.new(file(DOCROOT)).get("/cgi/test")

    path = File.join(DOCROOT, "/cgi/test")

    res.should.be.ok
    res["Last-Modified"].should.equal File.mtime(path).httpdate
  end

  should "return 304 if file isn't modified since last serve" do
    path = File.join(DOCROOT, "/cgi/test")
    res = Rack::MockRequest.new(file(DOCROOT)).
      get("/cgi/test", 'HTTP_IF_MODIFIED_SINCE' => File.mtime(path).httpdate)

    res.status.should.equal 304
    res.body.should.be.empty
  end

  should "return the file if it's modified since last serve" do
    path = File.join(DOCROOT, "/cgi/test")
    res = Rack::MockRequest.new(file(DOCROOT)).
      get("/cgi/test", 'HTTP_IF_MODIFIED_SINCE' => (File.mtime(path) - 100).httpdate)

    res.should.be.ok
  end

  should "serve files with URL encoded filenames" do
    res = Rack::MockRequest.new(file(DOCROOT)).get("/cgi/%74%65%73%74") # "/cgi/test"

    res.should.be.ok
    res.should =~ /ruby/
  end

  should "allow safe directory traversal" do
    req = Rack::MockRequest.new(file(DOCROOT))

    res = req.get('/cgi/../cgi/test')
    res.should.be.successful

    res = req.get('.')
    res.should.be.not_found

    res = req.get("test/..")
    res.should.be.not_found
  end

  should "not allow unsafe directory traversal" do
    req = Rack::MockRequest.new(file(DOCROOT))

    res = req.get("/../README.rdoc")
    res.should.be.client_error

    res = req.get("../test/spec_file.rb")
    res.should.be.client_error

    res = req.get("../README.rdoc")
    res.should.be.client_error

    res.should.be.not_found
  end

  should "allow files with .. in their name" do
    req = Rack::MockRequest.new(file(DOCROOT))
    res = req.get("/cgi/..test")
    res.should.be.not_found

    res = req.get("/cgi/test..")
    res.should.be.not_found

    res = req.get("/cgi../test..")
    res.should.be.not_found
  end

  should "not allow unsafe directory traversal with encoded periods" do
    res = Rack::MockRequest.new(file(DOCROOT)).get("/%2E%2E/README")

    res.should.be.client_error?
    res.should.be.not_found
  end

  should "allow safe directory traversal with encoded periods" do
    res = Rack::MockRequest.new(file(DOCROOT)).get("/cgi/%2E%2E/cgi/test")

    res.should.be.successful
  end

  should "404 if it can't find the file" do
    res = Rack::MockRequest.new(file(DOCROOT)).get("/cgi/blubb")

    res.should.be.not_found
  end

  should "detect SystemCallErrors" do
    res = Rack::MockRequest.new(file(DOCROOT)).get("/cgi")

    res.should.be.not_found
  end

  should "return bodies that respond to #to_path" do
    env = Rack::MockRequest.env_for("/cgi/test")
    status, _, body = Rack::File.new(DOCROOT).call(env)

    path = File.join(DOCROOT, "/cgi/test")

    status.should.equal 200
    body.should.respond_to :to_path
    body.to_path.should.equal path
  end

  should "return correct byte range in body" do
    env = Rack::MockRequest.env_for("/cgi/test")
    env["HTTP_RANGE"] = "bytes=22-33"
    res = Rack::MockResponse.new(*file(DOCROOT).call(env))

    res.status.should.equal 206
    res["Content-Length"].should.equal "12"
    res["Content-Range"].should.equal "bytes 22-33/193"
    res.body.should.equal "-*- ruby -*-"
  end

  should "return error for unsatisfiable byte range" do
    env = Rack::MockRequest.env_for("/cgi/test")
    env["HTTP_RANGE"] = "bytes=1234-5678"
    res = Rack::MockResponse.new(*file(DOCROOT).call(env))

    res.status.should.equal 416
    res["Content-Range"].should.equal "bytes */193"
  end

  should "support custom http headers" do
    env = Rack::MockRequest.env_for("/cgi/test")
    status, heads, _ = file(DOCROOT, 'Cache-Control' => 'public, max-age=38',
     'Access-Control-Allow-Origin' => '*').call(env)

    status.should.equal 200
    heads['Cache-Control'].should.equal 'public, max-age=38'
    heads['Access-Control-Allow-Origin'].should.equal '*'
  end

  should "support not add custom http headers if none are supplied" do
    env = Rack::MockRequest.env_for("/cgi/test")
    status, heads, _ = file(DOCROOT).call(env)

    status.should.equal 200
    heads['Cache-Control'].should.equal nil
    heads['Access-Control-Allow-Origin'].should.equal nil
  end

  should "only support GET and HEAD requests" do
    req = Rack::MockRequest.new(file(DOCROOT))

    forbidden = %w[post put patch delete]
    forbidden.each do |method|

      res = req.send(method, "/cgi/test")
      res.should.be.client_error
      res.should.be.method_not_allowed
    end

    allowed = %w[get head]
    allowed.each do |method|
      res = req.send(method, "/cgi/test")
      res.should.be.successful
    end
  end

  should "set Content-Length correctly for HEAD requests" do
    req = Rack::MockRequest.new(Rack::Lint.new(Rack::File.new(DOCROOT)))
    res = req.head "/cgi/test"
    res.should.be.successful
    res['Content-Length'].should.equal "193"
  end

end
Something went wrong with that request. Please try again.