about summary refs log tree commit homepage
diff options
context:
space:
mode:
-rw-r--r--ext/http11/http11_parser.h1
-rw-r--r--lib/mongrel.rb5
-rw-r--r--lib/mongrel/command.rb6
-rw-r--r--lib/mongrel/handlers.rb87
-rw-r--r--test/test_ws.rb4
5 files changed, 65 insertions, 38 deletions
diff --git a/ext/http11/http11_parser.h b/ext/http11/http11_parser.h
index 8d49a42..2927459 100644
--- a/ext/http11/http11_parser.h
+++ b/ext/http11/http11_parser.h
@@ -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
 
diff --git a/lib/mongrel.rb b/lib/mongrel.rb
index edf8527..60932df 100644
--- a/lib/mongrel.rb
+++ b/lib/mongrel.rb
@@ -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])
diff --git a/lib/mongrel/command.rb b/lib/mongrel/command.rb
index a500ce0..d538010 100644
--- a/lib/mongrel/command.rb
+++ b/lib/mongrel/command.rb
@@ -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
diff --git a/lib/mongrel/handlers.rb b/lib/mongrel/handlers.rb
index f9ad95f..de328b7 100644
--- a/lib/mongrel/handlers.rb
+++ b/lib/mongrel/handlers.rb
@@ -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
+
+        # 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)
+        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
diff --git a/test/test_ws.rb b/test/test_ws.rb
index dd7fa77..c3bf8a7 100644
--- a/test/test_ws.rb
+++ b/test/test_ws.rb
@@ -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