SVN: r25579 - in trunk/quixote: . demo server

Neil Schemenauer <nascheme-fVcApmY9cLvQ3/1i3zOLAti2O/[email protected]> Thu, 11 Nov 2004 15:56:49 -0500
Newsgroups gmane.comp.web.quixote.cvs
Message-ID <[email protected]>
Author: nascheme
Date: 2004-11-11 15:56:32 -0500 (Thu, 11 Nov 2004)
New Revision: 25579

Added:
   trunk/quixote/server/simple_server.py
   trunk/quixote/server/util.py
Removed:
   trunk/quixote/demo/demonstrate.py
Modified:
   trunk/quixote/demo/altdemo.py
   trunk/quixote/http_response.py
   trunk/quixote/server/__init__.py
   trunk/quixote/server/medusa_server.py
   trunk/quixote/server/scgi_server.py
   trunk/quixote/server/twisted_server.py
   trunk/quixote/util.py
Log:
Move the option parser out of the demonstrate.py script and make it
available to the stand alone HTTP servers.  Move the simple HTTP server
from the demonstrate.py script to server/simple_server.py.


Modified: trunk/quixote/demo/altdemo.py
===================================================================
--- trunk/quixote/demo/altdemo.py	2004-11-11 20:39:03 UTC (rev 25578)
+++ trunk/quixote/demo/altdemo.py	2004-11-11 20:56:32 UTC (rev 25579)
@@ -2,9 +2,16 @@
 $Id$
 
 An alternative Quixote demo.  This version is contained in a single module
