I took a stab at fixing the earlier attempts made to write a Twisted
driver for Quixote. This one seems quite stable, and apepars to solve
the problems encountered in earlier versions posted to the list
(redirects not working, headers munged into the body, ...).
I side-stepped the Publisher.publish method (similar to the approach
taken in medusa_http) -- this may require some rework if you have
overridden that method in your custom Publishers.
Lastly, I moved the publisher and http_port out of the global namespace
(the approach in the orignal versions), and into the run() function --
so this ought to work nicely as a basis for a quixote.server module...
Tested using quixote.demo, on Win32, Quixote 0.65b, Twisted 0.18.0. Your
mileage may vary (though I hope it doesn't!).
Regards,
-- Graham
"""
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
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
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.setResponseCode(qxrequest.response.status_code)
for hdr, value in qxrequest.response.headers.items():
self.setHeader(hdr, value)
self.write(qxrequest.response.body)
self.finish()
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)
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
print 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()