class Rack::File

Rack::File serves files below the root directory given, according to the path info of the Rack request. e.g. when ::new(“/etc”) is used, you can access ‘passwd’ file as localhost:9292/passwd

Handlers can detect if bodies are a Rack::File, and use mechanisms like sendfile on the path.

Constants

F

Attributes

path[RW]
root[RW]
to_path[RW]

Public Class Methods

new(root) click to toggle source
# File lib/rack/file.rb, line 20
def initialize(root)
  @root = root
end

Public Instance Methods

_call(env) click to toggle source
# File lib/rack/file.rb, line 30
def _call(env)
  @path_info = Utils.unescape(env["PATH_INFO"])
  return forbidden  if @path_info.include? ".."

  @path = F.join(@root, @path_info)

  begin
    if F.file?(@path) && F.readable?(@path)
      serving
    else
      raise Errno::EPERM
    end
  rescue SystemCallError
    not_found
  end
end
call(env) click to toggle source
# File lib/rack/file.rb, line 24
def call(env)
  dup._call(env)
end
each() { |part| ... } click to toggle source
# File lib/rack/file.rb, line 84
def each
  F.open(@path, "rb") { |file|
    while part = file.read(8192)
      yield part
    end
  }
end
forbidden() click to toggle source
# File lib/rack/file.rb, line 47
def forbidden
  body = "Forbidden\n"
  [403, {"Content-Type" => "text/plain",
         "Content-Length" => body.size.to_s,
         "X-Cascade" => "pass"},
   [body]]
end
not_found() click to toggle source
# File lib/rack/file.rb, line 76
def not_found
  body = "File not found: #{@path_info}\n"
  [404, {"Content-Type" => "text/plain",
     "Content-Length" => body.size.to_s,
     "X-Cascade" => "pass"},
   [body]]
end
serving() click to toggle source

NOTE:

We check via File::size? whether this file provides size info
via stat (e.g. /proc files often don't), otherwise we have to
figure it out by reading the whole file into memory. And while
we're at it we also use this as body then.
# File lib/rack/file.rb, line 61
def serving
  if size = F.size?(@path)
    body = self
  else
    body = [F.read(@path)]
    size = Utils.bytesize(body.first)
  end

  [200, {
    "Last-Modified"  => F.mtime(@path).httpdate,
    "Content-Type"   => Mime.mime_type(F.extname(@path), 'text/plain'),
    "Content-Length" => size.to_s
  }, body]
end