-and does not use PTL.
+and does not use PTL.  The easiest way to run this demo is to use the
+simple HTTP server included with Quixote.  For example:
+
+    $ server/simple_server.py --factory quixote.demo.altdemo.create_publisher
+
+The server listens on localhost:8080 by default.  Debug and error output
+will be sent to the terminal.
 """
 from quixote import get_response
+from quixote.publish import Publisher
 from quixote.directory import Directory
 from quixote.errors import PublishError
 from quixote.html import href
@@ -85,3 +92,5 @@
 
     sub = SubDirectory()
         
+def create_publisher():
+    return Publisher(RootDirectory(), display_exceptions='plain')

Deleted: trunk/quixote/demo/demonstrate.py
===================================================================
--- trunk/quixote/demo/demonstrate.py	2004-11-11 20:39:03 UTC (rev 25578)
+++ trunk/quixote/demo/demonstrate.py	2004-11-11 20:56:32 UTC (rev 25579)
@@ -1,113 +0,0 @@
-#!/usr/bin/env python
-"""
-$URL$
-$Id$
-
-Run a simple HTTPServer on a local port, processing all requests
-using a Quixote Directory instance.  Command line options allow
-the specification of Directory and/or Publisher classes.
-"""
-import sys
-from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
-from quixote import enable_ptl
-from quixote.http_request import HTTPRequest
-from quixote.publish import get_publisher
-
-class HTTPRequestHandler(BaseHTTPRequestHandler):
-
-    def get_cgi_env(self, method):
-        env = dict(
-            SERVER_SOFTWARE="demonstrate.py/1.0",
-            SERVER_NAME=self.server.server_name,
-            GATEWAY_INTERFACE='CGI/1.1',
-            SERVER_PROTOCOL=self.protocol_version,
-            SERVER_PORT=str(self.server.server_port),
-            REQUEST_METHOD=method,
-            REMOTE_ADDR=self.client_address[0],
-            SCRIPT_NAME='')
-        if '?' in self.path:
-            env['PATH_INFO'], env['QUERY_STRING'] = self.path.split('?')
-        else:
-            env['PATH_INFO'] = self.path
-        if self.headers.typeheader is None:
-            env['CONTENT_TYPE'] = self.headers.type
-        else:
-            env['CONTENT_TYPE'] = self.headers.typeheader
-        env['CONTENT_LENGTH'] = self.headers.getheader('content-length') or "0"
-        accept = []
-        for line in self.headers.getallmatchingheaders('accept'):
-            if line[:1] in "\t\n\r ":
-                accept.append(line.strip())
-            else:
-                accept = accept + line[7:].split(',')
-        env['HTTP_ACCEPT'] = ','.join(accept)
-        ua = self.headers.getheader('user-agent')
-        if ua:
-            env['HTTP_USER_AGENT'] = ua
-        co = filter(None, self.headers.getheaders('cookie'))
-        if co:
-            env['HTTP_COOKIE'] = ', '.join(co)
-        return env
-
-    def process(self, env):
-        request = HTTPRequest(self.rfile, env)
-        response = get_publisher().process_request(request)
-        try:
-            self.send_response(response.get_status_code(),
-                               'Script output follows')
-            response.write(self.wfile)
-        except IOError, err:
-            print "IOError while sending response ignored: %s" % err
-
-    def do_POST(self):
-        return self.process(self.get_cgi_env('POST'))
-
-    def do_GET(self):
-        return self.process(self.get_cgi_env('GET'))
-
-def demo_directory(port=8000,
-                   root_directory='quixote.demo.DemoUI',
-                   publisher='quixote.publish.Publisher'):
-    enable_ptl()
-    def import_class(name):
-        i = name.rfind('.')
-        module_name = name[:i]
-        class_name = name[i+1:]
-        __import__(module_name)
-        return getattr(sys.modules[module_name], class_name)
-    root_directory_instance = import_class(root_directory)()
-    if get_publisher() is None:
-        publisher_class = import_class(publisher)
-        publisher_class(root_directory_instance, display_exceptions='plain')
-    HTTPRequestHandler.protocol_version = 'HTTP/1.0'
-    httpd = HTTPServer(('', port), HTTPRequestHandler)
-    sa = httpd.socket.getsockname()
-    print "Serving HTTP on", sa[0], "port", sa[1], "..."
-    httpd.serve_forever()
-
-if __name__ == '__main__':
-    from optparse import OptionParser
-    parser = OptionParser()
-    parser.set_description(
-        "Runs a simple HTTP server that processes all requests "
-        "through a Directory.\n  "
-        "This is for demonstration purposes only.\n")
-    default_port = 8000
-    parser.add_option(
-        '--port', dest="port", default=default_port,
-        type="int",
-        help="Port for the server to listen on. (default=%s)" % default_port)
-    default_root_directory = 'quixote.demo.root.RootDirectory'
-    parser.add_option(
-        '--directory', dest="root_directory",
-        default=default_root_directory,
-        help="The root Directory class. (default=%s)" % default_root_directory)
-    default_publisher = 'quixote.publish.Publisher'
-    parser.add_option(
-        '--publisher', dest="publisher",
-        default=default_publisher,
-        help="The Publisher class. (default=%s)" % default_publisher)
-    (options, args) = parser.parse_args()
-    demo_directory(port=options.port,
-                   root_directory=options.root_directory,
-                   publisher = options.publisher)

Modified: trunk/quixote/http_response.py
===================================================================
--- trunk/quixote/http_response.py	2004-11-11 20:39:03 UTC (rev 25578)
+++ trunk/quixote/http_response.py	2004-11-11 20:56:32 UTC (rev 25579)
@@ -327,6 +327,9 @@
 
     def get_status_code(self):
         return self.status_code
+
+    def get_reason_phrase(self):
+        return self.reason_phrase
     
     def get_content_length(self):
         if self.body is None:

Modified: trunk/quixote/server/__init__.py
===================================================================
--- trunk/quixote/server/__init__.py	2004-11-11 20:39:03 UTC (rev 25578)
+++ trunk/quixote/server/__init__.py	2004-11-11 20:56:32 UTC (rev 25579)
@@ -1,5 +1,5 @@
 """$URL$
 $Id$
 
