diff roundup/cgi/client.py @ 4047:e70643990e9c

Support the use of sendfile() for file transfer, if available.
author Stefan Seefeld <stefan@seefeld.name>
date Tue, 17 Feb 2009 04:32:34 +0000
parents 48be910ebda1
children 662cd78df973
line wrap: on
line diff
--- a/roundup/cgi/client.py	Tue Feb 17 02:38:08 2009 +0000
+++ b/roundup/cgi/client.py	Tue Feb 17 04:32:34 2009 +0000
@@ -819,10 +819,30 @@
                 "this file.")
 
         mime_type = klass.get(nodeid, 'type')
-        content = klass.get(nodeid, 'content')
+
+        # If this object is a file (i.e., an instance of FileClass),
+        # see if we can find it in the filesystem.  If so, we may be
+        # able to use the more-efficient request.sendfile method of
+        # sending the file.  If not, just get the "content" property
+        # in the usual way, and use that.
+        content = None
+        filename = None
+        if isinstance(klass, hyperdb.FileClass):
+            try:
+                filename = self.db.filename(classname, nodeid)
+            except AttributeError:
+                # The database doesn't store files in the filesystem
+                # and therefore doesn't provide the "filename" method.
+                pass
+            except IOError:
+                # The file does not exist.
+                pass
+        if not filename:
+            content = klass.get(nodeid, 'content')
+        
         lmt = klass.get(nodeid, 'activity').timestamp()
 
-        self._serve_file(lmt, mime_type, content)
+        self._serve_file(lmt, mime_type, content, filename)
 
     def serve_static_file(self, file):
         ''' Serve up the file named from the templates dir
@@ -853,21 +873,20 @@
             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, '', filename)
 
-        self._serve_file(lmt, mime_type, content)
-
-    def _serve_file(self, lmt, mime_type, content):
+    def _serve_file(self, lmt, mime_type, content=None, filename=None):
         ''' guts of serve_file() and serve_static_file()
         '''
+
+        if not content:
+            length = os.stat(filename)[stat.ST_SIZE]
+        else:
+            length = len(content)
+
         # spit out headers
         self.additional_headers['Content-Type'] = mime_type
-        self.additional_headers['Content-Length'] = str(len(content))
+        self.additional_headers['Content-Length'] = str(length)
         self.additional_headers['Last-Modified'] = rfc822.formatdate(lmt)
 
         ims = None
@@ -884,7 +903,27 @@
             if lmtt <= ims:
                 raise NotModified
 
-        self.write(content)
+        if not self.headers_done:
+            self.header()
+
+        if self.env['REQUEST_METHOD'] == 'HEAD':
+            return
+
+        # If we have a file, and the 'sendfile' method is available,
+        # we can bypass reading and writing the content into application
+        # memory entirely.
+        if filename:
+            if hasattr(self.request, 'sendfile'):
+                self._socket_op(self.request.sendfile, filename)
+                return
+            f = open(filename, 'rb')
+            try:
+                content = f.read()
+            finally:
+                f.close()
+
+        self._socket_op(self.request.wfile.write, content)
+
 
     def renderContext(self):
         ''' Return a PageTemplate for the named page
@@ -1059,6 +1098,7 @@
         # and write
         self._socket_op(self.request.wfile.write, content)
 
+
     def setHeader(self, header, value):
         '''Override a header to be returned to the user's browser.
         '''

Roundup Issue Tracker: http://roundup-tracker.org/