about summary refs log tree commit homepage
path: root/lib/mongrel/handlers.rb
diff options
context:
space:
mode:
authorzedshaw <zedshaw@19e92222-5c0b-0410-8929-a290d50e31e9>2006-05-04 16:07:56 +0000
committerzedshaw <zedshaw@19e92222-5c0b-0410-8929-a290d50e31e9>2006-05-04 16:07:56 +0000
commit0ca765c199519f30e99bda26552905f9e4baf0b9 (patch)
tree80e7257b684f85fd503b0f66d2284addcdf58c38 /lib/mongrel/handlers.rb
parentd529c2b1f34c658842705d614722a91b23534ff5 (diff)
downloadunicorn-0ca765c199519f30e99bda26552905f9e4baf0b9.tar.gz
git-svn-id: svn+ssh://rubyforge.org/var/svn/mongrel/trunk@163 19e92222-5c0b-0410-8929-a290d50e31e9
Diffstat (limited to 'lib/mongrel/handlers.rb')
-rw-r--r--lib/mongrel/handlers.rb87
1 files changed, 54 insertions, 33 deletions
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