class Staticd::HTTPServer

Simple HTTP server Rack app.

If the resource is readable from the root folder at the path given by the request, the resource is sent to the client. Otherwise a 404 Not Found HTTP error is sent.

Constants

DEFAULT_MIME_TYPE

Mime type used when no type has been identified.

EXT_MIME_TYPE

Mime types served by the webserver (from NGiNX mime.types file).

Public Class Methods

new(http_root, access_logger=nil) click to toggle source
# File lib/staticd/http_server.rb, line 83
def initialize(http_root, access_logger=nil)
  @http_root = http_root
  unless (@access_logger = access_logger)
    @access_logger = Logger.new(STDOUT)
    @access_logger.formatter = proc { |_, _, _, msg| "#{msg}\n"}
  end

  raise "No HTTP root folder provided" unless @http_root
end

Public Instance Methods

_call(env) click to toggle source
# File lib/staticd/http_server.rb, line 97
def _call(env)
  @env = env
  req = Rack::Request.new(@env)
  file_path = @http_root + req.path
  res = File.readable?(file_path) ? serve(file_path) : send_404
  log(req, res)
end
call(env) click to toggle source
# File lib/staticd/http_server.rb, line 93
def call(env)
  dup._call(env)
end

Private Instance Methods

log(req, res) click to toggle source

Log a request using the “Extended Log File Format”. See: www.w3.org/TR/WD-logfile.html

Use the request.time key setup by the Rack::RequestTime middleware.

Version: 1.0 Fields: time cs-dns cs-ip date cs-method cs-uri sc-status sc-byte sc-time-taken

# File lib/staticd/http_server.rb, line 114
def log(req, res)
  request_stop_time = Time.now
  request_start_time =
    req.env.key?("request.time") ? req.env["request.time"] : nil
  request_completed_time =
    if request_start_time
      (request_stop_time - request_start_time).round(4)
    else
      "-"
    end
  content_length =
    res[1].key?("Content-Length") ? " #{res[1]["Content-Length"]}" : "-"

  log_string = "#{request_stop_time.strftime("%Y-%m-%d %H:%M:%S")}"
  log_string << " #{req.host}"
  log_string << " #{req.env["REMOTE_ADDR"]}"
  log_string << " #{req.env["REQUEST_METHOD"]} #{req.path_info}"
  log_string << " #{res[0]}"
  log_string << content_length
  log_string << " #{request_completed_time}"
  @access_logger.info(log_string)

  res
end
mime(file_path) click to toggle source
# File lib/staticd/http_server.rb, line 188
def mime(file_path)
  ext = File.extname(file_path).downcase
  EXT_MIME_TYPE.key?(ext) ? EXT_MIME_TYPE[ext] : DEFAULT_MIME_TYPE
end
send(file_path) click to toggle source

Send a file loading it in memory.

Method used in the first implementation. Keep it for compatibility purpose when the Rack hijack API is not supported.

# File lib/staticd/http_server.rb, line 153
def send(file_path)
  response = Rack::Response.new
  response["Content-Type"] = mime(file_path)
  File.foreach(file_path) { |chunk| response.write(chunk) }
  response.finish
end
send_404() click to toggle source
# File lib/staticd/http_server.rb, line 183
def send_404
  res = Rack::Response.new(["Not Found"], 404, {})
  res.finish
end
sendfile(file_path) click to toggle source

Use sendfile system call to send file without loading it into memory.

It use the sendfile gem and the rack hijacking api. See: github.com/codeslinger/sendfile See: blog.phusion.nl/2013/01/23/the-new-rack-socket-hijacking-api/

# File lib/staticd/http_server.rb, line 165
def sendfile(file_path)

  response_header = {
    "Content-Type" => mime(file_path),
    "Content-Length" => size(file_path),
    "Connection" => "close"
  }
  response_header["rack.hijack"] = lambda do |io|
    begin
      File.open(file_path) { |file| io.sendfile(file) }
      io.flush
    ensure
      io.close
    end
  end
  [200, response_header]
end
serve(file_path) click to toggle source

Serve a file.

This method will return a Rack compatible response ready to be served to the client. It will use the appropriate method (loading file into memory vs serving file using the sendfile system call) based on availability.

# File lib/staticd/http_server.rb, line 144
def serve(file_path)
  @env['rack.hijack?'] ? sendfile(file_path) : send(file_path)
end
size(file_path) click to toggle source
# File lib/staticd/http_server.rb, line 193
def size(file_path)
  File.size(file_path).to_s
end