-This package is for Quixote/HTTP server glue.
+This package is for Quixote to server glue.
 """

Modified: trunk/quixote/server/medusa_server.py
===================================================================
--- trunk/quixote/server/medusa_server.py	2004-11-11 20:39:03 UTC (rev 25578)
+++ trunk/quixote/server/medusa_server.py	2004-11-11 20:56:32 UTC (rev 25579)
@@ -8,6 +8,7 @@
 import asyncore, rfc822, socket, urllib
 from StringIO import StringIO
 from medusa import http_server, xmlrpc_handler
+import quixote
 from quixote.http_request import HTTPRequest
 
 
@@ -78,7 +79,7 @@
                    'SERVER_NAME': server_name,
                    'SERVER_PORT': server_port,
                    'SERVER_PROTOCOL': 'HTTP/1.1',
-                   'SERVER_SOFTWARE': 'Quixote/2',
+                   'SERVER_SOFTWARE': 'Quixote/%s' % quixote.__version__,
                    }
         for title, header in msg.items():
             envname = 'HTTP_' + title.replace('-', '_').upper()
@@ -100,6 +101,9 @@
 
 
 def run(create_publisher, host='', port=80):
+    """Runs a Medusa HTTP server that publishes a Quixote
+    application.
+    """
     server = http_server.http_server(host, port)
     publisher = create_publisher()
     handler = QuixoteHandler(publisher, server)
@@ -108,5 +112,5 @@
 
 
 if __name__ == '__main__':
-    from quixote.demo import create_publisher
-    run(create_publisher, port=8080)
+    from quixote.server.util import main
+    main(run)

Modified: trunk/quixote/server/scgi_server.py
===================================================================
--- trunk/quixote/server/scgi_server.py	2004-11-11 20:39:03 UTC (rev 25578)
+++ trunk/quixote/server/scgi_server.py	2004-11-11 20:56:32 UTC (rev 25579)
@@ -48,6 +48,37 @@
     s.serve()
 
 
+def main():
+    from optparse import OptionParser
+    from quixote.util import import_object
+    parser = OptionParser()
+    parser.set_description(run.__doc__)
+    default_host = 'localhost'
+    parser.add_option(
+        '--host', dest="host", default=default_host, type="string",
+        help="Host interface to listen on. (default=%s)" % default_host)
+    default_port = 3000
+    parser.add_option(
+        '--port', dest="port", default=default_port, type="int",
+        help="Port to listen on. (default=%s)" % default_port)
+    default_maxchild = 5
+    parser.add_option(
+        '--max-children', dest="maxchild", default=default_maxchild,
+        type="string",
+        help="Maximum number of children to spawn. (default=%s)" %
+            default_maxchild)
+    parser.add_option(
+        '--script-name', dest="script_name", default=None, type="string",
+        help="Value of SCRIPT_NAME (only needed if using mod_scgi)")
+    default_factory = 'quixote.demo.create_publisher'
+    parser.add_option(
+        '--factory', dest="factory",
+        default=default_factory,
+        help="Path to factory function to create the site Publisher. "
+             "(default=%s)" % default_factory)
+    (options, args) = parser.parse_args()
+    run(import_object(options.factory), host=options.host, port=options.port,
+        script_name=options.script_name, max_children=options.maxchild)
+
 if __name__ == '__main__':
-    from quixote.demo import create_publisher
-    run(create_publisher)
+    main()

Added: trunk/quixote/server/simple_server.py
===================================================================
--- trunk/quixote/server/simple_server.py	2004-11-11 20:39:03 UTC (rev 25578)
+++ trunk/quixote/server/simple_server.py	2004-11-11 20:56:32 UTC (rev 25579)
@@ -0,0 +1,76 @@
+#!/usr/bin/env python
+"""$URL$
+$Id$
+
+A simple, single threaded, synchronous HTTP server.
+"""
+from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
+import quixote
+from quixote import get_publisher
+from quixote.http_request import HTTPRequest
+
+class HTTPRequestHandler(BaseHTTPRequestHandler):
+
+    def get_cgi_env(self, method):
+        env = dict(
+            SERVER_SOFTWARE="Quixote/%s" % quixote.__version__,
+            SERVER_NAME=self.server.server_name,
+            GATEWAY_INTERFACE='CGI/1.1',
+            SERVER_PROTOCOL=self.protocol_version,
+            SERVER_PORT=str(self.server.server_port),
+            REQUEST_METHOD=method,
+            REMOTE_ADDR=self.client_address[0],
+            SCRIPT_NAME='')
+        if '?' in self.path:
+            env['PATH_INFO'], env['QUERY_STRING'] = self.path.split('?')
+        else:
+            env['PATH_INFO'] = self.path
+        if self.headers.typeheader is None:
+            env['CONTENT_TYPE'] = self.headers.type
+        else:
+            env['CONTENT_TYPE'] = self.headers.typeheader
+        env['CONTENT_LENGTH'] = self.headers.getheader('content-length') or "0"
+        accept = []
+        for line in self.headers.getallmatchingheaders('accept'):
+            if line[:1] in "\t\n\r ":
+                accept.append(line.strip())
+            else:
+                accept = accept + line[7:].split(',')
+        env['HTTP_ACCEPT'] = ','.join(accept)
+        ua = self.headers.getheader('user-agent')
+        if ua:
+            env['HTTP_USER_AGENT'] = ua
+        co = filter(None, self.headers.getheaders('cookie'))
+        if co:
+            env['HTTP_COOKIE'] = ', '.join(co)
+        return env
+
+    def process(self, env):
+        request = HTTPRequest(self.rfile, env)
+        response = get_publisher().process_request(request)
+        try:
+            self.send_response(response.get_status_code(),
+                               response.get_reason_phrase())
+            response.write(self.wfile)
+        except IOError, err:
+            print "IOError while sending response ignored: %s" % err
+
+    def do_POST(self):
+        return self.process(self.get_cgi_env('POST'))
+
+    def do_GET(self):
+        return self.process(self.get_cgi_env('GET'))
+
+
+def run(create_publisher, host='', port=80):
+    """Runs a simple, single threaded, synchronous HTTP server that
+    publishes a Quixote application.
+    """
+    httpd = HTTPServer((host, port), HTTPRequestHandler)
+    publisher = create_publisher()
+    httpd.serve_forever()
+
+
+if __name__ == '__main__':
+    from quixote.server.util import main
+    main(run)


Property changes on: trunk/quixote/server/simple_server.py
___________________________________________________________________
Name: svn:keywords
   + HeadURL Id

Modified: trunk/quixote/server/twisted_server.py
===================================================================
--- trunk/quixote/server/twisted_server.py	2004-11-11 20:39:03 UTC (rev 25578)
+++ trunk/quixote/server/twisted_server.py	2004-11-11 20:56:32 UTC (rev 25579)
@@ -127,6 +127,8 @@
 
 
 def run(create_publisher, host='', port=80):
+    """Runs a Twisted HTTP server server that publishes a Quixote
+    application."""
     publisher = create_publisher()
     factory = QuixoteFactory(publisher)
     reactor.listenTCP(port, factory, interface=host)
@@ -134,5 +136,5 @@
 
 
 if __name__ == '__main__':
-    from quixote.demo import create_publisher
-    run(create_publisher, port=8080)
+    from quixote.server.util import main
+    main(run)

Added: trunk/quixote/server/util.py
===================================================================
--- trunk/quixote/server/util.py	2004-11-11 20:39:03 UTC (rev 25578)
+++ trunk/quixote/server/util.py	2004-11-11 20:56:32 UTC (rev 25579)
@@ -0,0 +1,28 @@
+"""$URL$
+$Id$
+
+Miscellaneous utility functions shared by servers.
+"""
+
+from optparse import OptionParser
+from quixote.util import import_object
+
+def main(run):
+    parser = OptionParser()
+    parser.set_description(run.__doc__)
+    default_host = 'localhost'
+    parser.add_option(
+        '--host', dest="host", default=default_host, type="string",
+        help="Host interface to listen on. (default=%s)" % default_host)
+    default_port = 8080
+    parser.add_option(
+        '--port', dest="port", default=default_port, type="int",
+        help="Port to listen on. (default=%s)" % default_port)
+    default_factory = 'quixote.demo.create_publisher'
+    parser.add_option(
+        '--factory', dest="factory",
+        default=default_factory,
+        help="Path to factory function to create the site Publisher. "
+             "(default=%s)" % default_factory)
+    (options, args) = parser.parse_args()
+    run(import_object(options.factory), host=options.host, port=options.port)


Property changes on: trunk/quixote/server/util.py
___________________________________________________________________
Name: svn:keywords
   + HeadURL Id

Modified: trunk/quixote/util.py
===================================================================
--- trunk/quixote/util.py	2004-11-11 20:39:03 UTC (rev 25578)
+++ trunk/quixote/util.py	2004-11-11 20:56:32 UTC (rev 25579)
@@ -68,6 +68,13 @@
     randbytes = _PRNG().randbytes
 
 
+def import_object(name):
+    i = name.rfind('.')
+    module_name = name[:i]
+    object_name = name[i+1:]
+    __import__(module_name)
+    return getattr(sys.modules[module_name], object_name)
+
 def xmlrpc(request, func):
     """xmlrpc(request:Request, func:callable) : string