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