HTTPServer.py :  » Web-Frameworks » Aquarium » aquarium-2.3 » glass » Python Open Source

Home
Python Open Source
1.3.1.2 Python
2.Ajax
3.Aspect Oriented
4.Blog
5.Build
6.Business Application
7.Chart Report
8.Content Management Systems
9.Cryptographic
10.Database
11.Development
12.Editor
13.Email
14.ERP
15.Game 2D 3D
16.GIS
17.GUI
18.IDE
19.Installer
20.IRC
21.Issue Tracker
22.Language Interface
23.Log
24.Math
25.Media Sound Audio
26.Mobile
27.Network
28.Parser
29.PDF
30.Project Management
31.RSS
32.Search
33.Security
34.Template Engines
35.Test
36.UML
37.USB Serial
38.Web Frameworks
39.Web Server
40.Web Services
41.Web Unit
42.Wiki
43.Windows
44.XML
Python Open Source » Web Frameworks » Aquarium 
Aquarium » aquarium 2.3 » glass » HTTPServer.py
"""This is a replacement for ``BaseHTTPServer.HTTPServer``."""

__docformat__ = "restructuredtext"

import errno
import exceptions
import socket
from SocketServer import TCPServer
import sys

import aquarium.util.Ports as ports


class _HTTPServer(TCPServer):

    """This is a replacement for ``BaseHTTPServer.HTTPServer``.

    It does all the same work ``BaseHTTPServer.HTTPServer`` does.  However,
    instead of inheriting from something like
    ``SocketServer.ThreadingTCPServer``, it inherits directly from
    ``SocketServer.TCPServer`` and accepts a servertype for creating sockets
    and threads.  This is necessary to support new threading models and socket
    types.

    This whole class is a move away from the design espoused by
    ``SocketServer`` toward a design suggested by Paul Clegg.  Specifically,
    instead of using mixins for the various threading models, factories are
    used for both socket and thread creation.

    The following attributes are used:

    RequestHandlerClass
      By default, this is ``HTTPHandler``.

    server_ctx
      This is ``ServerContext``.

    servertype
      This is an instance of some class from the servertype package.

    is_secure
      Return ``getattr(self.servertype.create_socket, "is_secure", False)``.

    mounts
      This is configured using ``server_ctx.mounts``.  It is a list of tuples
      of the form ``[(url_path, vfs)]``.  Each ``vfs`` is a virtual filesystem
      object (i.e. an instance of some class from the ``glass.vfs`` package).
      Abstracting the filesystem allows me to do things like use a zip file for
      a document root.

    available_vfs_modules
      This is a list of the names of the available vfs modules.  When creating
      ``self.mounts``, I'll try to instantiate each vfs to see if it can handle
      the given ``path_translated``.

    """

    allow_reuse_address = 1         # Taken from BaseHTTPServer.

    available_vfs_modules = ["Standard", "Zip"]

    def __init__(self, server_address=('', ports.HTTP_PORT),
                 RequestHandlerClass=None, server_ctx=None, servertype=None):
        """Against doctor's orders, I must override (not extend) this.

        Specifically, I need to accept and make use of additional parameters as
        well as split part of ``__init__`` into ``__call__`` for coro safety
        reasons.  Otherwise, this is pretty much like ``TCPServer.__init__``.

        server_address
          By default, this is ``('', 80)``.

        RequestHandlerClass
          By default, this is ``HTTPHandler``.

        server_ctx
          By default, this is an instance of ``ServerContext``.

        servertype
          By default, this is an instance of the ``Standard`` servertype.

        """
        if not RequestHandlerClass:
            from HTTPHandler import HTTPHandler
            RequestHandlerClass = HTTPHandler
        if not server_ctx:
            from ServerContext import ServerContext
            server_ctx = ServerContext()
        self.request_queue_size = server_ctx.request_queue_size
        if not servertype:
            from servertype.Standard import Standard
            servertype = Standard()
        self.server_address = server_address
        self.RequestHandlerClass = RequestHandlerClass
        self.server_ctx = server_ctx
        self.servertype = servertype
        self.init_mounts()

    def __call__(self):
        """Create, bind, and activate the socket."""
        self.socket = self.servertype.create_socket()
        self.server_bind()
        self.server_activate()

    def init_mounts(self):
        """Setup ``self.mounts``.

        Implicitly add the document root as the first member.  Pick the right
        vfs for each ``path_translated``.  Make sure each ``url_path`` has a
        trailing slash.  If not, add one.

        """
        configured = [("/", self.server_ctx.doc_root)] + \
                     self.server_ctx.mounts
        self.mounts = []
        for (url_path, path_translated) in configured:
            if not url_path.endswith("/"):
                url_path += "/"
            for vfs_name in self.available_vfs_modules:
                try:
                    module_name = "glass.vfs." + vfs_name
                    module = __import__(module_name, {}, {}, [vfs_name])
                    vfs = getattr(module, vfs_name)(path_translated)
                    self.mounts.append((url_path, vfs))
                    break
                except ValueError:
                    continue
            else:
                raise ValueError("""\
Could not find an appropriate vfs module for: %s
The simplest cause for this would be a misspelled doc_root.""" %
                    path_translated)

    def server_bind(self):
        """Extend ``server_bind`` to store the server name and port."""
        TCPServer.server_bind(self)
        self.server_name, self.server_port = self.socket.getsockname()

    def handle_request(self):
        """Handle early socket exceptions.

        Call ``handle_early_socket_error``.

        """
        request, client_address = None, None
        try:
            request, client_address = self.get_request()
        except socket.error, e:
            self.handle_early_socket_error(e, request, client_address)
            return
        if self.verify_request(request, client_address):
            self.process_request(request, client_address)

    def process_request(self, request, client_address):
        """Call ``finish_request_threaded`` in a new "thread"."""
        self.servertype.create_thread(self.finish_request_threaded, request,
                                       client_address)

    def finish_request_threaded(self, request, client_address):
        """Call ``finish_request``, but handle exceptions.

        This is nice because I don't won't exceptions from one "thread" being
        handled in another "thread".

        """
        try:
            self.finish_request(request, client_address)
        except Exception, e:
            self.handle_error(e, request, client_address)
            self.close_request(request)

    def finish_request(self, request, client_address):
        """Instantiate the ``RequestHandlerClass`` and run it.

        In ``TCPServer.finish_request``, instantiating the
        ``RequestHandlerClass`` is sufficient to run it.  However, in coro, you
        can't have anything in an ``__init__`` that may lead to a context
        switch.  ``SocketServer.BaseRequestHandler.__init__``, unsurprisingly,
        is ignorant of this rule.  Hence, we require that the
        ``RequestHandlerClass`` passed to us implement a separate ``__init__``
        *and* ``__call__`` method, both of which will be called here, in order
        to work around this problem.  This is an API change that must apply to
        coro and non-coro alike since it affects the request handlers.

        """
        self.RequestHandlerClass(request, client_address, self,
                                 self.server_ctx)()
        self.close_request(request)

    def close_request(self, request):
        """HACK: Call ``TCPServer.close_request``, if defined.

        It isn't defined in Python 2.0, but it must be done in Python 2.3.

        """
        if hasattr(TCPServer, "close_request"):
            TCPServer.close_request(self, request)

    def handle_early_socket_error(self, e, request, client_address):
        """Override this to handle socket errors from ``get_request``.

        By default, I output to stderr just to make sure you see something if
        things are completely messed up.

        Beware, request and client_address might each be None.

        """
        print >> sys.stderr, """\
HTTPServer: early socket error encountered--dropping request: %s""" % `e`

    def handle_error(self, e, request, client_address):
        """Differentiate errors.

        Differentiate ``handle_disconnect_error`` and ``handle_other_error``.

        """
        if self.is_disconnect_error(e):
            self.handle_disconnect_error(e, request, client_address)
        else:
            self.handle_other_error(e, request, client_address)

    def is_disconnect_error(self, e):
        """Is ``e`` an error like ``errno.EPIPE`` or ``errno.ECONNRESET``?

        Various types of sockets might represent disconnect errors differently.
        Hopefully, one of the ways I've coded will apply.

        """
        if (isinstance(e, socket.error) or
            isinstance(e, exceptions.OSError) or
            isinstance(e, exceptions.IOError)):
            errnos = [errno.EPIPE, errno.ECONNRESET]
            if len(e.args) and e.args[0] in errnos:
                return True
            if getattr(e, "errno", None) in errnos:
                return True
        return False

    def handle_disconnect_error(self, e, request, client_address):
        """Handle errors such as ``errno.EPIPE`` or ``errno.ECONNRESET``.

        By default, they are ignored.

        """
        pass

    def handle_other_error(self, e, request, client_address):
        """Handle all other errors that ``handle_disconnect_error`` doesn't.

        Since I don't know what's appropriate for your app, I'll just call
        ``TCPServer.handle_error``.

        """
        TCPServer.handle_error(self, request, client_address)

    def __getattr__(self, attr):
        """Handle the ``is_secure`` attribute.

        ``is_secure`` returns
        ``getattr(self.servertype.create_socket, "is_secure", False)``.

        """
        if attr == "is_secure":
            return getattr(self.servertype.create_socket, "is_secure", False)
        raise AttributeError(attr)


