SVN: r22001 - trunk/quixote
Neil Schemenauer <[email protected]> Mon, 14 Jul 2003 14:15:03 -0400
| Newsgroups | gmane.comp.web.quixote.cvs |
|---|---|
| Message-ID | <[email protected]> |
Author: nascheme
Date: 2003-07-14 14:15:02 -0400 (Mon, 14 Jul 2003)
New Revision: 22001
Modified:
trunk/quixote/util.py
Log:
Add If-Modified-Since support for StaticFile objects. Also, don't set the
Expires header by default. Instead, set the Last-Modified header based on
the modification time of the file. The Expires header can be enabled
by providing a value for the 'cache_time' argument.
Modified: trunk/quixote/util.py
==============================================================================
--- trunk/quixote/util.py 2003-07-14 15:37:24 UTC (rev 22000)
+++ trunk/quixote/util.py 2003-07-14 18:15:02 UTC (rev 22001)
@@ -21,6 +21,7 @@
import os, mimetypes, urllib
from quixote import errors, html
from cStringIO import StringIO
+from rfc822 import formatdate
def xmlrpc (request, func):
"""xmlrpc(request:Request, func:callable) : string
@@ -66,7 +67,7 @@
"""
def __init__(self, path, follow_symlinks=0,
- mime_type=None, encoding=None):
+ mime_type=None, encoding=None, cache_time=None):
"""StaticFile(path:string, follow_symlinks:bool)
Initialize instance with the absolute path to the file. If
@@ -74,6 +75,12 @@
'mime_type' specifies the MIME type, and 'encoding' the
encoding; if omitted, the MIME type will be guessed,
defaulting to text/plain.
+
+ Optional cache_time parameter indicates the number of
+ seconds a response is considered to be valid, and will
+ be used to set the Expires header in the response when
+ quixote gets to that part. If the value is None then
+ the Expires header will not be set.
"""
# Check that the supplied path is absolute and (if a symbolic link) may
@@ -90,8 +97,15 @@
strict=0)
self.mime_type = mime_type or guess_mime or 'text/plain'
self.encoding = encoding or guess_enc or None
+ self.cache_time = cache_time
def __call__(self, request):
+ last_modified = formatdate(os.stat(self.path).st_mtime)
+ if last_modified == request.get_header('If-Modified-Since'):
+ # handle exact match of If-Modified-Since header
+ request.response.set_status(304)
+ return ''
+
# Set the Content-Type for the response and return the file's contents.
request.response.set_content_type(self.mime_type)
if self.encoding:
@@ -99,6 +113,18 @@
fsfile = open(self.path, 'rb')
contents = fsfile.read()
fsfile.close()
+
+ request.response.set_header('Last-Modified', last_modified)
+
+ if self.cache_time is None:
+ request.response.cache = None # don't set the Expires header
+ else:
+ # explicitly allow client to cache page by setting the Expires
+ # header, this is even more efficient than the using
+ # Last-Modified/If-Modified-Since since the browser does not need
+ # to contact the server
+ request.response.cache = self.cache_time
+
return contents
@@ -110,7 +136,8 @@
_q_exports = []
- def __init__(self, path, use_cache=0, list_directory=0, follow_symlinks=0):
+ def __init__(self, path, use_cache=0, list_directory=0, follow_symlinks=0,
+ cache_time=None):
"""StaticDirectory(path:string, use_cache:bool,
list_directory:bool, follow_symlinks:bool)
@@ -118,6 +145,9 @@
If 'use_cache' is true, StaticFile instances will be cached in memory.
If 'list_directory' is true, users can request a directory listing.
If 'follow_symlinks' is true, symbolic links will be followed.
+
+ Optional parameter cache_time allows setting of Expires header in
+ response object (see note for StaticFile for more detail).
"""
# Check that the supplied path is absolute
@@ -129,6 +159,7 @@
self.cache = {}
self.list_directory = list_directory
self.follow_symlinks = follow_symlinks
+ self.cache_time = cache_time
def _q_index(self, request):
"""
@@ -180,9 +211,10 @@
if os.path.isdir(item_filepath):
item = StaticDirectory(item_filepath, self.use_cache,
- self.list_directory, self.follow_symlinks)
+ self.list_directory, self.follow_symlinks, self.cache_time)
elif os.path.isfile(item_filepath):
- item = StaticFile(item_filepath, self.follow_symlinks)
+ item = StaticFile(item_filepath, self.follow_symlinks,
+ cache_time=self.cache_time)
else:
raise errors.TraversalError
if self.use_cache: