From bf23e8deed4d937a5219bd9cc20aab7364357e66 Mon Sep 17 00:00:00 2001 From: Neil Schemenauer Date: Fri, 9 Dec 2016 12:55:07 -0800 Subject: [PATCH] Remove unmaintained and probably broken server modules. --- quixote/server/_fcgi.py | 461 --------------------------- quixote/server/fastcgi_server.py | 23 -- quixote/server/medusa_server.py | 113 ------- quixote/server/mod_python_handler.py | 100 ------ quixote/server/twisted_server.py | 143 --------- 5 files changed, 840 deletions(-) delete mode 100644 quixote/server/_fcgi.py delete mode 100755 quixote/server/fastcgi_server.py delete mode 100755 quixote/server/medusa_server.py delete mode 100644 quixote/server/mod_python_handler.py delete mode 100755 quixote/server/twisted_server.py diff --git a/quixote/server/_fcgi.py b/quixote/server/_fcgi.py deleted file mode 100644 index 4de6858..0000000 --- a/quixote/server/_fcgi.py +++ /dev/null @@ -1,461 +0,0 @@ -# Derived from Robin Dunn's FastCGI module, -# available at http://alldunn.com/python/#fcgi. - -#------------------------------------------------------------------------ -# Copyright (c) 1998 by Total Control Software -# All Rights Reserved -#------------------------------------------------------------------------ -# -# Module Name: fcgi.py -# -# Description: Handles communication with the FastCGI module of the -# web server without using the FastCGI developers kit, but -# will also work in a non-FastCGI environment, (straight CGI.) -# This module was originally fetched from someplace on the -# Net (I don't remember where and I can't find it now...) and -# has been significantly modified to fix several bugs, be more -# readable, more robust at handling large CGI data and return -# document sizes, and also to fit the model that we had previously -# used for FastCGI. -# -# WARNING: If you don't know what you are doing, don't tinker with this -# module! -# -# Creation Date: 1/30/98 2:59:04PM -# -# License: This is free software. You may use this software for any -# purpose including modification/redistribution, so long as -# this header remains intact and that you do not claim any -# rights of ownership or authorship of this software. This -# software has been tested, but no warranty is expressed or -# implied. -# -#------------------------------------------------------------------------ - -import os, sys, string, socket, errno, struct -from io import StringIO -import cgi - -#--------------------------------------------------------------------------- - -# Set various FastCGI constants -# Maximum number of requests that can be handled -FCGI_MAX_REQS=1 -FCGI_MAX_CONNS = 1 - -# Supported version of the FastCGI protocol -FCGI_VERSION_1 = 1 - -# Boolean: can this application multiplex connections? -FCGI_MPXS_CONNS=0 - -# Record types -FCGI_BEGIN_REQUEST = 1 ; FCGI_ABORT_REQUEST = 2 ; FCGI_END_REQUEST = 3 -FCGI_PARAMS = 4 ; FCGI_STDIN = 5 ; FCGI_STDOUT = 6 -FCGI_STDERR = 7 ; FCGI_DATA = 8 ; FCGI_GET_VALUES = 9 -FCGI_GET_VALUES_RESULT = 10 -FCGI_UNKNOWN_TYPE = 11 -FCGI_MAXTYPE = FCGI_UNKNOWN_TYPE - -# Types of management records -ManagementTypes = [FCGI_GET_VALUES] - -FCGI_NULL_REQUEST_ID = 0 - -# Masks for flags component of FCGI_BEGIN_REQUEST -FCGI_KEEP_CONN = 1 - -# Values for role component of FCGI_BEGIN_REQUEST -FCGI_RESPONDER = 1 ; FCGI_AUTHORIZER = 2 ; FCGI_FILTER = 3 - -# Values for protocolStatus component of FCGI_END_REQUEST -FCGI_REQUEST_COMPLETE = 0 # Request completed nicely -FCGI_CANT_MPX_CONN = 1 # This app can't multiplex -FCGI_OVERLOADED = 2 # New request rejected; too busy -FCGI_UNKNOWN_ROLE = 3 # Role value not known - - -error = 'fcgi.error' - - -#--------------------------------------------------------------------------- - -# The following function is used during debugging; it isn't called -# anywhere at the moment - -def error(msg): - "Append a string to /tmp/err" - errf = open('/tmp/err', 'a+') - errf.write(msg+'\n') - errf.close() - -#--------------------------------------------------------------------------- - -class record: - "Class representing FastCGI records" - def __init__(self): - self.version = FCGI_VERSION_1 - self.recType = FCGI_UNKNOWN_TYPE - self.reqId = FCGI_NULL_REQUEST_ID - self.content = "" - - #---------------------------------------- - def readRecord(self, sock, unpack=struct.unpack): - (self.version, self.recType, self.reqId, contentLength, - paddingLength) = unpack(">BBHHBx", sock.recv(8)) - - content = "" - while len(content) < contentLength: - content = content + sock.recv(contentLength - len(content)) - self.content = content - - if paddingLength != 0: - padding = sock.recv(paddingLength) - - # Parse the content information - if self.recType == FCGI_BEGIN_REQUEST: - (self.role, self.flags) = unpack(">HB", content[:3]) - - elif self.recType == FCGI_UNKNOWN_TYPE: - self.unknownType = ord(content[0]) - - elif self.recType == FCGI_GET_VALUES or self.recType == FCGI_PARAMS: - self.values = {} - pos = 0 - while pos < len(content): - name, value, pos = readPair(content, pos) - self.values[name] = value - - elif self.recType == FCGI_END_REQUEST: - (self.appStatus, self.protocolStatus) = unpack(">IB", content[0:5]) - - #---------------------------------------- - def writeRecord(self, sock, pack=struct.pack): - content = self.content - if self.recType == FCGI_BEGIN_REQUEST: - content = pack(">HBxxxxx", self.role, self.flags) - - elif self.recType == FCGI_UNKNOWN_TYPE: - content = pack(">Bxxxxxx", self.unknownType) - - elif self.recType == FCGI_GET_VALUES or self.recType == FCGI_PARAMS: - content = "" - for i in self.values.keys(): - content = content + writePair(i, self.values[i]) - - elif self.recType == FCGI_END_REQUEST: - content = pack(">IBxxx", self.appStatus, self.protocolStatus) - - cLen = len(content) - eLen = (cLen + 7) & (0xFFFF - 7) # align to an 8-byte boundary - padLen = eLen - cLen - - hdr = pack(">BBHHBx", self.version, self.recType, self.reqId, cLen, - padLen) - - ##debug.write('Sending fcgi record: %s\n' % repr(content[:50]) ) - sock.send(hdr + content + padLen*'\000') - -#--------------------------------------------------------------------------- - -_lowbits = ~(1 << 31) # everything but the 31st bit - -def readPair(s, pos): - nameLen = ord(s[pos]) ; pos = pos+1 - if nameLen & 128: - pos = pos + 3 - nameLen = int(struct.unpack(">I", s[pos-4:pos])[0] & _lowbits) - valueLen = ord(s[pos]) ; pos = pos+1 - if valueLen & 128: - pos = pos + 3 - valueLen = int(struct.unpack(">I", s[pos-4:pos])[0] & _lowbits) - return ( s[pos:pos+nameLen], s[pos+nameLen:pos+nameLen+valueLen], - pos+nameLen+valueLen ) - -#--------------------------------------------------------------------------- - -_highbit = (1 << 31) - -def writePair(name, value): - l = len(name) - if l < 128: - s = chr(l) - else: - s = struct.pack(">I", l | _highbit) - l = len(value) - if l < 128: - s = s + chr(l) - else: - s = s + struct.pack(">I", l | _highbit) - return s + name + value - -#--------------------------------------------------------------------------- - -def HandleManTypes(r, conn): - if r.recType == FCGI_GET_VALUES: - r.recType = FCGI_GET_VALUES_RESULT - v = {} - vars = {'FCGI_MAX_CONNS' : FCGI_MAX_CONNS, - 'FCGI_MAX_REQS' : FCGI_MAX_REQS, - 'FCGI_MPXS_CONNS': FCGI_MPXS_CONNS} - for i in r.values.keys(): - if i in vars: - v[i] = vars[i] - r.values = vars - r.writeRecord(conn) - -#--------------------------------------------------------------------------- -#--------------------------------------------------------------------------- - - -_isFCGI = 1 # assume it is until we find out for sure - -def isFCGI(): - return _isFCGI - - - -#--------------------------------------------------------------------------- - - -_init = None -_sock = None - -class FCGI: - def __init__(self): - self.haveFinished = 0 - if _init == None: - _startup() - if not _isFCGI: - self.haveFinished = 1 - self.inp = sys.__stdin__ - self.out = sys.__stdout__ - self.err = sys.__stderr__ - self.env = os.environ - return - - if 'FCGI_WEB_SERVER_ADDRS' in os.environ: - good_addrs = string.split(os.environ['FCGI_WEB_SERVER_ADDRS'], ',') - good_addrs = list(map(string.strip, good_addrs)) # Remove whitespace - else: - good_addrs = None - - self.conn, addr = _sock.accept() - stdin, data = "", "" - self.env = {} - self.requestId = 0 - remaining = 1 - - # Check if the connection is from a legal address - if good_addrs != None and addr not in good_addrs: - raise error('Connection from invalid server!') - - while remaining: - r = record() - r.readRecord(self.conn) - - if r.recType in ManagementTypes: - HandleManTypes(r, self.conn) - - elif r.reqId == 0: - # Oh, poopy. It's a management record of an unknown - # type. Signal the error. - r2 = record() - r2.recType = FCGI_UNKNOWN_TYPE - r2.unknownType = r.recType - r2.writeRecord(self.conn) - continue # Charge onwards - - # Ignore requests that aren't active - elif r.reqId != self.requestId and r.recType != FCGI_BEGIN_REQUEST: - continue - - # If we're already doing a request, ignore further BEGIN_REQUESTs - elif r.recType == FCGI_BEGIN_REQUEST and self.requestId != 0: - continue - - # Begin a new request - if r.recType == FCGI_BEGIN_REQUEST: - self.requestId = r.reqId - if r.role == FCGI_AUTHORIZER: remaining = 1 - elif r.role == FCGI_RESPONDER: remaining = 2 - elif r.role == FCGI_FILTER: remaining = 3 - - elif r.recType == FCGI_PARAMS: - if r.content == "": - remaining = remaining-1 - else: - for i in r.values.keys(): - self.env[i] = r.values[i] - - elif r.recType == FCGI_STDIN: - if r.content == "": - remaining = remaining-1 - else: - stdin = stdin+r.content - - elif r.recType == FCGI_DATA: - if r.content == "": - remaining = remaining-1 - else: - data = data+r.content - # end of while remaining: - - self.inp = StringIO(stdin) - self.err = StringIO() - self.out = StringIO() - self.data = StringIO(data) - - def __del__(self): - self.Finish() - - def Finish(self, status=0): - if not self.haveFinished: - self.haveFinished = 1 - - self.err.seek(0,0) - self.out.seek(0,0) - - ##global debug - ##debug = open("/tmp/quixote-debug.log", "a+") - ##debug.write("fcgi.FCGI.Finish():\n") - - r = record() - r.recType = FCGI_STDERR - r.reqId = self.requestId - data = self.err.read() - ##debug.write(" sending stderr (%s)\n" % `self.err`) - ##debug.write(" data = %s\n" % `data`) - while data: - chunk, data = self.getNextChunk(data) - ##debug.write(" chunk, data = %s, %s\n" % (`chunk`, `data`)) - r.content = chunk - r.writeRecord(self.conn) - r.content = "" - r.writeRecord(self.conn) # Terminate stream - - r.recType = FCGI_STDOUT - data = self.out.read() - ##debug.write(" sending stdout (%s)\n" % `self.out`) - ##debug.write(" data = %s\n" % `data`) - while data: - chunk, data = self.getNextChunk(data) - r.content = chunk - r.writeRecord(self.conn) - r.content = "" - r.writeRecord(self.conn) # Terminate stream - - r = record() - r.recType = FCGI_END_REQUEST - r.reqId = self.requestId - r.appStatus = status - r.protocolStatus = FCGI_REQUEST_COMPLETE - r.writeRecord(self.conn) - self.conn.close() - - #debug.close() - - - def getFieldStorage(self): - method = 'GET' - if 'REQUEST_METHOD' in self.env: - method = string.upper(self.env['REQUEST_METHOD']) - if method == 'GET': - return cgi.FieldStorage(environ=self.env, keep_blank_values=1) - else: - return cgi.FieldStorage(fp=self.inp, - environ=self.env, - keep_blank_values=1) - - def getNextChunk(self, data): - chunk = data[:8192] - data = data[8192:] - return chunk, data - - -Accept = FCGI # alias for backwards compatibility -#--------------------------------------------------------------------------- - -def _startup(): - global _isFCGI, _init, _sock - # This function won't work on Windows at all. - if sys.platform[:3] == 'win': - _isFCGI = 0 - return - - _init = 1 - try: - s = socket.fromfd(sys.stdin.fileno(), socket.AF_INET, - socket.SOCK_STREAM) - s.getpeername() - except socket.error as xxx_todo_changeme: - (err, errmsg) = xxx_todo_changeme.args - if err != errno.ENOTCONN: # must be a non-fastCGI environment - _isFCGI = 0 - return - - _sock = s - - -#--------------------------------------------------------------------------- - -def _test(): - counter = 0 - try: - while isFCGI(): - req = Accept() - counter = counter+1 - - try: - fs = req.getFieldStorage() - size = string.atoi(fs['size'].value) - doc = ['*' * size] - except: - doc = ['' - 'FCGI TestApp' - '\n\n'] - doc.append('

