mirror of
https://github.com/puma/puma.git
synced 2022-11-09 13:48:40 -05:00
Implements minor little tweaks to reduce String cycling. Adds ETag and Last-Modified headers to DirHandler so static files are cached by the browser.
git-svn-id: svn+ssh://rubyforge.org/var/svn/mongrel/trunk@132 19e92222-5c0b-0410-8929-a290d50e31e9
This commit is contained in:
parent
57ff055dec
commit
b87e5685a2
3 changed files with 52 additions and 27 deletions
|
@ -33,6 +33,8 @@ static VALUE global_mongrel_version;
|
|||
static VALUE global_server_software;
|
||||
static VALUE global_port_80;
|
||||
|
||||
#define DEF_GLOBAL(name, val) global_##name = rb_obj_freeze(rb_str_new2(val)); rb_global_variable(&global_##name);
|
||||
|
||||
void http_field(void *data, const char *field, size_t flen, const char *value, size_t vlen)
|
||||
{
|
||||
char *ch, *end;
|
||||
|
@ -451,8 +453,6 @@ VALUE URIClassifier_resolve(VALUE self, VALUE uri)
|
|||
return result;
|
||||
}
|
||||
|
||||
#define DEF_GLOBAL(name, val) global_##name = rb_str_new2(val); rb_global_variable(&global_##name)
|
||||
|
||||
|
||||
void Init_http11()
|
||||
{
|
||||
|
|
|
@ -88,34 +88,52 @@ module Mongrel
|
|||
# REMOTE_USER, or REMOTE_HOST parameters since those are either a security problem or
|
||||
# too taxing on performance.
|
||||
module Const
|
||||
DATE = "Date".freeze
|
||||
|
||||
# This is the part of the path after the SCRIPT_NAME. URIClassifier will determine this.
|
||||
PATH_INFO="PATH_INFO"
|
||||
PATH_INFO="PATH_INFO".freeze
|
||||
|
||||
# This is the intial part that your handler is identified as by URIClassifier.
|
||||
SCRIPT_NAME="SCRIPT_NAME"
|
||||
SCRIPT_NAME="SCRIPT_NAME".freeze
|
||||
|
||||
# The original URI requested by the client. Passed to URIClassifier to build PATH_INFO and SCRIPT_NAME.
|
||||
REQUEST_URI='REQUEST_URI'
|
||||
REQUEST_URI='REQUEST_URI'.freeze
|
||||
|
||||
MONGREL_VERSION="0.3.12"
|
||||
MONGREL_VERSION="0.3.12".freeze
|
||||
|
||||
# The standard empty 404 response for bad requests. Use Error4040Handler for custom stuff.
|
||||
ERROR_404_RESPONSE="HTTP/1.1 404 Not Found\r\nConnection: close\r\nServer: #{MONGREL_VERSION}\r\n\r\nNOT FOUND"
|
||||
ERROR_404_RESPONSE="HTTP/1.1 404 Not Found\r\nConnection: close\r\nServer: #{MONGREL_VERSION}\r\n\r\nNOT FOUND".freeze
|
||||
|
||||
CONTENT_LENGTH="CONTENT_LENGTH"
|
||||
CONTENT_LENGTH="CONTENT_LENGTH".freeze
|
||||
|
||||
# A common header for indicating the server is too busy. Not used yet.
|
||||
ERROR_503_RESPONSE="HTTP/1.1 503 Service Unavailable\r\n\r\nBUSY"
|
||||
ERROR_503_RESPONSE="HTTP/1.1 503 Service Unavailable\r\n\r\nBUSY".freeze
|
||||
|
||||
# The basic max request size we'll try to read.
|
||||
CHUNK_SIZE=(16 * 1024)
|
||||
|
||||
# Format to generate a correct RFC 1123 date. rdoc for Time is wrong, there is no httpdate function.
|
||||
RFC_1123_DATE_FORMAT="%a, %d %B %Y %H:%M:%S GMT".freeze
|
||||
|
||||
# A frozen format for this is about 15% faster
|
||||
STATUS_FORMAT = "HTTP/1.1 %d %s\r\nContent-Length: %d\r\nConnection: close\r\n".freeze
|
||||
CONTENT_TYPE = "Content-Type".freeze
|
||||
LAST_MODIFIED = "Last-Modified".freeze
|
||||
ETAG = "ETag".freeze
|
||||
SLASH = "/".freeze
|
||||
REQUEST_METHOD="REQUEST_METHOD".freeze
|
||||
GET="GET".freeze
|
||||
HEAD="HEAD".freeze
|
||||
# ETag is based on the apache standard of hex mtime-size-inode (inode is 0 on win32)
|
||||
ETAG_FORMAT="\"%x-%x-%x\"".freeze
|
||||
HEADER_FORMAT="%s: %s\r\n".freeze
|
||||
LINE_END="\r\n".freeze
|
||||
end
|
||||
|
||||
|
||||
# When a handler is found for a registered URI then this class is constructed
|
||||
# and passed to your HttpHandler::process method. You should assume that
|
||||
# *one* handler processes all requests. Included in the HttpReqeust is a
|
||||
# *one* handler processes all requests. Included in the HttpRequest is a
|
||||
# HttpRequest.params Hash that matches common CGI params, and a HttpRequest.body
|
||||
# which is a string containing the request body (raw for now).
|
||||
#
|
||||
|
@ -195,10 +213,7 @@ module Mongrel
|
|||
|
||||
# Simply writes "#{key}: #{value}" to an output buffer.
|
||||
def[]=(key,value)
|
||||
@out.write(key)
|
||||
@out.write(": ")
|
||||
@out.write(value)
|
||||
@out.write("\r\n")
|
||||
@out.write(Const::HEADER_FORMAT % [key, value])
|
||||
end
|
||||
|
||||
end
|
||||
|
@ -247,6 +262,7 @@ module Mongrel
|
|||
@body = StringIO.new
|
||||
@status = 404
|
||||
@header = HeaderOut.new(StringIO.new)
|
||||
@header[Const::DATE] = HttpServer.httpdate(Time.now)
|
||||
@filter = filter
|
||||
@body_sent = false
|
||||
@header_sent = false
|
||||
|
@ -284,8 +300,7 @@ module Mongrel
|
|||
def send_status(content_length=nil)
|
||||
if not @status_sent
|
||||
content_length ||= @body.length
|
||||
status = "HTTP/1.1 #{@status} #{HTTP_STATUS_CODES[@status]}\r\nContent-Length: #{content_length}\r\nConnection: close\r\n"
|
||||
@socket.write(status)
|
||||
@socket.write(Const::STATUS_FORMAT % [status, HTTP_STATUS_CODES[@status], content_length])
|
||||
@status_sent = true
|
||||
end
|
||||
end
|
||||
|
@ -293,8 +308,7 @@ module Mongrel
|
|||
def send_header
|
||||
if not @header_sent
|
||||
@header.out.rewind
|
||||
@socket.write(@header.out.read)
|
||||
@socket.write("\r\n")
|
||||
@socket.write(@header.out.read + Const::LINE_END)
|
||||
@header_sent = true
|
||||
end
|
||||
end
|
||||
|
@ -502,7 +516,7 @@ module Mongrel
|
|||
if not handlers
|
||||
@classifier.register(uri, [handler])
|
||||
else
|
||||
if path_info.length == 0 or (script_name == "/" and path_info == "/")
|
||||
if path_info.length == 0 or (script_name == Const::SLASH and path_info == Const::SLASH)
|
||||
handlers << handler
|
||||
else
|
||||
@classifier.register(uri, [handler])
|
||||
|
@ -527,6 +541,11 @@ module Mongrel
|
|||
stopper.priority = 10
|
||||
end
|
||||
|
||||
# Given the a time object it converts it to GMT and applies the RFC1123 format to it.
|
||||
def HttpServer.httpdate(date)
|
||||
date.gmtime.strftime(Const::RFC_1123_DATE_FORMAT)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
|
|
|
@ -84,6 +84,7 @@ module Mongrel
|
|||
".txt" => "text/plain"
|
||||
}
|
||||
|
||||
ONLY_HEAD_GET="Only HEAD and GET allowed.".freeze
|
||||
|
||||
attr_reader :path
|
||||
|
||||
|
@ -134,7 +135,7 @@ module Mongrel
|
|||
|
||||
if @listing_allowed
|
||||
response.start(200) do |head,out|
|
||||
head['Content-Type'] = "text/html"
|
||||
head[Const::CONTENT_TYPE] = "text/html"
|
||||
out << "<html><head><title>Directory Listing</title></head><body>"
|
||||
Dir.entries(dir).each do |child|
|
||||
next if child == "."
|
||||
|
@ -167,7 +168,12 @@ module Mongrel
|
|||
if dot_at
|
||||
ext = req[dot_at .. -1]
|
||||
if MIME_TYPES[ext]
|
||||
response.header['Content-Type'] = MIME_TYPES[ext]
|
||||
stat = File.stat(req)
|
||||
response.header[Const::CONTENT_TYPE] = MIME_TYPES[ext]
|
||||
# TODO: Confirm this works for rfc 1123
|
||||
response.header[Const::LAST_MODIFIED] = HttpServer.httpdate(stat.mtime)
|
||||
# TODO that this is a valid way to calculate an etag
|
||||
response.header[Const::ETAG] = Const::ETAG_FORMAT % [stat.mtime.to_i, stat.size, stat.ino]
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -193,8 +199,8 @@ module Mongrel
|
|||
# Process the request to either serve a file or a directory listing
|
||||
# if allowed (based on the listing_allowed paramter to the constructor).
|
||||
def process(request, response)
|
||||
req_method = request.params['REQUEST_METHOD'] || "GET"
|
||||
req = can_serve request.params['PATH_INFO']
|
||||
req_method = request.params[Const::REQUEST_METHOD] || Const::GET
|
||||
req = can_serve request.params[Const::PATH_INFO]
|
||||
if not req
|
||||
# not found, return a 404
|
||||
response.start(404) do |head,out|
|
||||
|
@ -203,13 +209,13 @@ module Mongrel
|
|||
else
|
||||
begin
|
||||
if File.directory? req
|
||||
send_dir_listing(request.params["REQUEST_URI"],req, response)
|
||||
elsif req_method == "HEAD"
|
||||
send_dir_listing(request.params[Const::REQUEST_URI],req, response)
|
||||
elsif req_method == Const::HEAD
|
||||
send_file(req, response, true)
|
||||
elsif req_method == "GET"
|
||||
elsif req_method == Const::GET
|
||||
send_file(req, response, false)
|
||||
else
|
||||
response.start(403) {|head,out| out.write("Only HEAD and GET allowed.") }
|
||||
response.start(403) {|head,out| out.write(ONLY_HEAD_GET) }
|
||||
end
|
||||
rescue => details
|
||||
STDERR.puts "Error accessing file #{req}: #{details}"
|
||||
|
|
Loading…
Add table
Reference in a new issue