1
0
Fork 0
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:
zedshaw 2006-05-04 16:07:56 +00:00
parent d529c2b1f3
commit 0ca765c199
5 changed files with 65 additions and 38 deletions

View file

@ -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

View file

@ -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])

View file

@ -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

View file

@ -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

View file

@ -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