FCGI TestApp

') - doc.append('request count = %d
' % counter) - doc.append('pid = %s
' % os.getpid()) - if 'CONTENT_LENGTH' in req.env: - cl = string.atoi(req.env['CONTENT_LENGTH']) - doc.append('
POST data (%s):

' % cl)
-                    keys = sorted(fs.keys())
-                    for k in keys:
-                        val = fs[k]
-                        if type(val) == type([]):
-                            doc.append('    %-15s :  %s\n'
-                                       % (k, val))
-                        else:
-                            doc.append('    %-15s :  %s\n'
-                                       % (k, val.value))
-                    doc.append('
') - - - doc.append('


')
-                keys = sorted(req.env.keys())
-                for k in keys:
-                    doc.append('%-20s :  %s\n' % (k, req.env[k]))
-                doc.append('\n


\n') - doc.append('\n') - - - doc = string.join(doc, '') - req.out.write('Content-length: %s\r\n' - 'Content-type: text/html\r\n' - 'Cache-Control: no-cache\r\n' - '\r\n' - % len(doc)) - req.out.write(doc) - - req.Finish() - except: - import traceback - f = open('traceback', 'w') - traceback.print_exc( file = f ) -# f.write('%s' % doc) - -if __name__ == '__main__': - #import pdb - #pdb.run('_test()') - _test() diff --git a/quixote/server/fastcgi_server.py b/quixote/server/fastcgi_server.py deleted file mode 100755 index 77ad094..0000000 --- a/quixote/server/fastcgi_server.py +++ /dev/null @@ -1,23 +0,0 @@ -#!/usr/bin/env python3 -"""Server for Quixote applications that use FastCGI. It should work -for CGI too but the cgi_server module is preferred as it is more -portable. -""" - -from quixote.server import _fcgi - -def run(create_publisher): - publisher = create_publisher() - while _fcgi.isFCGI(): - f = _fcgi.FCGI() - response = publisher.process(f.inp, f.env) - try: - response.write(f.out) - except IOError as err: - publisher.log("IOError while sending response ignored: %s" % err) - f.Finish() - - -if __name__ == '__main__': - from quixote.demo import create_publisher - run(create_publisher) diff --git a/quixote/server/medusa_server.py b/quixote/server/medusa_server.py deleted file mode 100755 index d71ae8f..0000000 --- a/quixote/server/medusa_server.py +++ /dev/null @@ -1,113 +0,0 @@ -#!/usr/bin/env python3 -"""An HTTP handler for Medusa that publishes a Quixote application. -""" - -import asyncore -import email -import socket, urllib.request, urllib.parse, urllib.error -from io import StringIO -from medusa import http_server, xmlrpc_handler -import quixote - - -class StreamProducer: - def __init__(self, chunks): - self.chunks = chunks # a generator - - def more(self): - try: - return next(self.chunks) - except StopIteration: - return '' - - -class QuixoteHandler: - def __init__(self, publisher, server): - self.publisher = publisher - self.server = server - - def match(self, request): - # Always match, since this is the only handler there is. - return True - - def handle_request(self, request): - msg = email.Message(StringIO('\n'.join(request.header))) - length = int(msg.get('Content-Length', '0')) - if length: - request.collector = xmlrpc_handler.collector(self, request) - else: - self.continue_request('', request) - - def continue_request(self, data, request): - msg = email.Message(StringIO('\n'.join(request.header))) - remote_addr, remote_port = request.channel.addr - if '#' in request.uri: - # MSIE is buggy and sometimes includes fragments in URLs - [request.uri, fragment] = request.uri.split('#', 1) - if '?' in request.uri: - [path, query_string] = request.uri.split('?', 1) - else: - path = request.uri - query_string = '' - - path = urllib.parse.unquote(path) - server_port = str(self.server.port) - http_host = msg.get("Host") - if http_host: - if ":" in http_host: - server_name, server_port = http_host.split(":", 1) - else: - server_name = http_host - else: - server_name = (self.server.ip or - socket.gethostbyaddr(socket.gethostname())[0]) - - environ = {'REQUEST_METHOD': request.command, - 'ACCEPT_ENCODING': msg.get('Accept-encoding', ''), - 'CONTENT_TYPE': msg.get('Content-type', ''), - 'CONTENT_LENGTH': len(data), - "GATEWAY_INTERFACE": "CGI/1.1", - 'PATH_INFO': path, - 'QUERY_STRING': query_string, - 'REMOTE_ADDR': remote_addr, - 'REMOTE_PORT': str(remote_port), - 'REQUEST_URI': request.uri, - 'SCRIPT_NAME': '', - "SCRIPT_FILENAME": '', - 'SERVER_NAME': server_name, - 'SERVER_PORT': server_port, - 'SERVER_PROTOCOL': 'HTTP/1.1', - 'SERVER_SOFTWARE': 'Quixote/%s' % quixote.__version__, - } - for title, header in msg.items(): - envname = 'HTTP_' + title.replace('-', '_').upper() - environ[envname] = header - - stdin = StringIO(data) - qresponse = self.publisher.process(stdin, environ) - - # Copy headers from Quixote's HTTP response - for name, value in qresponse.generate_headers(): - # XXX Medusa's HTTP request is buggy, and only allows unique - # headers. - request[name] = value - - request.response(qresponse.status_code) - request.push(StreamProducer(qresponse.generate_body_chunks())) - request.done() - - -def run(create_publisher, host='', port=80): - """Runs a Medusa HTTP server that publishes a Quixote - application. - """ - server = http_server.http_server(host, port) - publisher = create_publisher() - handler = QuixoteHandler(publisher, server) - server.install_handler(handler) - asyncore.loop() - - -if __name__ == '__main__': - from quixote.server.util import main - main(run) diff --git a/quixote/server/mod_python_handler.py b/quixote/server/mod_python_handler.py deleted file mode 100644 index eaed114..0000000 --- a/quixote/server/mod_python_handler.py +++ /dev/null @@ -1,100 +0,0 @@ -""" -This needs testing. - -mod_python configuration ------------------------- - -mod_python is an Apache module for embedding a Python interpreter into -the Apache server. To use mod_python as the interface layer between -Apache and Quixote, add something like this to your httpd.conf:: - - LoadModule python_module /usr/lib/apache/1.3/mod_python.so - - SetHandler python-program - PythonHandler quixote.server.mod_python_handler - PythonOption quixote-publisher-factory quixote.demo.create_publisher - PythonInterpreter quixote.demo - PythonDebug On - - -This will attach URLs starting with ``/qdemo`` to the Quixote demo. -When you use mod_python, there's no need for rewrite rules (because of -the pattern in the ``LocationMatch`` directive), and no need for a -driver script. - -mod_python support was contributed to Quixote (1) by Erno Kuusela - and the Quixote 2 port comes from Clint. -""" - -import sys -from mod_python import apache -from quixote.publish import Publisher -from quixote.util import import_object - -class ErrorLog: - def __init__(self, publisher): - self.publisher = publisher - - def write(self, msg): - self.publisher.log(msg) - - def close(self): - pass - -class ModPythonPublisher(Publisher): - def __init__(self, package, **kwargs): - Publisher.__init__(self, package, **kwargs) - # may be overwritten - self.logger.error_log = self.__error_log = ErrorLog(self) - self.__apache_request = None - - def log(self, msg): - if self.logger.error_log is self.__error_log: - try: - self.__apache_request.log_error(msg) - except AttributeError: - apache.log_error(msg) - else: - Publisher.log(self, msg) - - def publish_modpython(self, req): - """publish_modpython() -> None - - Entry point from mod_python. - """ - self.__apache_request = req - try: - self.publish(apache.CGIStdin(req), - apache.CGIStdout(req), - sys.stderr, - apache.build_cgi_env(req)) - - return apache.OK - finally: - self.__apache_request = None - -name2publisher = {} - -def run(publisher, req): - response = publisher.process(apache.CGIStdin(req), - apache.build_cgi_env(req)) - try: - response.write(apache.CGIStdout(req)) - except IOError as err: - publisher.log("IOError while sending response ignored: %s" % err) - return apache.OK - -def handler(req): - opts = req.get_options() - try: - factory = opts['quixote-publisher-factory'] - except KeyError: - apache.log_error('quixote-publisher-factory setting required') - return apache.HTTP_INTERNAL_SERVER_ERROR - pub = name2publisher.get(factory) - if pub is None: - factory_fcn = import_object(factory) - pub = factory_fcn() - name2publisher[factory] = pub - return run(pub, req) - diff --git a/quixote/server/twisted_server.py b/quixote/server/twisted_server.py deleted file mode 100755 index 15a5ecb..0000000 --- a/quixote/server/twisted_server.py +++ /dev/null @@ -1,143 +0,0 @@ -#!/usr/bin/env python3 -"""An HTTP server for Twisted that publishes a Quixote application. -""" - -import urllib.request, urllib.parse, urllib.error -from twisted.protocols import http -from twisted.web import server -from twisted.python import threadable -from twisted.internet import reactor - - -class QuixoteFactory(http.HTTPFactory): - def __init__(self, publisher): - self.publisher = publisher - http.HTTPFactory.__init__(self, None) - - def buildProtocol(self, addr): - protocol = http.HTTPFactory.buildProtocol(self, addr) - protocol.requestFactory = QuixoteRequest - return protocol - - -class QuixoteRequest(server.Request): - def process(self): - environ = self.create_environment() - # this seek is important, it doesn't work without it (it doesn't - # matter for GETs, but POSTs will not work properly without it.) - self.content.seek(0, 0) - qxresponse = self.channel.factory.publisher.process(self.content, - environ) - self.setResponseCode(qxresponse.status_code) - for name, value in qxresponse.generate_headers(): - if name != 'Set-Cookie': - self.setHeader(name, value) - # Cookies get special treatment since it seems Twisted cannot handle - # multiple Set-Cookie headers. - for name, attrs in qxresponse.cookies.items(): - attrs = attrs.copy() - value = attrs.pop('value') - self.addCookie(name, value, **attrs) - QuixoteProducer(qxresponse, self) - - def create_environment(self): - """ - Borrowed heavily from twisted.web.twcgi - """ - # Twisted doesn't decode the path for us, so let's do it here. - if '%' in self.path: - self.path = urllib.parse.unquote(self.path) - - serverName = self.getRequestHostname().split(':')[0] - env = {"SERVER_SOFTWARE": server.version, - "SERVER_NAME": serverName, - "GATEWAY_INTERFACE": "CGI/1.1", - "SERVER_PROTOCOL": self.clientproto, - "SERVER_PORT": str(self.getHost().port), - "REQUEST_METHOD": self.method, - "SCRIPT_NAME": '', - "SCRIPT_FILENAME": '', - "REQUEST_URI": self.uri, - "HTTPS": (self.isSecure() and 'on') or 'off', - 'SERVER_PROTOCOL': 'HTTP/1.1', - } - - for env_var, header in [('ACCEPT_ENCODING', 'Accept-encoding'), - ('CONTENT_TYPE', 'Content-type'), - ('HTTP_COOKIE', 'Cookie'), - ('HTTP_REFERER', 'Referer'), - ('HTTP_USER_AGENT', 'User-agent')]: - value = self.getHeader(header) - if value is not None: - env[env_var] = value - - client = self.getClient() - if client is not None: - env['REMOTE_HOST'] = client - ip = self.getClientIP() - if ip is not None: - env['REMOTE_ADDR'] = ip - remote_port = self.transport.getPeer().port - env['REMOTE_PORT'] = remote_port - env["PATH_INFO"] = self.path - - qindex = self.uri.find('?') - if qindex != -1: - env['QUERY_STRING'] = self.uri[qindex+1:] - else: - env['QUERY_STRING'] = '' - - # Propogate HTTP headers - for title, header in self.getAllHeaders().items(): - envname = title.replace('-', '_').upper() - if title not in ('content-type', 'content-length'): - envname = "HTTP_" + envname - env[envname] = header - - return env - - -class QuixoteProducer: - """ - Produce the Quixote response for twisted. - """ - def __init__(self, qxresponse, request): - self.request = request - self.size = qxresponse.get_content_length() - self.stream = qxresponse.generate_body_chunks() - request.registerProducer(self, 0) - - def resumeProducing(self): - if self.request: - try: - chunk = next(self.stream) - except StopIteration: - self.request.unregisterProducer() - self.request.finish() - self.request = None - else: - self.request.write(chunk) - - def pauseProducing(self): - pass - - def stopProducing(self): - self.request = None - - synchronized = ['resumeProducing', 'stopProducing'] - -threadable.synchronize(QuixoteProducer) - - -def run(create_publisher, host='', port=80): - """Runs a Twisted HTTP server server that publishes a Quixote - application.""" - publisher = create_publisher() - factory = QuixoteFactory(publisher) - reactor.listenTCP(port, factory, interface=host) - reactor.run() - - -if __name__ == '__main__': - from quixote.server.util import main - main(run)