I found a couple of problems with my original script: The method http_response.HTTPResponse.write(), which I side-stepped, does more than just push out the body of the response, it write cookies and some extra headers. This version of the script replicates that behaviour. Also, I had forgotten the call to finish_successful_request(), so the session management post-handler wasn't being called. -- G. """ twist -- Demo of an HTTP server built on top of Twisted Python. """ # based on qserv, created 2002/03/19, AMK # last mod 2003.03.24, Graham Fawcett # tested on Win32 / Twisted 0.18.0 / Quixote 0.6b5 # # version 0.2 -- 2003.03.24 11:07 PM # adds missing support for session management, and for # standard Quixote response headers (expires, date) import quixote quixote.enable_ptl() import quixote.demo from quixote import errors from quixote.publish import Publisher from twisted.internet.app import Application from twisted.protocols import http from twisted.web import server from rfc822 import formatdate import time class QuixoteTWRequest(server.Request): def process(self): self.publisher = self.channel.factory.publisher environ = self.create_environment() ## this seek is important, it doesnt work without it self.content.seek(0,0) qxrequest = self.publisher.create_request(self.content, environ) self.quixote_publish(qxrequest, environ) self.massage_response(qxrequest) self.write(qxrequest.response.body) self.finish() def massage_response(self, qxrequest): """ Does the work of reponse.write(), without actually writing, just massages the response into a Twisted fashion. """ resp = qxrequest.response self.setResponseCode(resp.status_code) for hdr, value in resp.headers.items(): self.setHeader(hdr, value) now = time.time() if not resp.headers.has_key('date'): resp.headers['date'] = '%s\r\n' % formatdate(now) if not resp.headers.has_key('expires'): if resp.cache > 0: expire_date = formatdate(now + resp.cache) else: expire_date = "-1" # allowed by HTTP spec, may work # better with some clients resp.headers['expires'] = '%s\r\n' % expire_date for k, d in resp.cookies.items(): value = d['value'] del d['value'] # this might be a bit risky, the addCookie signature is # addCookie(self, k, v, expires=None, domain=None, # path=None, max_age=None, comment=None, secure=None) self.addCookie(k, value, **d) def quixote_publish(self, qxrequest, env): """ Warning, this sidesteps the Publisher.publish method, Hope you didn't override it... """ pub = self.publisher try: pub.parse_request(qxrequest) output = pub.process_request(qxrequest, env) # needed for session management! pub.finish_successful_request(qxrequest) except errors.PublishError, exc: # Exit the publishing loop and return a result right away. output = pub.finish_interrupted_request(qxrequest, exc) except: # other exception, generate error messages to logs, etc. output = pub.finish_failed_request(qxrequest) # don't write out the output, just set the response body # the calling method will do the rest. if output: qxrequest.response.set_body(str(output)) pub._clear_request() def create_environment(self): """ Borrowed heavily from twisted.web.twcgi """ 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()[2]), "REQUEST_METHOD": self.method, "SCRIPT_NAME": '', "SCRIPT_FILENAME": '', "REQUEST_URI": self.uri, } client = self.getClient() if client is not None: env['REMOTE_HOST'] = client ip = self.getClientIP() if ip is not None: env['REMOTE_ADDR'] = ip xx, xx, remote_port = self.transport.getPeer() 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 QuixoteFactory (http.HTTPFactory): def __init__(self, publisher): self.publisher = publisher http.HTTPFactory.__init__(self, None) def buildProtocol (self, addr): h = http.HTTPChannel() h.requestFactory = QuixoteTWRequest h.factory = self return h def run (): # Ports this server will listen on http_port = 8080 namespace = quixote.demo app = Application('Quixote') publisher = Publisher(namespace) publisher.setup_logs() qf = QuixoteFactory(publisher) app.listenTCP(http_port, qf) app.run(save=0) if __name__ == '__main__': run()