durusmail: quixote-users: Quixote Server on top of Twisted Python, revisited
Quixote Server on top of Twisted Python, revisited
2003-03-25
Re: Quixote Server on top of Twisted Python, revisited
2003-03-25
version 0.2 (Re: Quixote Server on top of Twisted Python, revisited)
2003-03-25
Quixote Server on top of Twisted Python, revisited
2003-04-03
Re: Quixote Server on top of Twisted Python, revisited
2003-04-03
Quixote Server on top of Twisted Python, revisited
Graham Fawcett
2003-03-25
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()



reply