Skip to content

Commit a59b630

Browse files
author
Richard Jones
committed
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.
1 parent d596e57 commit a59b630

File tree

4 files changed

+63
-29
lines changed

4 files changed

+63
-29
lines changed

CHANGES.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ Feature:
2020
- added script for copying user(s) from tracker to tracker (sf patch
2121
828963)
2222
- ignore incoming email with "Precedence: bulk" (sf patch 843489)
23+
- use HTTP 'Content-Length' header (modified sf patch 844577)
2324

2425
Fixed:
2526
- mysql documentation fixed to note requirement of 4.0+ and InnoDB

roundup/backends/blobfiles.py

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
# BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
1616
# SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
1717
#
18-
#$Id: blobfiles.py,v 1.9 2002-09-10 00:11:50 richard Exp $
18+
#$Id: blobfiles.py,v 1.10 2003-12-05 03:28:38 richard Exp $
1919
'''
2020
This module exports file storage for roundup backends.
2121
Files are stored into a directory hierarchy.
@@ -87,17 +87,21 @@ def storefile(self, classname, nodeid, property, content):
8787
def getfile(self, classname, nodeid, property):
8888
'''Get the content of the file in the database.
8989
'''
90+
# try a variety of different filenames - the file could be in the
91+
# usual place, or it could be in a temp file pre-commit *or* it
92+
# could be in an old-style, backwards-compatible flat directory
9093
filename = self.filename(classname, nodeid, property)
91-
try:
92-
return open(filename, 'rb').read()
93-
except:
94-
# now try the temp pre-commit filename
95-
try:
96-
return open(filename+'.tmp', 'rb').read()
97-
except:
98-
# fallback to flat file storage
99-
filename = self.filename_flat(classname, nodeid, property)
100-
return open(filename, 'rb').read()
94+
flat_filename = self.filename_flat(classname, nodeid, property)
95+
for filename in (filename, filename+'.tmp', flat_filename):
96+
if os.path.exists(filename):
97+
f = open(filename, 'rb')
98+
break
99+
else:
100+
raise IOError, 'content file not found'
101+
# snarf the contents and make sure we close the file
102+
content = f.read()
103+
f.close()
104+
return content
101105

102106
def numfiles(self):
103107
'''Get number of files in storage, even across subdirectories.

roundup/cgi/client.py

Lines changed: 41 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# $Id: client.py,v 1.148 2003-11-21 22:22:32 jlgijsbers Exp $
1+
# $Id: client.py,v 1.149 2003-12-05 03:28:38 richard Exp $
22

33
__doc__ = """
44
WWW request handler (also used in the stand-alone server).
@@ -471,39 +471,63 @@ def serve_file(self, designator, dre=re.compile(r'([^\d]+)(\d+)')):
471471
if classname != 'file':
472472
raise NotFound, designator
473473

474-
# we just want to serve up the file named
475474
self.opendb('admin')
476475
file = self.db.file
477-
self.additional_headers['Content-Type'] = file.get(nodeid, 'type')
478-
self.write(file.get(nodeid, 'content'))
476+
477+
mime_type = file.get(nodeid, 'type')
478+
content = file.get(nodeid, 'content')
479+
lmt = file.get(nodeid, 'activity').timestamp()
480+
481+
self._serve_file(lmt, mime_type, content)
479482

480483
def serve_static_file(self, file):
484+
''' Serve up the file named from the templates dir
485+
'''
486+
filename = os.path.join(self.instance.config.TEMPLATES, file)
487+
488+
# last-modified time
489+
lmt = os.stat(filename)[stat.ST_MTIME]
490+
491+
# detemine meta-type
492+
file = str(file)
493+
mime_type = mimetypes.guess_type(file)[0]
494+
if not mime_type:
495+
if file.endswith('.css'):
496+
mime_type = 'text/css'
497+
else:
498+
mime_type = 'text/plain'
499+
500+
# snarf the content
501+
f = open(filename, 'rb')
502+
try:
503+
content = f.read()
504+
finally:
505+
f.close()
506+
507+
self._serve_file(lmt, mime_type, content)
508+
509+
def _serve_file(self, last_modified, mime_type, content):
510+
''' guts of serve_file() and serve_static_file()
511+
'''
481512
ims = None
482513
# see if there's an if-modified-since...
483514
if hasattr(self.request, 'headers'):
484515
ims = self.request.headers.getheader('if-modified-since')
485516
elif self.env.has_key('HTTP_IF_MODIFIED_SINCE'):
486517
# cgi will put the header in the env var
487518
ims = self.env['HTTP_IF_MODIFIED_SINCE']
488-
filename = os.path.join(self.instance.config.TEMPLATES, file)
489-
lmt = os.stat(filename)[stat.ST_MTIME]
490519
if ims:
491520
ims = rfc822.parsedate(ims)[:6]
492521
lmtt = time.gmtime(lmt)[:6]
493522
if lmtt <= ims:
494523
raise NotModified
495524

496-
# we just want to serve up the file named
497-
file = str(file)
498-
mt = mimetypes.guess_type(file)[0]
499-
if not mt:
500-
if file.endswith('.css'):
501-
mt = 'text/css'
502-
else:
503-
mt = 'text/plain'
504-
self.additional_headers['Content-Type'] = mt
505-
self.additional_headers['Last-Modifed'] = rfc822.formatdate(lmt)
506-
self.write(open(filename, 'rb').read())
525+
# spit out headers
526+
self.additional_headers['Content-Type'] = mime_type
527+
self.additional_headers['Content-Length'] = len(content)
528+
lmt = rfc822.formatdate(last_modified)
529+
self.additional_headers['Last-Modifed'] = lmt
530+
self.write(content)
507531

508532
def renderContext(self):
509533
''' Return a PageTemplate for the named page

roundup/date.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
# BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
1616
# SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
1717
#
18-
# $Id: date.py,v 1.58 2003-12-04 23:06:53 richard Exp $
18+
# $Id: date.py,v 1.59 2003-12-05 03:28:38 richard Exp $
1919

2020
__doc__ = """
2121
Date, time and time interval handling.
@@ -323,6 +323,11 @@ def serialise(self):
323323
return '%4d%02d%02d%02d%02d%02d'%(self.year, self.month,
324324
self.day, self.hour, self.minute, self.second)
325325

326+
def timestamp(self):
327+
''' return a UNIX timestamp for this date '''
328+
return calendar.timegm((self.year, self.month, self.day, self.hour,
329+
self.minute, self.second, 0, 0, 0))
330+
326331
class Interval:
327332
'''
328333
Date intervals are specified using the suffixes "y", "m", and "d". The

0 commit comments

Comments
 (0)