From 1435bc5eb140db5a5c368641e70d50770f8258c8 Mon Sep 17 00:00:00 2001 From: richard Date: Fri, 5 Dec 2003 03:28:38 +0000 Subject: [PATCH] Refactored CGI file serving so that FileClass contents are a) read more cleanly and b) served with reasonable modification dates and handling of if-modified-since. File serving also sends a content-length now too. git-svn-id: http://svn.roundup-tracker.org/svnroot/roundup/trunk@2014 57a73879-2fb5-44c3-a270-3262357dd7e2 --- CHANGES.txt | 1 + roundup/backends/blobfiles.py | 26 +++++++++------- roundup/cgi/client.py | 58 +++++++++++++++++++++++++---------- roundup/date.py | 7 ++++- 4 files changed, 63 insertions(+), 29 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 069021a..5a8d645 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -20,6 +20,7 @@ Feature: - added script for copying user(s) from tracker to tracker (sf patch 828963) - ignore incoming email with "Precedence: bulk" (sf patch 843489) +- use HTTP 'Content-Length' header (modified sf patch 844577) Fixed: - mysql documentation fixed to note requirement of 4.0+ and InnoDB diff --git a/roundup/backends/blobfiles.py b/roundup/backends/blobfiles.py index 0008ba6..1a04aae 100644 --- a/roundup/backends/blobfiles.py +++ b/roundup/backends/blobfiles.py @@ -15,7 +15,7 @@ # BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE, # SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. # -#$Id: blobfiles.py,v 1.9 2002-09-10 00:11:50 richard Exp $ +#$Id: blobfiles.py,v 1.10 2003-12-05 03:28:38 richard Exp $ ''' This module exports file storage for roundup backends. Files are stored into a directory hierarchy. @@ -87,17 +87,21 @@ class FileStorage: def getfile(self, classname, nodeid, property): '''Get the content of the file in the database. ''' + # try a variety of different filenames - the file could be in the + # usual place, or it could be in a temp file pre-commit *or* it + # could be in an old-style, backwards-compatible flat directory filename = self.filename(classname, nodeid, property) - try: - return open(filename, 'rb').read() - except: - # now try the temp pre-commit filename - try: - return open(filename+'.tmp', 'rb').read() - except: - # fallback to flat file storage - filename = self.filename_flat(classname, nodeid, property) - return open(filename, 'rb').read() + flat_filename = self.filename_flat(classname, nodeid, property) + for filename in (filename, filename+'.tmp', flat_filename): + if os.path.exists(filename): + f = open(filename, 'rb') + break + else: + raise IOError, 'content file not found' + # snarf the contents and make sure we close the file + content = f.read() + f.close() + return content def numfiles(self): '''Get number of files in storage, even across subdirectories. diff --git a/roundup/cgi/client.py b/roundup/cgi/client.py index a72ae84..fe88313 100644 --- a/roundup/cgi/client.py +++ b/roundup/cgi/client.py @@ -1,4 +1,4 @@ -# $Id: client.py,v 1.148 2003-11-21 22:22:32 jlgijsbers Exp $ +# $Id: client.py,v 1.149 2003-12-05 03:28:38 richard Exp $ __doc__ = """ WWW request handler (also used in the stand-alone server). @@ -471,13 +471,44 @@ class Client: if classname != 'file': raise NotFound, designator - # we just want to serve up the file named self.opendb('admin') file = self.db.file - self.additional_headers['Content-Type'] = file.get(nodeid, 'type') - self.write(file.get(nodeid, 'content')) + + mime_type = file.get(nodeid, 'type') + content = file.get(nodeid, 'content') + lmt = file.get(nodeid, 'activity').timestamp() + + self._serve_file(lmt, mime_type, content) def serve_static_file(self, file): + ''' Serve up the file named from the templates dir + ''' + filename = os.path.join(self.instance.config.TEMPLATES, file) + + # last-modified time + lmt = os.stat(filename)[stat.ST_MTIME] + + # detemine meta-type + file = str(file) + mime_type = mimetypes.guess_type(file)[0] + if not mime_type: + if file.endswith('.css'): + mime_type = 'text/css' + else: + mime_type = 'text/plain' + + # snarf the content + f = open(filename, 'rb') + try: + content = f.read() + finally: + f.close() + + self._serve_file(lmt, mime_type, content) + + def _serve_file(self, last_modified, mime_type, content): + ''' guts of serve_file() and serve_static_file() + ''' ims = None # see if there's an if-modified-since... if hasattr(self.request, 'headers'): @@ -485,25 +516,18 @@ class Client: elif self.env.has_key('HTTP_IF_MODIFIED_SINCE'): # cgi will put the header in the env var ims = self.env['HTTP_IF_MODIFIED_SINCE'] - filename = os.path.join(self.instance.config.TEMPLATES, file) - lmt = os.stat(filename)[stat.ST_MTIME] if ims: ims = rfc822.parsedate(ims)[:6] lmtt = time.gmtime(lmt)[:6] if lmtt <= ims: raise NotModified - # we just want to serve up the file named - file = str(file) - mt = mimetypes.guess_type(file)[0] - if not mt: - if file.endswith('.css'): - mt = 'text/css' - else: - mt = 'text/plain' - self.additional_headers['Content-Type'] = mt - self.additional_headers['Last-Modifed'] = rfc822.formatdate(lmt) - self.write(open(filename, 'rb').read()) + # spit out headers + self.additional_headers['Content-Type'] = mime_type + self.additional_headers['Content-Length'] = len(content) + lmt = rfc822.formatdate(last_modified) + self.additional_headers['Last-Modifed'] = lmt + self.write(content) def renderContext(self): ''' Return a PageTemplate for the named page diff --git a/roundup/date.py b/roundup/date.py index b07bcf1..4915176 100644 --- a/roundup/date.py +++ b/roundup/date.py @@ -15,7 +15,7 @@ # BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE, # SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. # -# $Id: date.py,v 1.58 2003-12-04 23:06:53 richard Exp $ +# $Id: date.py,v 1.59 2003-12-05 03:28:38 richard Exp $ __doc__ = """ Date, time and time interval handling. @@ -323,6 +323,11 @@ class Date: return '%4d%02d%02d%02d%02d%02d'%(self.year, self.month, self.day, self.hour, self.minute, self.second) + def timestamp(self): + ''' return a UNIX timestamp for this date ''' + return calendar.timegm((self.year, self.month, self.day, self.hour, + self.minute, self.second, 0, 0, 0)) + class Interval: ''' Date intervals are specified using the suffixes "y", "m", and "d". The -- 2.30.2