durusmail: quixote-users: Adding If-Modified-Since support to util.StaticFile
Adding If-Modified-Since support to util.StaticFile
2003-05-19
Re: Adding If-Modified-Since support to util.StaticFile
2003-05-19
Augmenting StaticFile to use "Expires:"
2003-05-28
Jason Sibre (3 parts)
Re: Augmenting StaticFile to use "Expires:"
2003-05-30
Adding If-Modified-Since support to util.StaticFile
Graham Fawcett
2003-05-19
Hi folks,

I'd like to suggest the changes below to add support for the
If-Modified-Since header (hereafter called IMS), which can substantially
improve cache resuse (and thus server performance!) for static files.

There are two main requirements to meet in order to get IMS support
working:

-- all responses for the static asset must include a Last-Modified
header. This lets the browser know that IMS might be possible; next time
it requests the asset it includes the IMS header with the asset's
Last-Modified date;

-- the server may choose to detect an IMS header and, if the asset has
not been modified since the IMS date, it may return a 304 Not Modified
with a content-length of 0. This directs the browser to re-use the
cached copy of the file.

In Quixote, this means that the IMS header must be included in the
request.environ dict. I've only been using Medusa and Twisted as request
producers; a patch for medusa_http is included below, and the
twisted_http will work as is. Other handlers may need work to take
advantage of this optimization.

Note that IMS is totally separate in intent from the Expires cache
directive; it's an active protocol, not a caching hint. My experience
with Expires vs. IMS is that IMS leads to the expected behaviour (i.e.,
browser doesn't require a second download of the file) more frequently.

Here's the util.py patch:

--- util.py     Tue Apr 08 12:48:54 2003
+++ util_new.py Mon May 19 00:36:52 2003
@@ -22,4 +22,6 @@
  from quixote import errors, html
  from cStringIO import StringIO
+from rfc822 import formatdate, parsedate
+import time

  def xmlrpc (request, func):
@@ -93,4 +95,14 @@

      def __call__(self, request):
+        last_mod = os.stat(self.path).st_mtime
+        # check for an If-Modified-Since header, and try to honour it.
+        ims = request.get_header('if-modified-since')
+        if ims:
+            ims_value = parsedate(ims)
+            last_mod_tuple = time.gmtime(last_mod)
+            if last_mod_tuple[:6] <= ims_value[:6]:
+                request.response.set_status(304, 'Not Modified')
+                return ''
+
          # Set the Content-Type for the response and return the file's
contents.
          request.response.set_content_type(self.mime_type)
@@ -100,4 +112,9 @@
          contents = fsfile.read()
          fsfile.close()
+
+        # set the last-modified header (in GMT)
+        last_modified = formatdate(last_mod)
+        request.response.set_header('Last-Modified', last_modified)
+
          return contents



and for medusa_http:

--- server\medusa_http.py       Mon May 19 00:38:38 2003
+++ \python22\lib\site-packages\quixote\server\medusa_http.py   Mon May 19
00:37:47 2003
@@ -49,4 +49,5 @@

          environ = {'REQUEST_METHOD':request.command,
+                   'HTTP_IF_MODIFIED_SINCE':msg.get('If-modified-since'),
                     'ACCEPT_ENCODING':msg.get('Accept-encoding'),
                     'CONTENT_TYPE': msg.get('Content-type'),


-- Graham



reply