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
# 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
# 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
# File lib/staticd/http_server.rb, line 93 def call(env) dup._call(env) end
Private Instance Methods
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
# 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 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
# File lib/staticd/http_server.rb, line 183 def send_404 res = Rack::Response.new(["Not Found"], 404, {}) res.finish end
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 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
# File lib/staticd/http_server.rb, line 193 def size(file_path) File.size(file_path).to_s end