diff --git a/VERSION b/VERSION index 74ac0bee..a0187a34 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.0.9 (2012-09-30 16:50:47) dev +Version 2.0.9 (2012-10-01 11:57:39) dev diff --git a/gluon/rocket.py b/gluon/rocket.py index 05d6da6c..14de0669 100644 --- a/gluon/rocket.py +++ b/gluon/rocket.py @@ -2,6 +2,7 @@ # This file is part of the Rocket Web Server # Copyright (c) 2011 Timothy Farrell +# Modified by Massimo Di Pierro # Import System Modules import sys @@ -12,7 +13,7 @@ import platform import traceback # Define Constants -VERSION = '1.2.4' +VERSION = '1.2.5' SERVER_NAME = socket.gethostname() SERVER_SOFTWARE = 'Rocket %s' % VERSION HTTP_SERVER_SOFTWARE = '%s Python/%s' % (SERVER_SOFTWARE, sys.version.split(' ')[0]) @@ -1454,37 +1455,34 @@ class Worker(Thread): return req - def read_headers(self, sock_file): + def read_headers(self, sock_file, environ): try: - headers = dict() - lname = lval = '' + lname = None while True: - line = sock_file.readline() + l = sock_file.readline() if PY3K: try: - line = str(line, 'ISO-8859-1') + l = str(l, 'ISO-8859-1') except UnicodeDecodeError: - self.err_log.warning('Client sent invalid header: ' + repr(line)) - if line == '\r\n': - if lname: headers[str(lname)] = str(lval) + self.err_log.warning('Invalid request header: '+repr(l)) + + if l.strip() == '': break - elif line.strip() == '' or '\0' in line: - raise BadRequest("Empty line in hader") - elif line[0] in ' \t' and lname: + elif l[0] in ' \t' and lname: # Some headers take more than one line - lval += ' ' + line.strip() - elif ':' in line: - if lname: headers[str(lname)] = str(lval) - lname, lval = line.split(':', 1) - # HTTP header names are us-ascii encoded - lname = lname.strip().upper().replace('-', '_') + environ[lname] += ',' + l.strip() + else: # HTTP header values are latin-1 encoded - lval = lval.strip() + l = l.split(':', 1) + # HTTP header names are us-ascii encoded + + lname = str('HTTP_'+l[0].strip().upper().replace('-', '_')) + lval = str(l[-1].strip()) + environ[lname] = lval + except socket.timeout: raise SocketTimeout("Socket timed out before request.") - return headers - class SocketTimeout(Exception): "Exception for when a socket times out between requests." pass @@ -1545,209 +1543,10 @@ class ChunkedReader(object): yield self.readline() def get_method(method): - - - methods = dict(wsgi=WSGIWorker, - fs=FileSystemWorker) + methods = dict(wsgi=WSGIWorker) return methods[method.lower()] # Monolithic build...end of module: rocket\worker.py -# Monolithic build...start of module: rocket\methods\__init__.py - -# Monolithic build...end of module: rocket\methods\__init__.py -# Monolithic build...start of module: rocket\methods\fs.py - -# Import System Modules -import os -import time -import mimetypes -from email.utils import formatdate -from wsgiref.headers import Headers -from wsgiref.util import FileWrapper -# Import Package Modules -# package imports removed in monolithic build - - -# Define Constants -CHUNK_SIZE = 2**16 # 64 Kilobyte chunks -HEADER_RESPONSE = '''HTTP/1.1 %s\r\n%s''' -INDEX_HEADER = '''\ - -Directory Index: %(path)s - - -

Directory Index: %(path)s

