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()