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 );
|
||||
int http_parser_has_error(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
|
||||
|
||||
|
|
|
@ -144,6 +144,8 @@ module Mongrel
|
|||
LINE_END="\r\n".freeze
|
||||
REMOTE_ADDR="REMOTE_ADDR".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
|
||||
|
||||
|
||||
|
@ -371,7 +373,7 @@ module Mongrel
|
|||
@socket.write(chunk)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
rescue EOFError,Errno::ECONNRESET,Errno::EPIPE,Errno::EINVAL,Errno::EBADF
|
||||
# ignore these since it means the client closed off early
|
||||
STDERR.puts "Client closed socket requesting file #{req}: #$!"
|
||||
|
@ -724,6 +726,7 @@ module Mongrel
|
|||
# or defaults:
|
||||
#
|
||||
# * :handler => Handler to use for this location.
|
||||
# * :in_front => Rather than appending, it prepends this handler.
|
||||
def uri(location, options={})
|
||||
ops = resolve_defaults(options)
|
||||
@listener.register(location, ops[:handler], in_front=ops[:in_front])
|
||||
|
|
|
@ -58,7 +58,11 @@ module Mongrel
|
|||
|
||||
@opt.parse! argv
|
||||
end
|
||||
|
||||
|
||||
def configure
|
||||
options []
|
||||
end
|
||||
|
||||
# Returns true/false depending on whether the command is configured properly.
|
||||
def validate
|
||||
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).
|
||||
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
|
||||
if File.directory? req
|
||||
if File.directory? req_path
|
||||
# the request is for a directory
|
||||
index = File.join(req, @index_html)
|
||||
index = File.join(req_path, @index_html)
|
||||
if File.exist? index
|
||||
# serve the index
|
||||
return index
|
||||
elsif @listing_allowed
|
||||
# serve the directory
|
||||
req
|
||||
return req_path
|
||||
else
|
||||
# do not serve anything
|
||||
return nil
|
||||
end
|
||||
else
|
||||
# it's a file and it's there
|
||||
return req
|
||||
return req_path
|
||||
end
|
||||
else
|
||||
# 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
|
||||
# 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
|
||||
response.status = 200
|
||||
stat = File.stat(req)
|
||||
header = response.header
|
||||
stat = File.stat(req_path)
|
||||
|
||||
# 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
|
||||
header[Const::ETAG] = Const::ETAG_FORMAT % [stat.mtime.to_i, stat.size, stat.ino]
|
||||
|
||||
# set the mime type from our map based on the ending
|
||||
dot_at = req.rindex(".")
|
||||
if dot_at
|
||||
header[Const::CONTENT_TYPE] = MIME_TYPES[req[dot_at .. -1]] || @default_content_type
|
||||
etag = Const::ETAG_FORMAT % [mtime.to_i, stat.size, stat.ino]
|
||||
|
||||
unmodified_since = request.params[Const::HTTP_IF_UNMODIFIED_SINCE]
|
||||
none_match = request.params[Const::HTTP_IF_NONE_MATCH]
|
||||
|
||||
# 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
|
||||
|
||||
# send a status with out content length
|
||||
response.send_status(stat.size)
|
||||
response.send_header
|
||||
if same_response
|
||||
response.start(304) {}
|
||||
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
|
||||
response.send_file(req)
|
||||
# set the mime type from our map based on the ending
|
||||
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
|
||||
|
||||
|
@ -187,25 +208,25 @@ module Mongrel
|
|||
# if allowed (based on the listing_allowed paramter to the constructor).
|
||||
def process(request, response)
|
||||
req_method = request.params[Const::REQUEST_METHOD] || Const::GET
|
||||
req = can_serve request.params[Const::PATH_INFO]
|
||||
if not req
|
||||
req_path = can_serve request.params[Const::PATH_INFO]
|
||||
if not req_path
|
||||
# not found, return a 404
|
||||
response.start(404) do |head,out|
|
||||
out << "File not found"
|
||||
end
|
||||
else
|
||||
begin
|
||||
if File.directory? req
|
||||
send_dir_listing(request.params[Const::REQUEST_URI],req, response)
|
||||
if File.directory? req_path
|
||||
send_dir_listing(request.params[Const::REQUEST_URI], req_path, response)
|
||||
elsif req_method == Const::HEAD
|
||||
send_file(req, response, true)
|
||||
elsif req_method == Const::GET
|
||||
send_file(req, response, false)
|
||||
else
|
||||
response.start(403) {|head,out| out.write(ONLY_HEAD_GET) }
|
||||
send_file(req_path, request, response, true)
|
||||
elsif req_method == Const::GET
|
||||
send_file(req_path, request, response, false)
|
||||
else
|
||||
response.start(403) {|head,out| out.write(ONLY_HEAD_GET) }
|
||||
end
|
||||
rescue => details
|
||||
STDERR.puts "Error accessing file #{req}: #{details}"
|
||||
STDERR.puts "Error accessing file #{req_path}: #{details}"
|
||||
STDERR.puts details.backtrace.join("\n")
|
||||
end
|
||||
end
|
||||
|
|
|
@ -20,13 +20,13 @@ class WSTest < Test::Unit::TestCase
|
|||
def test_simple_server
|
||||
h = HttpServer.new("0.0.0.0", 9998)
|
||||
tester = TestHandler.new
|
||||
h.register("/test", tester)
|
||||
h.register("/test", tester)
|
||||
h.run
|
||||
|
||||
sleep(1)
|
||||
res = Net::HTTP.get(URI.parse('http://localhost:9998/test'))
|
||||
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
|
||||
|
|
Loading…
Reference in a new issue