- - -''' -INDEX_ROW = '''''' -INDEX_FOOTER = '''
Directories
\r\n''' - -class LimitingFileWrapper(FileWrapper): - def __init__(self, limit=None, *args, **kwargs): - self.limit = limit - FileWrapper.__init__(self, *args, **kwargs) - - def read(self, amt): - if amt > self.limit: - amt = self.limit - self.limit -= amt - return FileWrapper.read(self, amt) - -class FileSystemWorker(Worker): - def __init__(self, *args, **kwargs): - """Builds some instance variables that will last the life of the - thread.""" - - Worker.__init__(self, *args, **kwargs) - - self.root = os.path.abspath(self.app_info['document_root']) - self.display_index = self.app_info['display_index'] - - def serve_file(self, filepath, headers): - filestat = os.stat(filepath) - self.size = filestat.st_size - modtime = time.strftime("%a, %d %b %Y %H:%M:%S GMT", - time.gmtime(filestat.st_mtime)) - self.headers.add_header('Last-Modified', modtime) - if headers.get('if_modified_since') == modtime: - # The browser cache is up-to-date, send a 304. - self.status = "304 Not Modified" - self.data = [] - return - - ct = mimetypes.guess_type(filepath)[0] - self.content_type = ct if ct else 'text/plain' - try: - f = open(filepath, 'rb') - self.headers['Pragma'] = 'cache' - self.headers['Cache-Control'] = 'private' - self.headers['Content-Length'] = str(self.size) - if self.etag: - self.headers.add_header('Etag', self.etag) - if self.expires: - self.headers.add_header('Expires', self.expires) - - try: - # Implement 206 partial file support. - start, end = headers['range'].split('-') - start = 0 if not start.isdigit() else int(start) - end = self.size if not end.isdigit() else int(end) - if self.size < end or start < 0: - self.status = "214 Unsatisfiable Range Requested" - self.data = FileWrapper(f, CHUNK_SIZE) - else: - f.seek(start) - self.data = LimitingFileWrapper(f, CHUNK_SIZE, limit=end) - self.status = "206 Partial Content" - except: - self.data = FileWrapper(f, CHUNK_SIZE) - except IOError: - self.status = "403 Forbidden" - - def serve_dir(self, pth, rpth): - def rel_path(path): - return os.path.normpath(path[len(self.root):] if path.startswith(self.root) else path) - - if not self.display_index: - self.status = '404 File Not Found' - return b('') - else: - self.content_type = 'text/html' - - dir_contents = [os.path.join(pth, x) for x in os.listdir(os.path.normpath(pth))] - dir_contents.sort() - - dirs = [rel_path(x)+'/' for x in dir_contents if os.path.isdir(x)] - files = [rel_path(x) for x in dir_contents if os.path.isfile(x)] - - self.data = [INDEX_HEADER % dict(path='/'+rpth)] - if rpth: - self.data += [INDEX_ROW % dict(name='(parent directory)', cls='dir parent', link='/'.join(rpth[:-1].split('/')[:-1]))] - self.data += [INDEX_ROW % dict(name=os.path.basename(x[:-1]), link=os.path.join(rpth, os.path.basename(x[:-1])).replace('\\', '/'), cls='dir') for x in dirs] - self.data += ['Files'] - self.data += [INDEX_ROW % dict(name=os.path.basename(x), link=os.path.join(rpth, os.path.basename(x)).replace('\\', '/'), cls='file') for x in files] - self.data += [INDEX_FOOTER] - self.headers['Content-Length'] = self.size = str(sum([len(x) for x in self.data])) - self.status = '200 OK' - - def run_app(self, conn): - self.status = "200 OK" - self.size = 0 - self.expires = None - self.etag = None - self.content_type = 'text/plain' - self.content_length = None - - if __debug__: - self.err_log.debug('Getting sock_file') - - # Build our file-like object - sock_file = conn.makefile('rb',BUF_SIZE) - request = self.read_request_line(sock_file) - if request['method'].upper() not in ('GET', ): - self.status = "501 Not Implemented" - - try: - # Get our file path - reader = self.read_headers(sock_file) - headers = dict((k.lower(),v) for k,v in reader.iteritems()) - rpath = request.get('path', '').lstrip('/') - filepath = os.path.join(self.root, rpath) - filepath = os.path.abspath(filepath) - if __debug__: - self.err_log.debug('Request for path: %s' % filepath) - - self.closeConnection = headers.get('connection', 'close').lower() == 'close' - self.headers = Headers([('Date', formatdate(usegmt=True)), - ('Server', HTTP_SERVER_SOFTWARE), - ('Connection', headers.get('connection', 'close')), - ]) - - if not filepath.lower().startswith(self.root.lower()): - # File must be within our root directory - self.status = "400 Bad Request" - self.closeConnection = True - elif not os.path.exists(filepath): - self.status = "404 File Not Found" - self.closeConnection = True - elif os.path.isdir(filepath): - self.serve_dir(filepath, rpath) - elif os.path.isfile(filepath): - self.serve_file(filepath, headers) - else: - # It exists but it's not a file or a directory???? - # What is it then? - self.status = "501 Not Implemented" - self.closeConnection = True - - h = self.headers - statcode, statstr = self.status.split(' ', 1) - statcode = int(statcode) - if statcode >= 400: - h.add_header('Content-Type', self.content_type) - self.data = [statstr] - - # Build our output headers - header_data = HEADER_RESPONSE % (self.status, str(h)) - - # Send the headers - if __debug__: - self.err_log.debug('Sending Headers: %s' % repr(header_data)) - self.conn.sendall(b(header_data)) - - for data in self.data: - self.conn.sendall(b(data)) - - if hasattr(self.data, 'close'): - self.data.close() - - finally: - if __debug__: - self.err_log.debug('Finally closing sock_file') - sock_file.close() - -# Monolithic build...end of module: rocket\methods\fs.py # Monolithic build...start of module: rocket\methods\wsgi.py # Import System Modules @@ -1815,16 +1614,15 @@ class WSGIWorker(Worker): environ = self.base_environ.copy() # Grab the headers - for k, v in self.read_headers(sock_file).items(): - environ[str('HTTP_'+k)] = v + self.read_headers(sock_file,environ) # Add CGI Variables - environ['REQUEST_METHOD'] = request['method'] - environ['PATH_INFO'] = request['path'] - environ['SERVER_PROTOCOL'] = request['protocol'] environ['SERVER_PORT'] = str(conn.server_port) environ['REMOTE_PORT'] = str(conn.client_port) environ['REMOTE_ADDR'] = str(conn.client_addr) + environ['REQUEST_METHOD'] = request['method'] + environ['PATH_INFO'] = request['path'] + environ['SERVER_PROTOCOL'] = request['protocol'] environ['QUERY_STRING'] = request['query_string'] if 'HTTP_CONTENT_LENGTH' in environ: environ['CONTENT_LENGTH'] = environ['HTTP_CONTENT_LENGTH']