Mercurial > p > roundup > code
comparison 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 |
comparison
equal
deleted
inserted
replaced
| 4046:48be910ebda1 | 4047:e70643990e9c |
|---|---|
| 817 classname, 'content', nodeid): | 817 classname, 'content', nodeid): |
| 818 raise Unauthorised, self._("You are not allowed to view " | 818 raise Unauthorised, self._("You are not allowed to view " |
| 819 "this file.") | 819 "this file.") |
| 820 | 820 |
| 821 mime_type = klass.get(nodeid, 'type') | 821 mime_type = klass.get(nodeid, 'type') |
| 822 content = klass.get(nodeid, 'content') | 822 |
| 823 # If this object is a file (i.e., an instance of FileClass), | |
| 824 # see if we can find it in the filesystem. If so, we may be | |
| 825 # able to use the more-efficient request.sendfile method of | |
| 826 # sending the file. If not, just get the "content" property | |
| 827 # in the usual way, and use that. | |
| 828 content = None | |
| 829 filename = None | |
| 830 if isinstance(klass, hyperdb.FileClass): | |
| 831 try: | |
| 832 filename = self.db.filename(classname, nodeid) | |
| 833 except AttributeError: | |
| 834 # The database doesn't store files in the filesystem | |
| 835 # and therefore doesn't provide the "filename" method. | |
| 836 pass | |
| 837 except IOError: | |
| 838 # The file does not exist. | |
| 839 pass | |
| 840 if not filename: | |
| 841 content = klass.get(nodeid, 'content') | |
| 842 | |
| 823 lmt = klass.get(nodeid, 'activity').timestamp() | 843 lmt = klass.get(nodeid, 'activity').timestamp() |
| 824 | 844 |
| 825 self._serve_file(lmt, mime_type, content) | 845 self._serve_file(lmt, mime_type, content, filename) |
| 826 | 846 |
| 827 def serve_static_file(self, file): | 847 def serve_static_file(self, file): |
| 828 ''' Serve up the file named from the templates dir | 848 ''' Serve up the file named from the templates dir |
| 829 ''' | 849 ''' |
| 830 # figure the filename - try STATIC_FILES, then TEMPLATES dir | 850 # figure the filename - try STATIC_FILES, then TEMPLATES dir |
| 851 if file.endswith('.css'): | 871 if file.endswith('.css'): |
| 852 mime_type = 'text/css' | 872 mime_type = 'text/css' |
| 853 else: | 873 else: |
| 854 mime_type = 'text/plain' | 874 mime_type = 'text/plain' |
| 855 | 875 |
| 856 # snarf the content | 876 self._serve_file(lmt, mime_type, '', filename) |
| 857 f = open(filename, 'rb') | 877 |
| 858 try: | 878 def _serve_file(self, lmt, mime_type, content=None, filename=None): |
| 859 content = f.read() | |
| 860 finally: | |
| 861 f.close() | |
| 862 | |
| 863 self._serve_file(lmt, mime_type, content) | |
| 864 | |
| 865 def _serve_file(self, lmt, mime_type, content): | |
| 866 ''' guts of serve_file() and serve_static_file() | 879 ''' guts of serve_file() and serve_static_file() |
| 867 ''' | 880 ''' |
| 881 | |
| 882 if not content: | |
| 883 length = os.stat(filename)[stat.ST_SIZE] | |
| 884 else: | |
| 885 length = len(content) | |
| 886 | |
| 868 # spit out headers | 887 # spit out headers |
| 869 self.additional_headers['Content-Type'] = mime_type | 888 self.additional_headers['Content-Type'] = mime_type |
| 870 self.additional_headers['Content-Length'] = str(len(content)) | 889 self.additional_headers['Content-Length'] = str(length) |
| 871 self.additional_headers['Last-Modified'] = rfc822.formatdate(lmt) | 890 self.additional_headers['Last-Modified'] = rfc822.formatdate(lmt) |
| 872 | 891 |
| 873 ims = None | 892 ims = None |
| 874 # see if there's an if-modified-since... | 893 # see if there's an if-modified-since... |
| 875 # XXX see which interfaces set this | 894 # XXX see which interfaces set this |
| 882 ims = rfc822.parsedate(ims)[:6] | 901 ims = rfc822.parsedate(ims)[:6] |
| 883 lmtt = time.gmtime(lmt)[:6] | 902 lmtt = time.gmtime(lmt)[:6] |
| 884 if lmtt <= ims: | 903 if lmtt <= ims: |
| 885 raise NotModified | 904 raise NotModified |
| 886 | 905 |
| 887 self.write(content) | 906 if not self.headers_done: |
| 907 self.header() | |
| 908 | |
| 909 if self.env['REQUEST_METHOD'] == 'HEAD': | |
| 910 return | |
| 911 | |
| 912 # If we have a file, and the 'sendfile' method is available, | |
| 913 # we can bypass reading and writing the content into application | |
| 914 # memory entirely. | |
| 915 if filename: | |
| 916 if hasattr(self.request, 'sendfile'): | |
| 917 self._socket_op(self.request.sendfile, filename) | |
| 918 return | |
| 919 f = open(filename, 'rb') | |
| 920 try: | |
| 921 content = f.read() | |
| 922 finally: | |
| 923 f.close() | |
| 924 | |
| 925 self._socket_op(self.request.wfile.write, content) | |
| 926 | |
| 888 | 927 |
| 889 def renderContext(self): | 928 def renderContext(self): |
| 890 ''' Return a PageTemplate for the named page | 929 ''' Return a PageTemplate for the named page |
| 891 ''' | 930 ''' |
| 892 name = self.classname | 931 name = self.classname |
| 1057 content = content.encode(self.charset, 'xmlcharrefreplace') | 1096 content = content.encode(self.charset, 'xmlcharrefreplace') |
| 1058 | 1097 |
| 1059 # and write | 1098 # and write |
| 1060 self._socket_op(self.request.wfile.write, content) | 1099 self._socket_op(self.request.wfile.write, content) |
| 1061 | 1100 |
| 1101 | |
| 1062 def setHeader(self, header, value): | 1102 def setHeader(self, header, value): |
| 1063 '''Override a header to be returned to the user's browser. | 1103 '''Override a header to be returned to the user's browser. |
| 1064 ''' | 1104 ''' |
| 1065 self.additional_headers[header] = value | 1105 self.additional_headers[header] = value |
| 1066 | 1106 |
