mirror of
https://github.com/puma/puma.git
synced 2022-11-09 13:48:40 -05:00
Conditional response code from Dan Kubb.
git-svn-id: svn+ssh://rubyforge.org/var/svn/mongrel/trunk@163 19e92222-5c0b-0410-8929-a290d50e31e9
This commit is contained in:
parent
d529c2b1f3
commit
0ca765c199
5 changed files with 65 additions and 38 deletions
|
@ -35,7 +35,6 @@ int http_parser_finish(http_parser *parser);
|
||||||
size_t http_parser_execute(http_parser *parser, const char *data, size_t len );
|
size_t http_parser_execute(http_parser *parser, const char *data, size_t len );
|
||||||
int http_parser_has_error(http_parser *parser);
|
int http_parser_has_error(http_parser *parser);
|
||||||
int http_parser_is_finished(http_parser *parser);
|
int http_parser_is_finished(http_parser *parser);
|
||||||
void http_parser_destroy(http_parser *parser);
|
|
||||||
|
|
||||||
#define http_parser_nread(parser) (parser)->nread
|
#define http_parser_nread(parser) (parser)->nread
|
||||||
|
|
||||||
|
|
|
@ -144,6 +144,8 @@ module Mongrel
|
||||||
LINE_END="\r\n".freeze
|
LINE_END="\r\n".freeze
|
||||||
REMOTE_ADDR="REMOTE_ADDR".freeze
|
REMOTE_ADDR="REMOTE_ADDR".freeze
|
||||||
HTTP_X_FORWARDED_FOR="HTTP_X_FORWARDED_FOR".freeze
|
HTTP_X_FORWARDED_FOR="HTTP_X_FORWARDED_FOR".freeze
|
||||||
|
HTTP_IF_UNMODIFIED_SINCE="HTTP_IF_UNMODIFIED_SINCE".freeze
|
||||||
|
HTTP_IF_NONE_MATCH="HTTP_IF_NONE_MATCH".freeze
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
@ -371,7 +373,7 @@ module Mongrel
|
||||||
@socket.write(chunk)
|
@socket.write(chunk)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
rescue EOFError,Errno::ECONNRESET,Errno::EPIPE,Errno::EINVAL,Errno::EBADF
|
rescue EOFError,Errno::ECONNRESET,Errno::EPIPE,Errno::EINVAL,Errno::EBADF
|
||||||
# ignore these since it means the client closed off early
|
# ignore these since it means the client closed off early
|
||||||
STDERR.puts "Client closed socket requesting file #{req}: #$!"
|
STDERR.puts "Client closed socket requesting file #{req}: #$!"
|
||||||
|
@ -724,6 +726,7 @@ module Mongrel
|
||||||
# or defaults:
|
# or defaults:
|
||||||
#
|
#
|
||||||
# * :handler => Handler to use for this location.
|
# * :handler => Handler to use for this location.
|
||||||
|
# * :in_front => Rather than appending, it prepends this handler.
|
||||||
def uri(location, options={})
|
def uri(location, options={})
|
||||||
ops = resolve_defaults(options)
|
ops = resolve_defaults(options)
|
||||||
@listener.register(location, ops[:handler], in_front=ops[:in_front])
|
@listener.register(location, ops[:handler], in_front=ops[:in_front])
|
||||||
|
|
|
@ -58,7 +58,11 @@ module Mongrel
|
||||||
|
|
||||||
@opt.parse! argv
|
@opt.parse! argv
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def configure
|
||||||
|
options []
|
||||||
|
end
|
||||||
|
|
||||||
# Returns true/false depending on whether the command is configured properly.
|
# Returns true/false depending on whether the command is configured properly.
|
||||||
def validate
|
def validate
|
||||||
return @valid
|
return @valid
|
||||||
|
|
|
@ -95,26 +95,26 @@ module Mongrel
|
||||||
|
|
||||||
# Checks if the given path can be served and returns the full path (or nil if not).
|
# Checks if the given path can be served and returns the full path (or nil if not).
|
||||||
def can_serve(path_info)
|
def can_serve(path_info)
|
||||||
req = File.expand_path(File.join(@path,path_info), @path)
|
req_path = File.expand_path(File.join(@path,path_info), @path)
|
||||||
|
|
||||||
if req.index(@path) == 0 and File.exist? req
|
if req_path.index(@path) == 0 and File.exist? req_path
|
||||||
# it exists and it's in the right location
|
# it exists and it's in the right location
|
||||||
if File.directory? req
|
if File.directory? req_path
|
||||||
# the request is for a directory
|
# the request is for a directory
|
||||||
index = File.join(req, @index_html)
|
index = File.join(req_path, @index_html)
|
||||||
if File.exist? index
|
if File.exist? index
|
||||||
# serve the index
|
# serve the index
|
||||||
return index
|
return index
|
||||||
elsif @listing_allowed
|
elsif @listing_allowed
|
||||||
# serve the directory
|
# serve the directory
|
||||||
req
|
return req_path
|
||||||
else
|
else
|
||||||
# do not serve anything
|
# do not serve anything
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
# it's a file and it's there
|
# it's a file and it's there
|
||||||
return req
|
return req_path
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
# does not exist or isn't in the right spot
|
# does not exist or isn't in the right spot
|
||||||
|
@ -156,30 +156,51 @@ module Mongrel
|
||||||
|
|
||||||
# Sends the contents of a file back to the user. Not terribly efficient since it's
|
# Sends the contents of a file back to the user. Not terribly efficient since it's
|
||||||
# opening and closing the file for each read.
|
# opening and closing the file for each read.
|
||||||
def send_file(req, response, header_only=false)
|
def send_file(req_path, request, response, header_only=false)
|
||||||
|
|
||||||
# first we setup the headers and status then we do a very fast send on the socket directly
|
stat = File.stat(req_path)
|
||||||
response.status = 200
|
|
||||||
stat = File.stat(req)
|
|
||||||
header = response.header
|
|
||||||
|
|
||||||
# Set the last modified times as well and etag for all files
|
# Set the last modified times as well and etag for all files
|
||||||
header[Const::LAST_MODIFIED] = stat.mtime.httpdate
|
mtime = stat.mtime
|
||||||
# Calculated the same as apache, not sure how well the works on win32
|
# Calculated the same as apache, not sure how well the works on win32
|
||||||
header[Const::ETAG] = Const::ETAG_FORMAT % [stat.mtime.to_i, stat.size, stat.ino]
|
etag = Const::ETAG_FORMAT % [mtime.to_i, stat.size, stat.ino]
|
||||||
|
|
||||||
# set the mime type from our map based on the ending
|
unmodified_since = request.params[Const::HTTP_IF_UNMODIFIED_SINCE]
|
||||||
dot_at = req.rindex(".")
|
none_match = request.params[Const::HTTP_IF_NONE_MATCH]
|
||||||
if dot_at
|
|
||||||
header[Const::CONTENT_TYPE] = MIME_TYPES[req[dot_at .. -1]] || @default_content_type
|
# test to see if this is a conditional request, and test if
|
||||||
|
# the response would be identical to the last response
|
||||||
|
same_response = case
|
||||||
|
when unmodified_since && !last_response_time = Time.httpdate(unmodified_since) rescue nil : false
|
||||||
|
when unmodified_since && last_response_time > Time.now : false
|
||||||
|
when unmodified_since && mtime > last_response_time : false
|
||||||
|
when none_match && none_match == '*' : false
|
||||||
|
when none_match && !none_match.strip.split(/\s*,\s*/).include?(etag) : false
|
||||||
|
else unmodified_since || none_match # validation successful if we get this far and at least one of the header exists
|
||||||
end
|
end
|
||||||
|
|
||||||
# send a status with out content length
|
if same_response
|
||||||
response.send_status(stat.size)
|
response.start(304) {}
|
||||||
response.send_header
|
else
|
||||||
|
# first we setup the headers and status then we do a very fast send on the socket directly
|
||||||
|
response.status = 200
|
||||||
|
header = response.header
|
||||||
|
header[Const::LAST_MODIFIED] = mtime.httpdate
|
||||||
|
header[Const::ETAG] = etag
|
||||||
|
|
||||||
if not header_only
|
# set the mime type from our map based on the ending
|
||||||
response.send_file(req)
|
dot_at = req_path.rindex('.')
|
||||||
|
if dot_at
|
||||||
|
header[Const::CONTENT_TYPE] = MIME_TYPES[req_path[dot_at .. -1]] || @default_content_type
|
||||||
|
end
|
||||||
|
|
||||||
|
# send a status with out content length
|
||||||
|
response.send_status(stat.size)
|
||||||
|
response.send_header
|
||||||
|
|
||||||
|
if not header_only
|
||||||
|
response.send_file(req_path)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -187,25 +208,25 @@ module Mongrel
|
||||||
# if allowed (based on the listing_allowed paramter to the constructor).
|
# if allowed (based on the listing_allowed paramter to the constructor).
|
||||||
def process(request, response)
|
def process(request, response)
|
||||||
req_method = request.params[Const::REQUEST_METHOD] || Const::GET
|
req_method = request.params[Const::REQUEST_METHOD] || Const::GET
|
||||||
req = can_serve request.params[Const::PATH_INFO]
|
req_path = can_serve request.params[Const::PATH_INFO]
|
||||||
if not req
|
if not req_path
|
||||||
# not found, return a 404
|
# not found, return a 404
|
||||||
response.start(404) do |head,out|
|
response.start(404) do |head,out|
|
||||||
out << "File not found"
|
out << "File not found"
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
begin
|
begin
|
||||||
if File.directory? req
|
if File.directory? req_path
|
||||||
send_dir_listing(request.params[Const::REQUEST_URI],req, response)
|
send_dir_listing(request.params[Const::REQUEST_URI], req_path, response)
|
||||||
elsif req_method == Const::HEAD
|
elsif req_method == Const::HEAD
|
||||||
send_file(req, response, true)
|
send_file(req_path, request, response, true)
|
||||||
elsif req_method == Const::GET
|
elsif req_method == Const::GET
|
||||||
send_file(req, response, false)
|
send_file(req_path, request, response, false)
|
||||||
else
|
else
|
||||||
response.start(403) {|head,out| out.write(ONLY_HEAD_GET) }
|
response.start(403) {|head,out| out.write(ONLY_HEAD_GET) }
|
||||||
end
|
end
|
||||||
rescue => details
|
rescue => details
|
||||||
STDERR.puts "Error accessing file #{req}: #{details}"
|
STDERR.puts "Error accessing file #{req_path}: #{details}"
|
||||||
STDERR.puts details.backtrace.join("\n")
|
STDERR.puts details.backtrace.join("\n")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -20,13 +20,13 @@ class WSTest < Test::Unit::TestCase
|
||||||
def test_simple_server
|
def test_simple_server
|
||||||
h = HttpServer.new("0.0.0.0", 9998)
|
h = HttpServer.new("0.0.0.0", 9998)
|
||||||
tester = TestHandler.new
|
tester = TestHandler.new
|
||||||
h.register("/test", tester)
|
h.register("/test", tester)
|
||||||
h.run
|
h.run
|
||||||
|
|
||||||
sleep(1)
|
sleep(1)
|
||||||
res = Net::HTTP.get(URI.parse('http://localhost:9998/test'))
|
res = Net::HTTP.get(URI.parse('http://localhost:9998/test'))
|
||||||
assert res != nil, "Didn't get a response"
|
assert res != nil, "Didn't get a response"
|
||||||
assert tester.ran_test, "Handler didn't really run"
|
assert tester.ran_test, "Handler didn't really run"
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in a new issue