SVN: r21276 - in trunk/quixote: . server
Andrew Kuchling <akuchlin-fVcApmY9cLvQ3/1i3zOLAti2O/[email protected]> Thu, 03 Apr 2003 11:06:21 -0500
| Newsgroups | gmane.comp.web.quixote.cvs |
|---|---|
| Message-ID | <[email protected]> |
Author: akuchlin
Date: 2003-04-03 11:06:18 -0500 (Thu, 03 Apr 2003)
New Revision: 21276
Added:
trunk/quixote/server/twisted_http.py
Modified:
trunk/quixote/CHANGES
Log:
Add Graham Fawcett's Twisted server
Added: trunk/quixote/server/twisted_http.py
==============================================================================
--- trunk/quixote/server/twisted_http.py (original)
+++ trunk/quixote/server/twisted_http.py 2003-04-03 11:06:20.000000000 -0500
@@ -0,0 +1,172 @@
+#!/usr/bin/env python
+
+"""
+twist -- Demo of an HTTP server built on top of Twisted Python.
+"""
+
+__revision__ = "$Id: medusa_http.py 21221 2003-03-20 16:02:41Z akuchlin $"
+
+# 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)
+
+
+from twisted.internet.app import Application
+from twisted.protocols import http
+from twisted.web import server
+
+from rfc822 import formatdate
+import time
+
+import quixote
+quixote.enable_ptl()
+import quixote.demo
+from quixote import errors
+from quixote.publish import Publisher
+
+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()
Modified: trunk/quixote/CHANGES
==============================================================================
--- trunk/quixote/CHANGES (original)
+++ trunk/quixote/CHANGES 2003-04-03 11:06:20.000000000 -0500
@@ -7,6 +7,10 @@
passing them to Medusa, fixing duplicate Content-length headers.
(Fix by Graham Fawcett.)
+ * Added quixote.server.twisted_http, which serves a Quixote application
+ using the Twisted event-driven framework (www.twistedmatrix.com).
+ Contributed by Graham Fawcett.
+
0.6b5 (13 Mar 2003):