def HTTPServer(*args, **kargs):
    """HACK: This is a wrapper around ``_HTTPServer`` to make it coro safe.

    Existing code does not know that ``__init__`` has been broken into
    ``__init__`` and ``__call__``.  Hence, this wrapper abstracts that detail.

    """
    http_server = _HTTPServer(*args, **kargs)
    http_server()
    return http_server


def test(ServerClass=HTTPServer, RequestHandlerClass=None, server_ctx=None,
         servertype=None, protocol="HTTP/1.0"):
    """Test the HTTP request handler class.

    This runs an HTTP server on port 8000 (or the first command line argument).
    This code is a modified version of ``BaseHTTPServer.test``.  I had to
    modify it due to extra parameters passed to the ``ServerClass``.  Each of
    the above arguments has a reasonable default.

    """
    if sys.argv[1:]:
        port = int(sys.argv[1])
    else:
        port = 8000
    server_address = ('', port)
    httpd = ServerClass(server_address, RequestHandlerClass, server_ctx,
                        servertype)
    httpd.RequestHandlerClass.protocol_version = protocol
    sa = httpd.socket.getsockname()
    print "Serving HTTP on", sa[0], "port", sa[1], "..."
    httpd.serve_forever()


# Do some testing.
if __name__ == "__main__":
    test()
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.