changeset 4303:eb463542664e

Move flup also to support (now that it's added to path we can just ship it)
author Florian Krupicka <florian.krupicka@googlemail.com>
date Thu, 14 Aug 2008 00:59:13 +0200
parents 775e8f24c1b1
children d4917074d0ce
files MoinMoin/support/flup/ChangeLog MoinMoin/support/flup/NOTES.moin MoinMoin/support/flup/__init__.py MoinMoin/support/flup/client/__init__.py MoinMoin/support/flup/client/fcgi_app.py MoinMoin/support/flup/client/scgi_app.py MoinMoin/support/flup/server/__init__.py MoinMoin/support/flup/server/ajp.py MoinMoin/support/flup/server/ajp_base.py MoinMoin/support/flup/server/ajp_fork.py MoinMoin/support/flup/server/cgi.py MoinMoin/support/flup/server/fcgi.py MoinMoin/support/flup/server/fcgi_base.py MoinMoin/support/flup/server/fcgi_fork.py MoinMoin/support/flup/server/fcgi_single.py MoinMoin/support/flup/server/paste_factory.py MoinMoin/support/flup/server/preforkserver.py MoinMoin/support/flup/server/scgi.py MoinMoin/support/flup/server/scgi_base.py MoinMoin/support/flup/server/scgi_fork.py MoinMoin/support/flup/server/singleserver.py MoinMoin/support/flup/server/threadedserver.py MoinMoin/support/flup/server/threadpool.py contrib/flup-server/ChangeLog contrib/flup-server/NOTES.moin contrib/flup-server/flup/__init__.py contrib/flup-server/flup/client/__init__.py contrib/flup-server/flup/client/fcgi_app.py contrib/flup-server/flup/client/scgi_app.py contrib/flup-server/flup/server/__init__.py contrib/flup-server/flup/server/ajp.py contrib/flup-server/flup/server/ajp_base.py contrib/flup-server/flup/server/ajp_fork.py contrib/flup-server/flup/server/cgi.py contrib/flup-server/flup/server/fcgi.py contrib/flup-server/flup/server/fcgi_base.py contrib/flup-server/flup/server/fcgi_fork.py contrib/flup-server/flup/server/fcgi_single.py contrib/flup-server/flup/server/paste_factory.py contrib/flup-server/flup/server/preforkserver.py contrib/flup-server/flup/server/scgi.py contrib/flup-server/flup/server/scgi_base.py contrib/flup-server/flup/server/scgi_fork.py contrib/flup-server/flup/server/singleserver.py contrib/flup-server/flup/server/threadedserver.py contrib/flup-server/flup/server/threadpool.py
diffstat 46 files changed, 5814 insertions(+), 5814 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MoinMoin/support/flup/ChangeLog	Thu Aug 14 00:59:13 2008 +0200
@@ -0,0 +1,243 @@
+2008-07-23  Allan Saddi  <allan@saddi.com>
+
+	* Add support for configuring UNIX domain sockets (for servers that
+	  support them) in the paste.server_factory implementations. Thanks
+	  to Dan Roberts for the code.
+
+2008-07-22  Allan Saddi  <allan@saddi.com>
+
+	* Release 1.0.1
+
+	* Attempt to deduce missing PATH_INFO and/or QUERY_STRING from
+	  REQUEST_URI, if present. Patch provided by Richard Davies.
+
+2007-09-10  Allan Saddi  <allan@saddi.com>
+
+	* Fix readline implementations so size argument is checked
+	  earlier.
+
+2007-07-14  Allan Saddi  <allan@saddi.com>
+
+	* Prevent ThreadPool inconsistences if an exception is
+	  actually raised. Thanks to Tim Chen for the patch.
+
+2007-06-05  Allan Saddi  <allan@saddi.com>
+
+	* Remove publisher and middleware packages.
+	* Add cgi server for completeness.
+
+2007-05-17  Allan Saddi  <allan@saddi.com>
+
+	* Fix fcgi_fork so it can run on Solaris. Thanks to
+	  Basil Crow for the patch.
+
+2007-01-22  Allan Saddi  <allan@saddi.com>
+
+	* Fix eunuchs import issue.
+
+2007-01-10  Allan Saddi  <allan@saddi.com>
+
+	* Support gzip compression of XHTML pages using the
+	  correct MIME type.
+
+2006-12-29  Allan Saddi  <allan@saddi.com>
+
+	* Deprecate WSGI_SCRIPT_NAME and scriptName in scgi_base.
+	  Modern versions of mod_scgi correctly set SCRIPT_NAME &
+	  PATH_INFO.
+
+2006-12-13  Allan Saddi  <allan@saddi.com>
+
+	* Fix problem in session.py seen when optimization is on.
+
+2006-12-05  Allan Saddi  <allan@saddi.com>
+
+	* Update servers to default to an empty QUERY_STRING if
+	  not present in the environ.
+	* Update gzip.py: compresslevel -> compress_level
+	* Update gzip.py by updating docstrings and renaming
+	  classes/methods/functions to better follow Python naming
+	  conventions. NB: mimeTypes keyword parameter is now
+	  mime_types.
+
+2006-12-02  Allan Saddi  <allan@saddi.com>
+
+	* Change intra-package imports into absolute imports.
+
+2006-12-02  Allan Saddi  <allan@saddi.com>
+
+	* Add forceCookieOutput attribute to SessionService to
+	  force Set-Cookie output for the current request.
+
+2006-12-01  Allan Saddi  <allan@saddi.com>
+
+	* Update setup script.
+
+2006-11-26  Allan Saddi  <allan@saddi.com>
+
+	* Don't attempt to install signal handlers under Windows
+	  to improve compatibility.
+
+2006-11-24  Allan Saddi  <allan@saddi.com>
+
+	* Add *_thread egg entry-point aliases.
+	* Add UNIX domain socket support to scgi, scgi_fork,
+	  scgi_app.
+	* Add flup.client package which contains various
+	  WSGI -> connector client implentations. (So far: FastCGI,
+	  and SCGI.)
+
+2006-11-19  Allan Saddi  <allan@saddi.com>
+
+	* Change mime-type matching algorithm in GzipMiddleware.
+	  Strip parameters (e.g. "encoding") and accept a list of
+	  regexps. By default, compress 'text/.*' mime-types.
+
+2006-11-10  Allan Saddi  <allan@saddi.com>
+
+	* Add cookieAttributes to SessionService to make it easier
+	  to customize the generated cookie's attributes.
+
+2006-08-28  Allan Saddi  <allan@saddi.com>
+
+	* Add support for FastCGI roles other than FCGI_RESPONDER.
+	  Patch provided by Seairth Jacobs.
+
+2006-08-02  Allan Saddi  <allan@saddi.com>
+
+	* Add cookieExpiration keyword to SessionService /
+	  SessionMiddleware to adjust the session cookie's expiration.
+	  Thanks to Blaise Laflamme for the suggestion.
+
+2006-06-27  Allan Saddi  <allan@saddi.com>
+
+	* Set close-on-exec flag on all server sockets. Thanks to
+	  Ralf Schmitt for reporting the problem.
+
+2006-06-18  Allan Saddi  <allan@saddi.com>
+
+	* Stop ignoring EPIPE exceptions, as this is probably the
+	  wrong thing to do. (Application is unaware of disconnected
+	  clients and the CPU spins when sending large files to a
+	  disconnected client.) Thanks to Ivan Sagalaev for bringing
+	  this to my attention.
+
+	  NB: Existing applications that use the flup servers may begin
+	  seeing socket.error exceptions...
+
+2006-05-18  Allan Saddi  <allan@saddi.com>
+
+	* Added umask keyword parameter to fcgi and fcgi_fork,
+	  for use when binding to a UNIX socket.
+
+2006-05-03  Allan Saddi  <allan@saddi.com>
+
+	* Fix illusive problem with AJP implementation. Thanks to
+	  Moshe Van der Sterre for explaining the problem and
+	  providing a fix.
+
+2006-04-06  Allan Saddi  <allan@saddi.com>
+
+	* Catch a strange FieldStorage case. Seen in production.
+	  Not quite sure what causes it.
+
+2006-03-21  Allan Saddi  <allan@saddi.com>
+
+	* Add maxRequests option to PreforkServer. Patch provided by
+	  Wojtek Sobczuk.
+
+2006-02-23  Allan Saddi  <allan@saddi.com>
+
+	* Add paste.server_factory-compliant factories and respective
+	  egg entry points. Thanks to Luis Bruno for the code.
+
+	  Add debug option to servers, which is True by default.
+	  Currently, only server-level error handling is affected.
+	
+2006-01-15  Allan Saddi  <allan@saddi.com>
+
+	* Change the behavior of ImportingModuleResolver when dealing
+	  with ImportErrors. Previously, it would act as if the module
+	  did not exist. Now, it propagates the exception to another
+	  level (outer middleware or WSGI). Reported by Scot Doyle.
+
+2006-01-05  Allan Saddi  <allan@saddi.com>
+
+	* Improve Windows compatibility by conditionally installing
+	  SIGHUP handler. Thanks to Brad Miller for pointing out the
+	  problem and providing a fix.
+
+2005-12-19  Allan Saddi  <allan@saddi.com>
+
+	* Fix socket leak in eunuchs socketpair() wrapper. Thanks to
+	  Georg Bauer for pointing this out.
+
+2005-12-16  Allan Saddi  <allan@saddi.com>
+
+	* Switch to setuptools for egg support.
+	* Add higher-level 404 error page support. Thanks to Scot Doyle
+	  for suggesting the idea and providing code. If you previously
+	  subclassed Publisher to provide a custom 404 error page, this
+	  is now broken. It will have to be massaged to fit the new
+	  calling convention.
+
+2005-11-28  Allan Saddi  <allan@saddi.com>
+
+	* Fix issue with FCGI_GET_VALUES handling. Thanks to
+	  Timothy Wright for pointing this out.
+
+2005-11-18  Allan Saddi  <allan@saddi.com>
+
+	* When running under Python < 2.4, attempt to use socketpair()
+	  from eunuchs module.
+
+2005-09-07  Allan Saddi  <allan@saddi.com>
+
+	* Python 2.3 doesn't define socket.SHUT_WR, which affected
+	  the closing of the FastCGI socket with the server. This would
+	  cause output to hang. Thanks to Eugene Lazutkin for bringing
+	  the problem to my attention and going out of his way to help
+	  me debug it!
+
+2005-07-03  Allan Saddi  <allan@saddi.com>
+
+	* Ensure session identifiers only contain ASCII characters when
+	  using a non-ASCII locale. Thanks to Ksenia Marasanova for the
+	  the fix.
+
+2005-06-12  Allan Saddi  <allan@saddi.com>
+
+	* Cleanly close connection socket to avoid sending a TCP RST to
+	  the web server. (fcgi_base) Fix suggested by Dima Barsky.
+
+2005-05-31  Allan Saddi  <allan@saddi.com>
+
+	* Take scriptName from the WSGI_SCRIPT_NAME environment variable
+	  passed from the web server, if present.
+	* Check if scriptName is None, and if so, don't modify SCRIPT_NAME
+	  & PATH_INFO. For better compatibility with cgi2scgi. (scgi_base)
+
+2005-05-18  Allan Saddi  <allan@saddi.com>
+
+	* Change default allowedServers for ajp and scgi to ['127.0.0.1'].
+	* Accept PATH_INFO from environment for scgi servers, in case
+	  cgi2scgi is being used. Submitted by Ian Bicking.
+	* Change threaded servers so wsgi.multiprocess is False by default.
+	  Allow it to be changed by keyword argument.
+	* Fix wsgi.multiprocess for scgi_fork. (Set to True.)
+
+2005-05-15  Allan Saddi  <allan@saddi.com>
+
+	* Prevent possible deadlock related to DiskSessionStore locking.
+	* Add logic to SessionStore so that it will block if attempting to
+	  check out a Session that's already been checked out.
+
+2005-05-14  Allan Saddi  <allan@saddi.com>
+
+	* Convert the use of decorators in session.py to something
+	  compatible with Python <2.4.
+
+2005-04-23  Allan Saddi  <allan@saddi.com>
+
+	* Ensure that SessionStore.checkOutSession() never returns an
+	  invalidated Session. Reported by Rene Dudfield.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MoinMoin/support/flup/NOTES.moin	Thu Aug 14 00:59:13 2008 +0200
@@ -0,0 +1,17 @@
+NOTES for bundled flup:
+=======================
+This directory contains the flup WSGI adapter package. It has been
+patched to support completely singlethreaded non-forking servers for
+deployment in webservers, that have their own process spawning strategy.
+
+The shipped version is available via mercurial checkout:
+
+hg clone -r aa983e43105e http://hg.saddi.com/flup-server
+
+The singlethreaded server patch was provided via a trac bugreport
+(http://trac.saddi.com/flup/ticket/22) and is provided in this directory
+as singleserver.diff for convenience. It has already been applied.
+
+Thanks to Allan Saddi <allan@saddi.com> for writing flup and to
+the unnamed contributor <tv@inoi.fi> for the single-thread patch.
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MoinMoin/support/flup/__init__.py	Thu Aug 14 00:59:13 2008 +0200
@@ -0,0 +1,1 @@
+#
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MoinMoin/support/flup/client/__init__.py	Thu Aug 14 00:59:13 2008 +0200
@@ -0,0 +1,1 @@
+#
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MoinMoin/support/flup/client/fcgi_app.py	Thu Aug 14 00:59:13 2008 +0200
@@ -0,0 +1,461 @@
+# Copyright (c) 2006 Allan Saddi <allan@saddi.com>
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+#    notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+#    notice, this list of conditions and the following disclaimer in the
+#    documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# $Id$
+
+__author__ = 'Allan Saddi <allan@saddi.com>'
+__version__ = '$Revision$'
+
+import select
+import struct
+import socket
+import errno
+
+__all__ = ['FCGIApp']
+
+# Constants from the spec.
+FCGI_LISTENSOCK_FILENO = 0
+
+FCGI_HEADER_LEN = 8
+
+FCGI_VERSION_1 = 1
+
+FCGI_BEGIN_REQUEST = 1
+FCGI_ABORT_REQUEST = 2
+FCGI_END_REQUEST = 3
+FCGI_PARAMS = 4
+FCGI_STDIN = 5
+FCGI_STDOUT = 6
+FCGI_STDERR = 7
+FCGI_DATA = 8
+FCGI_GET_VALUES = 9
+FCGI_GET_VALUES_RESULT = 10
+FCGI_UNKNOWN_TYPE = 11
+FCGI_MAXTYPE = FCGI_UNKNOWN_TYPE
+
+FCGI_NULL_REQUEST_ID = 0
+
+FCGI_KEEP_CONN = 1
+
+FCGI_RESPONDER = 1
+FCGI_AUTHORIZER = 2
+FCGI_FILTER = 3
+
+FCGI_REQUEST_COMPLETE = 0
+FCGI_CANT_MPX_CONN = 1
+FCGI_OVERLOADED = 2
+FCGI_UNKNOWN_ROLE = 3
+
+FCGI_MAX_CONNS = 'FCGI_MAX_CONNS'
+FCGI_MAX_REQS = 'FCGI_MAX_REQS'
+FCGI_MPXS_CONNS = 'FCGI_MPXS_CONNS'
+
+FCGI_Header = '!BBHHBx'
+FCGI_BeginRequestBody = '!HB5x'
+FCGI_EndRequestBody = '!LB3x'
+FCGI_UnknownTypeBody = '!B7x'
+
+FCGI_BeginRequestBody_LEN = struct.calcsize(FCGI_BeginRequestBody)
+FCGI_EndRequestBody_LEN = struct.calcsize(FCGI_EndRequestBody)
+FCGI_UnknownTypeBody_LEN = struct.calcsize(FCGI_UnknownTypeBody)
+
+if __debug__:
+    import time
+
+    # Set non-zero to write debug output to a file.
+    DEBUG = 0
+    DEBUGLOG = '/tmp/fcgi_app.log'
+
+    def _debug(level, msg):
+        if DEBUG < level:
+            return
+
+        try:
+            f = open(DEBUGLOG, 'a')
+            f.write('%sfcgi: %s\n' % (time.ctime()[4:-4], msg))
+            f.close()
+        except:
+            pass
+
+def decode_pair(s, pos=0):
+    """
+    Decodes a name/value pair.
+
+    The number of bytes decoded as well as the name/value pair
+    are returned.
+    """
+    nameLength = ord(s[pos])
+    if nameLength & 128:
+        nameLength = struct.unpack('!L', s[pos:pos+4])[0] & 0x7fffffff
+        pos += 4
+    else:
+        pos += 1
+
+    valueLength = ord(s[pos])
+    if valueLength & 128:
+        valueLength = struct.unpack('!L', s[pos:pos+4])[0] & 0x7fffffff
+        pos += 4
+    else:
+        pos += 1
+
+    name = s[pos:pos+nameLength]
+    pos += nameLength
+    value = s[pos:pos+valueLength]
+    pos += valueLength
+
+    return (pos, (name, value))
+
+def encode_pair(name, value):
+    """
+    Encodes a name/value pair.
+
+    The encoded string is returned.
+    """
+    nameLength = len(name)
+    if nameLength < 128:
+        s = chr(nameLength)
+    else:
+        s = struct.pack('!L', nameLength | 0x80000000L)
+
+    valueLength = len(value)
+    if valueLength < 128:
+        s += chr(valueLength)
+    else:
+        s += struct.pack('!L', valueLength | 0x80000000L)
+
+    return s + name + value
+
+class Record(object):
+    """
+    A FastCGI Record.
+
+    Used for encoding/decoding records.
+    """
+    def __init__(self, type=FCGI_UNKNOWN_TYPE, requestId=FCGI_NULL_REQUEST_ID):
+        self.version = FCGI_VERSION_1
+        self.type = type
+        self.requestId = requestId
+        self.contentLength = 0
+        self.paddingLength = 0
+        self.contentData = ''
+
+    def _recvall(sock, length):
+        """
+        Attempts to receive length bytes from a socket, blocking if necessary.
+        (Socket may be blocking or non-blocking.)
+        """
+        dataList = []
+        recvLen = 0
+        while length:
+            try:
+                data = sock.recv(length)
+            except socket.error, e:
+                if e[0] == errno.EAGAIN:
+                    select.select([sock], [], [])
+                    continue
+                else:
+                    raise
+            if not data: # EOF
+                break
+            dataList.append(data)
+            dataLen = len(data)
+            recvLen += dataLen
+            length -= dataLen
+        return ''.join(dataList), recvLen
+    _recvall = staticmethod(_recvall)
+
+    def read(self, sock):
+        """Read and decode a Record from a socket."""
+        try:
+            header, length = self._recvall(sock, FCGI_HEADER_LEN)
+        except:
+            raise EOFError
+
+        if length < FCGI_HEADER_LEN:
+            raise EOFError
+        
+        self.version, self.type, self.requestId, self.contentLength, \
+                      self.paddingLength = struct.unpack(FCGI_Header, header)
+
+        if __debug__: _debug(9, 'read: fd = %d, type = %d, requestId = %d, '
+                             'contentLength = %d' %
+                             (sock.fileno(), self.type, self.requestId,
+                              self.contentLength))
+        
+        if self.contentLength:
+            try:
+                self.contentData, length = self._recvall(sock,
+                                                         self.contentLength)
+            except:
+                raise EOFError
+
+            if length < self.contentLength:
+                raise EOFError
+
+        if self.paddingLength:
+            try:
+                self._recvall(sock, self.paddingLength)
+            except:
+                raise EOFError
+
+    def _sendall(sock, data):
+        """
+        Writes data to a socket and does not return until all the data is sent.
+        """
+        length = len(data)
+        while length:
+            try:
+                sent = sock.send(data)
+            except socket.error, e:
+                if e[0] == errno.EAGAIN:
+                    select.select([], [sock], [])
+                    continue
+                else:
+                    raise
+            data = data[sent:]
+            length -= sent
+    _sendall = staticmethod(_sendall)
+
+    def write(self, sock):
+        """Encode and write a Record to a socket."""
+        self.paddingLength = -self.contentLength & 7
+
+        if __debug__: _debug(9, 'write: fd = %d, type = %d, requestId = %d, '
+                             'contentLength = %d' %
+                             (sock.fileno(), self.type, self.requestId,
+                              self.contentLength))
+
+        header = struct.pack(FCGI_Header, self.version, self.type,
+                             self.requestId, self.contentLength,
+                             self.paddingLength)
+        self._sendall(sock, header)
+        if self.contentLength:
+            self._sendall(sock, self.contentData)
+        if self.paddingLength:
+            self._sendall(sock, '\x00'*self.paddingLength)
+
+class FCGIApp(object):
+    def __init__(self, command=None, connect=None, host=None, port=None,
+                 filterEnviron=True):
+        if host is not None:
+            assert port is not None
+            connect=(host, port)
+
+        assert (command is not None and connect is None) or \
+               (command is None and connect is not None)
+
+        self._command = command
+        self._connect = connect
+
+        self._filterEnviron = filterEnviron
+        
+        #sock = self._getConnection()
+        #print self._fcgiGetValues(sock, ['FCGI_MAX_CONNS', 'FCGI_MAX_REQS', 'FCGI_MPXS_CONNS'])
+        #sock.close()
+        
+    def __call__(self, environ, start_response):
+        # For sanity's sake, we don't care about FCGI_MPXS_CONN
+        # (connection multiplexing). For every request, we obtain a new
+        # transport socket, perform the request, then discard the socket.
+        # This is, I believe, how mod_fastcgi does things...
+
+        sock = self._getConnection()
+
+        # Since this is going to be the only request on this connection,
+        # set the request ID to 1.
+        requestId = 1
+
+        # Begin the request
+        rec = Record(FCGI_BEGIN_REQUEST, requestId)
+        rec.contentData = struct.pack(FCGI_BeginRequestBody, FCGI_RESPONDER, 0)
+        rec.contentLength = FCGI_BeginRequestBody_LEN
+        rec.write(sock)
+
+        # Filter WSGI environ and send it as FCGI_PARAMS
+        if self._filterEnviron:
+            params = self._defaultFilterEnviron(environ)
+        else:
+            params = self._lightFilterEnviron(environ)
+        # TODO: Anything not from environ that needs to be sent also?
+        self._fcgiParams(sock, requestId, params)
+        self._fcgiParams(sock, requestId, {})
+
+        # Transfer wsgi.input to FCGI_STDIN
+        content_length = int(environ.get('CONTENT_LENGTH') or 0)
+        while True:
+            chunk_size = min(content_length, 4096)
+            s = environ['wsgi.input'].read(chunk_size)
+            content_length -= len(s)
+            rec = Record(FCGI_STDIN, requestId)
+            rec.contentData = s
+            rec.contentLength = len(s)
+            rec.write(sock)
+
+            if not s: break
+
+        # Empty FCGI_DATA stream
+        rec = Record(FCGI_DATA, requestId)
+        rec.write(sock)
+
+        # Main loop. Process FCGI_STDOUT, FCGI_STDERR, FCGI_END_REQUEST
+        # records from the application.
+        result = []
+        while True:
+            inrec = Record()
+            inrec.read(sock)
+            if inrec.type == FCGI_STDOUT:
+                if inrec.contentData:
+                    result.append(inrec.contentData)
+                else:
+                    # TODO: Should probably be pedantic and no longer
+                    # accept FCGI_STDOUT records?
+                    pass
+            elif inrec.type == FCGI_STDERR:
+                # Simply forward to wsgi.errors
+                environ['wsgi.errors'].write(inrec.contentData)
+            elif inrec.type == FCGI_END_REQUEST:
+                # TODO: Process appStatus/protocolStatus fields?
+                break
+
+        # Done with this transport socket, close it. (FCGI_KEEP_CONN was not
+        # set in the FCGI_BEGIN_REQUEST record we sent above. So the
+        # application is expected to do the same.)
+        sock.close()
+
+        result = ''.join(result)
+
+        # Parse response headers from FCGI_STDOUT
+        status = '200 OK'
+        headers = []
+        pos = 0
+        while True:
+            eolpos = result.find('\n', pos)
+            if eolpos < 0: break
+            line = result[pos:eolpos-1]
+            pos = eolpos + 1
+
+            # strip in case of CR. NB: This will also strip other
+            # whitespace...
+            line = line.strip()
+            
+            # Empty line signifies end of headers
+            if not line: break
+
+            # TODO: Better error handling
+            header, value = line.split(':', 1)
+            header = header.strip().lower()
+            value = value.strip()
+
+            if header == 'status':
+                # Special handling of Status header
+                status = value
+                if status.find(' ') < 0:
+                    # Append a dummy reason phrase if one was not provided
+                    status += ' FCGIApp'
+            else:
+                headers.append((header, value))
+
+        result = result[pos:]
+
+        # Set WSGI status, headers, and return result.
+        start_response(status, headers)
+        return [result]
+
+    def _getConnection(self):
+        if self._connect is not None:
+            # The simple case. Create a socket and connect to the
+            # application.
+            if type(self._connect) is str:
+                sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
+            else:
+                sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+            sock.connect(self._connect)
+            return sock
+
+        # To be done when I have more time...
+        raise NotImplementedError, 'Launching and managing FastCGI programs not yet implemented'
+    
+    def _fcgiGetValues(self, sock, vars):
+        # Construct FCGI_GET_VALUES record
+        outrec = Record(FCGI_GET_VALUES)
+        data = []
+        for name in vars:
+            data.append(encode_pair(name, ''))
+        data = ''.join(data)
+        outrec.contentData = data
+        outrec.contentLength = len(data)
+        outrec.write(sock)
+
+        # Await response
+        inrec = Record()
+        inrec.read(sock)
+        result = {}
+        if inrec.type == FCGI_GET_VALUES_RESULT:
+            pos = 0
+            while pos < inrec.contentLength:
+                pos, (name, value) = decode_pair(inrec.contentData, pos)
+                result[name] = value
+        return result
+
+    def _fcgiParams(self, sock, requestId, params):
+        rec = Record(FCGI_PARAMS, requestId)
+        data = []
+        for name,value in params.items():
+            data.append(encode_pair(name, value))
+        data = ''.join(data)
+        rec.contentData = data
+        rec.contentLength = len(data)
+        rec.write(sock)
+
+    _environPrefixes = ['SERVER_', 'HTTP_', 'REQUEST_', 'REMOTE_', 'PATH_',
+                        'CONTENT_']
+    _environCopies = ['SCRIPT_NAME', 'QUERY_STRING', 'AUTH_TYPE']
+    _environRenames = {}
+
+    def _defaultFilterEnviron(self, environ):
+        result = {}
+        for n in environ.keys():
+            for p in self._environPrefixes:
+                if n.startswith(p):
+                    result[n] = environ[n]
+            if n in self._environCopies:
+                result[n] = environ[n]
+            if n in self._environRenames:
+                result[self._environRenames[n]] = environ[n]
+                
+        return result
+
+    def _lightFilterEnviron(self, environ):
+        result = {}
+        for n in environ.keys():
+            if n.upper() == n:
+                result[n] = environ[n]
+        return result
+
+if __name__ == '__main__':
+    from flup.server.ajp import WSGIServer
+    app = FCGIApp(connect=('localhost', 4242))
+    #import paste.lint
+    #app = paste.lint.middleware(app)
+    WSGIServer(app).run()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MoinMoin/support/flup/client/scgi_app.py	Thu Aug 14 00:59:13 2008 +0200
@@ -0,0 +1,176 @@
+# Copyright (c) 2006 Allan Saddi <allan@saddi.com>
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+#    notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+#    notice, this list of conditions and the following disclaimer in the
+#    documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# $Id$
+
+__author__ = 'Allan Saddi <allan@saddi.com>'
+__version__ = '$Revision$'
+
+import select
+import struct
+import socket
+import errno
+
+__all__ = ['SCGIApp']
+
+def encodeNetstring(s):
+    return ''.join([str(len(s)), ':', s, ','])
+
+class SCGIApp(object):
+    def __init__(self, connect=None, host=None, port=None,
+                 filterEnviron=True):
+        if host is not None:
+            assert port is not None
+            connect=(host, port)
+
+        assert connect is not None
+        self._connect = connect
+
+        self._filterEnviron = filterEnviron
+        
+    def __call__(self, environ, start_response):
+        sock = self._getConnection()
+
+        outfile = sock.makefile('w')
+        infile = sock.makefile('r')
+
+        sock.close()
+
+        # Filter WSGI environ and send as request headers
+        if self._filterEnviron:
+            headers = self._defaultFilterEnviron(environ)
+        else:
+            headers = self._lightFilterEnviron(environ)
+        # TODO: Anything not from environ that needs to be sent also?
+
+        content_length = int(environ.get('CONTENT_LENGTH') or 0)
+        if headers.has_key('CONTENT_LENGTH'):
+            del headers['CONTENT_LENGTH']
+            
+        headers_out = ['CONTENT_LENGTH', str(content_length), 'SCGI', '1']
+        for k,v in headers.items():
+            headers_out.append(k)
+            headers_out.append(v)
+        headers_out.append('') # For trailing NUL
+        outfile.write(encodeNetstring('\x00'.join(headers_out)))
+
+        # Transfer wsgi.input to outfile
+        while True:
+            chunk_size = min(content_length, 4096)
+            s = environ['wsgi.input'].read(chunk_size)
+            content_length -= len(s)
+            outfile.write(s)
+
+            if not s: break
+
+        outfile.close()
+        
+        # Read result from SCGI server
+        result = []
+        while True:
+            buf = infile.read(4096)
+            if not buf: break
+
+            result.append(buf)
+
+        infile.close()
+        
+        result = ''.join(result)
+
+        # Parse response headers
+        status = '200 OK'
+        headers = []
+        pos = 0
+        while True:
+            eolpos = result.find('\n', pos)
+            if eolpos < 0: break
+            line = result[pos:eolpos-1]
+            pos = eolpos + 1
+
+            # strip in case of CR. NB: This will also strip other
+            # whitespace...
+            line = line.strip()
+            
+            # Empty line signifies end of headers
+            if not line: break
+
+            # TODO: Better error handling
+            header, value = line.split(':', 1)
+            header = header.strip().lower()
+            value = value.strip()
+
+            if header == 'status':
+                # Special handling of Status header
+                status = value
+                if status.find(' ') < 0:
+                    # Append a dummy reason phrase if one was not provided
+                    status += ' SCGIApp'
+            else:
+                headers.append((header, value))
+
+        result = result[pos:]
+
+        # Set WSGI status, headers, and return result.
+        start_response(status, headers)
+        return [result]
+
+    def _getConnection(self):
+        if type(self._connect) is str:
+            sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
+        else:
+            sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+        sock.connect(self._connect)
+        return sock
+    
+    _environPrefixes = ['SERVER_', 'HTTP_', 'REQUEST_', 'REMOTE_', 'PATH_',
+                        'CONTENT_']
+    _environCopies = ['SCRIPT_NAME', 'QUERY_STRING', 'AUTH_TYPE']
+    _environRenames = {}
+
+    def _defaultFilterEnviron(self, environ):
+        result = {}
+        for n in environ.keys():
+            for p in self._environPrefixes:
+                if n.startswith(p):
+                    result[n] = environ[n]
+            if n in self._environCopies:
+                result[n] = environ[n]
+            if n in self._environRenames:
+                result[self._environRenames[n]] = environ[n]
+                
+        return result
+
+    def _lightFilterEnviron(self, environ):
+        result = {}
+        for n in environ.keys():
+            if n.upper() == n:
+                result[n] = environ[n]
+        return result
+
+if __name__ == '__main__':
+    from flup.server.ajp import WSGIServer
+    app = SCGIApp(connect=('localhost', 4000))
+    #import paste.lint
+    #app = paste.lint.middleware(app)
+    WSGIServer(app).run()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MoinMoin/support/flup/server/__init__.py	Thu Aug 14 00:59:13 2008 +0200
@@ -0,0 +1,1 @@
+#
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MoinMoin/support/flup/server/ajp.py	Thu Aug 14 00:59:13 2008 +0200
@@ -0,0 +1,201 @@
+# Copyright (c) 2005, 2006 Allan Saddi <allan@saddi.com>
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+#    notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+#    notice, this list of conditions and the following disclaimer in the
+#    documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# $Id$
+
+"""
+ajp - an AJP 1.3/WSGI gateway.
+
+For more information about AJP and AJP connectors for your web server, see
+<http://jakarta.apache.org/tomcat/connectors-doc/>.
+
+For more information about the Web Server Gateway Interface, see
+<http://www.python.org/peps/pep-0333.html>.
+
+Example usage:
+
+  #!/usr/bin/env python
+  import sys
+  from myapplication import app # Assume app is your WSGI application object
+  from ajp import WSGIServer
+  ret = WSGIServer(app).run()
+  sys.exit(ret and 42 or 0)
+
+See the documentation for WSGIServer for more information.
+
+About the bit of logic at the end:
+Upon receiving SIGHUP, the python script will exit with status code 42. This
+can be used by a wrapper script to determine if the python script should be
+re-run. When a SIGINT or SIGTERM is received, the script exits with status
+code 0, possibly indicating a normal exit.
+
+Example wrapper script:
+
+  #!/bin/sh
+  STATUS=42
+  while test $STATUS -eq 42; do
+    python "$@" that_script_above.py
+    STATUS=$?
+  done
+
+Example workers.properties (for mod_jk):
+
+  worker.list=foo
+  worker.foo.port=8009
+  worker.foo.host=localhost
+  worker.foo.type=ajp13
+
+Example httpd.conf (for mod_jk):
+
+  JkWorkersFile /path/to/workers.properties
+  JkMount /* foo
+
+Note that if you mount your ajp application anywhere but the root ("/"), you
+SHOULD specifiy scriptName to the WSGIServer constructor. This will ensure
+that SCRIPT_NAME/PATH_INFO are correctly deduced.
+"""
+
+__author__ = 'Allan Saddi <allan@saddi.com>'
+__version__ = '$Revision$'
+
+import socket
+import logging
+
+from flup.server.ajp_base import BaseAJPServer, Connection
+from flup.server.threadedserver import ThreadedServer
+
+__all__ = ['WSGIServer']
+
+class WSGIServer(BaseAJPServer, ThreadedServer):
+    """
+    AJP1.3/WSGI server. Runs your WSGI application as a persistant program
+    that understands AJP1.3. Opens up a TCP socket, binds it, and then
+    waits for forwarded requests from your webserver.
+
+    Why AJP? Two good reasons are that AJP provides load-balancing and
+    fail-over support. Personally, I just wanted something new to
+    implement. :)
+
+    Of course you will need an AJP1.3 connector for your webserver (e.g.
+    mod_jk) - see <http://jakarta.apache.org/tomcat/connectors-doc/>.
+    """
+    def __init__(self, application, scriptName='', environ=None,
+                 multithreaded=True, multiprocess=False,
+                 bindAddress=('localhost', 8009), allowedServers=None,
+                 loggingLevel=logging.INFO, debug=True, **kw):
+        """
+        scriptName is the initial portion of the URL path that "belongs"
+        to your application. It is used to determine PATH_INFO (which doesn't
+        seem to be passed in). An empty scriptName means your application
+        is mounted at the root of your virtual host.
+
+        environ, which must be a dictionary, can contain any additional
+        environment variables you want to pass to your application.
+
+        bindAddress is the address to bind to, which must be a tuple of
+        length 2. The first element is a string, which is the host name
+        or IPv4 address of a local interface. The 2nd element is the port
+        number.
+
+        allowedServers must be None or a list of strings representing the
+        IPv4 addresses of servers allowed to connect. None means accept
+        connections from anywhere.
+
+        loggingLevel sets the logging level of the module-level logger.
+        """
+        BaseAJPServer.__init__(self, application,
+                               scriptName=scriptName,
+                               environ=environ,
+                               multithreaded=multithreaded,
+                               multiprocess=multiprocess,
+                               bindAddress=bindAddress,
+                               allowedServers=allowedServers,
+                               loggingLevel=loggingLevel,
+                               debug=debug)
+        for key in ('jobClass', 'jobArgs'):
+            if kw.has_key(key):
+                del kw[key]
+        ThreadedServer.__init__(self, jobClass=Connection, jobArgs=(self,),
+                                **kw)
+
+    def run(self):
+        """
+        Main loop. Call this after instantiating WSGIServer. SIGHUP, SIGINT,
+        SIGQUIT, SIGTERM cause it to cleanup and return. (If a SIGHUP
+        is caught, this method returns True. Returns False otherwise.)
+        """
+        self.logger.info('%s starting up', self.__class__.__name__)
+
+        try:
+            sock = self._setupSocket()
+        except socket.error, e:
+            self.logger.error('Failed to bind socket (%s), exiting', e[1])
+            return False
+
+        ret = ThreadedServer.run(self, sock)
+
+        self._cleanupSocket(sock)
+
+        self.logger.info('%s shutting down%s', self.__class__.__name__,
+                         self._hupReceived and ' (reload requested)' or '')
+
+        return ret
+
+def factory(global_conf, host=None, port=None, **local):
+    import paste_factory
+    return paste_factory.helper(WSGIServer, global_conf, host, port, **local)
+
+if __name__ == '__main__':
+    def test_app(environ, start_response):
+        """Probably not the most efficient example."""
+        import cgi
+        start_response('200 OK', [('Content-Type', 'text/html')])
+        yield '<html><head><title>Hello World!</title></head>\n' \
+              '<body>\n' \
+              '<p>Hello World!</p>\n' \
+              '<table border="1">'
+        names = environ.keys()
+        names.sort()
+        for name in names:
+            yield '<tr><td>%s</td><td>%s</td></tr>\n' % (
+                name, cgi.escape(`environ[name]`))
+
+        form = cgi.FieldStorage(fp=environ['wsgi.input'], environ=environ,
+                                keep_blank_values=1)
+        if form.list:
+            yield '<tr><th colspan="2">Form data</th></tr>'
+
+        for field in form.list:
+            yield '<tr><td>%s</td><td>%s</td></tr>\n' % (
+                field.name, field.value)
+
+        yield '</table>\n' \
+              '</body></html>\n'
+
+    from wsgiref import validate
+    test_app = validate.validator(test_app)
+    # Explicitly set bindAddress to *:8009 for testing.
+    WSGIServer(test_app,
+               bindAddress=('', 8009), allowedServers=None,
+               loggingLevel=logging.DEBUG).run()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MoinMoin/support/flup/server/ajp_base.py	Thu Aug 14 00:59:13 2008 +0200
@@ -0,0 +1,956 @@
+# Copyright (c) 2005, 2006 Allan Saddi <allan@saddi.com>
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+#    notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+#    notice, this list of conditions and the following disclaimer in the
+#    documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# $Id$
+
+__author__ = 'Allan Saddi <allan@saddi.com>'
+__version__ = '$Revision$'
+
+import sys
+import socket
+import select
+import struct
+import signal
+import logging
+import errno
+import datetime
+import time
+
+# Unfortunately, for now, threads are required.
+import thread
+import threading
+
+__all__ = ['BaseAJPServer']
+
+class NoDefault(object):
+    pass
+
+# Packet header prefixes.
+SERVER_PREFIX = '\x12\x34'
+CONTAINER_PREFIX = 'AB'
+
+# Server packet types.
+PKTTYPE_FWD_REQ = '\x02'
+PKTTYPE_SHUTDOWN = '\x07'
+PKTTYPE_PING = '\x08'
+PKTTYPE_CPING = '\x0a'
+
+# Container packet types.
+PKTTYPE_SEND_BODY = '\x03'
+PKTTYPE_SEND_HEADERS = '\x04'
+PKTTYPE_END_RESPONSE = '\x05'
+PKTTYPE_GET_BODY = '\x06'
+PKTTYPE_CPONG = '\x09'
+
+# Code tables for methods/headers/attributes.
+methodTable = [
+    None,
+    'OPTIONS',
+    'GET',
+    'HEAD',
+    'POST',
+    'PUT',
+    'DELETE',
+    'TRACE',
+    'PROPFIND',
+    'PROPPATCH',
+    'MKCOL',
+    'COPY',
+    'MOVE',
+    'LOCK',
+    'UNLOCK',
+    'ACL',
+    'REPORT',
+    'VERSION-CONTROL',
+    'CHECKIN',
+    'CHECKOUT',
+    'UNCHECKOUT',
+    'SEARCH',
+    'MKWORKSPACE',
+    'UPDATE',
+    'LABEL',
+    'MERGE',
+    'BASELINE_CONTROL',
+    'MKACTIVITY'
+    ]
+
+requestHeaderTable = [
+    None,
+    'Accept',
+    'Accept-Charset',
+    'Accept-Encoding',
+    'Accept-Language',
+    'Authorization',
+    'Connection',
+    'Content-Type',
+    'Content-Length',
+    'Cookie',
+    'Cookie2',
+    'Host',
+    'Pragma',
+    'Referer',
+    'User-Agent'
+    ]
+
+attributeTable = [
+    None,
+    'CONTEXT',
+    'SERVLET_PATH',
+    'REMOTE_USER',
+    'AUTH_TYPE',
+    'QUERY_STRING',
+    'JVM_ROUTE',
+    'SSL_CERT',
+    'SSL_CIPHER',
+    'SSL_SESSION',
+    None, # name follows
+    'SSL_KEY_SIZE'
+    ]
+
+responseHeaderTable = [
+    None,
+    'content-type',
+    'content-language',
+    'content-length',
+    'date',
+    'last-modified',
+    'location',
+    'set-cookie',
+    'set-cookie2',
+    'servlet-engine',
+    'status',
+    'www-authenticate'
+    ]
+
+# The main classes use this name for logging.
+LoggerName = 'ajp-wsgi'
+
+# Set up module-level logger.
+console = logging.StreamHandler()
+console.setLevel(logging.DEBUG)
+console.setFormatter(logging.Formatter('%(asctime)s : %(message)s',
+                                       '%Y-%m-%d %H:%M:%S'))
+logging.getLogger(LoggerName).addHandler(console)
+del console
+
+class ProtocolError(Exception):
+    """
+    Exception raised when the server does something unexpected or
+    sends garbled data. Usually leads to a Connection closing.
+    """
+    pass
+
+def decodeString(data, pos=0):
+    """Decode a string."""
+    try:
+        length = struct.unpack('>H', data[pos:pos+2])[0]
+        pos += 2
+        if length == 0xffff: # This was undocumented!
+            return '', pos
+        s = data[pos:pos+length]
+        return s, pos+length+1 # Don't forget NUL
+    except Exception, e:
+        raise ProtocolError, 'decodeString: '+str(e)
+
+def decodeRequestHeader(data, pos=0):
+    """Decode a request header/value pair."""
+    try:
+        if data[pos] == '\xa0':
+            # Use table
+            i = ord(data[pos+1])
+            name = requestHeaderTable[i]
+            if name is None:
+                raise ValueError, 'bad request header code'
+            pos += 2
+        else:
+            name, pos = decodeString(data, pos)
+        value, pos = decodeString(data, pos)
+        return name, value, pos
+    except Exception, e:
+        raise ProtocolError, 'decodeRequestHeader: '+str(e)
+
+def decodeAttribute(data, pos=0):
+    """Decode a request attribute."""
+    try:
+        i = ord(data[pos])
+        pos += 1
+        if i == 0xff:
+            # end
+            return None, None, pos
+        elif i == 0x0a:
+            # name follows
+            name, pos = decodeString(data, pos)
+        elif i == 0x0b:
+            # Special handling of SSL_KEY_SIZE.
+            name = attributeTable[i]
+            # Value is an int, not a string.
+            value = struct.unpack('>H', data[pos:pos+2])[0]
+            return name, str(value), pos+2
+        else:
+            name = attributeTable[i]
+            if name is None:
+                raise ValueError, 'bad attribute code'
+        value, pos = decodeString(data, pos)
+        return name, value, pos
+    except Exception, e:
+        raise ProtocolError, 'decodeAttribute: '+str(e)
+
+def encodeString(s):
+    """Encode a string."""
+    return struct.pack('>H', len(s)) + s + '\x00'
+
+def encodeResponseHeader(name, value):
+    """Encode a response header/value pair."""
+    lname = name.lower()
+    if lname in responseHeaderTable:
+        # Use table
+        i = responseHeaderTable.index(lname)
+        out = '\xa0' + chr(i)
+    else:
+        out = encodeString(name)
+    out += encodeString(value)
+    return out
+
+class Packet(object):
+    """An AJP message packet."""
+    def __init__(self):
+        self.data = ''
+        # Don't set this on write, it will be calculated automatically.
+        self.length = 0
+
+    def _recvall(sock, length):
+        """
+        Attempts to receive length bytes from a socket, blocking if necessary.
+        (Socket may be blocking or non-blocking.)
+        """
+        dataList = []
+        recvLen = 0
+        while length:
+            try:
+                data = sock.recv(length)
+            except socket.error, e:
+                if e[0] == errno.EAGAIN:
+                    select.select([sock], [], [])
+                    continue
+                else:
+                    raise
+            if not data: # EOF
+                break
+            dataList.append(data)
+            dataLen = len(data)
+            recvLen += dataLen
+            length -= dataLen
+        return ''.join(dataList), recvLen
+    _recvall = staticmethod(_recvall)
+
+    def read(self, sock):
+        """Attempt to read a packet from the server."""
+        try:
+            header, length = self._recvall(sock, 4)
+        except socket.error:
+            # Treat any sort of socket errors as EOF (close Connection).
+            raise EOFError
+
+        if length < 4:
+            raise EOFError
+
+        if header[:2] != SERVER_PREFIX:
+            raise ProtocolError, 'invalid header'
+
+        self.length = struct.unpack('>H', header[2:4])[0]
+        if self.length:
+            try:
+                self.data, length = self._recvall(sock, self.length)
+            except socket.error:
+                raise EOFError
+
+            if length < self.length:
+                raise EOFError
+
+    def _sendall(sock, data):
+        """
+        Writes data to a socket and does not return until all the data is sent.
+        """
+        length = len(data)
+        while length:
+            try:
+                sent = sock.send(data)
+            except socket.error, e:
+                if e[0] == errno.EAGAIN:
+                    select.select([], [sock], [])
+                    continue
+                else:
+                    raise
+            data = data[sent:]
+            length -= sent
+    _sendall = staticmethod(_sendall)
+
+    def write(self, sock):
+        """Send a packet to the server."""
+        self.length = len(self.data)
+        self._sendall(sock, CONTAINER_PREFIX + struct.pack('>H', self.length))
+        if self.length:
+            self._sendall(sock, self.data)
+
+class InputStream(object):
+    """
+    File-like object that represents the request body (if any). Supports
+    the bare mininum methods required by the WSGI spec. Thanks to
+    StringIO for ideas.
+    """
+    def __init__(self, conn):
+        self._conn = conn
+
+        # See WSGIServer.
+        self._shrinkThreshold = conn.server.inputStreamShrinkThreshold
+
+        self._buf = ''
+        self._bufList = []
+        self._pos = 0 # Current read position.
+        self._avail = 0 # Number of bytes currently available.
+        self._length = 0 # Set to Content-Length in request.
+
+        self.logger = logging.getLogger(LoggerName)
+
+    def bytesAvailForAdd(self):
+        return self._length - self._avail
+
+    def _shrinkBuffer(self):
+        """Gets rid of already read data (since we can't rewind)."""
+        if self._pos >= self._shrinkThreshold:
+            self._buf = self._buf[self._pos:]
+            self._avail -= self._pos
+            self._length -= self._pos
+            self._pos = 0
+
+            assert self._avail >= 0 and self._length >= 0
+
+    def _waitForData(self):
+        toAdd = min(self.bytesAvailForAdd(), 0xffff)
+        assert toAdd > 0
+        pkt = Packet()
+        pkt.data = PKTTYPE_GET_BODY + \
+                   struct.pack('>H', toAdd)
+        self._conn.writePacket(pkt)
+        self._conn.processInput()
+
+    def read(self, n=-1):
+        if self._pos == self._length:
+            return ''
+        while True:
+            if n < 0 or (self._avail - self._pos) < n:
+                # Not enough data available.
+                if not self.bytesAvailForAdd():
+                    # And there's no more coming.
+                    newPos = self._avail
+                    break
+                else:
+                    # Ask for more data and wait.
+                    self._waitForData()
+                    continue
+            else:
+                newPos = self._pos + n
+                break
+        # Merge buffer list, if necessary.
+        if self._bufList:
+            self._buf += ''.join(self._bufList)
+            self._bufList = []
+        r = self._buf[self._pos:newPos]
+        self._pos = newPos
+        self._shrinkBuffer()
+        return r
+
+    def readline(self, length=None):
+        if self._pos == self._length:
+            return ''
+        while True:
+            # Unfortunately, we need to merge the buffer list early.
+            if self._bufList:
+                self._buf += ''.join(self._bufList)
+                self._bufList = []
+            # Find newline.
+            i = self._buf.find('\n', self._pos)
+            if i < 0:
+                # Not found?
+                if not self.bytesAvailForAdd():
+                    # No more data coming.
+                    newPos = self._avail
+                    break
+                else:
+                    if length is not None and len(self._buf) >= length + self._pos:
+                        newPos = self._pos + length
+                        break
+                    # Wait for more to come.
+                    self._waitForData()
+                    continue
+            else:
+                newPos = i + 1
+                break
+        r = self._buf[self._pos:newPos]
+        self._pos = newPos
+        self._shrinkBuffer()
+        return r
+
+    def readlines(self, sizehint=0):
+        total = 0
+        lines = []
+        line = self.readline()
+        while line:
+            lines.append(line)
+            total += len(line)
+            if 0 < sizehint <= total:
+                break
+            line = self.readline()
+        return lines
+
+    def __iter__(self):
+        return self
+
+    def next(self):
+        r = self.readline()
+        if not r:
+            raise StopIteration
+        return r
+
+    def setDataLength(self, length):
+        """
+        Once Content-Length is known, Request calls this method to set it.
+        """
+        self._length = length
+
+    def addData(self, data):
+        """
+        Adds data from the server to this InputStream. Note that we never ask
+        the server for data beyond the Content-Length, so the server should
+        never send us an EOF (empty string argument).
+        """
+        if not data:
+            raise ProtocolError, 'short data'
+        self._bufList.append(data)
+        length = len(data)
+        self._avail += length
+        if self._avail > self._length:
+            raise ProtocolError, 'too much data'
+
+class Request(object):
+    """
+    A Request object. A more fitting name would probably be Transaction, but
+    it's named Request to mirror my FastCGI driver. :) This object
+    encapsulates all the data about the HTTP request and allows the handler
+    to send a response.
+
+    The only attributes/methods that the handler should concern itself
+    with are: environ, input, startResponse(), and write().
+    """
+    # Do not ever change the following value.
+    _maxWrite = 8192 - 4 - 3 - 1 # 8k - pkt header - send body header - NUL
+
+    def __init__(self, conn):
+        self._conn = conn
+
+        self.environ = {}
+        self.input = InputStream(conn)
+
+        self._headersSent = False
+
+        self.logger = logging.getLogger(LoggerName)
+
+    def run(self):
+        self.logger.info('%s %s',
+                         self.environ['REQUEST_METHOD'],
+                         self.environ['REQUEST_URI'])
+
+        start = datetime.datetime.now()
+
+        try:
+            self._conn.server.handler(self)
+        except:
+            self.logger.exception('Exception caught from handler')
+            if not self._headersSent:
+                self._conn.server.error(self)
+
+        end = datetime.datetime.now()
+
+        # Notify server of end of response (reuse flag is set to true).
+        pkt = Packet()
+        pkt.data = PKTTYPE_END_RESPONSE + '\x01'
+        self._conn.writePacket(pkt)
+
+        handlerTime = end - start
+        self.logger.debug('%s %s done (%.3f secs)',
+                          self.environ['REQUEST_METHOD'],
+                          self.environ['REQUEST_URI'],
+                          handlerTime.seconds +
+                          handlerTime.microseconds / 1000000.0)
+
+    # The following methods are called from the Connection to set up this
+    # Request.
+
+    def setMethod(self, value):
+        self.environ['REQUEST_METHOD'] = value
+
+    def setProtocol(self, value):
+        self.environ['SERVER_PROTOCOL'] = value
+
+    def setRequestURI(self, value):
+        self.environ['REQUEST_URI'] = value
+
+    def setRemoteAddr(self, value):
+        self.environ['REMOTE_ADDR'] = value
+
+    def setRemoteHost(self, value):
+        self.environ['REMOTE_HOST'] = value
+
+    def setServerName(self, value):
+        self.environ['SERVER_NAME'] = value
+
+    def setServerPort(self, value):
+        self.environ['SERVER_PORT'] = str(value)
+
+    def setIsSSL(self, value):
+        if value:
+            self.environ['HTTPS'] = 'on'
+
+    def addHeader(self, name, value):
+        name = name.replace('-', '_').upper()
+        if name in ('CONTENT_TYPE', 'CONTENT_LENGTH'):
+            self.environ[name] = value
+            if name == 'CONTENT_LENGTH':
+                length = int(value)
+                self.input.setDataLength(length)
+        else:
+            self.environ['HTTP_'+name] = value
+
+    def addAttribute(self, name, value):
+        self.environ[name] = value
+
+    # The only two methods that should be called from the handler.
+
+    def startResponse(self, statusCode, statusMsg, headers):
+        """
+        Begin the HTTP response. This must only be called once and it
+        must be called before any calls to write().
+
+        statusCode is the integer status code (e.g. 200). statusMsg
+        is the associated reason message (e.g.'OK'). headers is a list
+        of 2-tuples - header name/value pairs. (Both header name and value
+        must be strings.)
+        """
+        assert not self._headersSent, 'Headers already sent!'
+
+        pkt = Packet()
+        pkt.data = PKTTYPE_SEND_HEADERS + \
+                   struct.pack('>H', statusCode) + \
+                   encodeString(statusMsg) + \
+                   struct.pack('>H', len(headers)) + \
+                   ''.join([encodeResponseHeader(name, value)
+                            for name,value in headers])
+
+        self._conn.writePacket(pkt)
+
+        self._headersSent = True
+
+    def write(self, data):
+        """
+        Write data (which comprises the response body). Note that due to
+        restrictions on AJP packet size, we limit our writes to 8185 bytes
+        each packet.
+        """
+        assert self._headersSent, 'Headers must be sent first!'
+
+        bytesLeft = len(data)
+        while bytesLeft:
+            toWrite = min(bytesLeft, self._maxWrite)
+
+            pkt = Packet()
+            pkt.data = PKTTYPE_SEND_BODY + \
+                       struct.pack('>H', toWrite) + \
+                       data[:toWrite] + '\x00' # Undocumented
+            self._conn.writePacket(pkt)
+
+            data = data[toWrite:]
+            bytesLeft -= toWrite
+
+class Connection(object):
+    """
+    A single Connection with the server. Requests are not multiplexed over the
+    same connection, so at any given time, the Connection is either
+    waiting for a request, or processing a single request.
+    """
+    def __init__(self, sock, addr, server):
+        self.server = server
+        self._sock = sock
+        self._addr = addr
+
+        self._request = None
+
+        self.logger = logging.getLogger(LoggerName)
+
+    def run(self):
+        self.logger.debug('Connection starting up (%s:%d)',
+                          self._addr[0], self._addr[1])
+
+        # Main loop. Errors will cause the loop to be exited and
+        # the socket to be closed.
+        while True:
+            try:
+                self.processInput()
+            except ProtocolError, e:
+                self.logger.error("Protocol error '%s'", str(e))
+                break
+            except (EOFError, KeyboardInterrupt):
+                break
+            except:
+                self.logger.exception('Exception caught in Connection')
+                break
+
+        self.logger.debug('Connection shutting down (%s:%d)',
+                          self._addr[0], self._addr[1])
+
+        self._sock.close()
+
+    def processInput(self):
+        """Wait for and process a single packet."""
+        pkt = Packet()
+        select.select([self._sock], [], [])
+        pkt.read(self._sock)
+
+        # Body chunks have no packet type code.
+        if self._request is not None:
+            self._processBody(pkt)
+            return
+
+        if not pkt.length:
+            raise ProtocolError, 'unexpected empty packet'
+
+        pkttype = pkt.data[0]
+        if pkttype == PKTTYPE_FWD_REQ:
+            self._forwardRequest(pkt)
+        elif pkttype == PKTTYPE_SHUTDOWN:
+            self._shutdown(pkt)
+        elif pkttype == PKTTYPE_PING:
+            self._ping(pkt)
+        elif pkttype == PKTTYPE_CPING:
+            self._cping(pkt)
+        else:
+            raise ProtocolError, 'unknown packet type'
+
+    def _forwardRequest(self, pkt):
+        """
+        Creates a Request object, fills it in from the packet, then runs it.
+        """
+        assert self._request is None
+
+        req = self.server.requestClass(self)
+        i = ord(pkt.data[1])
+        method = methodTable[i]
+        if method is None:
+            raise ValueError, 'bad method field'
+        req.setMethod(method)
+        value, pos = decodeString(pkt.data, 2)
+        req.setProtocol(value)
+        value, pos = decodeString(pkt.data, pos)
+        req.setRequestURI(value)
+        value, pos = decodeString(pkt.data, pos)
+        req.setRemoteAddr(value)
+        value, pos = decodeString(pkt.data, pos)
+        req.setRemoteHost(value)
+        value, pos = decodeString(pkt.data, pos)
+        req.setServerName(value)
+        value = struct.unpack('>H', pkt.data[pos:pos+2])[0]
+        req.setServerPort(value)
+        i = ord(pkt.data[pos+2])
+        req.setIsSSL(i != 0)
+
+        # Request headers.
+        numHeaders = struct.unpack('>H', pkt.data[pos+3:pos+5])[0]
+        pos += 5
+        for i in range(numHeaders):
+            name, value, pos = decodeRequestHeader(pkt.data, pos)
+            req.addHeader(name, value)
+
+        # Attributes.
+        while True:
+            name, value, pos = decodeAttribute(pkt.data, pos)
+            if name is None:
+                break
+            req.addAttribute(name, value)
+
+        self._request = req
+
+        # Read first body chunk, if needed.
+        if req.input.bytesAvailForAdd():
+            self.processInput()
+
+        # Run Request.
+        req.run()
+
+        self._request = None
+
+    def _shutdown(self, pkt):
+        """Not sure what to do with this yet."""
+        self.logger.info('Received shutdown request from server')
+
+    def _ping(self, pkt):
+        """I have no idea what this packet means."""
+        self.logger.debug('Received ping')
+
+    def _cping(self, pkt):
+        """Respond to a PING (CPING) packet."""
+        self.logger.debug('Received PING, sending PONG')
+        pkt = Packet()
+        pkt.data = PKTTYPE_CPONG
+        self.writePacket(pkt)
+
+    def _processBody(self, pkt):
+        """
+        Handles a body chunk from the server by appending it to the
+        InputStream.
+        """
+        if pkt.length:
+            length = struct.unpack('>H', pkt.data[:2])[0]
+            self._request.input.addData(pkt.data[2:2+length])
+        else:
+            # Shouldn't really ever get here.
+            self._request.input.addData('')
+
+    def writePacket(self, pkt):
+        """Sends a Packet to the server."""
+        pkt.write(self._sock)
+
+class BaseAJPServer(object):
+    # What Request class to use.
+    requestClass = Request
+
+    # Limits the size of the InputStream's string buffer to this size + 8k.
+    # Since the InputStream is not seekable, we throw away already-read
+    # data once this certain amount has been read. (The 8k is there because
+    # it is the maximum size of new data added per chunk.)
+    inputStreamShrinkThreshold = 102400 - 8192
+
+    def __init__(self, application, scriptName='', environ=None,
+                 multithreaded=True, multiprocess=False,
+                 bindAddress=('localhost', 8009), allowedServers=NoDefault,
+                 loggingLevel=logging.INFO, debug=True):
+        """
+        scriptName is the initial portion of the URL path that "belongs"
+        to your application. It is used to determine PATH_INFO (which doesn't
+        seem to be passed in). An empty scriptName means your application
+        is mounted at the root of your virtual host.
+
+        environ, which must be a dictionary, can contain any additional
+        environment variables you want to pass to your application.
+
+        Set multithreaded to False if your application is not thread-safe.
+
+        Set multiprocess to True to explicitly set wsgi.multiprocess to
+        True. (Only makes sense with threaded servers.)
+
+        bindAddress is the address to bind to, which must be a tuple of
+        length 2. The first element is a string, which is the host name
+        or IPv4 address of a local interface. The 2nd element is the port
+        number.
+
+        allowedServers must be None or a list of strings representing the
+        IPv4 addresses of servers allowed to connect. None means accept
+        connections from anywhere. By default, it is a list containing
+        the single item '127.0.0.1'.
+
+        loggingLevel sets the logging level of the module-level logger.
+        """
+        if environ is None:
+            environ = {}
+
+        self.application = application
+        self.scriptName = scriptName
+        self.environ = environ
+        self.multithreaded = multithreaded
+        self.multiprocess = multiprocess
+        self.debug = debug
+        self._bindAddress = bindAddress
+        if allowedServers is NoDefault:
+            allowedServers = ['127.0.0.1']
+        self._allowedServers = allowedServers
+
+        # Used to force single-threadedness.
+        self._appLock = thread.allocate_lock()
+
+        self.logger = logging.getLogger(LoggerName)
+        self.logger.setLevel(loggingLevel)
+
+    def _setupSocket(self):
+        """Creates and binds the socket for communication with the server."""
+        sock = socket.socket()
+        sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+        sock.bind(self._bindAddress)
+        sock.listen(socket.SOMAXCONN)
+        return sock
+
+    def _cleanupSocket(self, sock):
+        """Closes the main socket."""
+        sock.close()
+
+    def _isClientAllowed(self, addr):
+        ret = self._allowedServers is None or addr[0] in self._allowedServers
+        if not ret:
+            self.logger.warning('Server connection from %s disallowed',
+                                addr[0])
+        return ret
+
+    def handler(self, request):
+        """
+        WSGI handler. Sets up WSGI environment, calls the application,
+        and sends the application's response.
+        """
+        environ = request.environ
+        environ.update(self.environ)
+
+        environ['wsgi.version'] = (1,0)
+        environ['wsgi.input'] = request.input
+        environ['wsgi.errors'] = sys.stderr
+        environ['wsgi.multithread'] = self.multithreaded
+        environ['wsgi.multiprocess'] = self.multiprocess
+        environ['wsgi.run_once'] = False
+
+        if environ.get('HTTPS', 'off') in ('on', '1'):
+            environ['wsgi.url_scheme'] = 'https'
+        else:
+            environ['wsgi.url_scheme'] = 'http'
+
+        self._sanitizeEnv(environ)
+
+        headers_set = []
+        headers_sent = []
+        result = None
+
+        def write(data):
+            assert type(data) is str, 'write() argument must be string'
+            assert headers_set, 'write() before start_response()'
+
+            if not headers_sent:
+                status, responseHeaders = headers_sent[:] = headers_set
+                statusCode = int(status[:3])
+                statusMsg = status[4:]
+                found = False
+                for header,value in responseHeaders:
+                    if header.lower() == 'content-length':
+                        found = True
+                        break
+                if not found and result is not None:
+                    try:
+                        if len(result) == 1:
+                            responseHeaders.append(('Content-Length',
+                                                    str(len(data))))
+                    except:
+                        pass
+                request.startResponse(statusCode, statusMsg, responseHeaders)
+
+            request.write(data)
+
+        def start_response(status, response_headers, exc_info=None):
+            if exc_info:
+                try:
+                    if headers_sent:
+                        # Re-raise if too late
+                        raise exc_info[0], exc_info[1], exc_info[2]
+                finally:
+                    exc_info = None # avoid dangling circular ref
+            else:
+                assert not headers_set, 'Headers already set!'
+
+            assert type(status) is str, 'Status must be a string'
+            assert len(status) >= 4, 'Status must be at least 4 characters'
+            assert int(status[:3]), 'Status must begin with 3-digit code'
+            assert status[3] == ' ', 'Status must have a space after code'
+            assert type(response_headers) is list, 'Headers must be a list'
+            if __debug__:
+                for name,val in response_headers:
+                    assert type(name) is str, 'Header names must be strings'
+                    assert type(val) is str, 'Header values must be strings'
+
+            headers_set[:] = [status, response_headers]
+            return write
+
+        if not self.multithreaded:
+            self._appLock.acquire()
+        try:
+            try:
+                result = self.application(environ, start_response)
+                try:
+                    for data in result:
+                        if data:
+                            write(data)
+                    if not headers_sent:
+                        write('') # in case body was empty
+                finally:
+                    if hasattr(result, 'close'):
+                        result.close()
+            except socket.error, e:
+                if e[0] != errno.EPIPE:
+                    raise # Don't let EPIPE propagate beyond server
+        finally:
+            if not self.multithreaded:
+                self._appLock.release()
+
+    def _sanitizeEnv(self, environ):
+        """Fill-in/deduce missing values in environ."""
+        # Namely SCRIPT_NAME/PATH_INFO
+        value = environ['REQUEST_URI']
+        scriptName = environ.get('WSGI_SCRIPT_NAME', self.scriptName)
+        if not value.startswith(scriptName):
+            self.logger.warning('scriptName does not match request URI')
+
+        environ['PATH_INFO'] = value[len(scriptName):]
+        environ['SCRIPT_NAME'] = scriptName
+
+        reqUri = None
+        if environ.has_key('REQUEST_URI'):
+            reqUri = environ['REQUEST_URI'].split('?', 1)
+
+        if not environ.has_key('QUERY_STRING') or not environ['QUERY_STRING']:
+            if reqUri is not None and len(reqUri) > 1:
+                environ['QUERY_STRING'] = reqUri[1]
+            else:
+                environ['QUERY_STRING'] = ''
+
+    def error(self, request):
+        """
+        Override to provide custom error handling. Ideally, however,
+        all errors should be caught at the application level.
+        """
+        if self.debug:
+            request.startResponse(200, 'OK', [('Content-Type', 'text/html')])
+            import cgitb
+            request.write(cgitb.html(sys.exc_info()))
+        else:
+            errorpage = """<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
+<html><head>
+<title>Unhandled Exception</title>
+</head><body>
+<h1>Unhandled Exception</h1>
+<p>An unhandled exception was thrown by the application.</p>
+</body></html>
+"""
+            request.startResponse(200, 'OK', [('Content-Type', 'text/html')])
+            request.write(errorpage)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MoinMoin/support/flup/server/ajp_fork.py	Thu Aug 14 00:59:13 2008 +0200
@@ -0,0 +1,199 @@
+# Copyright (c) 2005, 2006 Allan Saddi <allan@saddi.com>
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+#    notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+#    notice, this list of conditions and the following disclaimer in the
+#    documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# $Id$
+
+"""
+ajp - an AJP 1.3/WSGI gateway.
+
+For more information about AJP and AJP connectors for your web server, see
+<http://jakarta.apache.org/tomcat/connectors-doc/>.
+
+For more information about the Web Server Gateway Interface, see
+<http://www.python.org/peps/pep-0333.html>.
+
+Example usage:
+
+  #!/usr/bin/env python
+  import sys
+  from myapplication import app # Assume app is your WSGI application object
+  from ajp import WSGIServer
+  ret = WSGIServer(app).run()
+  sys.exit(ret and 42 or 0)
+
+See the documentation for WSGIServer for more information.
+
+About the bit of logic at the end:
+Upon receiving SIGHUP, the python script will exit with status code 42. This
+can be used by a wrapper script to determine if the python script should be
+re-run. When a SIGINT or SIGTERM is received, the script exits with status
+code 0, possibly indicating a normal exit.
+
+Example wrapper script:
+
+  #!/bin/sh
+  STATUS=42
+  while test $STATUS -eq 42; do
+    python "$@" that_script_above.py
+    STATUS=$?
+  done
+
+Example workers.properties (for mod_jk):
+
+  worker.list=foo
+  worker.foo.port=8009
+  worker.foo.host=localhost
+  worker.foo.type=ajp13
+
+Example httpd.conf (for mod_jk):
+
+  JkWorkersFile /path/to/workers.properties
+  JkMount /* foo
+
+Note that if you mount your ajp application anywhere but the root ("/"), you
+SHOULD specifiy scriptName to the WSGIServer constructor. This will ensure
+that SCRIPT_NAME/PATH_INFO are correctly deduced.
+"""
+
+__author__ = 'Allan Saddi <allan@saddi.com>'
+__version__ = '$Revision$'
+
+import socket
+import logging
+
+from flup.server.ajp_base import BaseAJPServer, Connection
+from flup.server.preforkserver import PreforkServer
+
+__all__ = ['WSGIServer']
+
+class WSGIServer(BaseAJPServer, PreforkServer):
+    """
+    AJP1.3/WSGI server. Runs your WSGI application as a persistant program
+    that understands AJP1.3. Opens up a TCP socket, binds it, and then
+    waits for forwarded requests from your webserver.
+
+    Why AJP? Two good reasons are that AJP provides load-balancing and
+    fail-over support. Personally, I just wanted something new to
+    implement. :)
+
+    Of course you will need an AJP1.3 connector for your webserver (e.g.
+    mod_jk) - see <http://jakarta.apache.org/tomcat/connectors-doc/>.
+    """
+    def __init__(self, application, scriptName='', environ=None,
+                 bindAddress=('localhost', 8009), allowedServers=None,
+                 loggingLevel=logging.INFO, debug=True, **kw):
+        """
+        scriptName is the initial portion of the URL path that "belongs"
+        to your application. It is used to determine PATH_INFO (which doesn't
+        seem to be passed in). An empty scriptName means your application
+        is mounted at the root of your virtual host.
+
+        environ, which must be a dictionary, can contain any additional
+        environment variables you want to pass to your application.
+
+        bindAddress is the address to bind to, which must be a tuple of
+        length 2. The first element is a string, which is the host name
+        or IPv4 address of a local interface. The 2nd element is the port
+        number.
+
+        allowedServers must be None or a list of strings representing the
+        IPv4 addresses of servers allowed to connect. None means accept
+        connections from anywhere.
+
+        loggingLevel sets the logging level of the module-level logger.
+        """
+        BaseAJPServer.__init__(self, application,
+                               scriptName=scriptName,
+                               environ=environ,
+                               multithreaded=False,
+                               multiprocess=True,
+                               bindAddress=bindAddress,
+                               allowedServers=allowedServers,
+                               loggingLevel=loggingLevel,
+                               debug=debug)
+        for key in ('multithreaded', 'multiprocess', 'jobClass', 'jobArgs'):
+            if kw.has_key(key):
+                del kw[key]
+        PreforkServer.__init__(self, jobClass=Connection, jobArgs=(self,), **kw)
+
+    def run(self):
+        """
+        Main loop. Call this after instantiating WSGIServer. SIGHUP, SIGINT,
+        SIGQUIT, SIGTERM cause it to cleanup and return. (If a SIGHUP
+        is caught, this method returns True. Returns False otherwise.)
+        """
+        self.logger.info('%s starting up', self.__class__.__name__)
+
+        try:
+            sock = self._setupSocket()
+        except socket.error, e:
+            self.logger.error('Failed to bind socket (%s), exiting', e[1])
+            return False
+
+        ret = PreforkServer.run(self, sock)
+
+        self._cleanupSocket(sock)
+
+        self.logger.info('%s shutting down%s', self.__class__.__name__,
+                         self._hupReceived and ' (reload requested)' or '')
+
+        return ret
+
+def factory(global_conf, host=None, port=None, **local):
+    import paste_factory
+    return paste_factory.helper(WSGIServer, global_conf, host, port, **local)
+
+if __name__ == '__main__':
+    def test_app(environ, start_response):
+        """Probably not the most efficient example."""
+        import cgi
+        start_response('200 OK', [('Content-Type', 'text/html')])
+        yield '<html><head><title>Hello World!</title></head>\n' \
+              '<body>\n' \
+              '<p>Hello World!</p>\n' \
+              '<table border="1">'
+        names = environ.keys()
+        names.sort()
+        for name in names:
+            yield '<tr><td>%s</td><td>%s</td></tr>\n' % (
+                name, cgi.escape(`environ[name]`))
+
+        form = cgi.FieldStorage(fp=environ['wsgi.input'], environ=environ,
+                                keep_blank_values=1)
+        if form.list:
+            yield '<tr><th colspan="2">Form data</th></tr>'
+
+        for field in form.list:
+            yield '<tr><td>%s</td><td>%s</td></tr>\n' % (
+                field.name, field.value)
+
+        yield '</table>\n' \
+              '</body></html>\n'
+
+    from wsgiref import validate
+    test_app = validate.validator(test_app)
+    # Explicitly set bindAddress to *:8009 for testing.
+    WSGIServer(test_app,
+               bindAddress=('', 8009), allowedServers=None,
+               loggingLevel=logging.DEBUG).run()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MoinMoin/support/flup/server/cgi.py	Thu Aug 14 00:59:13 2008 +0200
@@ -0,0 +1,71 @@
+# Taken from <http://www.python.org/dev/peps/pep-0333/>
+# which was placed in the public domain.
+
+import os, sys
+
+
+__all__ = ['WSGIServer']
+
+
+class WSGIServer(object):
+
+    def __init__(self, application):
+        self.application = application
+
+    def run(self):
+
+        environ = dict(os.environ.items())
+        environ['wsgi.input']        = sys.stdin
+        environ['wsgi.errors']       = sys.stderr
+        environ['wsgi.version']      = (1,0)
+        environ['wsgi.multithread']  = False
+        environ['wsgi.multiprocess'] = True
+        environ['wsgi.run_once']     = True
+
+        if environ.get('HTTPS','off') in ('on','1'):
+            environ['wsgi.url_scheme'] = 'https'
+        else:
+            environ['wsgi.url_scheme'] = 'http'
+
+        headers_set = []
+        headers_sent = []
+
+        def write(data):
+            if not headers_set:
+                 raise AssertionError("write() before start_response()")
+
+            elif not headers_sent:
+                 # Before the first output, send the stored headers
+                 status, response_headers = headers_sent[:] = headers_set
+                 sys.stdout.write('Status: %s\r\n' % status)
+                 for header in response_headers:
+                     sys.stdout.write('%s: %s\r\n' % header)
+                 sys.stdout.write('\r\n')
+
+            sys.stdout.write(data)
+            sys.stdout.flush()
+
+        def start_response(status,response_headers,exc_info=None):
+            if exc_info:
+                try:
+                    if headers_sent:
+                        # Re-raise original exception if headers sent
+                        raise exc_info[0], exc_info[1], exc_info[2]
+                finally:
+                    exc_info = None     # avoid dangling circular ref
+            elif headers_set:
+                raise AssertionError("Headers already set!")
+
+            headers_set[:] = [status,response_headers]
+            return write
+
+        result = self.application(environ, start_response)
+        try:
+            for data in result:
+                if data:    # don't send headers until body appears
+                    write(data)
+            if not headers_sent:
+                write('')   # send headers now if body was empty
+        finally:
+            if hasattr(result,'close'):
+                result.close()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MoinMoin/support/flup/server/fcgi.py	Thu Aug 14 00:59:13 2008 +0200
@@ -0,0 +1,152 @@
+# Copyright (c) 2005, 2006 Allan Saddi <allan@saddi.com>
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+#    notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+#    notice, this list of conditions and the following disclaimer in the
+#    documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# $Id$
+
+"""
+fcgi - a FastCGI/WSGI gateway.
+
+For more information about FastCGI, see <http://www.fastcgi.com/>.
+
+For more information about the Web Server Gateway Interface, see
+<http://www.python.org/peps/pep-0333.html>.
+
+Example usage:
+
+  #!/usr/bin/env python
+  from myapplication import app # Assume app is your WSGI application object
+  from fcgi import WSGIServer
+  WSGIServer(app).run()
+
+See the documentation for WSGIServer for more information.
+
+On most platforms, fcgi will fallback to regular CGI behavior if run in a
+non-FastCGI context. If you want to force CGI behavior, set the environment
+variable FCGI_FORCE_CGI to "Y" or "y".
+"""
+
+__author__ = 'Allan Saddi <allan@saddi.com>'
+__version__ = '$Revision$'
+
+import os
+
+from flup.server.fcgi_base import BaseFCGIServer, FCGI_RESPONDER
+from flup.server.threadedserver import ThreadedServer
+
+__all__ = ['WSGIServer']
+
+class WSGIServer(BaseFCGIServer, ThreadedServer):
+    """
+    FastCGI server that supports the Web Server Gateway Interface. See
+    <http://www.python.org/peps/pep-0333.html>.
+    """
+    def __init__(self, application, environ=None,
+                 multithreaded=True, multiprocess=False,
+                 bindAddress=None, umask=None, multiplexed=False,
+                 debug=True, roles=(FCGI_RESPONDER,), **kw):
+        """
+        environ, if present, must be a dictionary-like object. Its
+        contents will be copied into application's environ. Useful
+        for passing application-specific variables.
+
+        bindAddress, if present, must either be a string or a 2-tuple. If
+        present, run() will open its own listening socket. You would use
+        this if you wanted to run your application as an 'external' FastCGI
+        app. (i.e. the webserver would no longer be responsible for starting
+        your app) If a string, it will be interpreted as a filename and a UNIX
+        socket will be opened. If a tuple, the first element, a string,
+        is the interface name/IP to bind to, and the second element (an int)
+        is the port number.
+        """
+        BaseFCGIServer.__init__(self, application,
+                                environ=environ,
+                                multithreaded=multithreaded,
+                                multiprocess=multiprocess,
+                                bindAddress=bindAddress,
+                                umask=umask,
+                                multiplexed=multiplexed,
+                                debug=debug,
+                                roles=roles)
+        for key in ('jobClass', 'jobArgs'):
+            if kw.has_key(key):
+                del kw[key]
+        ThreadedServer.__init__(self, jobClass=self._connectionClass,
+                                jobArgs=(self,), **kw)
+
+    def _isClientAllowed(self, addr):
+        return self._web_server_addrs is None or \
+               (len(addr) == 2 and addr[0] in self._web_server_addrs)
+
+    def run(self):
+        """
+        The main loop. Exits on SIGHUP, SIGINT, SIGTERM. Returns True if
+        SIGHUP was received, False otherwise.
+        """
+        self._web_server_addrs = os.environ.get('FCGI_WEB_SERVER_ADDRS')
+        if self._web_server_addrs is not None:
+            self._web_server_addrs = map(lambda x: x.strip(),
+                                         self._web_server_addrs.split(','))
+
+        sock = self._setupSocket()
+
+        ret = ThreadedServer.run(self, sock)
+
+        self._cleanupSocket(sock)
+
+        return ret
+
+def factory(global_conf, host=None, port=None, **local):
+    import paste_factory
+    return paste_factory.helper(WSGIServer, global_conf, host, port, **local)
+
+if __name__ == '__main__':
+    def test_app(environ, start_response):
+        """Probably not the most efficient example."""
+        import cgi
+        start_response('200 OK', [('Content-Type', 'text/html')])
+        yield '<html><head><title>Hello World!</title></head>\n' \
+              '<body>\n' \
+              '<p>Hello World!</p>\n' \
+              '<table border="1">'
+        names = environ.keys()
+        names.sort()
+        for name in names:
+            yield '<tr><td>%s</td><td>%s</td></tr>\n' % (
+                name, cgi.escape(`environ[name]`))
+
+        form = cgi.FieldStorage(fp=environ['wsgi.input'], environ=environ,
+                                keep_blank_values=1)
+        if form.list:
+            yield '<tr><th colspan="2">Form data</th></tr>'
+
+        for field in form.list:
+            yield '<tr><td>%s</td><td>%s</td></tr>\n' % (
+                field.name, field.value)
+
+        yield '</table>\n' \
+              '</body></html>\n'
+
+    from wsgiref import validate
+    test_app = validate.validator(test_app)
+    WSGIServer(test_app).run()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MoinMoin/support/flup/server/fcgi_base.py	Thu Aug 14 00:59:13 2008 +0200
@@ -0,0 +1,1186 @@
+# Copyright (c) 2002, 2003, 2005, 2006 Allan Saddi <allan@saddi.com>
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+#    notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+#    notice, this list of conditions and the following disclaimer in the
+#    documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# $Id$
+
+__author__ = 'Allan Saddi <allan@saddi.com>'
+__version__ = '$Revision$'
+
+import sys
+import os
+import signal
+import struct
+import cStringIO as StringIO
+import select
+import socket
+import errno
+import traceback
+
+try:
+    import thread
+    import threading
+    thread_available = True
+except ImportError:
+    import dummy_thread as thread
+    import dummy_threading as threading
+    thread_available = False
+
+# Apparently 2.3 doesn't define SHUT_WR? Assume it is 1 in this case.
+if not hasattr(socket, 'SHUT_WR'):
+    socket.SHUT_WR = 1
+
+__all__ = ['BaseFCGIServer']
+
+# Constants from the spec.
+FCGI_LISTENSOCK_FILENO = 0
+
+FCGI_HEADER_LEN = 8
+
+FCGI_VERSION_1 = 1
+
+FCGI_BEGIN_REQUEST = 1
+FCGI_ABORT_REQUEST = 2
+FCGI_END_REQUEST = 3
+FCGI_PARAMS = 4
+FCGI_STDIN = 5
+FCGI_STDOUT = 6
+FCGI_STDERR = 7
+FCGI_DATA = 8
+FCGI_GET_VALUES = 9
+FCGI_GET_VALUES_RESULT = 10
+FCGI_UNKNOWN_TYPE = 11
+FCGI_MAXTYPE = FCGI_UNKNOWN_TYPE
+
+FCGI_NULL_REQUEST_ID = 0
+
+FCGI_KEEP_CONN = 1
+
+FCGI_RESPONDER = 1
+FCGI_AUTHORIZER = 2
+FCGI_FILTER = 3
+
+FCGI_REQUEST_COMPLETE = 0
+FCGI_CANT_MPX_CONN = 1
+FCGI_OVERLOADED = 2
+FCGI_UNKNOWN_ROLE = 3
+
+FCGI_MAX_CONNS = 'FCGI_MAX_CONNS'
+FCGI_MAX_REQS = 'FCGI_MAX_REQS'
+FCGI_MPXS_CONNS = 'FCGI_MPXS_CONNS'
+
+FCGI_Header = '!BBHHBx'
+FCGI_BeginRequestBody = '!HB5x'
+FCGI_EndRequestBody = '!LB3x'
+FCGI_UnknownTypeBody = '!B7x'
+
+FCGI_EndRequestBody_LEN = struct.calcsize(FCGI_EndRequestBody)
+FCGI_UnknownTypeBody_LEN = struct.calcsize(FCGI_UnknownTypeBody)
+
+if __debug__:
+    import time
+
+    # Set non-zero to write debug output to a file.
+    DEBUG = 0
+    DEBUGLOG = '/tmp/fcgi.log'
+
+    def _debug(level, msg):
+        if DEBUG < level:
+            return
+
+        try:
+            f = open(DEBUGLOG, 'a')
+            f.write('%sfcgi: %s\n' % (time.ctime()[4:-4], msg))
+            f.close()
+        except:
+            pass
+
+class InputStream(object):
+    """
+    File-like object representing FastCGI input streams (FCGI_STDIN and
+    FCGI_DATA). Supports the minimum methods required by WSGI spec.
+    """
+    def __init__(self, conn):
+        self._conn = conn
+
+        # See Server.
+        self._shrinkThreshold = conn.server.inputStreamShrinkThreshold
+
+        self._buf = ''
+        self._bufList = []
+        self._pos = 0 # Current read position.
+        self._avail = 0 # Number of bytes currently available.
+
+        self._eof = False # True when server has sent EOF notification.
+
+    def _shrinkBuffer(self):
+        """Gets rid of already read data (since we can't rewind)."""
+        if self._pos >= self._shrinkThreshold:
+            self._buf = self._buf[self._pos:]
+            self._avail -= self._pos
+            self._pos = 0
+
+            assert self._avail >= 0
+
+    def _waitForData(self):
+        """Waits for more data to become available."""
+        self._conn.process_input()
+
+    def read(self, n=-1):
+        if self._pos == self._avail and self._eof:
+            return ''
+        while True:
+            if n < 0 or (self._avail - self._pos) < n:
+                # Not enough data available.
+                if self._eof:
+                    # And there's no more coming.
+                    newPos = self._avail
+                    break
+                else:
+                    # Wait for more data.
+                    self._waitForData()
+                    continue
+            else:
+                newPos = self._pos + n
+                break
+        # Merge buffer list, if necessary.
+        if self._bufList:
+            self._buf += ''.join(self._bufList)
+            self._bufList = []
+        r = self._buf[self._pos:newPos]
+        self._pos = newPos
+        self._shrinkBuffer()
+        return r
+
+    def readline(self, length=None):
+        if self._pos == self._avail and self._eof:
+            return ''
+        while True:
+            # Unfortunately, we need to merge the buffer list early.
+            if self._bufList:
+                self._buf += ''.join(self._bufList)
+                self._bufList = []
+            # Find newline.
+            i = self._buf.find('\n', self._pos)
+            if i < 0:
+                # Not found?
+                if self._eof:
+                    # No more data coming.
+                    newPos = self._avail
+                    break
+                else:
+                    if length is not None and len(self._buf) >= length + self._pos:
+                        newPos = self._pos + length
+                        break
+                    # Wait for more to come.
+                    self._waitForData()
+                    continue
+            else:
+                newPos = i + 1
+                break
+        r = self._buf[self._pos:newPos]
+        self._pos = newPos
+        self._shrinkBuffer()
+        return r
+
+    def readlines(self, sizehint=0):
+        total = 0
+        lines = []
+        line = self.readline()
+        while line:
+            lines.append(line)
+            total += len(line)
+            if 0 < sizehint <= total:
+                break
+            line = self.readline()
+        return lines
+
+    def __iter__(self):
+        return self
+
+    def next(self):
+        r = self.readline()
+        if not r:
+            raise StopIteration
+        return r
+
+    def add_data(self, data):
+        if not data:
+            self._eof = True
+        else:
+            self._bufList.append(data)
+            self._avail += len(data)
+
+class MultiplexedInputStream(InputStream):
+    """
+    A version of InputStream meant to be used with MultiplexedConnections.
+    Assumes the MultiplexedConnection (the producer) and the Request
+    (the consumer) are running in different threads.
+    """
+    def __init__(self, conn):
+        super(MultiplexedInputStream, self).__init__(conn)
+
+        # Arbitrates access to this InputStream (it's used simultaneously
+        # by a Request and its owning Connection object).
+        lock = threading.RLock()
+
+        # Notifies Request thread that there is new data available.
+        self._lock = threading.Condition(lock)
+
+    def _waitForData(self):
+        # Wait for notification from add_data().
+        self._lock.wait()
+
+    def read(self, n=-1):
+        self._lock.acquire()
+        try:
+            return super(MultiplexedInputStream, self).read(n)
+        finally:
+            self._lock.release()
+
+    def readline(self, length=None):
+        self._lock.acquire()
+        try:
+            return super(MultiplexedInputStream, self).readline(length)
+        finally:
+            self._lock.release()
+
+    def add_data(self, data):
+        self._lock.acquire()
+        try:
+            super(MultiplexedInputStream, self).add_data(data)
+            self._lock.notify()
+        finally:
+            self._lock.release()
+
+class OutputStream(object):
+    """
+    FastCGI output stream (FCGI_STDOUT/FCGI_STDERR). By default, calls to
+    write() or writelines() immediately result in Records being sent back
+    to the server. Buffering should be done in a higher level!
+    """
+    def __init__(self, conn, req, type, buffered=False):
+        self._conn = conn
+        self._req = req
+        self._type = type
+        self._buffered = buffered
+        self._bufList = [] # Used if buffered is True
+        self.dataWritten = False
+        self.closed = False
+
+    def _write(self, data):
+        length = len(data)
+        while length:
+            toWrite = min(length, self._req.server.maxwrite - FCGI_HEADER_LEN)
+
+            rec = Record(self._type, self._req.requestId)
+            rec.contentLength = toWrite
+            rec.contentData = data[:toWrite]
+            self._conn.writeRecord(rec)
+
+            data = data[toWrite:]
+            length -= toWrite
+
+    def write(self, data):
+        assert not self.closed
+
+        if not data:
+            return
+
+        self.dataWritten = True
+
+        if self._buffered:
+            self._bufList.append(data)
+        else:
+            self._write(data)
+
+    def writelines(self, lines):
+        assert not self.closed
+
+        for line in lines:
+            self.write(line)
+
+    def flush(self):
+        # Only need to flush if this OutputStream is actually buffered.
+        if self._buffered:
+            data = ''.join(self._bufList)
+            self._bufList = []
+            self._write(data)
+
+    # Though available, the following should NOT be called by WSGI apps.
+    def close(self):
+        """Sends end-of-stream notification, if necessary."""
+        if not self.closed and self.dataWritten:
+            self.flush()
+            rec = Record(self._type, self._req.requestId)
+            self._conn.writeRecord(rec)
+            self.closed = True
+
+class TeeOutputStream(object):
+    """
+    Simple wrapper around two or more output file-like objects that copies
+    written data to all streams.
+    """
+    def __init__(self, streamList):
+        self._streamList = streamList
+
+    def write(self, data):
+        for f in self._streamList:
+            f.write(data)
+
+    def writelines(self, lines):
+        for line in lines:
+            self.write(line)
+
+    def flush(self):
+        for f in self._streamList:
+            f.flush()
+
+class StdoutWrapper(object):
+    """
+    Wrapper for sys.stdout so we know if data has actually been written.
+    """
+    def __init__(self, stdout):
+        self._file = stdout
+        self.dataWritten = False
+
+    def write(self, data):
+        if data:
+            self.dataWritten = True
+        self._file.write(data)
+
+    def writelines(self, lines):
+        for line in lines:
+            self.write(line)
+
+    def __getattr__(self, name):
+        return getattr(self._file, name)
+
+def decode_pair(s, pos=0):
+    """
+    Decodes a name/value pair.
+
+    The number of bytes decoded as well as the name/value pair
+    are returned.
+    """
+    nameLength = ord(s[pos])
+    if nameLength & 128:
+        nameLength = struct.unpack('!L', s[pos:pos+4])[0] & 0x7fffffff
+        pos += 4
+    else:
+        pos += 1
+
+    valueLength = ord(s[pos])
+    if valueLength & 128:
+        valueLength = struct.unpack('!L', s[pos:pos+4])[0] & 0x7fffffff
+        pos += 4
+    else:
+        pos += 1
+
+    name = s[pos:pos+nameLength]
+    pos += nameLength
+    value = s[pos:pos+valueLength]
+    pos += valueLength
+
+    return (pos, (name, value))
+
+def encode_pair(name, value):
+    """
+    Encodes a name/value pair.
+
+    The encoded string is returned.
+    """
+    nameLength = len(name)
+    if nameLength < 128:
+        s = chr(nameLength)
+    else:
+        s = struct.pack('!L', nameLength | 0x80000000L)
+
+    valueLength = len(value)
+    if valueLength < 128:
+        s += chr(valueLength)
+    else:
+        s += struct.pack('!L', valueLength | 0x80000000L)
+
+    return s + name + value
+    
+class Record(object):
+    """
+    A FastCGI Record.
+
+    Used for encoding/decoding records.
+    """
+    def __init__(self, type=FCGI_UNKNOWN_TYPE, requestId=FCGI_NULL_REQUEST_ID):
+        self.version = FCGI_VERSION_1
+        self.type = type
+        self.requestId = requestId
+        self.contentLength = 0
+        self.paddingLength = 0
+        self.contentData = ''
+
+    def _recvall(sock, length):
+        """
+        Attempts to receive length bytes from a socket, blocking if necessary.
+        (Socket may be blocking or non-blocking.)
+        """
+        dataList = []
+        recvLen = 0
+        while length:
+            try:
+                data = sock.recv(length)
+            except socket.error, e:
+                if e[0] == errno.EAGAIN:
+                    select.select([sock], [], [])
+                    continue
+                else:
+                    raise
+            if not data: # EOF
+                break
+            dataList.append(data)
+            dataLen = len(data)
+            recvLen += dataLen
+            length -= dataLen
+        return ''.join(dataList), recvLen
+    _recvall = staticmethod(_recvall)
+
+    def read(self, sock):
+        """Read and decode a Record from a socket."""
+        try:
+            header, length = self._recvall(sock, FCGI_HEADER_LEN)
+        except:
+            raise EOFError
+
+        if length < FCGI_HEADER_LEN:
+            raise EOFError
+        
+        self.version, self.type, self.requestId, self.contentLength, \
+                      self.paddingLength = struct.unpack(FCGI_Header, header)
+
+        if __debug__: _debug(9, 'read: fd = %d, type = %d, requestId = %d, '
+                             'contentLength = %d' %
+                             (sock.fileno(), self.type, self.requestId,
+                              self.contentLength))
+        
+        if self.contentLength:
+            try:
+                self.contentData, length = self._recvall(sock,
+                                                         self.contentLength)
+            except:
+                raise EOFError
+
+            if length < self.contentLength:
+                raise EOFError
+
+        if self.paddingLength:
+            try:
+                self._recvall(sock, self.paddingLength)
+            except:
+                raise EOFError
+
+    def _sendall(sock, data):
+        """
+        Writes data to a socket and does not return until all the data is sent.
+        """
+        length = len(data)
+        while length:
+            try:
+                sent = sock.send(data)
+            except socket.error, e:
+                if e[0] == errno.EAGAIN:
+                    select.select([], [sock], [])
+                    continue
+                else:
+                    raise
+            data = data[sent:]
+            length -= sent
+    _sendall = staticmethod(_sendall)
+
+    def write(self, sock):
+        """Encode and write a Record to a socket."""
+        self.paddingLength = -self.contentLength & 7
+
+        if __debug__: _debug(9, 'write: fd = %d, type = %d, requestId = %d, '
+                             'contentLength = %d' %
+                             (sock.fileno(), self.type, self.requestId,
+                              self.contentLength))
+
+        header = struct.pack(FCGI_Header, self.version, self.type,
+                             self.requestId, self.contentLength,
+                             self.paddingLength)
+        self._sendall(sock, header)
+        if self.contentLength:
+            self._sendall(sock, self.contentData)
+        if self.paddingLength:
+            self._sendall(sock, '\x00'*self.paddingLength)
+            
+class Request(object):
+    """
+    Represents a single FastCGI request.
+
+    These objects are passed to your handler and is the main interface
+    between your handler and the fcgi module. The methods should not
+    be called by your handler. However, server, params, stdin, stdout,
+    stderr, and data are free for your handler's use.
+    """
+    def __init__(self, conn, inputStreamClass):
+        self._conn = conn
+
+        self.server = conn.server
+        self.params = {}
+        self.stdin = inputStreamClass(conn)
+        self.stdout = OutputStream(conn, self, FCGI_STDOUT)
+        self.stderr = OutputStream(conn, self, FCGI_STDERR, buffered=True)
+        self.data = inputStreamClass(conn)
+
+    def run(self):
+        """Runs the handler, flushes the streams, and ends the request."""
+        try:
+            protocolStatus, appStatus = self.server.handler(self)
+        except:
+            traceback.print_exc(file=self.stderr)
+            self.stderr.flush()
+            if not self.stdout.dataWritten:
+                self.server.error(self)
+
+            protocolStatus, appStatus = FCGI_REQUEST_COMPLETE, 0
+
+        if __debug__: _debug(1, 'protocolStatus = %d, appStatus = %d' %
+                             (protocolStatus, appStatus))
+
+        try:
+            self._flush()
+            self._end(appStatus, protocolStatus)
+        except socket.error, e:
+            if e[0] != errno.EPIPE:
+                raise
+
+    def _end(self, appStatus=0L, protocolStatus=FCGI_REQUEST_COMPLETE):
+        self._conn.end_request(self, appStatus, protocolStatus)
+        
+    def _flush(self):
+        self.stdout.close()
+        self.stderr.close()
+
+class CGIRequest(Request):
+    """A normal CGI request disguised as a FastCGI request."""
+    def __init__(self, server):
+        # These are normally filled in by Connection.
+        self.requestId = 1
+        self.role = FCGI_RESPONDER
+        self.flags = 0
+        self.aborted = False
+        
+        self.server = server
+        self.params = dict(os.environ)
+        self.stdin = sys.stdin
+        self.stdout = StdoutWrapper(sys.stdout) # Oh, the humanity!
+        self.stderr = sys.stderr
+        self.data = StringIO.StringIO()
+        
+    def _end(self, appStatus=0L, protocolStatus=FCGI_REQUEST_COMPLETE):
+        sys.exit(appStatus)
+
+    def _flush(self):
+        # Not buffered, do nothing.
+        pass
+
+class Connection(object):
+    """
+    A Connection with the web server.
+
+    Each Connection is associated with a single socket (which is
+    connected to the web server) and is responsible for handling all
+    the FastCGI message processing for that socket.
+    """
+    _multiplexed = False
+    _inputStreamClass = InputStream
+
+    def __init__(self, sock, addr, server):
+        self._sock = sock
+        self._addr = addr
+        self.server = server
+
+        # Active Requests for this Connection, mapped by request ID.
+        self._requests = {}
+
+    def _cleanupSocket(self):
+        """Close the Connection's socket."""
+        try:
+            self._sock.shutdown(socket.SHUT_WR)
+        except:
+            return
+        try:
+            while True:
+                r, w, e = select.select([self._sock], [], [])
+                if not r or not self._sock.recv(1024):
+                    break
+        except:
+            pass
+        self._sock.close()
+        
+    def run(self):
+        """Begin processing data from the socket."""
+        self._keepGoing = True
+        while self._keepGoing:
+            try:
+                self.process_input()
+            except (EOFError, KeyboardInterrupt):
+                break
+            except (select.error, socket.error), e:
+                if e[0] == errno.EBADF: # Socket was closed by Request.
+                    break
+                raise
+
+        self._cleanupSocket()
+
+    def process_input(self):
+        """Attempt to read a single Record from the socket and process it."""
+        # Currently, any children Request threads notify this Connection
+        # that it is no longer needed by closing the Connection's socket.
+        # We need to put a timeout on select, otherwise we might get
+        # stuck in it indefinitely... (I don't like this solution.)
+        while self._keepGoing:
+            try:
+                r, w, e = select.select([self._sock], [], [], 1.0)
+            except ValueError:
+                # Sigh. ValueError gets thrown sometimes when passing select
+                # a closed socket.
+                raise EOFError
+            if r: break
+        if not self._keepGoing:
+            return
+        rec = Record()
+        rec.read(self._sock)
+
+        if rec.type == FCGI_GET_VALUES:
+            self._do_get_values(rec)
+        elif rec.type == FCGI_BEGIN_REQUEST:
+            self._do_begin_request(rec)
+        elif rec.type == FCGI_ABORT_REQUEST:
+            self._do_abort_request(rec)
+        elif rec.type == FCGI_PARAMS:
+            self._do_params(rec)
+        elif rec.type == FCGI_STDIN:
+            self._do_stdin(rec)
+        elif rec.type == FCGI_DATA:
+            self._do_data(rec)
+        elif rec.requestId == FCGI_NULL_REQUEST_ID:
+            self._do_unknown_type(rec)
+        else:
+            # Need to complain about this.
+            pass
+
+    def writeRecord(self, rec):
+        """
+        Write a Record to the socket.
+        """
+        rec.write(self._sock)
+
+    def end_request(self, req, appStatus=0L,
+                    protocolStatus=FCGI_REQUEST_COMPLETE, remove=True):
+        """
+        End a Request.
+
+        Called by Request objects. An FCGI_END_REQUEST Record is
+        sent to the web server. If the web server no longer requires
+        the connection, the socket is closed, thereby ending this
+        Connection (run() returns).
+        """
+        rec = Record(FCGI_END_REQUEST, req.requestId)
+        rec.contentData = struct.pack(FCGI_EndRequestBody, appStatus,
+                                      protocolStatus)
+        rec.contentLength = FCGI_EndRequestBody_LEN
+        self.writeRecord(rec)
+
+        if remove:
+            del self._requests[req.requestId]
+
+        if __debug__: _debug(2, 'end_request: flags = %d' % req.flags)
+
+        if not (req.flags & FCGI_KEEP_CONN) and not self._requests:
+            self._cleanupSocket()
+            self._keepGoing = False
+
+    def _do_get_values(self, inrec):
+        """Handle an FCGI_GET_VALUES request from the web server."""
+        outrec = Record(FCGI_GET_VALUES_RESULT)
+
+        pos = 0
+        while pos < inrec.contentLength:
+            pos, (name, value) = decode_pair(inrec.contentData, pos)
+            cap = self.server.capability.get(name)
+            if cap is not None:
+                outrec.contentData += encode_pair(name, str(cap))
+
+        outrec.contentLength = len(outrec.contentData)
+        self.writeRecord(outrec)
+
+    def _do_begin_request(self, inrec):
+        """Handle an FCGI_BEGIN_REQUEST from the web server."""
+        role, flags = struct.unpack(FCGI_BeginRequestBody, inrec.contentData)
+
+        req = self.server.request_class(self, self._inputStreamClass)
+        req.requestId, req.role, req.flags = inrec.requestId, role, flags
+        req.aborted = False
+
+        if not self._multiplexed and self._requests:
+            # Can't multiplex requests.
+            self.end_request(req, 0L, FCGI_CANT_MPX_CONN, remove=False)
+        else:
+            self._requests[inrec.requestId] = req
+
+    def _do_abort_request(self, inrec):
+        """
+        Handle an FCGI_ABORT_REQUEST from the web server.
+
+        We just mark a flag in the associated Request.
+        """
+        req = self._requests.get(inrec.requestId)
+        if req is not None:
+            req.aborted = True
+
+    def _start_request(self, req):
+        """Run the request."""
+        # Not multiplexed, so run it inline.
+        req.run()
+
+    def _do_params(self, inrec):
+        """
+        Handle an FCGI_PARAMS Record.
+
+        If the last FCGI_PARAMS Record is received, start the request.
+        """
+        req = self._requests.get(inrec.requestId)
+        if req is not None:
+            if inrec.contentLength:
+                pos = 0
+                while pos < inrec.contentLength:
+                    pos, (name, value) = decode_pair(inrec.contentData, pos)
+                    req.params[name] = value
+            else:
+                self._start_request(req)
+
+    def _do_stdin(self, inrec):
+        """Handle the FCGI_STDIN stream."""
+        req = self._requests.get(inrec.requestId)
+        if req is not None:
+            req.stdin.add_data(inrec.contentData)
+
+    def _do_data(self, inrec):
+        """Handle the FCGI_DATA stream."""
+        req = self._requests.get(inrec.requestId)
+        if req is not None:
+            req.data.add_data(inrec.contentData)
+
+    def _do_unknown_type(self, inrec):
+        """Handle an unknown request type. Respond accordingly."""
+        outrec = Record(FCGI_UNKNOWN_TYPE)
+        outrec.contentData = struct.pack(FCGI_UnknownTypeBody, inrec.type)
+        outrec.contentLength = FCGI_UnknownTypeBody_LEN
+        self.writeRecord(rec)
+        
+class MultiplexedConnection(Connection):
+    """
+    A version of Connection capable of handling multiple requests
+    simultaneously.
+    """
+    _multiplexed = True
+    _inputStreamClass = MultiplexedInputStream
+
+    def __init__(self, sock, addr, server):
+        super(MultiplexedConnection, self).__init__(sock, addr, server)
+
+        # Used to arbitrate access to self._requests.
+        lock = threading.RLock()
+
+        # Notification is posted everytime a request completes, allowing us
+        # to quit cleanly.
+        self._lock = threading.Condition(lock)
+
+    def _cleanupSocket(self):
+        # Wait for any outstanding requests before closing the socket.
+        self._lock.acquire()
+        while self._requests:
+            self._lock.wait()
+        self._lock.release()
+
+        super(MultiplexedConnection, self)._cleanupSocket()
+        
+    def writeRecord(self, rec):
+        # Must use locking to prevent intermingling of Records from different
+        # threads.
+        self._lock.acquire()
+        try:
+            # Probably faster than calling super. ;)
+            rec.write(self._sock)
+        finally:
+            self._lock.release()
+
+    def end_request(self, req, appStatus=0L,
+                    protocolStatus=FCGI_REQUEST_COMPLETE, remove=True):
+        self._lock.acquire()
+        try:
+            super(MultiplexedConnection, self).end_request(req, appStatus,
+                                                           protocolStatus,
+                                                           remove)
+            self._lock.notify()
+        finally:
+            self._lock.release()
+
+    def _do_begin_request(self, inrec):
+        self._lock.acquire()
+        try:
+            super(MultiplexedConnection, self)._do_begin_request(inrec)
+        finally:
+            self._lock.release()
+
+    def _do_abort_request(self, inrec):
+        self._lock.acquire()
+        try:
+            super(MultiplexedConnection, self)._do_abort_request(inrec)
+        finally:
+            self._lock.release()
+
+    def _start_request(self, req):
+        thread.start_new_thread(req.run, ())
+
+    def _do_params(self, inrec):
+        self._lock.acquire()
+        try:
+            super(MultiplexedConnection, self)._do_params(inrec)
+        finally:
+            self._lock.release()
+
+    def _do_stdin(self, inrec):
+        self._lock.acquire()
+        try:
+            super(MultiplexedConnection, self)._do_stdin(inrec)
+        finally:
+            self._lock.release()
+
+    def _do_data(self, inrec):
+        self._lock.acquire()
+        try:
+            super(MultiplexedConnection, self)._do_data(inrec)
+        finally:
+            self._lock.release()
+        
+class BaseFCGIServer(object):
+    request_class = Request
+    cgirequest_class = CGIRequest
+
+    # The maximum number of bytes (per Record) to write to the server.
+    # I've noticed mod_fastcgi has a relatively small receive buffer (8K or
+    # so).
+    maxwrite = 8192
+
+    # Limits the size of the InputStream's string buffer to this size + the
+    # server's maximum Record size. Since the InputStream is not seekable,
+    # we throw away already-read data once this certain amount has been read.
+    inputStreamShrinkThreshold = 102400 - 8192
+
+    def __init__(self, application, environ=None,
+                 multithreaded=True, multiprocess=False,
+                 bindAddress=None, umask=None, multiplexed=False,
+                 debug=True, roles=(FCGI_RESPONDER,)):
+        """
+        bindAddress, if present, must either be a string or a 2-tuple. If
+        present, run() will open its own listening socket. You would use
+        this if you wanted to run your application as an 'external' FastCGI
+        app. (i.e. the webserver would no longer be responsible for starting
+        your app) If a string, it will be interpreted as a filename and a UNIX
+        socket will be opened. If a tuple, the first element, a string,
+        is the interface name/IP to bind to, and the second element (an int)
+        is the port number.
+
+        If binding to a UNIX socket, umask may be set to specify what
+        the umask is to be changed to before the socket is created in the
+        filesystem. After the socket is created, the previous umask is
+        restored.
+        
+        Set multiplexed to True if you want to handle multiple requests
+        per connection. Some FastCGI backends (namely mod_fastcgi) don't
+        multiplex requests at all, so by default this is off (which saves
+        on thread creation/locking overhead). If threads aren't available,
+        this keyword is ignored; it's not possible to multiplex requests
+        at all.
+        """
+        if environ is None:
+            environ = {}
+
+        self.application = application
+        self.environ = environ
+        self.multithreaded = multithreaded
+        self.multiprocess = multiprocess
+        self.debug = debug
+        self.roles = roles
+
+        self._bindAddress = bindAddress
+        self._umask = umask
+        
+        # Used to force single-threadedness
+        self._appLock = thread.allocate_lock()
+
+        if thread_available:
+            try:
+                import resource
+                # Attempt to glean the maximum number of connections
+                # from the OS.
+                maxConns = resource.getrlimit(resource.RLIMIT_NOFILE)[0]
+            except ImportError:
+                maxConns = 100 # Just some made up number.
+            maxReqs = maxConns
+            if multiplexed:
+                self._connectionClass = MultiplexedConnection
+                maxReqs *= 5 # Another made up number.
+            else:
+                self._connectionClass = Connection
+            self.capability = {
+                FCGI_MAX_CONNS: maxConns,
+                FCGI_MAX_REQS: maxReqs,
+                FCGI_MPXS_CONNS: multiplexed and 1 or 0
+                }
+        else:
+            self._connectionClass = Connection
+            self.capability = {
+                # If threads aren't available, these are pretty much correct.
+                FCGI_MAX_CONNS: 1,
+                FCGI_MAX_REQS: 1,
+                FCGI_MPXS_CONNS: 0
+                }
+
+    def _setupSocket(self):
+        if self._bindAddress is None: # Run as a normal FastCGI?
+            isFCGI = True
+
+            sock = socket.fromfd(FCGI_LISTENSOCK_FILENO, socket.AF_INET,
+                                 socket.SOCK_STREAM)
+            try:
+                sock.getpeername()
+            except socket.error, e:
+                if e[0] == errno.ENOTSOCK:
+                    # Not a socket, assume CGI context.
+                    isFCGI = False
+                elif e[0] != errno.ENOTCONN:
+                    raise
+
+            # FastCGI/CGI discrimination is broken on Mac OS X.
+            # Set the environment variable FCGI_FORCE_CGI to "Y" or "y"
+            # if you want to run your app as a simple CGI. (You can do
+            # this with Apache's mod_env [not loaded by default in OS X
+            # client, ha ha] and the SetEnv directive.)
+            if not isFCGI or \
+               os.environ.get('FCGI_FORCE_CGI', 'N').upper().startswith('Y'):
+                req = self.cgirequest_class(self)
+                req.run()
+                sys.exit(0)
+        else:
+            # Run as a server
+            oldUmask = None
+            if type(self._bindAddress) is str:
+                # Unix socket
+                sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
+                try:
+                    os.unlink(self._bindAddress)
+                except OSError:
+                    pass
+                if self._umask is not None:
+                    oldUmask = os.umask(self._umask)
+            else:
+                # INET socket
+                assert type(self._bindAddress) is tuple
+                assert len(self._bindAddress) == 2
+                sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+                sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+
+            sock.bind(self._bindAddress)
+            sock.listen(socket.SOMAXCONN)
+
+            if oldUmask is not None:
+                os.umask(oldUmask)
+                
+        return sock
+
+    def _cleanupSocket(self, sock):
+        """Closes the main socket."""
+        sock.close()
+
+    def handler(self, req):
+        """Special handler for WSGI."""
+        if req.role not in self.roles:
+            return FCGI_UNKNOWN_ROLE, 0
+
+        # Mostly taken from example CGI gateway.
+        environ = req.params
+        environ.update(self.environ)
+
+        environ['wsgi.version'] = (1,0)
+        environ['wsgi.input'] = req.stdin
+        if self._bindAddress is None:
+            stderr = req.stderr
+        else:
+            stderr = TeeOutputStream((sys.stderr, req.stderr))
+        environ['wsgi.errors'] = stderr
+        environ['wsgi.multithread'] = not isinstance(req, CGIRequest) and \
+                                      thread_available and self.multithreaded
+        environ['wsgi.multiprocess'] = isinstance(req, CGIRequest) or \
+                                       self.multiprocess
+        environ['wsgi.run_once'] = isinstance(req, CGIRequest)
+
+        if environ.get('HTTPS', 'off') in ('on', '1'):
+            environ['wsgi.url_scheme'] = 'https'
+        else:
+            environ['wsgi.url_scheme'] = 'http'
+
+        self._sanitizeEnv(environ)
+
+        headers_set = []
+        headers_sent = []
+        result = None
+
+        def write(data):
+            assert type(data) is str, 'write() argument must be string'
+            assert headers_set, 'write() before start_response()'
+
+            if not headers_sent:
+                status, responseHeaders = headers_sent[:] = headers_set
+                found = False
+                for header,value in responseHeaders:
+                    if header.lower() == 'content-length':
+                        found = True
+                        break
+                if not found and result is not None:
+                    try:
+                        if len(result) == 1:
+                            responseHeaders.append(('Content-Length',
+                                                    str(len(data))))
+                    except:
+                        pass
+                s = 'Status: %s\r\n' % status
+                for header in responseHeaders:
+                    s += '%s: %s\r\n' % header
+                s += '\r\n'
+                req.stdout.write(s)
+
+            req.stdout.write(data)
+            req.stdout.flush()
+
+        def start_response(status, response_headers, exc_info=None):
+            if exc_info:
+                try:
+                    if headers_sent:
+                        # Re-raise if too late
+                        raise exc_info[0], exc_info[1], exc_info[2]
+                finally:
+                    exc_info = None # avoid dangling circular ref
+            else:
+                assert not headers_set, 'Headers already set!'
+
+            assert type(status) is str, 'Status must be a string'
+            assert len(status) >= 4, 'Status must be at least 4 characters'
+            assert int(status[:3]), 'Status must begin with 3-digit code'
+            assert status[3] == ' ', 'Status must have a space after code'
+            assert type(response_headers) is list, 'Headers must be a list'
+            if __debug__:
+                for name,val in response_headers:
+                    assert type(name) is str, 'Header names must be strings'
+                    assert type(val) is str, 'Header values must be strings'
+
+            headers_set[:] = [status, response_headers]
+            return write
+
+        if not self.multithreaded:
+            self._appLock.acquire()
+        try:
+            try:
+                result = self.application(environ, start_response)
+                try:
+                    for data in result:
+                        if data:
+                            write(data)
+                    if not headers_sent:
+                        write('') # in case body was empty
+                finally:
+                    if hasattr(result, 'close'):
+                        result.close()
+            except socket.error, e:
+                if e[0] != errno.EPIPE:
+                    raise # Don't let EPIPE propagate beyond server
+        finally:
+            if not self.multithreaded:
+                self._appLock.release()
+
+        return FCGI_REQUEST_COMPLETE, 0
+
+    def _sanitizeEnv(self, environ):
+        """Ensure certain values are present, if required by WSGI."""
+        if not environ.has_key('SCRIPT_NAME'):
+            environ['SCRIPT_NAME'] = ''
+
+        reqUri = None
+        if environ.has_key('REQUEST_URI'):
+            reqUri = environ['REQUEST_URI'].split('?', 1)
+
+        if not environ.has_key('PATH_INFO') or not environ['PATH_INFO']:
+            if reqUri is not None:
+                environ['PATH_INFO'] = reqUri[0]
+            else:
+                environ['PATH_INFO'] = ''
+        if not environ.has_key('QUERY_STRING') or not environ['QUERY_STRING']:
+            if reqUri is not None and len(reqUri) > 1:
+                environ['QUERY_STRING'] = reqUri[1]
+            else:
+                environ['QUERY_STRING'] = ''
+
+        # If any of these are missing, it probably signifies a broken
+        # server...
+        for name,default in [('REQUEST_METHOD', 'GET'),
+                             ('SERVER_NAME', 'localhost'),
+                             ('SERVER_PORT', '80'),
+                             ('SERVER_PROTOCOL', 'HTTP/1.0')]:
+            if not environ.has_key(name):
+                environ['wsgi.errors'].write('%s: missing FastCGI param %s '
+                                             'required by WSGI!\n' %
+                                             (self.__class__.__name__, name))
+                environ[name] = default
+            
+    def error(self, req):
+        """
+        Called by Request if an exception occurs within the handler. May and
+        should be overridden.
+        """
+        if self.debug:
+            import cgitb
+            req.stdout.write('Content-Type: text/html\r\n\r\n' +
+                             cgitb.html(sys.exc_info()))
+        else:
+            errorpage = """<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
+<html><head>
+<title>Unhandled Exception</title>
+</head><body>
+<h1>Unhandled Exception</h1>
+<p>An unhandled exception was thrown by the application.</p>
+</body></html>
+"""
+            req.stdout.write('Content-Type: text/html\r\n\r\n' +
+                             errorpage)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MoinMoin/support/flup/server/fcgi_fork.py	Thu Aug 14 00:59:13 2008 +0200
@@ -0,0 +1,170 @@
+# Copyright (c) 2005, 2006 Allan Saddi <allan@saddi.com>
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+#    notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+#    notice, this list of conditions and the following disclaimer in the
+#    documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# $Id$
+
+"""
+fcgi - a FastCGI/WSGI gateway.
+
+For more information about FastCGI, see <http://www.fastcgi.com/>.
+
+For more information about the Web Server Gateway Interface, see
+<http://www.python.org/peps/pep-0333.html>.
+
+Example usage:
+
+  #!/usr/bin/env python
+  from myapplication import app # Assume app is your WSGI application object
+  from fcgi import WSGIServer
+  WSGIServer(app).run()
+
+See the documentation for WSGIServer for more information.
+
+On most platforms, fcgi will fallback to regular CGI behavior if run in a
+non-FastCGI context. If you want to force CGI behavior, set the environment
+variable FCGI_FORCE_CGI to "Y" or "y".
+"""
+
+__author__ = 'Allan Saddi <allan@saddi.com>'
+__version__ = '$Revision$'
+
+import os
+
+from flup.server.fcgi_base import BaseFCGIServer, \
+     FCGI_MAX_CONNS, FCGI_MAX_REQS, FCGI_MPXS_CONNS
+from flup.server.preforkserver import PreforkServer
+
+__all__ = ['WSGIServer']
+
+class WSGIServer(BaseFCGIServer, PreforkServer):
+    """
+    FastCGI server that supports the Web Server Gateway Interface. See
+    <http://www.python.org/peps/pep-0333.html>.
+    """
+    def __init__(self, application, environ=None,
+                 bindAddress=None, umask=None, multiplexed=False,
+                 debug=True, **kw):
+        """
+        environ, if present, must be a dictionary-like object. Its
+        contents will be copied into application's environ. Useful
+        for passing application-specific variables.
+
+        bindAddress, if present, must either be a string or a 2-tuple. If
+        present, run() will open its own listening socket. You would use
+        this if you wanted to run your application as an 'external' FastCGI
+        app. (i.e. the webserver would no longer be responsible for starting
+        your app) If a string, it will be interpreted as a filename and a UNIX
+        socket will be opened. If a tuple, the first element, a string,
+        is the interface name/IP to bind to, and the second element (an int)
+        is the port number.
+        """
+        BaseFCGIServer.__init__(self, application,
+                                environ=environ,
+                                multithreaded=False,
+                                multiprocess=True,
+                                bindAddress=bindAddress,
+                                umask=umask,
+                                multiplexed=multiplexed,
+                                debug=debug)
+        for key in ('multithreaded', 'multiprocess', 'jobClass', 'jobArgs'):
+            if kw.has_key(key):
+                del kw[key]
+        PreforkServer.__init__(self, jobClass=self._connectionClass,
+                               jobArgs=(self,), **kw)
+
+        try:
+            import resource
+            # Attempt to glean the maximum number of connections
+            # from the OS.
+            try:
+                maxProcs = resource.getrlimit(resource.RLIMIT_NPROC)[0]
+                maxConns = resource.getrlimit(resource.RLIMIT_NOFILE)[0]
+                maxConns = min(maxConns, maxProcs)
+            except AttributeError:
+                maxConns = resource.getrlimit(resource.RLIMIT_NOFILE)[0]
+        except ImportError:
+            maxConns = 100 # Just some made up number.
+        maxReqs = maxConns
+        self.capability = {
+            FCGI_MAX_CONNS: maxConns,
+            FCGI_MAX_REQS: maxReqs,
+            FCGI_MPXS_CONNS: 0
+            }
+
+    def _isClientAllowed(self, addr):
+        return self._web_server_addrs is None or \
+               (len(addr) == 2 and addr[0] in self._web_server_addrs)
+
+    def run(self):
+        """
+        The main loop. Exits on SIGHUP, SIGINT, SIGTERM. Returns True if
+        SIGHUP was received, False otherwise.
+        """
+        self._web_server_addrs = os.environ.get('FCGI_WEB_SERVER_ADDRS')
+        if self._web_server_addrs is not None:
+            self._web_server_addrs = map(lambda x: x.strip(),
+                                         self._web_server_addrs.split(','))
+
+        sock = self._setupSocket()
+
+        ret = PreforkServer.run(self, sock)
+
+        self._cleanupSocket(sock)
+
+        return ret
+
+def factory(global_conf, host=None, port=None, **local):
+    import paste_factory
+    return paste_factory.helper(WSGIServer, global_conf, host, port, **local)
+
+if __name__ == '__main__':
+    def test_app(environ, start_response):
+        """Probably not the most efficient example."""
+        import cgi
+        start_response('200 OK', [('Content-Type', 'text/html')])
+        yield '<html><head><title>Hello World!</title></head>\n' \
+              '<body>\n' \
+              '<p>Hello World!</p>\n' \
+              '<table border="1">'
+        names = environ.keys()
+        names.sort()
+        for name in names:
+            yield '<tr><td>%s</td><td>%s</td></tr>\n' % (
+                name, cgi.escape(`environ[name]`))
+
+        form = cgi.FieldStorage(fp=environ['wsgi.input'], environ=environ,
+                                keep_blank_values=1)
+        if form.list:
+            yield '<tr><th colspan="2">Form data</th></tr>'
+
+        for field in form.list:
+            yield '<tr><td>%s</td><td>%s</td></tr>\n' % (
+                field.name, field.value)
+
+        yield '</table>\n' \
+              '</body></html>\n'
+
+    from wsgiref import validate
+    test_app = validate.validator(test_app)
+    WSGIServer(test_app).run()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MoinMoin/support/flup/server/fcgi_single.py	Thu Aug 14 00:59:13 2008 +0200
@@ -0,0 +1,157 @@
+# Copyright (c) 2005, 2006 Allan Saddi <allan@saddi.com>
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+#    notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+#    notice, this list of conditions and the following disclaimer in the
+#    documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# $Id$
+
+"""
+fcgi - a FastCGI/WSGI gateway.
+
+For more information about FastCGI, see <http://www.fastcgi.com/>.
+
+For more information about the Web Server Gateway Interface, see
+<http://www.python.org/peps/pep-0333.html>.
+
+Example usage:
+
+  #!/usr/bin/env python
+  from myapplication import app # Assume app is your WSGI application object
+  from fcgi import WSGIServer
+  WSGIServer(app).run()
+
+See the documentation for WSGIServer for more information.
+
+On most platforms, fcgi will fallback to regular CGI behavior if run in a
+non-FastCGI context. If you want to force CGI behavior, set the environment
+variable FCGI_FORCE_CGI to "Y" or "y".
+"""
+
+__author__ = 'Allan Saddi <allan@saddi.com>'
+__version__ = '$Revision$'
+
+import os
+
+from flup.server.fcgi_base import BaseFCGIServer, FCGI_RESPONDER, \
+     FCGI_MAX_CONNS, FCGI_MAX_REQS, FCGI_MPXS_CONNS
+from flup.server.singleserver import SingleServer
+
+__all__ = ['WSGIServer']
+
+class WSGIServer(BaseFCGIServer, SingleServer):
+    """
+    FastCGI server that supports the Web Server Gateway Interface. See
+    <http://www.python.org/peps/pep-0333.html>.
+    """
+    def __init__(self, application, environ=None,
+                 bindAddress=None, umask=None, multiplexed=False,
+                 debug=True, roles=(FCGI_RESPONDER,), **kw):
+        """
+        environ, if present, must be a dictionary-like object. Its
+        contents will be copied into application's environ. Useful
+        for passing application-specific variables.
+
+        bindAddress, if present, must either be a string or a 2-tuple. If
+        present, run() will open its own listening socket. You would use
+        this if you wanted to run your application as an 'external' FastCGI
+        app. (i.e. the webserver would no longer be responsible for starting
+        your app) If a string, it will be interpreted as a filename and a UNIX
+        socket will be opened. If a tuple, the first element, a string,
+        is the interface name/IP to bind to, and the second element (an int)
+        is the port number.
+        """
+        BaseFCGIServer.__init__(self, application,
+                                environ=environ,
+                                multithreaded=False,
+                                multiprocess=False,
+                                bindAddress=bindAddress,
+                                umask=umask,
+                                multiplexed=multiplexed,
+                                debug=debug,
+                                roles=roles)
+        for key in ('jobClass', 'jobArgs'):
+            if kw.has_key(key):
+                del kw[key]
+        SingleServer.__init__(self, jobClass=self._connectionClass,
+                              jobArgs=(self,), **kw)
+        self.capability = {
+            FCGI_MAX_CONNS: 1,
+            FCGI_MAX_REQS: 1,
+            FCGI_MPXS_CONNS: 0
+            }
+
+    def _isClientAllowed(self, addr):
+        return self._web_server_addrs is None or \
+               (len(addr) == 2 and addr[0] in self._web_server_addrs)
+
+    def run(self):
+        """
+        The main loop. Exits on SIGHUP, SIGINT, SIGTERM. Returns True if
+        SIGHUP was received, False otherwise.
+        """
+        self._web_server_addrs = os.environ.get('FCGI_WEB_SERVER_ADDRS')
+        if self._web_server_addrs is not None:
+            self._web_server_addrs = map(lambda x: x.strip(),
+                                         self._web_server_addrs.split(','))
+
+        sock = self._setupSocket()
+
+        ret = SingleServer.run(self, sock)
+
+        self._cleanupSocket(sock)
+
+        return ret
+
+def factory(global_conf, host=None, port=None, **local):
+    import paste_factory
+    return paste_factory.helper(WSGIServer, global_conf, host, port, **local)
+
+if __name__ == '__main__':
+    def test_app(environ, start_response):
+        """Probably not the most efficient example."""
+        import cgi
+        start_response('200 OK', [('Content-Type', 'text/html')])
+        yield '<html><head><title>Hello World!</title></head>\n' \
+              '<body>\n' \
+              '<p>Hello World!</p>\n' \
+              '<table border="1">'
+        names = environ.keys()
+        names.sort()
+        for name in names:
+            yield '<tr><td>%s</td><td>%s</td></tr>\n' % (
+                name, cgi.escape(`environ[name]`))
+
+        form = cgi.FieldStorage(fp=environ['wsgi.input'], environ=environ,
+                                keep_blank_values=1)
+        if form.list:
+            yield '<tr><th colspan="2">Form data</th></tr>'
+
+        for field in form.list:
+            yield '<tr><td>%s</td><td>%s</td></tr>\n' % (
+                field.name, field.value)
+
+        yield '</table>\n' \
+              '</body></html>\n'
+
+    from wsgiref import validate
+    test_app = validate.validator(test_app)
+    WSGIServer(test_app).run()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MoinMoin/support/flup/server/paste_factory.py	Thu Aug 14 00:59:13 2008 +0200
@@ -0,0 +1,16 @@
+def helper(wsgiServerClass, global_conf, host, port, **local_conf):
+    # I think I can't write a tuple for bindAddress in .ini file
+    host = host or global_conf.get('host', 'localhost')
+    port = port or global_conf.get('port', 4000)
+
+    if 'socket' in local_conf:
+        local_conf['bindAddress'] = local_conf['socket']
+        del local_conf['socket']
+    else:
+        local_conf['bindAddress'] = (host, int(port))
+    
+    def server(application):
+        server = wsgiServerClass(application, **local_conf)
+        server.run()
+
+    return server
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MoinMoin/support/flup/server/preforkserver.py	Thu Aug 14 00:59:13 2008 +0200
@@ -0,0 +1,414 @@
+# Copyright (c) 2005 Allan Saddi <allan@saddi.com>
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+#    notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+#    notice, this list of conditions and the following disclaimer in the
+#    documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# $Id$
+
+__author__ = 'Allan Saddi <allan@saddi.com>'
+__version__ = '$Revision$'
+
+import sys
+import os
+import socket
+import select
+import errno
+import signal
+
+try:
+    import fcntl
+except ImportError:
+    def setCloseOnExec(sock):
+        pass
+else:
+    def setCloseOnExec(sock):
+        fcntl.fcntl(sock.fileno(), fcntl.F_SETFD, fcntl.FD_CLOEXEC)
+
+# If running Python < 2.4, require eunuchs module for socket.socketpair().
+# See <http://www.inoi.fi/open/trac/eunuchs>.
+if not hasattr(socket, 'socketpair'):
+    try:
+        import eunuchs.socketpair
+    except ImportError:
+        # TODO: Other alternatives? Perhaps using os.pipe()?
+        raise ImportError, 'Requires eunuchs module for Python < 2.4'
+
+    def socketpair():
+        s1, s2 = eunuchs.socketpair.socketpair()
+        p, c = (socket.fromfd(s1, socket.AF_UNIX, socket.SOCK_STREAM),
+                socket.fromfd(s2, socket.AF_UNIX, socket.SOCK_STREAM))
+        os.close(s1)
+        os.close(s2)
+        return p, c
+
+    socket.socketpair = socketpair
+
+class PreforkServer(object):
+    """
+    A preforked server model conceptually similar to Apache httpd(2). At
+    any given time, ensures there are at least minSpare children ready to
+    process new requests (up to a maximum of maxChildren children total).
+    If the number of idle children is ever above maxSpare, the extra
+    children are killed.
+
+    If maxRequests is positive, each child will only handle that many
+    requests in its lifetime before exiting.
+    
+    jobClass should be a class whose constructor takes at least two
+    arguments: the client socket and client address. jobArgs, which
+    must be a list or tuple, is any additional (static) arguments you
+    wish to pass to the constructor.
+
+    jobClass should have a run() method (taking no arguments) that does
+    the actual work. When run() returns, the request is considered
+    complete and the child process moves to idle state.
+    """
+    def __init__(self, minSpare=1, maxSpare=5, maxChildren=50,
+                 maxRequests=0, jobClass=None, jobArgs=()):
+        self._minSpare = minSpare
+        self._maxSpare = maxSpare
+        self._maxChildren = max(maxSpare, maxChildren)
+        self._maxRequests = maxRequests
+        self._jobClass = jobClass
+        self._jobArgs = jobArgs
+
+        # Internal state of children. Maps pids to dictionaries with two
+        # members: 'file' and 'avail'. 'file' is the socket to that
+        # individidual child and 'avail' is whether or not the child is
+        # free to process requests.
+        self._children = {}
+
+    def run(self, sock):
+        """
+        The main loop. Pass a socket that is ready to accept() client
+        connections. Return value will be True or False indiciating whether
+        or not the loop was exited due to SIGHUP.
+        """
+        # Set up signal handlers.
+        self._keepGoing = True
+        self._hupReceived = False
+        self._installSignalHandlers()
+
+        # Don't want operations on main socket to block.
+        sock.setblocking(0)
+
+        # Set close-on-exec
+        setCloseOnExec(sock)
+        
+        # Main loop.
+        while self._keepGoing:
+            # Maintain minimum number of children.
+            while len(self._children) < self._maxSpare:
+                if not self._spawnChild(sock): break
+
+            # Wait on any socket activity from live children.
+            r = [x['file'] for x in self._children.values()
+                 if x['file'] is not None]
+
+            if len(r) == len(self._children):
+                timeout = None
+            else:
+                # There are dead children that need to be reaped, ensure
+                # that they are by timing out, if necessary.
+                timeout = 2
+
+            try:
+                r, w, e = select.select(r, [], [], timeout)
+            except select.error, e:
+                if e[0] != errno.EINTR:
+                    raise
+
+            # Scan child sockets and tend to those that need attention.
+            for child in r:
+                # Receive status byte.
+                try:
+                    state = child.recv(1)
+                except socket.error, e:
+                    if e[0] in (errno.EAGAIN, errno.EINTR):
+                        # Guess it really didn't need attention?
+                        continue
+                    raise
+                # Try to match it with a child. (Do we need a reverse map?)
+                for pid,d in self._children.items():
+                    if child is d['file']:
+                        if state:
+                            # Set availability status accordingly.
+                            self._children[pid]['avail'] = state != '\x00'
+                        else:
+                            # Didn't receive anything. Child is most likely
+                            # dead.
+                            d = self._children[pid]
+                            d['file'].close()
+                            d['file'] = None
+                            d['avail'] = False
+
+            # Reap children.
+            self._reapChildren()
+
+            # See who and how many children are available.
+            availList = filter(lambda x: x[1]['avail'], self._children.items())
+            avail = len(availList)
+
+            if avail < self._minSpare:
+                # Need to spawn more children.
+                while avail < self._minSpare and \
+                      len(self._children) < self._maxChildren:
+                    if not self._spawnChild(sock): break
+                    avail += 1
+            elif avail > self._maxSpare:
+                # Too many spares, kill off the extras.
+                pids = [x[0] for x in availList]
+                pids.sort()
+                pids = pids[self._maxSpare:]
+                for pid in pids:
+                    d = self._children[pid]
+                    d['file'].close()
+                    d['file'] = None
+                    d['avail'] = False
+
+        # Clean up all child processes.
+        self._cleanupChildren()
+
+        # Restore signal handlers.
+        self._restoreSignalHandlers()
+
+        # Return bool based on whether or not SIGHUP was received.
+        return self._hupReceived
+
+    def _cleanupChildren(self):
+        """
+        Closes all child sockets (letting those that are available know
+        that it's time to exit). Sends SIGINT to those that are currently
+        processing (and hopes that it finishses ASAP).
+
+        Any children remaining after 10 seconds is SIGKILLed.
+        """
+        # Let all children know it's time to go.
+        for pid,d in self._children.items():
+            if d['file'] is not None:
+                d['file'].close()
+                d['file'] = None
+            if not d['avail']:
+                # Child is unavailable. SIGINT it.
+                try:
+                    os.kill(pid, signal.SIGINT)
+                except OSError, e:
+                    if e[0] != errno.ESRCH:
+                        raise
+
+        def alrmHandler(signum, frame):
+            pass
+
+        # Set up alarm to wake us up after 10 seconds.
+        oldSIGALRM = signal.getsignal(signal.SIGALRM)
+        signal.signal(signal.SIGALRM, alrmHandler)
+        signal.alarm(10)
+
+        # Wait for all children to die.
+        while len(self._children):
+            try:
+                pid, status = os.wait()
+            except OSError, e:
+                if e[0] in (errno.ECHILD, errno.EINTR):
+                    break
+            if self._children.has_key(pid):
+                del self._children[pid]
+
+        signal.signal(signal.SIGALRM, oldSIGALRM)
+
+        # Forcefully kill any remaining children.
+        for pid in self._children.keys():
+            try:
+                os.kill(pid, signal.SIGKILL)
+            except OSError, e:
+                if e[0] != errno.ESRCH:
+                    raise
+
+    def _reapChildren(self):
+        """Cleans up self._children whenever children die."""
+        while True:
+            try:
+                pid, status = os.waitpid(-1, os.WNOHANG)
+            except OSError, e:
+                if e[0] == errno.ECHILD:
+                    break
+                raise
+            if pid <= 0:
+                break
+            if self._children.has_key(pid): # Sanity check.
+                if self._children[pid]['file'] is not None:
+                    self._children[pid]['file'].close()
+                del self._children[pid]
+
+    def _spawnChild(self, sock):
+        """
+        Spawn a single child. Returns True if successful, False otherwise.
+        """
+        # This socket pair is used for very simple communication between
+        # the parent and its children.
+        parent, child = socket.socketpair()
+        parent.setblocking(0)
+        setCloseOnExec(parent)
+        child.setblocking(0)
+        setCloseOnExec(child)
+        try:
+            pid = os.fork()
+        except OSError, e:
+            if e[0] in (errno.EAGAIN, errno.ENOMEM):
+                return False # Can't fork anymore.
+            raise
+        if not pid:
+            # Child
+            child.close()
+            # Put child into its own process group.
+            pid = os.getpid()
+            os.setpgid(pid, pid)
+            # Restore signal handlers.
+            self._restoreSignalHandlers()
+            # Close copies of child sockets.
+            for f in [x['file'] for x in self._children.values()
+                      if x['file'] is not None]:
+                f.close()
+            self._children = {}
+            try:
+                # Enter main loop.
+                self._child(sock, parent)
+            except KeyboardInterrupt:
+                pass
+            sys.exit(0)
+        else:
+            # Parent
+            parent.close()
+            d = self._children[pid] = {}
+            d['file'] = child
+            d['avail'] = True
+            return True
+
+    def _isClientAllowed(self, addr):
+        """Override to provide access control."""
+        return True
+
+    def _child(self, sock, parent):
+        """Main loop for children."""
+        requestCount = 0
+        
+        while True:
+            # Wait for any activity on the main socket or parent socket.
+            r, w, e = select.select([sock, parent], [], [])
+
+            for f in r:
+                # If there's any activity on the parent socket, it
+                # means the parent wants us to die or has died itself.
+                # Either way, exit.
+                if f is parent:
+                    return
+
+            # Otherwise, there's activity on the main socket...
+            try:
+                clientSock, addr = sock.accept()
+            except socket.error, e:
+                if e[0] == errno.EAGAIN:
+                    # Or maybe not.
+                    continue
+                raise
+
+            setCloseOnExec(clientSock)
+            
+            # Check if this client is allowed.
+            if not self._isClientAllowed(addr):
+                clientSock.close()
+                continue
+
+            # Notify parent we're no longer available.
+            try:
+                parent.send('\x00')
+            except socket.error, e:
+                # If parent is gone, finish up this request.
+                if e[0] != errno.EPIPE:
+                    raise
+
+            # Do the job.
+            self._jobClass(clientSock, addr, *self._jobArgs).run()
+
+            # If we've serviced the maximum number of requests, exit.
+            if self._maxRequests > 0:
+                requestCount += 1
+                if requestCount >= self._maxRequests:
+                    break
+                
+            # Tell parent we're free again.
+            try:
+                parent.send('\xff')
+            except socket.error, e:
+                if e[0] == errno.EPIPE:
+                    # Parent is gone.
+                    return
+                raise
+
+    # Signal handlers
+
+    def _hupHandler(self, signum, frame):
+        self._keepGoing = False
+        self._hupReceived = True
+
+    def _intHandler(self, signum, frame):
+        self._keepGoing = False
+
+    def _chldHandler(self, signum, frame):
+        # Do nothing (breaks us out of select and allows us to reap children).
+        pass
+
+    def _installSignalHandlers(self):
+        supportedSignals = [signal.SIGINT, signal.SIGTERM]
+        if hasattr(signal, 'SIGHUP'):
+            supportedSignals.append(signal.SIGHUP)
+
+        self._oldSIGs = [(x,signal.getsignal(x)) for x in supportedSignals]
+
+        for sig in supportedSignals:
+            if hasattr(signal, 'SIGHUP') and sig == signal.SIGHUP:
+                signal.signal(sig, self._hupHandler)
+            else:
+                signal.signal(sig, self._intHandler)
+
+    def _restoreSignalHandlers(self):
+        """Restores previous signal handlers."""
+        for signum,handler in self._oldSIGs:
+            signal.signal(signum, handler)
+
+if __name__ == '__main__':
+    class TestJob(object):
+        def __init__(self, sock, addr):
+            self._sock = sock
+            self._addr = addr
+        def run(self):
+            print "Client connection opened from %s:%d" % self._addr
+            self._sock.send('Hello World!\n')
+            self._sock.setblocking(1)
+            self._sock.recv(1)
+            self._sock.close()
+            print "Client connection closed from %s:%d" % self._addr
+    sock = socket.socket()
+    sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+    sock.bind(('', 8080))
+    sock.listen(socket.SOMAXCONN)
+    PreforkServer(maxChildren=10, jobClass=TestJob).run(sock)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MoinMoin/support/flup/server/scgi.py	Thu Aug 14 00:59:13 2008 +0200
@@ -0,0 +1,194 @@
+# Copyright (c) 2005, 2006 Allan Saddi <allan@saddi.com>
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+#    notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+#    notice, this list of conditions and the following disclaimer in the
+#    documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# $Id$
+
+"""
+scgi - an SCGI/WSGI gateway.
+
+For more information about SCGI and mod_scgi for Apache1/Apache2, see
+<http://www.mems-exchange.org/software/scgi/>.
+
+For more information about the Web Server Gateway Interface, see
+<http://www.python.org/peps/pep-0333.html>.
+
+Example usage:
+
+  #!/usr/bin/env python
+  import sys
+  from myapplication import app # Assume app is your WSGI application object
+  from scgi import WSGIServer
+  ret = WSGIServer(app).run()
+  sys.exit(ret and 42 or 0)
+
+See the documentation for WSGIServer for more information.
+
+About the bit of logic at the end:
+Upon receiving SIGHUP, the python script will exit with status code 42. This
+can be used by a wrapper script to determine if the python script should be
+re-run. When a SIGINT or SIGTERM is received, the script exits with status
+code 0, possibly indicating a normal exit.
+
+Example wrapper script:
+
+  #!/bin/sh
+  STATUS=42
+  while test $STATUS -eq 42; do
+    python "$@" that_script_above.py
+    STATUS=$?
+  done
+"""
+
+__author__ = 'Allan Saddi <allan@saddi.com>'
+__version__ = '$Revision$'
+
+import logging
+import socket
+
+from flup.server.scgi_base import BaseSCGIServer, Connection, NoDefault
+from flup.server.threadedserver import ThreadedServer
+
+__all__ = ['WSGIServer']
+
+class WSGIServer(BaseSCGIServer, ThreadedServer):
+    """
+    SCGI/WSGI server. For information about SCGI (Simple Common Gateway
+    Interface), see <http://www.mems-exchange.org/software/scgi/>.
+
+    This server is similar to SWAP <http://www.idyll.org/~t/www-tools/wsgi/>,
+    another SCGI/WSGI server.
+
+    It differs from SWAP in that it isn't based on scgi.scgi_server and
+    therefore, it allows me to implement concurrency using threads. (Also,
+    this server was written from scratch and really has no other depedencies.)
+    Which server to use really boils down to whether you want multithreading
+    or forking. (But as an aside, I've found scgi.scgi_server's implementation
+    of preforking to be quite superior. So if your application really doesn't
+    mind running in multiple processes, go use SWAP. ;)
+    """
+    def __init__(self, application, scriptName=NoDefault, environ=None,
+                 multithreaded=True, multiprocess=False,
+                 bindAddress=('localhost', 4000), umask=None,
+                 allowedServers=None,
+                 loggingLevel=logging.INFO, debug=True, **kw):
+        """
+        scriptName is the initial portion of the URL path that "belongs"
+        to your application. It is used to determine PATH_INFO (which doesn't
+        seem to be passed in). An empty scriptName means your application
+        is mounted at the root of your virtual host.
+
+        environ, which must be a dictionary, can contain any additional
+        environment variables you want to pass to your application.
+
+        bindAddress is the address to bind to, which must be a string or
+        a tuple of length 2. If a tuple, the first element must be a string,
+        which is the host name or IPv4 address of a local interface. The
+        2nd element of the tuple is the port number. If a string, it will
+        be interpreted as a filename and a UNIX socket will be opened.
+
+        If binding to a UNIX socket, umask may be set to specify what
+        the umask is to be changed to before the socket is created in the
+        filesystem. After the socket is created, the previous umask is
+        restored.
+        
+        allowedServers must be None or a list of strings representing the
+        IPv4 addresses of servers allowed to connect. None means accept
+        connections from anywhere.
+
+        loggingLevel sets the logging level of the module-level logger.
+        """
+        BaseSCGIServer.__init__(self, application,
+                                scriptName=scriptName,
+                                environ=environ,
+                                multithreaded=multithreaded,
+                                multiprocess=multiprocess,
+                                bindAddress=bindAddress,
+                                umask=umask,
+                                allowedServers=allowedServers,
+                                loggingLevel=loggingLevel,
+                                debug=debug)
+        for key in ('jobClass', 'jobArgs'):
+            if kw.has_key(key):
+                del kw[key]
+        ThreadedServer.__init__(self, jobClass=Connection, jobArgs=(self,),
+                                **kw)
+
+    def run(self):
+        """
+        Main loop. Call this after instantiating WSGIServer. SIGHUP, SIGINT,
+        SIGQUIT, SIGTERM cause it to cleanup and return. (If a SIGHUP
+        is caught, this method returns True. Returns False otherwise.)
+        """
+        self.logger.info('%s starting up', self.__class__.__name__)
+
+        try:
+            sock = self._setupSocket()
+        except socket.error, e:
+            self.logger.error('Failed to bind socket (%s), exiting', e[1])
+            return False
+
+        ret = ThreadedServer.run(self, sock)
+
+        self._cleanupSocket(sock)
+
+        self.logger.info('%s shutting down%s', self.__class__.__name__,
+                         self._hupReceived and ' (reload requested)' or '')
+
+        return ret
+
+def factory(global_conf, host=None, port=None, **local):
+    import paste_factory
+    return paste_factory.helper(WSGIServer, global_conf, host, port, **local)
+
+if __name__ == '__main__':
+    def test_app(environ, start_response):
+        """Probably not the most efficient example."""
+        import cgi
+        start_response('200 OK', [('Content-Type', 'text/html')])
+        yield '<html><head><title>Hello World!</title></head>\n' \
+              '<body>\n' \
+              '<p>Hello World!</p>\n' \
+              '<table border="1">'
+        names = environ.keys()
+        names.sort()
+        for name in names:
+            yield '<tr><td>%s</td><td>%s</td></tr>\n' % (
+                name, cgi.escape(`environ[name]`))
+
+        form = cgi.FieldStorage(fp=environ['wsgi.input'], environ=environ,
+                                keep_blank_values=1)
+        if form.list:
+            yield '<tr><th colspan="2">Form data</th></tr>'
+
+        for field in form.list:
+            yield '<tr><td>%s</td><td>%s</td></tr>\n' % (
+                field.name, field.value)
+
+        yield '</table>\n' \
+              '</body></html>\n'
+
+    from wsgiref import validate
+    test_app = validate.validator(test_app)
+    WSGIServer(test_app,
+               loggingLevel=logging.DEBUG).run()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MoinMoin/support/flup/server/scgi_base.py	Thu Aug 14 00:59:13 2008 +0200
@@ -0,0 +1,544 @@
+# Copyright (c) 2005, 2006 Allan Saddi <allan@saddi.com>
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+#    notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+#    notice, this list of conditions and the following disclaimer in the
+#    documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# $Id$
+
+__author__ = 'Allan Saddi <allan@saddi.com>'
+__version__ = '$Revision$'
+
+import sys
+import logging
+import socket
+import select
+import errno
+import cStringIO as StringIO
+import signal
+import datetime
+import os
+import warnings
+
+# Threads are required. If you want a non-threaded (forking) version, look at
+# SWAP <http://www.idyll.org/~t/www-tools/wsgi/>.
+import thread
+import threading
+
+__all__ = ['BaseSCGIServer']
+
+class NoDefault(object):
+    pass
+
+# The main classes use this name for logging.
+LoggerName = 'scgi-wsgi'
+
+# Set up module-level logger.
+console = logging.StreamHandler()
+console.setLevel(logging.DEBUG)
+console.setFormatter(logging.Formatter('%(asctime)s : %(message)s',
+                                       '%Y-%m-%d %H:%M:%S'))
+logging.getLogger(LoggerName).addHandler(console)
+del console
+
+class ProtocolError(Exception):
+    """
+    Exception raised when the server does something unexpected or
+    sends garbled data. Usually leads to a Connection closing.
+    """
+    pass
+
+def recvall(sock, length):
+    """
+    Attempts to receive length bytes from a socket, blocking if necessary.
+    (Socket may be blocking or non-blocking.)
+    """
+    dataList = []
+    recvLen = 0
+    while length:
+        try:
+            data = sock.recv(length)
+        except socket.error, e:
+            if e[0] == errno.EAGAIN:
+                select.select([sock], [], [])
+                continue
+            else:
+                raise
+        if not data: # EOF
+            break
+        dataList.append(data)
+        dataLen = len(data)
+        recvLen += dataLen
+        length -= dataLen
+    return ''.join(dataList), recvLen
+
+def readNetstring(sock):
+    """
+    Attempt to read a netstring from a socket.
+    """
+    # First attempt to read the length.
+    size = ''
+    while True:
+        try:
+            c = sock.recv(1)
+        except socket.error, e:
+            if e[0] == errno.EAGAIN:
+                select.select([sock], [], [])
+                continue
+            else:
+                raise
+        if c == ':':
+            break
+        if not c:
+            raise EOFError
+        size += c
+
+    # Try to decode the length.
+    try:
+        size = int(size)
+        if size < 0:
+            raise ValueError
+    except ValueError:
+        raise ProtocolError, 'invalid netstring length'
+
+    # Now read the string.
+    s, length = recvall(sock, size)
+
+    if length < size:
+        raise EOFError
+
+    # Lastly, the trailer.
+    trailer, length = recvall(sock, 1)
+
+    if length < 1:
+        raise EOFError
+
+    if trailer != ',':
+        raise ProtocolError, 'invalid netstring trailer'
+
+    return s
+
+class StdoutWrapper(object):
+    """
+    Wrapper for sys.stdout so we know if data has actually been written.
+    """
+    def __init__(self, stdout):
+        self._file = stdout
+        self.dataWritten = False
+
+    def write(self, data):
+        if data:
+            self.dataWritten = True
+        self._file.write(data)
+
+    def writelines(self, lines):
+        for line in lines:
+            self.write(line)
+
+    def __getattr__(self, name):
+        return getattr(self._file, name)
+
+class Request(object):
+    """
+    Encapsulates data related to a single request.
+
+    Public attributes:
+      environ - Environment variables from web server.
+      stdin - File-like object representing the request body.
+      stdout - File-like object for writing the response.
+    """
+    def __init__(self, conn, environ, input, output):
+        self._conn = conn
+        self.environ = environ
+        self.stdin = input
+        self.stdout = StdoutWrapper(output)
+
+        self.logger = logging.getLogger(LoggerName)
+
+    def run(self):
+        self.logger.info('%s %s%s',
+                         self.environ['REQUEST_METHOD'],
+                         self.environ.get('SCRIPT_NAME', ''),
+                         self.environ.get('PATH_INFO', ''))
+
+        start = datetime.datetime.now()
+
+        try:
+            self._conn.server.handler(self)
+        except:
+            self.logger.exception('Exception caught from handler')
+            if not self.stdout.dataWritten:
+                self._conn.server.error(self)
+
+        end = datetime.datetime.now()
+
+        handlerTime = end - start
+        self.logger.debug('%s %s%s done (%.3f secs)',
+                          self.environ['REQUEST_METHOD'],
+                          self.environ.get('SCRIPT_NAME', ''),
+                          self.environ.get('PATH_INFO', ''),
+                          handlerTime.seconds +
+                          handlerTime.microseconds / 1000000.0)
+
+class Connection(object):
+    """
+    Represents a single client (web server) connection. A single request
+    is handled, after which the socket is closed.
+    """
+    def __init__(self, sock, addr, server):
+        self._sock = sock
+        self._addr = addr
+        self.server = server
+
+        self.logger = logging.getLogger(LoggerName)
+
+    def run(self):
+        if len(self._addr) == 2:
+            self.logger.debug('Connection starting up (%s:%d)',
+                              self._addr[0], self._addr[1])
+
+        try:
+            self.processInput()
+        except (EOFError, KeyboardInterrupt):
+            pass
+        except ProtocolError, e:
+            self.logger.error("Protocol error '%s'", str(e))
+        except:
+            self.logger.exception('Exception caught in Connection')
+
+        if len(self._addr) == 2:
+            self.logger.debug('Connection shutting down (%s:%d)',
+                              self._addr[0], self._addr[1])
+
+        # All done!
+        self._sock.close()
+
+    def processInput(self):
+        # Read headers
+        headers = readNetstring(self._sock)
+        headers = headers.split('\x00')[:-1]
+        if len(headers) % 2 != 0:
+            raise ProtocolError, 'invalid headers'
+        environ = {}
+        for i in range(len(headers) / 2):
+            environ[headers[2*i]] = headers[2*i+1]
+
+        clen = environ.get('CONTENT_LENGTH')
+        if clen is None:
+            raise ProtocolError, 'missing CONTENT_LENGTH'
+        try:
+            clen = int(clen)
+            if clen < 0:
+                raise ValueError
+        except ValueError:
+            raise ProtocolError, 'invalid CONTENT_LENGTH'
+
+        self._sock.setblocking(1)
+        if clen:
+            input = self._sock.makefile('r')
+        else:
+            # Empty input.
+            input = StringIO.StringIO()
+
+        # stdout
+        output = self._sock.makefile('w')
+
+        # Allocate Request
+        req = Request(self, environ, input, output)
+
+        # Run it.
+        req.run()
+
+        output.close()
+        input.close()
+
+class BaseSCGIServer(object):
+    # What Request class to use.
+    requestClass = Request
+
+    def __init__(self, application, scriptName=NoDefault, environ=None,
+                 multithreaded=True, multiprocess=False,
+                 bindAddress=('localhost', 4000), umask=None,
+                 allowedServers=NoDefault,
+                 loggingLevel=logging.INFO, debug=True):
+        """
+        scriptName is the initial portion of the URL path that "belongs"
+        to your application. It is used to determine PATH_INFO (which doesn't
+        seem to be passed in). An empty scriptName means your application
+        is mounted at the root of your virtual host.
+
+        environ, which must be a dictionary, can contain any additional
+        environment variables you want to pass to your application.
+
+        Set multithreaded to False if your application is not thread-safe.
+
+        Set multiprocess to True to explicitly set wsgi.multiprocess to
+        True. (Only makes sense with threaded servers.)
+
+        bindAddress is the address to bind to, which must be a string or
+        a tuple of length 2. If a tuple, the first element must be a string,
+        which is the host name or IPv4 address of a local interface. The
+        2nd element of the tuple is the port number. If a string, it will
+        be interpreted as a filename and a UNIX socket will be opened.
+
+        If binding to a UNIX socket, umask may be set to specify what
+        the umask is to be changed to before the socket is created in the
+        filesystem. After the socket is created, the previous umask is
+        restored.
+        
+        allowedServers must be None or a list of strings representing the
+        IPv4 addresses of servers allowed to connect. None means accept
+        connections from anywhere. By default, it is a list containing
+        the single item '127.0.0.1'.
+
+        loggingLevel sets the logging level of the module-level logger.
+        """
+        if environ is None:
+            environ = {}
+
+        self.application = application
+        self.scriptName = scriptName
+        self.environ = environ
+        self.multithreaded = multithreaded
+        self.multiprocess = multiprocess
+        self.debug = debug
+        self._bindAddress = bindAddress
+        self._umask = umask
+        if allowedServers is NoDefault:
+            allowedServers = ['127.0.0.1']
+        self._allowedServers = allowedServers
+
+        # Used to force single-threadedness.
+        self._appLock = thread.allocate_lock()
+
+        self.logger = logging.getLogger(LoggerName)
+        self.logger.setLevel(loggingLevel)
+
+    def _setupSocket(self):
+        """Creates and binds the socket for communication with the server."""
+        oldUmask = None
+        if type(self._bindAddress) is str:
+            # Unix socket
+            sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
+            try:
+                os.unlink(self._bindAddress)
+            except OSError:
+                pass
+            if self._umask is not None:
+                oldUmask = os.umask(self._umask)
+        else:
+            # INET socket
+            assert type(self._bindAddress) is tuple
+            assert len(self._bindAddress) == 2
+            sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+            sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+
+        sock.bind(self._bindAddress)
+        sock.listen(socket.SOMAXCONN)
+
+        if oldUmask is not None:
+            os.umask(oldUmask)
+
+        return sock
+
+    def _cleanupSocket(self, sock):
+        """Closes the main socket."""
+        sock.close()
+
+    def _isClientAllowed(self, addr):
+        ret = self._allowedServers is None or \
+              len(addr) != 2 or \
+              (len(addr) == 2 and addr[0] in self._allowedServers)
+        if not ret:
+            self.logger.warning('Server connection from %s disallowed',
+                                addr[0])
+        return ret
+
+    def handler(self, request):
+        """
+        WSGI handler. Sets up WSGI environment, calls the application,
+        and sends the application's response.
+        """
+        environ = request.environ
+        environ.update(self.environ)
+
+        environ['wsgi.version'] = (1,0)
+        environ['wsgi.input'] = request.stdin
+        environ['wsgi.errors'] = sys.stderr
+        environ['wsgi.multithread'] = self.multithreaded
+        environ['wsgi.multiprocess'] = self.multiprocess
+        environ['wsgi.run_once'] = False
+
+        if environ.get('HTTPS', 'off') in ('on', '1'):
+            environ['wsgi.url_scheme'] = 'https'
+        else:
+            environ['wsgi.url_scheme'] = 'http'
+
+        self._sanitizeEnv(environ)
+
+        headers_set = []
+        headers_sent = []
+        result = None
+
+        def write(data):
+            assert type(data) is str, 'write() argument must be string'
+            assert headers_set, 'write() before start_response()'
+
+            if not headers_sent:
+                status, responseHeaders = headers_sent[:] = headers_set
+                found = False
+                for header,value in responseHeaders:
+                    if header.lower() == 'content-length':
+                        found = True
+                        break
+                if not found and result is not None:
+                    try:
+                        if len(result) == 1:
+                            responseHeaders.append(('Content-Length',
+                                                    str(len(data))))
+                    except:
+                        pass
+                s = 'Status: %s\r\n' % status
+                for header in responseHeaders:
+                    s += '%s: %s\r\n' % header
+                s += '\r\n'
+                request.stdout.write(s)
+
+            request.stdout.write(data)
+            request.stdout.flush()
+
+        def start_response(status, response_headers, exc_info=None):
+            if exc_info:
+                try:
+                    if headers_sent:
+                        # Re-raise if too late
+                        raise exc_info[0], exc_info[1], exc_info[2]
+                finally:
+                    exc_info = None # avoid dangling circular ref
+            else:
+                assert not headers_set, 'Headers already set!'
+
+            assert type(status) is str, 'Status must be a string'
+            assert len(status) >= 4, 'Status must be at least 4 characters'
+            assert int(status[:3]), 'Status must begin with 3-digit code'
+            assert status[3] == ' ', 'Status must have a space after code'
+            assert type(response_headers) is list, 'Headers must be a list'
+            if __debug__:
+                for name,val in response_headers:
+                    assert type(name) is str, 'Header names must be strings'
+                    assert type(val) is str, 'Header values must be strings'
+
+            headers_set[:] = [status, response_headers]
+            return write
+
+        if not self.multithreaded:
+            self._appLock.acquire()
+        try:
+            try:
+                result = self.application(environ, start_response)
+                try:
+                    for data in result:
+                        if data:
+                            write(data)
+                    if not headers_sent:
+                        write('') # in case body was empty
+                finally:
+                    if hasattr(result, 'close'):
+                        result.close()
+            except socket.error, e:
+                if e[0] != errno.EPIPE:
+                    raise # Don't let EPIPE propagate beyond server
+        finally:
+            if not self.multithreaded:
+                self._appLock.release()
+
+    def _sanitizeEnv(self, environ):
+        """Fill-in/deduce missing values in environ."""
+        reqUri = None
+        if environ.has_key('REQUEST_URI'):
+            reqUri = environ['REQUEST_URI'].split('?', 1)
+
+        # Ensure QUERY_STRING exists
+        if not environ.has_key('QUERY_STRING') or not environ['QUERY_STRING']:
+            if reqUri is not None and len(reqUri) > 1:
+                environ['QUERY_STRING'] = reqUri[1]
+            else:
+                environ['QUERY_STRING'] = ''
+
+        # Check WSGI_SCRIPT_NAME
+        scriptName = environ.get('WSGI_SCRIPT_NAME')
+        if scriptName is None:
+            scriptName = self.scriptName
+        else:
+            warnings.warn('WSGI_SCRIPT_NAME environment variable for scgi '
+                          'servers is deprecated',
+                          DeprecationWarning)
+            if scriptName.lower() == 'none':
+                scriptName = None
+
+        if scriptName is None:
+            # Do nothing (most likely coming from cgi2scgi)
+            return
+
+        if scriptName is NoDefault:
+            # Pull SCRIPT_NAME/PATH_INFO from environment, with empty defaults
+            if not environ.has_key('SCRIPT_NAME'):
+                environ['SCRIPT_INFO'] = ''
+            if not environ.has_key('PATH_INFO') or not environ['PATH_INFO']:
+                if reqUri is not None:
+                    environ['PATH_INFO'] = reqUri[0]
+                else:
+                    environ['PATH_INFO'] = ''
+        else:
+            # Configured scriptName
+            warnings.warn('Configured SCRIPT_NAME is deprecated\n'
+                          'Do not use WSGI_SCRIPT_NAME or the scriptName\n'
+                          'keyword parameter -- they will be going away',
+                          DeprecationWarning)
+
+            value = environ['SCRIPT_NAME']
+            value += environ.get('PATH_INFO', '')
+            if not value.startswith(scriptName):
+                self.logger.warning('scriptName does not match request URI')
+
+            environ['PATH_INFO'] = value[len(scriptName):]
+            environ['SCRIPT_NAME'] = scriptName
+
+    def error(self, request):
+        """
+        Override to provide custom error handling. Ideally, however,
+        all errors should be caught at the application level.
+        """
+        if self.debug:
+            import cgitb
+            request.stdout.write('Content-Type: text/html\r\n\r\n' +
+                                 cgitb.html(sys.exc_info()))
+        else:
+            errorpage = """<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
+<html><head>
+<title>Unhandled Exception</title>
+</head><body>
+<h1>Unhandled Exception</h1>
+<p>An unhandled exception was thrown by the application.</p>
+</body></html>
+"""
+            request.stdout.write('Content-Type: text/html\r\n\r\n' +
+                                 errorpage)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MoinMoin/support/flup/server/scgi_fork.py	Thu Aug 14 00:59:13 2008 +0200
@@ -0,0 +1,192 @@
+# Copyright (c) 2005, 2006 Allan Saddi <allan@saddi.com>
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+#    notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+#    notice, this list of conditions and the following disclaimer in the
+#    documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# $Id$
+
+"""
+scgi - an SCGI/WSGI gateway.
+
+For more information about SCGI and mod_scgi for Apache1/Apache2, see
+<http://www.mems-exchange.org/software/scgi/>.
+
+For more information about the Web Server Gateway Interface, see
+<http://www.python.org/peps/pep-0333.html>.
+
+Example usage:
+
+  #!/usr/bin/env python
+  import sys
+  from myapplication import app # Assume app is your WSGI application object
+  from scgi import WSGIServer
+  ret = WSGIServer(app).run()
+  sys.exit(ret and 42 or 0)
+
+See the documentation for WSGIServer for more information.
+
+About the bit of logic at the end:
+Upon receiving SIGHUP, the python script will exit with status code 42. This
+can be used by a wrapper script to determine if the python script should be
+re-run. When a SIGINT or SIGTERM is received, the script exits with status
+code 0, possibly indicating a normal exit.
+
+Example wrapper script:
+
+  #!/bin/sh
+  STATUS=42
+  while test $STATUS -eq 42; do
+    python "$@" that_script_above.py
+    STATUS=$?
+  done
+"""
+
+__author__ = 'Allan Saddi <allan@saddi.com>'
+__version__ = '$Revision$'
+
+import logging
+import socket
+
+from flup.server.scgi_base import BaseSCGIServer, Connection, NoDefault
+from flup.server.preforkserver import PreforkServer
+
+__all__ = ['WSGIServer']
+
+class WSGIServer(BaseSCGIServer, PreforkServer):
+    """
+    SCGI/WSGI server. For information about SCGI (Simple Common Gateway
+    Interface), see <http://www.mems-exchange.org/software/scgi/>.
+
+    This server is similar to SWAP <http://www.idyll.org/~t/www-tools/wsgi/>,
+    another SCGI/WSGI server.
+
+    It differs from SWAP in that it isn't based on scgi.scgi_server and
+    therefore, it allows me to implement concurrency using threads. (Also,
+    this server was written from scratch and really has no other depedencies.)
+    Which server to use really boils down to whether you want multithreading
+    or forking. (But as an aside, I've found scgi.scgi_server's implementation
+    of preforking to be quite superior. So if your application really doesn't
+    mind running in multiple processes, go use SWAP. ;)
+    """
+    def __init__(self, application, scriptName=NoDefault, environ=None,
+                 bindAddress=('localhost', 4000), umask=None,
+                 allowedServers=None,
+                 loggingLevel=logging.INFO, debug=True, **kw):
+        """
+        scriptName is the initial portion of the URL path that "belongs"
+        to your application. It is used to determine PATH_INFO (which doesn't
+        seem to be passed in). An empty scriptName means your application
+        is mounted at the root of your virtual host.
+
+        environ, which must be a dictionary, can contain any additional
+        environment variables you want to pass to your application.
+
+        bindAddress is the address to bind to, which must be a string or
+        a tuple of length 2. If a tuple, the first element must be a string,
+        which is the host name or IPv4 address of a local interface. The
+        2nd element of the tuple is the port number. If a string, it will
+        be interpreted as a filename and a UNIX socket will be opened.
+
+        If binding to a UNIX socket, umask may be set to specify what
+        the umask is to be changed to before the socket is created in the
+        filesystem. After the socket is created, the previous umask is
+        restored.
+        
+        allowedServers must be None or a list of strings representing the
+        IPv4 addresses of servers allowed to connect. None means accept
+        connections from anywhere.
+
+        loggingLevel sets the logging level of the module-level logger.
+        """
+        BaseSCGIServer.__init__(self, application,
+                                scriptName=scriptName,
+                                environ=environ,
+                                multithreaded=False,
+                                multiprocess=True,
+                                bindAddress=bindAddress,
+                                umask=umask,
+                                allowedServers=allowedServers,
+                                loggingLevel=loggingLevel,
+                                debug=debug)
+        for key in ('multithreaded', 'multiprocess', 'jobClass', 'jobArgs'):
+            if kw.has_key(key):
+                del kw[key]
+        PreforkServer.__init__(self, jobClass=Connection, jobArgs=(self,), **kw)
+
+    def run(self):
+        """
+        Main loop. Call this after instantiating WSGIServer. SIGHUP, SIGINT,
+        SIGQUIT, SIGTERM cause it to cleanup and return. (If a SIGHUP
+        is caught, this method returns True. Returns False otherwise.)
+        """
+        self.logger.info('%s starting up', self.__class__.__name__)
+
+        try:
+            sock = self._setupSocket()
+        except socket.error, e:
+            self.logger.error('Failed to bind socket (%s), exiting', e[1])
+            return False
+
+        ret = PreforkServer.run(self, sock)
+
+        self._cleanupSocket(sock)
+
+        self.logger.info('%s shutting down%s', self.__class__.__name__,
+                         self._hupReceived and ' (reload requested)' or '')
+
+        return ret
+
+def factory(global_conf, host=None, port=None, **local):
+    import paste_factory
+    return paste_factory.helper(WSGIServer, global_conf, host, port, **local)
+
+if __name__ == '__main__':
+    def test_app(environ, start_response):
+        """Probably not the most efficient example."""
+        import cgi
+        start_response('200 OK', [('Content-Type', 'text/html')])
+        yield '<html><head><title>Hello World!</title></head>\n' \
+              '<body>\n' \
+              '<p>Hello World!</p>\n' \
+              '<table border="1">'
+        names = environ.keys()
+        names.sort()
+        for name in names:
+            yield '<tr><td>%s</td><td>%s</td></tr>\n' % (
+                name, cgi.escape(`environ[name]`))
+
+        form = cgi.FieldStorage(fp=environ['wsgi.input'], environ=environ,
+                                keep_blank_values=1)
+        if form.list:
+            yield '<tr><th colspan="2">Form data</th></tr>'
+
+        for field in form.list:
+            yield '<tr><td>%s</td><td>%s</td></tr>\n' % (
+                field.name, field.value)
+
+        yield '</table>\n' \
+              '</body></html>\n'
+
+    from wsgiref import validate
+    test_app = validate.validator(test_app)
+    WSGIServer(test_app,
+               loggingLevel=logging.DEBUG).run()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MoinMoin/support/flup/server/singleserver.py	Thu Aug 14 00:59:13 2008 +0200
@@ -0,0 +1,166 @@
+# Copyright (c) 2005 Allan Saddi <allan@saddi.com>
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+#    notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+#    notice, this list of conditions and the following disclaimer in the
+#    documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# $Id$
+
+__author__ = 'Allan Saddi <allan@saddi.com>'
+__version__ = '$Revision$'
+
+import sys
+import socket
+import select
+import signal
+import errno
+
+try:
+    import fcntl
+except ImportError:
+    def setCloseOnExec(sock):
+        pass
+else:
+    def setCloseOnExec(sock):
+        fcntl.fcntl(sock.fileno(), fcntl.F_SETFD, fcntl.FD_CLOEXEC)
+
+__all__ = ['SingleServer']
+
+class SingleServer(object):
+    def __init__(self, jobClass=None, jobArgs=(), **kw):
+        self._jobClass = jobClass
+        self._jobArgs = jobArgs
+
+    def run(self, sock, timeout=1.0):
+        """
+        The main loop. Pass a socket that is ready to accept() client
+        connections. Return value will be True or False indiciating whether
+        or not the loop was exited due to SIGHUP.
+        """
+        # Set up signal handlers.
+        self._keepGoing = True
+        self._hupReceived = False
+
+        # Might need to revisit this?
+        if not sys.platform.startswith('win'):
+            self._installSignalHandlers()
+
+        # Set close-on-exec
+        setCloseOnExec(sock)
+        
+        # Main loop.
+        while self._keepGoing:
+            try:
+                r, w, e = select.select([sock], [], [], timeout)
+            except select.error, e:
+                if e[0] == errno.EINTR:
+                    continue
+                raise
+
+            if r:
+                try:
+                    clientSock, addr = sock.accept()
+                except socket.error, e:
+                    if e[0] in (errno.EINTR, errno.EAGAIN):
+                        continue
+                    raise
+
+                setCloseOnExec(clientSock)
+                
+                if not self._isClientAllowed(addr):
+                    clientSock.close()
+                    continue
+
+                # Hand off to Connection.
+                conn = self._jobClass(clientSock, addr, *self._jobArgs)
+                conn.run()
+
+            self._mainloopPeriodic()
+
+        # Restore signal handlers.
+        self._restoreSignalHandlers()
+
+        # Return bool based on whether or not SIGHUP was received.
+        return self._hupReceived
+
+    def _mainloopPeriodic(self):
+        """
+        Called with just about each iteration of the main loop. Meant to
+        be overridden.
+        """
+        pass
+
+    def _exit(self, reload=False):
+        """
+        Protected convenience method for subclasses to force an exit. Not
+        really thread-safe, which is why it isn't public.
+        """
+        if self._keepGoing:
+            self._keepGoing = False
+            self._hupReceived = reload
+
+    def _isClientAllowed(self, addr):
+        """Override to provide access control."""
+        return True
+
+    # Signal handlers
+
+    def _hupHandler(self, signum, frame):
+        self._hupReceived = True
+        self._keepGoing = False
+
+    def _intHandler(self, signum, frame):
+        self._keepGoing = False
+
+    def _installSignalHandlers(self):
+        supportedSignals = [signal.SIGINT, signal.SIGTERM]
+        if hasattr(signal, 'SIGHUP'):
+            supportedSignals.append(signal.SIGHUP)
+
+        self._oldSIGs = [(x,signal.getsignal(x)) for x in supportedSignals]
+
+        for sig in supportedSignals:
+            if hasattr(signal, 'SIGHUP') and sig == signal.SIGHUP:
+                signal.signal(sig, self._hupHandler)
+            else:
+                signal.signal(sig, self._intHandler)
+
+    def _restoreSignalHandlers(self):
+        for signum,handler in self._oldSIGs:
+            signal.signal(signum, handler)
+
+if __name__ == '__main__':
+    class TestJob(object):
+        def __init__(self, sock, addr):
+            self._sock = sock
+            self._addr = addr
+        def run(self):
+            print "Client connection opened from %s:%d" % self._addr
+            self._sock.send('Hello World!\n')
+            self._sock.setblocking(1)
+            self._sock.recv(1)
+            self._sock.close()
+            print "Client connection closed from %s:%d" % self._addr
+    sock = socket.socket()
+    sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+    sock.bind(('', 8080))
+    sock.listen(socket.SOMAXCONN)
+    SingleServer(jobClass=TestJob).run(sock)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MoinMoin/support/flup/server/threadedserver.py	Thu Aug 14 00:59:13 2008 +0200
@@ -0,0 +1,175 @@
+# Copyright (c) 2005 Allan Saddi <allan@saddi.com>
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+#    notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+#    notice, this list of conditions and the following disclaimer in the
+#    documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# $Id$
+
+__author__ = 'Allan Saddi <allan@saddi.com>'
+__version__ = '$Revision$'
+
+import sys
+import socket
+import select
+import signal
+import errno
+
+try:
+    import fcntl
+except ImportError:
+    def setCloseOnExec(sock):
+        pass
+else:
+    def setCloseOnExec(sock):
+        fcntl.fcntl(sock.fileno(), fcntl.F_SETFD, fcntl.FD_CLOEXEC)
+
+from flup.server.threadpool import ThreadPool
+
+__all__ = ['ThreadedServer']
+
+class ThreadedServer(object):
+    def __init__(self, jobClass=None, jobArgs=(), **kw):
+        self._jobClass = jobClass
+        self._jobArgs = jobArgs
+
+        self._threadPool = ThreadPool(**kw)
+
+    def run(self, sock, timeout=1.0):
+        """
+        The main loop. Pass a socket that is ready to accept() client
+        connections. Return value will be True or False indiciating whether
+        or not the loop was exited due to SIGHUP.
+        """
+        # Set up signal handlers.
+        self._keepGoing = True
+        self._hupReceived = False
+
+        # Might need to revisit this?
+        if not sys.platform.startswith('win'):
+            self._installSignalHandlers()
+
+        # Set close-on-exec
+        setCloseOnExec(sock)
+        
+        # Main loop.
+        while self._keepGoing:
+            try:
+                r, w, e = select.select([sock], [], [], timeout)
+            except select.error, e:
+                if e[0] == errno.EINTR:
+                    continue
+                raise
+
+            if r:
+                try:
+                    clientSock, addr = sock.accept()
+                except socket.error, e:
+                    if e[0] in (errno.EINTR, errno.EAGAIN):
+                        continue
+                    raise
+
+                setCloseOnExec(clientSock)
+                
+                if not self._isClientAllowed(addr):
+                    clientSock.close()
+                    continue
+
+                # Hand off to Connection.
+                conn = self._jobClass(clientSock, addr, *self._jobArgs)
+                if not self._threadPool.addJob(conn, allowQueuing=False):
+                    # No thread left, immediately close the socket to hopefully
+                    # indicate to the web server that we're at our limit...
+                    # and to prevent having too many opened (and useless)
+                    # files.
+                    clientSock.close()
+
+            self._mainloopPeriodic()
+
+        # Restore signal handlers.
+        self._restoreSignalHandlers()
+
+        # Return bool based on whether or not SIGHUP was received.
+        return self._hupReceived
+
+    def _mainloopPeriodic(self):
+        """
+        Called with just about each iteration of the main loop. Meant to
+        be overridden.
+        """
+        pass
+
+    def _exit(self, reload=False):
+        """
+        Protected convenience method for subclasses to force an exit. Not
+        really thread-safe, which is why it isn't public.
+        """
+        if self._keepGoing:
+            self._keepGoing = False
+            self._hupReceived = reload
+
+    def _isClientAllowed(self, addr):
+        """Override to provide access control."""
+        return True
+
+    # Signal handlers
+
+    def _hupHandler(self, signum, frame):
+        self._hupReceived = True
+        self._keepGoing = False
+
+    def _intHandler(self, signum, frame):
+        self._keepGoing = False
+
+    def _installSignalHandlers(self):
+        supportedSignals = [signal.SIGINT, signal.SIGTERM]
+        if hasattr(signal, 'SIGHUP'):
+            supportedSignals.append(signal.SIGHUP)
+
+        self._oldSIGs = [(x,signal.getsignal(x)) for x in supportedSignals]
+
+        for sig in supportedSignals:
+            if hasattr(signal, 'SIGHUP') and sig == signal.SIGHUP:
+                signal.signal(sig, self._hupHandler)
+            else:
+                signal.signal(sig, self._intHandler)
+
+    def _restoreSignalHandlers(self):
+        for signum,handler in self._oldSIGs:
+            signal.signal(signum, handler)
+
+if __name__ == '__main__':
+    class TestJob(object):
+        def __init__(self, sock, addr):
+            self._sock = sock
+            self._addr = addr
+        def run(self):
+            print "Client connection opened from %s:%d" % self._addr
+            self._sock.send('Hello World!\n')
+            self._sock.setblocking(1)
+            self._sock.recv(1)
+            self._sock.close()
+            print "Client connection closed from %s:%d" % self._addr
+    sock = socket.socket()
+    sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+    sock.bind(('', 8080))
+    sock.listen(socket.SOMAXCONN)
+    ThreadedServer(maxThreads=10, jobClass=TestJob).run(sock)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MoinMoin/support/flup/server/threadpool.py	Thu Aug 14 00:59:13 2008 +0200
@@ -0,0 +1,121 @@
+# Copyright (c) 2005 Allan Saddi <allan@saddi.com>
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+#    notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+#    notice, this list of conditions and the following disclaimer in the
+#    documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# $Id$
+
+__author__ = 'Allan Saddi <allan@saddi.com>'
+__version__ = '$Revision$'
+
+import sys
+import thread
+import threading
+
+class ThreadPool(object):
+    """
+    Thread pool that maintains the number of idle threads between
+    minSpare and maxSpare inclusive. By default, there is no limit on
+    the number of threads that can be started, but this can be controlled
+    by maxThreads.
+    """
+    def __init__(self, minSpare=1, maxSpare=5, maxThreads=sys.maxint):
+        self._minSpare = minSpare
+        self._maxSpare = maxSpare
+        self._maxThreads = max(minSpare, maxThreads)
+
+        self._lock = threading.Condition()
+        self._workQueue = []
+        self._idleCount = self._workerCount = maxSpare
+
+        # Start the minimum number of worker threads.
+        for i in range(maxSpare):
+            thread.start_new_thread(self._worker, ())
+
+    def addJob(self, job, allowQueuing=True):
+        """
+        Adds a job to the work queue. The job object should have a run()
+        method. If allowQueuing is True (the default), the job will be
+        added to the work queue regardless if there are any idle threads
+        ready. (The only way for there to be no idle threads is if maxThreads
+        is some reasonable, finite limit.)
+
+        Otherwise, if allowQueuing is False, and there are no more idle
+        threads, the job will not be queued.
+
+        Returns True if the job was queued, False otherwise.
+        """
+        self._lock.acquire()
+        try:
+            # Maintain minimum number of spares.
+            while self._idleCount < self._minSpare and \
+                  self._workerCount < self._maxThreads:
+                self._workerCount += 1
+                self._idleCount += 1
+                thread.start_new_thread(self._worker, ())
+
+            # Hand off the job.
+            if self._idleCount or allowQueuing:
+                self._workQueue.append(job)
+                self._lock.notify()
+                return True
+            else:
+                return False
+        finally:
+            self._lock.release()
+
+    def _worker(self):
+        """
+        Worker thread routine. Waits for a job, executes it, repeat.
+        """
+        self._lock.acquire()
+        while True:
+            while not self._workQueue:
+                self._lock.wait()
+
+            # We have a job to do...
+            job = self._workQueue.pop(0)
+
+            assert self._idleCount > 0
+            self._idleCount -= 1
+
+            self._lock.release()
+
+            try:
+                job.run()
+            except:
+                # FIXME: This should really be reported somewhere.
+                # But we can't simply report it to stderr because of fcgi
+                pass
+
+            self._lock.acquire()
+
+            if self._idleCount == self._maxSpare:
+                break # NB: lock still held
+            self._idleCount += 1
+            assert self._idleCount <= self._maxSpare
+
+        # Die off...
+        assert self._workerCount > self._maxSpare
+        self._workerCount -= 1
+
+        self._lock.release()
--- a/contrib/flup-server/ChangeLog	Wed Aug 13 21:06:45 2008 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,243 +0,0 @@
-2008-07-23  Allan Saddi  <allan@saddi.com>
-
-	* Add support for configuring UNIX domain sockets (for servers that
-	  support them) in the paste.server_factory implementations. Thanks
-	  to Dan Roberts for the code.
-
-2008-07-22  Allan Saddi  <allan@saddi.com>
-
-	* Release 1.0.1
-
-	* Attempt to deduce missing PATH_INFO and/or QUERY_STRING from
-	  REQUEST_URI, if present. Patch provided by Richard Davies.
-
-2007-09-10  Allan Saddi  <allan@saddi.com>
-
-	* Fix readline implementations so size argument is checked
-	  earlier.
-
-2007-07-14  Allan Saddi  <allan@saddi.com>
-
-	* Prevent ThreadPool inconsistences if an exception is
-	  actually raised. Thanks to Tim Chen for the patch.
-
-2007-06-05  Allan Saddi  <allan@saddi.com>
-
-	* Remove publisher and middleware packages.
-	* Add cgi server for completeness.
-
-2007-05-17  Allan Saddi  <allan@saddi.com>
-
-	* Fix fcgi_fork so it can run on Solaris. Thanks to
-	  Basil Crow for the patch.
-
-2007-01-22  Allan Saddi  <allan@saddi.com>
-
-	* Fix eunuchs import issue.
-
-2007-01-10  Allan Saddi  <allan@saddi.com>
-
-	* Support gzip compression of XHTML pages using the
-	  correct MIME type.
-
-2006-12-29  Allan Saddi  <allan@saddi.com>
-
-	* Deprecate WSGI_SCRIPT_NAME and scriptName in scgi_base.
-	  Modern versions of mod_scgi correctly set SCRIPT_NAME &
-	  PATH_INFO.
-
-2006-12-13  Allan Saddi  <allan@saddi.com>
-
-	* Fix problem in session.py seen when optimization is on.
-
-2006-12-05  Allan Saddi  <allan@saddi.com>
-
-	* Update servers to default to an empty QUERY_STRING if
-	  not present in the environ.
-	* Update gzip.py: compresslevel -> compress_level
-	* Update gzip.py by updating docstrings and renaming
-	  classes/methods/functions to better follow Python naming
-	  conventions. NB: mimeTypes keyword parameter is now
-	  mime_types.
-
-2006-12-02  Allan Saddi  <allan@saddi.com>
-
-	* Change intra-package imports into absolute imports.
-
-2006-12-02  Allan Saddi  <allan@saddi.com>
-
-	* Add forceCookieOutput attribute to SessionService to
-	  force Set-Cookie output for the current request.
-
-2006-12-01  Allan Saddi  <allan@saddi.com>
-
-	* Update setup script.
-
-2006-11-26  Allan Saddi  <allan@saddi.com>
-
-	* Don't attempt to install signal handlers under Windows
-	  to improve compatibility.
-
-2006-11-24  Allan Saddi  <allan@saddi.com>
-
-	* Add *_thread egg entry-point aliases.
-	* Add UNIX domain socket support to scgi, scgi_fork,
-	  scgi_app.
-	* Add flup.client package which contains various
-	  WSGI -> connector client implentations. (So far: FastCGI,
-	  and SCGI.)
-
-2006-11-19  Allan Saddi  <allan@saddi.com>
-
-	* Change mime-type matching algorithm in GzipMiddleware.
-	  Strip parameters (e.g. "encoding") and accept a list of
-	  regexps. By default, compress 'text/.*' mime-types.
-
-2006-11-10  Allan Saddi  <allan@saddi.com>
-
-	* Add cookieAttributes to SessionService to make it easier
-	  to customize the generated cookie's attributes.
-
-2006-08-28  Allan Saddi  <allan@saddi.com>
-
-	* Add support for FastCGI roles other than FCGI_RESPONDER.
-	  Patch provided by Seairth Jacobs.
-
-2006-08-02  Allan Saddi  <allan@saddi.com>
-
-	* Add cookieExpiration keyword to SessionService /
-	  SessionMiddleware to adjust the session cookie's expiration.
-	  Thanks to Blaise Laflamme for the suggestion.
-
-2006-06-27  Allan Saddi  <allan@saddi.com>
-
-	* Set close-on-exec flag on all server sockets. Thanks to
-	  Ralf Schmitt for reporting the problem.
-
-2006-06-18  Allan Saddi  <allan@saddi.com>
-
-	* Stop ignoring EPIPE exceptions, as this is probably the
-	  wrong thing to do. (Application is unaware of disconnected
-	  clients and the CPU spins when sending large files to a
-	  disconnected client.) Thanks to Ivan Sagalaev for bringing
-	  this to my attention.
-
-	  NB: Existing applications that use the flup servers may begin
-	  seeing socket.error exceptions...
-
-2006-05-18  Allan Saddi  <allan@saddi.com>
-
-	* Added umask keyword parameter to fcgi and fcgi_fork,
-	  for use when binding to a UNIX socket.
-
-2006-05-03  Allan Saddi  <allan@saddi.com>
-
-	* Fix illusive problem with AJP implementation. Thanks to
-	  Moshe Van der Sterre for explaining the problem and
-	  providing a fix.
-
-2006-04-06  Allan Saddi  <allan@saddi.com>
-
-	* Catch a strange FieldStorage case. Seen in production.
-	  Not quite sure what causes it.
-
-2006-03-21  Allan Saddi  <allan@saddi.com>
-
-	* Add maxRequests option to PreforkServer. Patch provided by
-	  Wojtek Sobczuk.
-
-2006-02-23  Allan Saddi  <allan@saddi.com>
-
-	* Add paste.server_factory-compliant factories and respective
-	  egg entry points. Thanks to Luis Bruno for the code.
-
-	  Add debug option to servers, which is True by default.
-	  Currently, only server-level error handling is affected.
-	
-2006-01-15  Allan Saddi  <allan@saddi.com>
-
-	* Change the behavior of ImportingModuleResolver when dealing
-	  with ImportErrors. Previously, it would act as if the module
-	  did not exist. Now, it propagates the exception to another
-	  level (outer middleware or WSGI). Reported by Scot Doyle.
-
-2006-01-05  Allan Saddi  <allan@saddi.com>
-
-	* Improve Windows compatibility by conditionally installing
-	  SIGHUP handler. Thanks to Brad Miller for pointing out the
-	  problem and providing a fix.
-
-2005-12-19  Allan Saddi  <allan@saddi.com>
-
-	* Fix socket leak in eunuchs socketpair() wrapper. Thanks to
-	  Georg Bauer for pointing this out.
-
-2005-12-16  Allan Saddi  <allan@saddi.com>
-
-	* Switch to setuptools for egg support.
-	* Add higher-level 404 error page support. Thanks to Scot Doyle
-	  for suggesting the idea and providing code. If you previously
-	  subclassed Publisher to provide a custom 404 error page, this
-	  is now broken. It will have to be massaged to fit the new
-	  calling convention.
-
-2005-11-28  Allan Saddi  <allan@saddi.com>
-
-	* Fix issue with FCGI_GET_VALUES handling. Thanks to
-	  Timothy Wright for pointing this out.
-
-2005-11-18  Allan Saddi  <allan@saddi.com>
-
-	* When running under Python < 2.4, attempt to use socketpair()
-	  from eunuchs module.
-
-2005-09-07  Allan Saddi  <allan@saddi.com>
-
-	* Python 2.3 doesn't define socket.SHUT_WR, which affected
-	  the closing of the FastCGI socket with the server. This would
-	  cause output to hang. Thanks to Eugene Lazutkin for bringing
-	  the problem to my attention and going out of his way to help
-	  me debug it!
-
-2005-07-03  Allan Saddi  <allan@saddi.com>
-
-	* Ensure session identifiers only contain ASCII characters when
-	  using a non-ASCII locale. Thanks to Ksenia Marasanova for the
-	  the fix.
-
-2005-06-12  Allan Saddi  <allan@saddi.com>
-
-	* Cleanly close connection socket to avoid sending a TCP RST to
-	  the web server. (fcgi_base) Fix suggested by Dima Barsky.
-
-2005-05-31  Allan Saddi  <allan@saddi.com>
-
-	* Take scriptName from the WSGI_SCRIPT_NAME environment variable
-	  passed from the web server, if present.
-	* Check if scriptName is None, and if so, don't modify SCRIPT_NAME
-	  & PATH_INFO. For better compatibility with cgi2scgi. (scgi_base)
-
-2005-05-18  Allan Saddi  <allan@saddi.com>
-
-	* Change default allowedServers for ajp and scgi to ['127.0.0.1'].
-	* Accept PATH_INFO from environment for scgi servers, in case
-	  cgi2scgi is being used. Submitted by Ian Bicking.
-	* Change threaded servers so wsgi.multiprocess is False by default.
-	  Allow it to be changed by keyword argument.
-	* Fix wsgi.multiprocess for scgi_fork. (Set to True.)
-
-2005-05-15  Allan Saddi  <allan@saddi.com>
-
-	* Prevent possible deadlock related to DiskSessionStore locking.
-	* Add logic to SessionStore so that it will block if attempting to
-	  check out a Session that's already been checked out.
-
-2005-05-14  Allan Saddi  <allan@saddi.com>
-
-	* Convert the use of decorators in session.py to something
-	  compatible with Python <2.4.
-
-2005-04-23  Allan Saddi  <allan@saddi.com>
-
-	* Ensure that SessionStore.checkOutSession() never returns an
-	  invalidated Session. Reported by Rene Dudfield.
--- a/contrib/flup-server/NOTES.moin	Wed Aug 13 21:06:45 2008 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,17 +0,0 @@
-NOTES for bundled flup:
-=======================
-This directory contains the flup WSGI adapter package. It has been
-patched to support completely singlethreaded non-forking servers for
-deployment in webservers, that have their own process spawning strategy.
-
-The shipped version is available via mercurial checkout:
-
-hg clone -r aa983e43105e http://hg.saddi.com/flup-server
-
-The singlethreaded server patch was provided via a trac bugreport
-(http://trac.saddi.com/flup/ticket/22) and is provided in this directory
-as singleserver.diff for convenience. It has already been applied.
-
-Thanks to Allan Saddi <allan@saddi.com> for writing flup and to
-the unnamed contributor <tv@inoi.fi> for the single-thread patch.
-
--- a/contrib/flup-server/flup/__init__.py	Wed Aug 13 21:06:45 2008 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1 +0,0 @@
-#
--- a/contrib/flup-server/flup/client/__init__.py	Wed Aug 13 21:06:45 2008 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1 +0,0 @@
-#
--- a/contrib/flup-server/flup/client/fcgi_app.py	Wed Aug 13 21:06:45 2008 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,461 +0,0 @@
-# Copyright (c) 2006 Allan Saddi <allan@saddi.com>
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions
-# are met:
-# 1. Redistributions of source code must retain the above copyright
-#    notice, this list of conditions and the following disclaimer.
-# 2. Redistributions in binary form must reproduce the above copyright
-#    notice, this list of conditions and the following disclaimer in the
-#    documentation and/or other materials provided with the distribution.
-#
-# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
-# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
-# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-# SUCH DAMAGE.
-#
-# $Id$
-
-__author__ = 'Allan Saddi <allan@saddi.com>'
-__version__ = '$Revision$'
-
-import select
-import struct
-import socket
-import errno
-
-__all__ = ['FCGIApp']
-
-# Constants from the spec.
-FCGI_LISTENSOCK_FILENO = 0
-
-FCGI_HEADER_LEN = 8
-
-FCGI_VERSION_1 = 1
-
-FCGI_BEGIN_REQUEST = 1
-FCGI_ABORT_REQUEST = 2
-FCGI_END_REQUEST = 3
-FCGI_PARAMS = 4
-FCGI_STDIN = 5
-FCGI_STDOUT = 6
-FCGI_STDERR = 7
-FCGI_DATA = 8
-FCGI_GET_VALUES = 9
-FCGI_GET_VALUES_RESULT = 10
-FCGI_UNKNOWN_TYPE = 11
-FCGI_MAXTYPE = FCGI_UNKNOWN_TYPE
-
-FCGI_NULL_REQUEST_ID = 0
-
-FCGI_KEEP_CONN = 1
-
-FCGI_RESPONDER = 1
-FCGI_AUTHORIZER = 2
-FCGI_FILTER = 3
-
-FCGI_REQUEST_COMPLETE = 0
-FCGI_CANT_MPX_CONN = 1
-FCGI_OVERLOADED = 2
-FCGI_UNKNOWN_ROLE = 3
-
-FCGI_MAX_CONNS = 'FCGI_MAX_CONNS'
-FCGI_MAX_REQS = 'FCGI_MAX_REQS'
-FCGI_MPXS_CONNS = 'FCGI_MPXS_CONNS'
-
-FCGI_Header = '!BBHHBx'
-FCGI_BeginRequestBody = '!HB5x'
-FCGI_EndRequestBody = '!LB3x'
-FCGI_UnknownTypeBody = '!B7x'
-
-FCGI_BeginRequestBody_LEN = struct.calcsize(FCGI_BeginRequestBody)
-FCGI_EndRequestBody_LEN = struct.calcsize(FCGI_EndRequestBody)
-FCGI_UnknownTypeBody_LEN = struct.calcsize(FCGI_UnknownTypeBody)
-
-if __debug__:
-    import time
-
-    # Set non-zero to write debug output to a file.
-    DEBUG = 0
-    DEBUGLOG = '/tmp/fcgi_app.log'
-
-    def _debug(level, msg):
-        if DEBUG < level:
-            return
-
-        try:
-            f = open(DEBUGLOG, 'a')
-            f.write('%sfcgi: %s\n' % (time.ctime()[4:-4], msg))
-            f.close()
-        except:
-            pass
-
-def decode_pair(s, pos=0):
-    """
-    Decodes a name/value pair.
-
-    The number of bytes decoded as well as the name/value pair
-    are returned.
-    """
-    nameLength = ord(s[pos])
-    if nameLength & 128:
-        nameLength = struct.unpack('!L', s[pos:pos+4])[0] & 0x7fffffff
-        pos += 4
-    else:
-        pos += 1
-
-    valueLength = ord(s[pos])
-    if valueLength & 128:
-        valueLength = struct.unpack('!L', s[pos:pos+4])[0] & 0x7fffffff
-        pos += 4
-    else:
-        pos += 1
-
-    name = s[pos:pos+nameLength]
-    pos += nameLength
-    value = s[pos:pos+valueLength]
-    pos += valueLength
-
-    return (pos, (name, value))
-
-def encode_pair(name, value):
-    """
-    Encodes a name/value pair.
-
-    The encoded string is returned.
-    """
-    nameLength = len(name)
-    if nameLength < 128:
-        s = chr(nameLength)
-    else:
-        s = struct.pack('!L', nameLength | 0x80000000L)
-
-    valueLength = len(value)
-    if valueLength < 128:
-        s += chr(valueLength)
-    else:
-        s += struct.pack('!L', valueLength | 0x80000000L)
-
-    return s + name + value
-
-class Record(object):
-    """
-    A FastCGI Record.
-
-    Used for encoding/decoding records.
-    """
-    def __init__(self, type=FCGI_UNKNOWN_TYPE, requestId=FCGI_NULL_REQUEST_ID):
-        self.version = FCGI_VERSION_1
-        self.type = type
-        self.requestId = requestId
-        self.contentLength = 0
-        self.paddingLength = 0
-        self.contentData = ''
-
-    def _recvall(sock, length):
-        """
-        Attempts to receive length bytes from a socket, blocking if necessary.
-        (Socket may be blocking or non-blocking.)
-        """
-        dataList = []
-        recvLen = 0
-        while length:
-            try:
-                data = sock.recv(length)
-            except socket.error, e:
-                if e[0] == errno.EAGAIN:
-                    select.select([sock], [], [])
-                    continue
-                else:
-                    raise
-            if not data: # EOF
-                break
-            dataList.append(data)
-            dataLen = len(data)
-            recvLen += dataLen
-            length -= dataLen
-        return ''.join(dataList), recvLen
-    _recvall = staticmethod(_recvall)
-
-    def read(self, sock):
-        """Read and decode a Record from a socket."""
-        try:
-            header, length = self._recvall(sock, FCGI_HEADER_LEN)
-        except:
-            raise EOFError
-
-        if length < FCGI_HEADER_LEN:
-            raise EOFError
-        
-        self.version, self.type, self.requestId, self.contentLength, \
-                      self.paddingLength = struct.unpack(FCGI_Header, header)
-
-        if __debug__: _debug(9, 'read: fd = %d, type = %d, requestId = %d, '
-                             'contentLength = %d' %
-                             (sock.fileno(), self.type, self.requestId,
-                              self.contentLength))
-        
-        if self.contentLength:
-            try:
-                self.contentData, length = self._recvall(sock,
-                                                         self.contentLength)
-            except:
-                raise EOFError
-
-            if length < self.contentLength:
-                raise EOFError
-
-        if self.paddingLength:
-            try:
-                self._recvall(sock, self.paddingLength)
-            except:
-                raise EOFError
-
-    def _sendall(sock, data):
-        """
-        Writes data to a socket and does not return until all the data is sent.
-        """
-        length = len(data)
-        while length:
-            try:
-                sent = sock.send(data)
-            except socket.error, e:
-                if e[0] == errno.EAGAIN:
-                    select.select([], [sock], [])
-                    continue
-                else:
-                    raise
-            data = data[sent:]
-            length -= sent
-    _sendall = staticmethod(_sendall)
-
-    def write(self, sock):
-        """Encode and write a Record to a socket."""
-        self.paddingLength = -self.contentLength & 7
-
-        if __debug__: _debug(9, 'write: fd = %d, type = %d, requestId = %d, '
-                             'contentLength = %d' %
-                             (sock.fileno(), self.type, self.requestId,
-                              self.contentLength))
-
-        header = struct.pack(FCGI_Header, self.version, self.type,
-                             self.requestId, self.contentLength,
-                             self.paddingLength)
-        self._sendall(sock, header)
-        if self.contentLength:
-            self._sendall(sock, self.contentData)
-        if self.paddingLength:
-            self._sendall(sock, '\x00'*self.paddingLength)
-
-class FCGIApp(object):
-    def __init__(self, command=None, connect=None, host=None, port=None,
-                 filterEnviron=True):
-        if host is not None:
-            assert port is not None
-            connect=(host, port)
-
-        assert (command is not None and connect is None) or \
-               (command is None and connect is not None)
-
-        self._command = command
-        self._connect = connect
-
-        self._filterEnviron = filterEnviron
-        
-        #sock = self._getConnection()
-        #print self._fcgiGetValues(sock, ['FCGI_MAX_CONNS', 'FCGI_MAX_REQS', 'FCGI_MPXS_CONNS'])
-        #sock.close()
-        
-    def __call__(self, environ, start_response):
-        # For sanity's sake, we don't care about FCGI_MPXS_CONN
-        # (connection multiplexing). For every request, we obtain a new
-        # transport socket, perform the request, then discard the socket.
-        # This is, I believe, how mod_fastcgi does things...
-
-        sock = self._getConnection()
-
-        # Since this is going to be the only request on this connection,
-        # set the request ID to 1.
-        requestId = 1
-
-        # Begin the request
-        rec = Record(FCGI_BEGIN_REQUEST, requestId)
-        rec.contentData = struct.pack(FCGI_BeginRequestBody, FCGI_RESPONDER, 0)
-        rec.contentLength = FCGI_BeginRequestBody_LEN
-        rec.write(sock)
-
-        # Filter WSGI environ and send it as FCGI_PARAMS
-        if self._filterEnviron:
-            params = self._defaultFilterEnviron(environ)
-        else:
-            params = self._lightFilterEnviron(environ)
-        # TODO: Anything not from environ that needs to be sent also?
-        self._fcgiParams(sock, requestId, params)
-        self._fcgiParams(sock, requestId, {})
-
-        # Transfer wsgi.input to FCGI_STDIN
-        content_length = int(environ.get('CONTENT_LENGTH') or 0)
-        while True:
-            chunk_size = min(content_length, 4096)
-            s = environ['wsgi.input'].read(chunk_size)
-            content_length -= len(s)
-            rec = Record(FCGI_STDIN, requestId)
-            rec.contentData = s
-            rec.contentLength = len(s)
-            rec.write(sock)
-
-            if not s: break
-
-        # Empty FCGI_DATA stream
-        rec = Record(FCGI_DATA, requestId)
-        rec.write(sock)
-
-        # Main loop. Process FCGI_STDOUT, FCGI_STDERR, FCGI_END_REQUEST
-        # records from the application.
-        result = []
-        while True:
-            inrec = Record()
-            inrec.read(sock)
-            if inrec.type == FCGI_STDOUT:
-                if inrec.contentData:
-                    result.append(inrec.contentData)
-                else:
-                    # TODO: Should probably be pedantic and no longer
-                    # accept FCGI_STDOUT records?
-                    pass
-            elif inrec.type == FCGI_STDERR:
-                # Simply forward to wsgi.errors
-                environ['wsgi.errors'].write(inrec.contentData)
-            elif inrec.type == FCGI_END_REQUEST:
-                # TODO: Process appStatus/protocolStatus fields?
-                break
-
-        # Done with this transport socket, close it. (FCGI_KEEP_CONN was not
-        # set in the FCGI_BEGIN_REQUEST record we sent above. So the
-        # application is expected to do the same.)
-        sock.close()
-
-        result = ''.join(result)
-
-        # Parse response headers from FCGI_STDOUT
-        status = '200 OK'
-        headers = []
-        pos = 0
-        while True:
-            eolpos = result.find('\n', pos)
-            if eolpos < 0: break
-            line = result[pos:eolpos-1]
-            pos = eolpos + 1
-
-            # strip in case of CR. NB: This will also strip other
-            # whitespace...
-            line = line.strip()
-            
-            # Empty line signifies end of headers
-            if not line: break
-
-            # TODO: Better error handling
-            header, value = line.split(':', 1)
-            header = header.strip().lower()
-            value = value.strip()
-
-            if header == 'status':
-                # Special handling of Status header
-                status = value
-                if status.find(' ') < 0:
-                    # Append a dummy reason phrase if one was not provided
-                    status += ' FCGIApp'
-            else:
-                headers.append((header, value))
-
-        result = result[pos:]
-
-        # Set WSGI status, headers, and return result.
-        start_response(status, headers)
-        return [result]
-
-    def _getConnection(self):
-        if self._connect is not None:
-            # The simple case. Create a socket and connect to the
-            # application.
-            if type(self._connect) is str:
-                sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
-            else:
-                sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
-            sock.connect(self._connect)
-            return sock
-
-        # To be done when I have more time...
-        raise NotImplementedError, 'Launching and managing FastCGI programs not yet implemented'
-    
-    def _fcgiGetValues(self, sock, vars):
-        # Construct FCGI_GET_VALUES record
-        outrec = Record(FCGI_GET_VALUES)
-        data = []
-        for name in vars:
-            data.append(encode_pair(name, ''))
-        data = ''.join(data)
-        outrec.contentData = data
-        outrec.contentLength = len(data)
-        outrec.write(sock)
-
-        # Await response
-        inrec = Record()
-        inrec.read(sock)
-        result = {}
-        if inrec.type == FCGI_GET_VALUES_RESULT:
-            pos = 0
-            while pos < inrec.contentLength:
-                pos, (name, value) = decode_pair(inrec.contentData, pos)
-                result[name] = value
-        return result
-
-    def _fcgiParams(self, sock, requestId, params):
-        rec = Record(FCGI_PARAMS, requestId)
-        data = []
-        for name,value in params.items():
-            data.append(encode_pair(name, value))
-        data = ''.join(data)
-        rec.contentData = data
-        rec.contentLength = len(data)
-        rec.write(sock)
-
-    _environPrefixes = ['SERVER_', 'HTTP_', 'REQUEST_', 'REMOTE_', 'PATH_',
-                        'CONTENT_']
-    _environCopies = ['SCRIPT_NAME', 'QUERY_STRING', 'AUTH_TYPE']
-    _environRenames = {}
-
-    def _defaultFilterEnviron(self, environ):
-        result = {}
-        for n in environ.keys():
-            for p in self._environPrefixes:
-                if n.startswith(p):
-                    result[n] = environ[n]
-            if n in self._environCopies:
-                result[n] = environ[n]
-            if n in self._environRenames:
-                result[self._environRenames[n]] = environ[n]
-                
-        return result
-
-    def _lightFilterEnviron(self, environ):
-        result = {}
-        for n in environ.keys():
-            if n.upper() == n:
-                result[n] = environ[n]
-        return result
-
-if __name__ == '__main__':
-    from flup.server.ajp import WSGIServer
-    app = FCGIApp(connect=('localhost', 4242))
-    #import paste.lint
-    #app = paste.lint.middleware(app)
-    WSGIServer(app).run()
--- a/contrib/flup-server/flup/client/scgi_app.py	Wed Aug 13 21:06:45 2008 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,176 +0,0 @@
-# Copyright (c) 2006 Allan Saddi <allan@saddi.com>
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions
-# are met:
-# 1. Redistributions of source code must retain the above copyright
-#    notice, this list of conditions and the following disclaimer.
-# 2. Redistributions in binary form must reproduce the above copyright
-#    notice, this list of conditions and the following disclaimer in the
-#    documentation and/or other materials provided with the distribution.
-#
-# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
-# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
-# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-# SUCH DAMAGE.
-#
-# $Id$
-
-__author__ = 'Allan Saddi <allan@saddi.com>'
-__version__ = '$Revision$'
-
-import select
-import struct
-import socket
-import errno
-
-__all__ = ['SCGIApp']
-
-def encodeNetstring(s):
-    return ''.join([str(len(s)), ':', s, ','])
-
-class SCGIApp(object):
-    def __init__(self, connect=None, host=None, port=None,
-                 filterEnviron=True):
-        if host is not None:
-            assert port is not None
-            connect=(host, port)
-
-        assert connect is not None
-        self._connect = connect
-
-        self._filterEnviron = filterEnviron
-        
-    def __call__(self, environ, start_response):
-        sock = self._getConnection()
-
-        outfile = sock.makefile('w')
-        infile = sock.makefile('r')
-
-        sock.close()
-
-        # Filter WSGI environ and send as request headers
-        if self._filterEnviron:
-            headers = self._defaultFilterEnviron(environ)
-        else:
-            headers = self._lightFilterEnviron(environ)
-        # TODO: Anything not from environ that needs to be sent also?
-
-        content_length = int(environ.get('CONTENT_LENGTH') or 0)
-        if headers.has_key('CONTENT_LENGTH'):
-            del headers['CONTENT_LENGTH']
-            
-        headers_out = ['CONTENT_LENGTH', str(content_length), 'SCGI', '1']
-        for k,v in headers.items():
-            headers_out.append(k)
-            headers_out.append(v)
-        headers_out.append('') # For trailing NUL
-        outfile.write(encodeNetstring('\x00'.join(headers_out)))
-
-        # Transfer wsgi.input to outfile
-        while True:
-            chunk_size = min(content_length, 4096)
-            s = environ['wsgi.input'].read(chunk_size)
-            content_length -= len(s)
-            outfile.write(s)
-
-            if not s: break
-
-        outfile.close()
-        
-        # Read result from SCGI server
-        result = []
-        while True:
-            buf = infile.read(4096)
-            if not buf: break
-
-            result.append(buf)
-
-        infile.close()
-        
-        result = ''.join(result)
-
-        # Parse response headers
-        status = '200 OK'
-        headers = []
-        pos = 0
-        while True:
-            eolpos = result.find('\n', pos)
-            if eolpos < 0: break
-            line = result[pos:eolpos-1]
-            pos = eolpos + 1
-
-            # strip in case of CR. NB: This will also strip other
-            # whitespace...
-            line = line.strip()
-            
-            # Empty line signifies end of headers
-            if not line: break
-
-            # TODO: Better error handling
-            header, value = line.split(':', 1)
-            header = header.strip().lower()
-            value = value.strip()
-
-            if header == 'status':
-                # Special handling of Status header
-                status = value
-                if status.find(' ') < 0:
-                    # Append a dummy reason phrase if one was not provided
-                    status += ' SCGIApp'
-            else:
-                headers.append((header, value))
-
-        result = result[pos:]
-
-        # Set WSGI status, headers, and return result.
-        start_response(status, headers)
-        return [result]
-
-    def _getConnection(self):
-        if type(self._connect) is str:
-            sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
-        else:
-            sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
-        sock.connect(self._connect)
-        return sock
-    
-    _environPrefixes = ['SERVER_', 'HTTP_', 'REQUEST_', 'REMOTE_', 'PATH_',
-                        'CONTENT_']
-    _environCopies = ['SCRIPT_NAME', 'QUERY_STRING', 'AUTH_TYPE']
-    _environRenames = {}
-
-    def _defaultFilterEnviron(self, environ):
-        result = {}
-        for n in environ.keys():
-            for p in self._environPrefixes:
-                if n.startswith(p):
-                    result[n] = environ[n]
-            if n in self._environCopies:
-                result[n] = environ[n]
-            if n in self._environRenames:
-                result[self._environRenames[n]] = environ[n]
-                
-        return result
-
-    def _lightFilterEnviron(self, environ):
-        result = {}
-        for n in environ.keys():
-            if n.upper() == n:
-                result[n] = environ[n]
-        return result
-
-if __name__ == '__main__':
-    from flup.server.ajp import WSGIServer
-    app = SCGIApp(connect=('localhost', 4000))
-    #import paste.lint
-    #app = paste.lint.middleware(app)
-    WSGIServer(app).run()
--- a/contrib/flup-server/flup/server/__init__.py	Wed Aug 13 21:06:45 2008 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1 +0,0 @@
-#
--- a/contrib/flup-server/flup/server/ajp.py	Wed Aug 13 21:06:45 2008 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,201 +0,0 @@
-# Copyright (c) 2005, 2006 Allan Saddi <allan@saddi.com>
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions
-# are met:
-# 1. Redistributions of source code must retain the above copyright
-#    notice, this list of conditions and the following disclaimer.
-# 2. Redistributions in binary form must reproduce the above copyright
-#    notice, this list of conditions and the following disclaimer in the
-#    documentation and/or other materials provided with the distribution.
-#
-# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
-# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
-# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-# SUCH DAMAGE.
-#
-# $Id$
-
-"""
-ajp - an AJP 1.3/WSGI gateway.
-
-For more information about AJP and AJP connectors for your web server, see
-<http://jakarta.apache.org/tomcat/connectors-doc/>.
-
-For more information about the Web Server Gateway Interface, see
-<http://www.python.org/peps/pep-0333.html>.
-
-Example usage:
-
-  #!/usr/bin/env python
-  import sys
-  from myapplication import app # Assume app is your WSGI application object
-  from ajp import WSGIServer
-  ret = WSGIServer(app).run()
-  sys.exit(ret and 42 or 0)
-
-See the documentation for WSGIServer for more information.
-
-About the bit of logic at the end:
-Upon receiving SIGHUP, the python script will exit with status code 42. This
-can be used by a wrapper script to determine if the python script should be
-re-run. When a SIGINT or SIGTERM is received, the script exits with status
-code 0, possibly indicating a normal exit.
-
-Example wrapper script:
-
-  #!/bin/sh
-  STATUS=42
-  while test $STATUS -eq 42; do
-    python "$@" that_script_above.py
-    STATUS=$?
-  done
-
-Example workers.properties (for mod_jk):
-
-  worker.list=foo
-  worker.foo.port=8009
-  worker.foo.host=localhost
-  worker.foo.type=ajp13
-
-Example httpd.conf (for mod_jk):
-
-  JkWorkersFile /path/to/workers.properties
-  JkMount /* foo
-
-Note that if you mount your ajp application anywhere but the root ("/"), you
-SHOULD specifiy scriptName to the WSGIServer constructor. This will ensure
-that SCRIPT_NAME/PATH_INFO are correctly deduced.
-"""
-
-__author__ = 'Allan Saddi <allan@saddi.com>'
-__version__ = '$Revision$'
-
-import socket
-import logging
-
-from flup.server.ajp_base import BaseAJPServer, Connection
-from flup.server.threadedserver import ThreadedServer
-
-__all__ = ['WSGIServer']
-
-class WSGIServer(BaseAJPServer, ThreadedServer):
-    """
-    AJP1.3/WSGI server. Runs your WSGI application as a persistant program
-    that understands AJP1.3. Opens up a TCP socket, binds it, and then
-    waits for forwarded requests from your webserver.
-
-    Why AJP? Two good reasons are that AJP provides load-balancing and
-    fail-over support. Personally, I just wanted something new to
-    implement. :)
-
-    Of course you will need an AJP1.3 connector for your webserver (e.g.
-    mod_jk) - see <http://jakarta.apache.org/tomcat/connectors-doc/>.
-    """
-    def __init__(self, application, scriptName='', environ=None,
-                 multithreaded=True, multiprocess=False,
-                 bindAddress=('localhost', 8009), allowedServers=None,
-                 loggingLevel=logging.INFO, debug=True, **kw):
-        """
-        scriptName is the initial portion of the URL path that "belongs"
-        to your application. It is used to determine PATH_INFO (which doesn't
-        seem to be passed in). An empty scriptName means your application
-        is mounted at the root of your virtual host.
-
-        environ, which must be a dictionary, can contain any additional
-        environment variables you want to pass to your application.
-
-        bindAddress is the address to bind to, which must be a tuple of
-        length 2. The first element is a string, which is the host name
-        or IPv4 address of a local interface. The 2nd element is the port
-        number.
-
-        allowedServers must be None or a list of strings representing the
-        IPv4 addresses of servers allowed to connect. None means accept
-        connections from anywhere.
-
-        loggingLevel sets the logging level of the module-level logger.
-        """
-        BaseAJPServer.__init__(self, application,
-                               scriptName=scriptName,
-                               environ=environ,
-                               multithreaded=multithreaded,
-                               multiprocess=multiprocess,
-                               bindAddress=bindAddress,
-                               allowedServers=allowedServers,
-                               loggingLevel=loggingLevel,
-                               debug=debug)
-        for key in ('jobClass', 'jobArgs'):
-            if kw.has_key(key):
-                del kw[key]
-        ThreadedServer.__init__(self, jobClass=Connection, jobArgs=(self,),
-                                **kw)
-
-    def run(self):
-        """
-        Main loop. Call this after instantiating WSGIServer. SIGHUP, SIGINT,
-        SIGQUIT, SIGTERM cause it to cleanup and return. (If a SIGHUP
-        is caught, this method returns True. Returns False otherwise.)
-        """
-        self.logger.info('%s starting up', self.__class__.__name__)
-
-        try:
-            sock = self._setupSocket()
-        except socket.error, e:
-            self.logger.error('Failed to bind socket (%s), exiting', e[1])
-            return False
-
-        ret = ThreadedServer.run(self, sock)
-
-        self._cleanupSocket(sock)
-
-        self.logger.info('%s shutting down%s', self.__class__.__name__,
-                         self._hupReceived and ' (reload requested)' or '')
-
-        return ret
-
-def factory(global_conf, host=None, port=None, **local):
-    import paste_factory
-    return paste_factory.helper(WSGIServer, global_conf, host, port, **local)
-
-if __name__ == '__main__':
-    def test_app(environ, start_response):
-        """Probably not the most efficient example."""
-        import cgi
-        start_response('200 OK', [('Content-Type', 'text/html')])
-        yield '<html><head><title>Hello World!</title></head>\n' \
-              '<body>\n' \
-              '<p>Hello World!</p>\n' \
-              '<table border="1">'
-        names = environ.keys()
-        names.sort()
-        for name in names:
-            yield '<tr><td>%s</td><td>%s</td></tr>\n' % (
-                name, cgi.escape(`environ[name]`))
-
-        form = cgi.FieldStorage(fp=environ['wsgi.input'], environ=environ,
-                                keep_blank_values=1)
-        if form.list:
-            yield '<tr><th colspan="2">Form data</th></tr>'
-
-        for field in form.list:
-            yield '<tr><td>%s</td><td>%s</td></tr>\n' % (
-                field.name, field.value)
-
-        yield '</table>\n' \
-              '</body></html>\n'
-
-    from wsgiref import validate
-    test_app = validate.validator(test_app)
-    # Explicitly set bindAddress to *:8009 for testing.
-    WSGIServer(test_app,
-               bindAddress=('', 8009), allowedServers=None,
-               loggingLevel=logging.DEBUG).run()
--- a/contrib/flup-server/flup/server/ajp_base.py	Wed Aug 13 21:06:45 2008 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,956 +0,0 @@
-# Copyright (c) 2005, 2006 Allan Saddi <allan@saddi.com>
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions
-# are met:
-# 1. Redistributions of source code must retain the above copyright
-#    notice, this list of conditions and the following disclaimer.
-# 2. Redistributions in binary form must reproduce the above copyright
-#    notice, this list of conditions and the following disclaimer in the
-#    documentation and/or other materials provided with the distribution.
-#
-# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
-# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
-# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-# SUCH DAMAGE.
-#
-# $Id$
-
-__author__ = 'Allan Saddi <allan@saddi.com>'
-__version__ = '$Revision$'
-
-import sys
-import socket
-import select
-import struct
-import signal
-import logging
-import errno
-import datetime
-import time
-
-# Unfortunately, for now, threads are required.
-import thread
-import threading
-
-__all__ = ['BaseAJPServer']
-
-class NoDefault(object):
-    pass
-
-# Packet header prefixes.
-SERVER_PREFIX = '\x12\x34'
-CONTAINER_PREFIX = 'AB'
-
-# Server packet types.
-PKTTYPE_FWD_REQ = '\x02'
-PKTTYPE_SHUTDOWN = '\x07'
-PKTTYPE_PING = '\x08'
-PKTTYPE_CPING = '\x0a'
-
-# Container packet types.
-PKTTYPE_SEND_BODY = '\x03'
-PKTTYPE_SEND_HEADERS = '\x04'
-PKTTYPE_END_RESPONSE = '\x05'
-PKTTYPE_GET_BODY = '\x06'
-PKTTYPE_CPONG = '\x09'
-
-# Code tables for methods/headers/attributes.
-methodTable = [
-    None,
-    'OPTIONS',
-    'GET',
-    'HEAD',
-    'POST',
-    'PUT',
-    'DELETE',
-    'TRACE',
-    'PROPFIND',
-    'PROPPATCH',
-    'MKCOL',
-    'COPY',
-    'MOVE',
-    'LOCK',
-    'UNLOCK',
-    'ACL',
-    'REPORT',
-    'VERSION-CONTROL',
-    'CHECKIN',
-    'CHECKOUT',
-    'UNCHECKOUT',
-    'SEARCH',
-    'MKWORKSPACE',
-    'UPDATE',
-    'LABEL',
-    'MERGE',
-    'BASELINE_CONTROL',
-    'MKACTIVITY'
-    ]
-
-requestHeaderTable = [
-    None,
-    'Accept',
-    'Accept-Charset',
-    'Accept-Encoding',
-    'Accept-Language',
-    'Authorization',
-    'Connection',
-    'Content-Type',
-    'Content-Length',
-    'Cookie',
-    'Cookie2',
-    'Host',
-    'Pragma',
-    'Referer',
-    'User-Agent'
-    ]
-
-attributeTable = [
-    None,
-    'CONTEXT',
-    'SERVLET_PATH',
-    'REMOTE_USER',
-    'AUTH_TYPE',
-    'QUERY_STRING',
-    'JVM_ROUTE',
-    'SSL_CERT',
-    'SSL_CIPHER',
-    'SSL_SESSION',
-    None, # name follows
-    'SSL_KEY_SIZE'
-    ]
-
-responseHeaderTable = [
-    None,
-    'content-type',
-    'content-language',
-    'content-length',
-    'date',
-    'last-modified',
-    'location',
-    'set-cookie',
-    'set-cookie2',
-    'servlet-engine',
-    'status',
-    'www-authenticate'
-    ]
-
-# The main classes use this name for logging.
-LoggerName = 'ajp-wsgi'
-
-# Set up module-level logger.
-console = logging.StreamHandler()
-console.setLevel(logging.DEBUG)
-console.setFormatter(logging.Formatter('%(asctime)s : %(message)s',
-                                       '%Y-%m-%d %H:%M:%S'))
-logging.getLogger(LoggerName).addHandler(console)
-del console
-
-class ProtocolError(Exception):
-    """
-    Exception raised when the server does something unexpected or
-    sends garbled data. Usually leads to a Connection closing.
-    """
-    pass
-
-def decodeString(data, pos=0):
-    """Decode a string."""
-    try:
-        length = struct.unpack('>H', data[pos:pos+2])[0]
-        pos += 2
-        if length == 0xffff: # This was undocumented!
-            return '', pos
-        s = data[pos:pos+length]
-        return s, pos+length+1 # Don't forget NUL
-    except Exception, e:
-        raise ProtocolError, 'decodeString: '+str(e)
-
-def decodeRequestHeader(data, pos=0):
-    """Decode a request header/value pair."""
-    try:
-        if data[pos] == '\xa0':
-            # Use table
-            i = ord(data[pos+1])
-            name = requestHeaderTable[i]
-            if name is None:
-                raise ValueError, 'bad request header code'
-            pos += 2
-        else:
-            name, pos = decodeString(data, pos)
-        value, pos = decodeString(data, pos)
-        return name, value, pos
-    except Exception, e:
-        raise ProtocolError, 'decodeRequestHeader: '+str(e)
-
-def decodeAttribute(data, pos=0):
-    """Decode a request attribute."""
-    try:
-        i = ord(data[pos])
-        pos += 1
-        if i == 0xff:
-            # end
-            return None, None, pos
-        elif i == 0x0a:
-            # name follows
-            name, pos = decodeString(data, pos)
-        elif i == 0x0b:
-            # Special handling of SSL_KEY_SIZE.
-            name = attributeTable[i]
-            # Value is an int, not a string.
-            value = struct.unpack('>H', data[pos:pos+2])[0]
-            return name, str(value), pos+2
-        else:
-            name = attributeTable[i]
-            if name is None:
-                raise ValueError, 'bad attribute code'
-        value, pos = decodeString(data, pos)
-        return name, value, pos
-    except Exception, e:
-        raise ProtocolError, 'decodeAttribute: '+str(e)
-
-def encodeString(s):
-    """Encode a string."""
-    return struct.pack('>H', len(s)) + s + '\x00'
-
-def encodeResponseHeader(name, value):
-    """Encode a response header/value pair."""
-    lname = name.lower()
-    if lname in responseHeaderTable:
-        # Use table
-        i = responseHeaderTable.index(lname)
-        out = '\xa0' + chr(i)
-    else:
-        out = encodeString(name)
-    out += encodeString(value)
-    return out
-
-class Packet(object):
-    """An AJP message packet."""
-    def __init__(self):
-        self.data = ''
-        # Don't set this on write, it will be calculated automatically.
-        self.length = 0
-
-    def _recvall(sock, length):
-        """
-        Attempts to receive length bytes from a socket, blocking if necessary.
-        (Socket may be blocking or non-blocking.)
-        """
-        dataList = []
-        recvLen = 0
-        while length:
-            try:
-                data = sock.recv(length)
-            except socket.error, e:
-                if e[0] == errno.EAGAIN:
-                    select.select([sock], [], [])
-                    continue
-                else:
-                    raise
-            if not data: # EOF
-                break
-            dataList.append(data)
-            dataLen = len(data)
-            recvLen += dataLen
-            length -= dataLen
-        return ''.join(dataList), recvLen
-    _recvall = staticmethod(_recvall)
-
-    def read(self, sock):
-        """Attempt to read a packet from the server."""
-        try:
-            header, length = self._recvall(sock, 4)
-        except socket.error:
-            # Treat any sort of socket errors as EOF (close Connection).
-            raise EOFError
-
-        if length < 4:
-            raise EOFError
-
-        if header[:2] != SERVER_PREFIX:
-            raise ProtocolError, 'invalid header'
-
-        self.length = struct.unpack('>H', header[2:4])[0]
-        if self.length:
-            try:
-                self.data, length = self._recvall(sock, self.length)
-            except socket.error:
-                raise EOFError
-
-            if length < self.length:
-                raise EOFError
-
-    def _sendall(sock, data):
-        """
-        Writes data to a socket and does not return until all the data is sent.
-        """
-        length = len(data)
-        while length:
-            try:
-                sent = sock.send(data)
-            except socket.error, e:
-                if e[0] == errno.EAGAIN:
-                    select.select([], [sock], [])
-                    continue
-                else:
-                    raise
-            data = data[sent:]
-            length -= sent
-    _sendall = staticmethod(_sendall)
-
-    def write(self, sock):
-        """Send a packet to the server."""
-        self.length = len(self.data)
-        self._sendall(sock, CONTAINER_PREFIX + struct.pack('>H', self.length))
-        if self.length:
-            self._sendall(sock, self.data)
-
-class InputStream(object):
-    """
-    File-like object that represents the request body (if any). Supports
-    the bare mininum methods required by the WSGI spec. Thanks to
-    StringIO for ideas.
-    """
-    def __init__(self, conn):
-        self._conn = conn
-
-        # See WSGIServer.
-        self._shrinkThreshold = conn.server.inputStreamShrinkThreshold
-
-        self._buf = ''
-        self._bufList = []
-        self._pos = 0 # Current read position.
-        self._avail = 0 # Number of bytes currently available.
-        self._length = 0 # Set to Content-Length in request.
-
-        self.logger = logging.getLogger(LoggerName)
-
-    def bytesAvailForAdd(self):
-        return self._length - self._avail
-
-    def _shrinkBuffer(self):
-        """Gets rid of already read data (since we can't rewind)."""
-        if self._pos >= self._shrinkThreshold:
-            self._buf = self._buf[self._pos:]
-            self._avail -= self._pos
-            self._length -= self._pos
-            self._pos = 0
-
-            assert self._avail >= 0 and self._length >= 0
-
-    def _waitForData(self):
-        toAdd = min(self.bytesAvailForAdd(), 0xffff)
-        assert toAdd > 0
-        pkt = Packet()
-        pkt.data = PKTTYPE_GET_BODY + \
-                   struct.pack('>H', toAdd)
-        self._conn.writePacket(pkt)
-        self._conn.processInput()
-
-    def read(self, n=-1):
-        if self._pos == self._length:
-            return ''
-        while True:
-            if n < 0 or (self._avail - self._pos) < n:
-                # Not enough data available.
-                if not self.bytesAvailForAdd():
-                    # And there's no more coming.
-                    newPos = self._avail
-                    break
-                else:
-                    # Ask for more data and wait.
-                    self._waitForData()
-                    continue
-            else:
-                newPos = self._pos + n
-                break
-        # Merge buffer list, if necessary.
-        if self._bufList:
-            self._buf += ''.join(self._bufList)
-            self._bufList = []
-        r = self._buf[self._pos:newPos]
-        self._pos = newPos
-        self._shrinkBuffer()
-        return r
-
-    def readline(self, length=None):
-        if self._pos == self._length:
-            return ''
-        while True:
-            # Unfortunately, we need to merge the buffer list early.
-            if self._bufList:
-                self._buf += ''.join(self._bufList)
-                self._bufList = []
-            # Find newline.
-            i = self._buf.find('\n', self._pos)
-            if i < 0:
-                # Not found?
-                if not self.bytesAvailForAdd():
-                    # No more data coming.
-                    newPos = self._avail
-                    break
-                else:
-                    if length is not None and len(self._buf) >= length + self._pos:
-                        newPos = self._pos + length
-                        break
-                    # Wait for more to come.
-                    self._waitForData()
-                    continue
-            else:
-                newPos = i + 1
-                break
-        r = self._buf[self._pos:newPos]
-        self._pos = newPos
-        self._shrinkBuffer()
-        return r
-
-    def readlines(self, sizehint=0):
-        total = 0
-        lines = []
-        line = self.readline()
-        while line:
-            lines.append(line)
-            total += len(line)
-            if 0 < sizehint <= total:
-                break
-            line = self.readline()
-        return lines
-
-    def __iter__(self):
-        return self
-
-    def next(self):
-        r = self.readline()
-        if not r:
-            raise StopIteration
-        return r
-
-    def setDataLength(self, length):
-        """
-        Once Content-Length is known, Request calls this method to set it.
-        """
-        self._length = length
-
-    def addData(self, data):
-        """
-        Adds data from the server to this InputStream. Note that we never ask
-        the server for data beyond the Content-Length, so the server should
-        never send us an EOF (empty string argument).
-        """
-        if not data:
-            raise ProtocolError, 'short data'
-        self._bufList.append(data)
-        length = len(data)
-        self._avail += length
-        if self._avail > self._length:
-            raise ProtocolError, 'too much data'
-
-class Request(object):
-    """
-    A Request object. A more fitting name would probably be Transaction, but
-    it's named Request to mirror my FastCGI driver. :) This object
-    encapsulates all the data about the HTTP request and allows the handler
-    to send a response.
-
-    The only attributes/methods that the handler should concern itself
-    with are: environ, input, startResponse(), and write().
-    """
-    # Do not ever change the following value.
-    _maxWrite = 8192 - 4 - 3 - 1 # 8k - pkt header - send body header - NUL
-
-    def __init__(self, conn):
-        self._conn = conn
-
-        self.environ = {}
-        self.input = InputStream(conn)
-
-        self._headersSent = False
-
-        self.logger = logging.getLogger(LoggerName)
-
-    def run(self):
-        self.logger.info('%s %s',
-                         self.environ['REQUEST_METHOD'],
-                         self.environ['REQUEST_URI'])
-
-        start = datetime.datetime.now()
-
-        try:
-            self._conn.server.handler(self)
-        except:
-            self.logger.exception('Exception caught from handler')
-            if not self._headersSent:
-                self._conn.server.error(self)
-
-        end = datetime.datetime.now()
-
-        # Notify server of end of response (reuse flag is set to true).
-        pkt = Packet()
-        pkt.data = PKTTYPE_END_RESPONSE + '\x01'
-        self._conn.writePacket(pkt)
-
-        handlerTime = end - start
-        self.logger.debug('%s %s done (%.3f secs)',
-                          self.environ['REQUEST_METHOD'],
-                          self.environ['REQUEST_URI'],
-                          handlerTime.seconds +
-                          handlerTime.microseconds / 1000000.0)
-
-    # The following methods are called from the Connection to set up this
-    # Request.
-
-    def setMethod(self, value):
-        self.environ['REQUEST_METHOD'] = value
-
-    def setProtocol(self, value):
-        self.environ['SERVER_PROTOCOL'] = value
-
-    def setRequestURI(self, value):
-        self.environ['REQUEST_URI'] = value
-
-    def setRemoteAddr(self, value):
-        self.environ['REMOTE_ADDR'] = value
-
-    def setRemoteHost(self, value):
-        self.environ['REMOTE_HOST'] = value
-
-    def setServerName(self, value):
-        self.environ['SERVER_NAME'] = value
-
-    def setServerPort(self, value):
-        self.environ['SERVER_PORT'] = str(value)
-
-    def setIsSSL(self, value):
-        if value:
-            self.environ['HTTPS'] = 'on'
-
-    def addHeader(self, name, value):
-        name = name.replace('-', '_').upper()
-        if name in ('CONTENT_TYPE', 'CONTENT_LENGTH'):
-            self.environ[name] = value
-            if name == 'CONTENT_LENGTH':
-                length = int(value)
-                self.input.setDataLength(length)
-        else:
-            self.environ['HTTP_'+name] = value
-
-    def addAttribute(self, name, value):
-        self.environ[name] = value
-
-    # The only two methods that should be called from the handler.
-
-    def startResponse(self, statusCode, statusMsg, headers):
-        """
-        Begin the HTTP response. This must only be called once and it
-        must be called before any calls to write().
-
-        statusCode is the integer status code (e.g. 200). statusMsg
-        is the associated reason message (e.g.'OK'). headers is a list
-        of 2-tuples - header name/value pairs. (Both header name and value
-        must be strings.)
-        """
-        assert not self._headersSent, 'Headers already sent!'
-
-        pkt = Packet()
-        pkt.data = PKTTYPE_SEND_HEADERS + \
-                   struct.pack('>H', statusCode) + \
-                   encodeString(statusMsg) + \
-                   struct.pack('>H', len(headers)) + \
-                   ''.join([encodeResponseHeader(name, value)
-                            for name,value in headers])
-
-        self._conn.writePacket(pkt)
-
-        self._headersSent = True
-
-    def write(self, data):
-        """
-        Write data (which comprises the response body). Note that due to
-        restrictions on AJP packet size, we limit our writes to 8185 bytes
-        each packet.
-        """
-        assert self._headersSent, 'Headers must be sent first!'
-
-        bytesLeft = len(data)
-        while bytesLeft:
-            toWrite = min(bytesLeft, self._maxWrite)
-
-            pkt = Packet()
-            pkt.data = PKTTYPE_SEND_BODY + \
-                       struct.pack('>H', toWrite) + \
-                       data[:toWrite] + '\x00' # Undocumented
-            self._conn.writePacket(pkt)
-
-            data = data[toWrite:]
-            bytesLeft -= toWrite
-
-class Connection(object):
-    """
-    A single Connection with the server. Requests are not multiplexed over the
-    same connection, so at any given time, the Connection is either
-    waiting for a request, or processing a single request.
-    """
-    def __init__(self, sock, addr, server):
-        self.server = server
-        self._sock = sock
-        self._addr = addr
-
-        self._request = None
-
-        self.logger = logging.getLogger(LoggerName)
-
-    def run(self):
-        self.logger.debug('Connection starting up (%s:%d)',
-                          self._addr[0], self._addr[1])
-
-        # Main loop. Errors will cause the loop to be exited and
-        # the socket to be closed.
-        while True:
-            try:
-                self.processInput()
-            except ProtocolError, e:
-                self.logger.error("Protocol error '%s'", str(e))
-                break
-            except (EOFError, KeyboardInterrupt):
-                break
-            except:
-                self.logger.exception('Exception caught in Connection')
-                break
-
-        self.logger.debug('Connection shutting down (%s:%d)',
-                          self._addr[0], self._addr[1])
-
-        self._sock.close()
-
-    def processInput(self):
-        """Wait for and process a single packet."""
-        pkt = Packet()
-        select.select([self._sock], [], [])
-        pkt.read(self._sock)
-
-        # Body chunks have no packet type code.
-        if self._request is not None:
-            self._processBody(pkt)
-            return
-
-        if not pkt.length:
-            raise ProtocolError, 'unexpected empty packet'
-
-        pkttype = pkt.data[0]
-        if pkttype == PKTTYPE_FWD_REQ:
-            self._forwardRequest(pkt)
-        elif pkttype == PKTTYPE_SHUTDOWN:
-            self._shutdown(pkt)
-        elif pkttype == PKTTYPE_PING:
-            self._ping(pkt)
-        elif pkttype == PKTTYPE_CPING:
-            self._cping(pkt)
-        else:
-            raise ProtocolError, 'unknown packet type'
-
-    def _forwardRequest(self, pkt):
-        """
-        Creates a Request object, fills it in from the packet, then runs it.
-        """
-        assert self._request is None
-
-        req = self.server.requestClass(self)
-        i = ord(pkt.data[1])
-        method = methodTable[i]
-        if method is None:
-            raise ValueError, 'bad method field'
-        req.setMethod(method)
-        value, pos = decodeString(pkt.data, 2)
-        req.setProtocol(value)
-        value, pos = decodeString(pkt.data, pos)
-        req.setRequestURI(value)
-        value, pos = decodeString(pkt.data, pos)
-        req.setRemoteAddr(value)
-        value, pos = decodeString(pkt.data, pos)
-        req.setRemoteHost(value)
-        value, pos = decodeString(pkt.data, pos)
-        req.setServerName(value)
-        value = struct.unpack('>H', pkt.data[pos:pos+2])[0]
-        req.setServerPort(value)
-        i = ord(pkt.data[pos+2])
-        req.setIsSSL(i != 0)
-
-        # Request headers.
-        numHeaders = struct.unpack('>H', pkt.data[pos+3:pos+5])[0]
-        pos += 5
-        for i in range(numHeaders):
-            name, value, pos = decodeRequestHeader(pkt.data, pos)
-            req.addHeader(name, value)
-
-        # Attributes.
-        while True:
-            name, value, pos = decodeAttribute(pkt.data, pos)
-            if name is None:
-                break
-            req.addAttribute(name, value)
-
-        self._request = req
-
-        # Read first body chunk, if needed.
-        if req.input.bytesAvailForAdd():
-            self.processInput()
-
-        # Run Request.
-        req.run()
-
-        self._request = None
-
-    def _shutdown(self, pkt):
-        """Not sure what to do with this yet."""
-        self.logger.info('Received shutdown request from server')
-
-    def _ping(self, pkt):
-        """I have no idea what this packet means."""
-        self.logger.debug('Received ping')
-
-    def _cping(self, pkt):
-        """Respond to a PING (CPING) packet."""
-        self.logger.debug('Received PING, sending PONG')
-        pkt = Packet()
-        pkt.data = PKTTYPE_CPONG
-        self.writePacket(pkt)
-
-    def _processBody(self, pkt):
-        """
-        Handles a body chunk from the server by appending it to the
-        InputStream.
-        """
-        if pkt.length:
-            length = struct.unpack('>H', pkt.data[:2])[0]
-            self._request.input.addData(pkt.data[2:2+length])
-        else:
-            # Shouldn't really ever get here.
-            self._request.input.addData('')
-
-    def writePacket(self, pkt):
-        """Sends a Packet to the server."""
-        pkt.write(self._sock)
-
-class BaseAJPServer(object):
-    # What Request class to use.
-    requestClass = Request
-
-    # Limits the size of the InputStream's string buffer to this size + 8k.
-    # Since the InputStream is not seekable, we throw away already-read
-    # data once this certain amount has been read. (The 8k is there because
-    # it is the maximum size of new data added per chunk.)
-    inputStreamShrinkThreshold = 102400 - 8192
-
-    def __init__(self, application, scriptName='', environ=None,
-                 multithreaded=True, multiprocess=False,
-                 bindAddress=('localhost', 8009), allowedServers=NoDefault,
-                 loggingLevel=logging.INFO, debug=True):
-        """
-        scriptName is the initial portion of the URL path that "belongs"
-        to your application. It is used to determine PATH_INFO (which doesn't
-        seem to be passed in). An empty scriptName means your application
-        is mounted at the root of your virtual host.
-
-        environ, which must be a dictionary, can contain any additional
-        environment variables you want to pass to your application.
-
-        Set multithreaded to False if your application is not thread-safe.
-
-        Set multiprocess to True to explicitly set wsgi.multiprocess to
-        True. (Only makes sense with threaded servers.)
-
-        bindAddress is the address to bind to, which must be a tuple of
-        length 2. The first element is a string, which is the host name
-        or IPv4 address of a local interface. The 2nd element is the port
-        number.
-
-        allowedServers must be None or a list of strings representing the
-        IPv4 addresses of servers allowed to connect. None means accept
-        connections from anywhere. By default, it is a list containing
-        the single item '127.0.0.1'.
-
-        loggingLevel sets the logging level of the module-level logger.
-        """
-        if environ is None:
-            environ = {}
-
-        self.application = application
-        self.scriptName = scriptName
-        self.environ = environ
-        self.multithreaded = multithreaded
-        self.multiprocess = multiprocess
-        self.debug = debug
-        self._bindAddress = bindAddress
-        if allowedServers is NoDefault:
-            allowedServers = ['127.0.0.1']
-        self._allowedServers = allowedServers
-
-        # Used to force single-threadedness.
-        self._appLock = thread.allocate_lock()
-
-        self.logger = logging.getLogger(LoggerName)
-        self.logger.setLevel(loggingLevel)
-
-    def _setupSocket(self):
-        """Creates and binds the socket for communication with the server."""
-        sock = socket.socket()
-        sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
-        sock.bind(self._bindAddress)
-        sock.listen(socket.SOMAXCONN)
-        return sock
-
-    def _cleanupSocket(self, sock):
-        """Closes the main socket."""
-        sock.close()
-
-    def _isClientAllowed(self, addr):
-        ret = self._allowedServers is None or addr[0] in self._allowedServers
-        if not ret:
-            self.logger.warning('Server connection from %s disallowed',
-                                addr[0])
-        return ret
-
-    def handler(self, request):
-        """
-        WSGI handler. Sets up WSGI environment, calls the application,
-        and sends the application's response.
-        """
-        environ = request.environ
-        environ.update(self.environ)
-
-        environ['wsgi.version'] = (1,0)
-        environ['wsgi.input'] = request.input
-        environ['wsgi.errors'] = sys.stderr
-        environ['wsgi.multithread'] = self.multithreaded
-        environ['wsgi.multiprocess'] = self.multiprocess
-        environ['wsgi.run_once'] = False
-
-        if environ.get('HTTPS', 'off') in ('on', '1'):
-            environ['wsgi.url_scheme'] = 'https'
-        else:
-            environ['wsgi.url_scheme'] = 'http'
-
-        self._sanitizeEnv(environ)
-
-        headers_set = []
-        headers_sent = []
-        result = None
-
-        def write(data):
-            assert type(data) is str, 'write() argument must be string'
-            assert headers_set, 'write() before start_response()'
-
-            if not headers_sent:
-                status, responseHeaders = headers_sent[:] = headers_set
-                statusCode = int(status[:3])
-                statusMsg = status[4:]
-                found = False
-                for header,value in responseHeaders:
-                    if header.lower() == 'content-length':
-                        found = True
-                        break
-                if not found and result is not None:
-                    try:
-                        if len(result) == 1:
-                            responseHeaders.append(('Content-Length',
-                                                    str(len(data))))
-                    except:
-                        pass
-                request.startResponse(statusCode, statusMsg, responseHeaders)
-
-            request.write(data)
-
-        def start_response(status, response_headers, exc_info=None):
-            if exc_info:
-                try:
-                    if headers_sent:
-                        # Re-raise if too late
-                        raise exc_info[0], exc_info[1], exc_info[2]
-                finally:
-                    exc_info = None # avoid dangling circular ref
-            else:
-                assert not headers_set, 'Headers already set!'
-
-            assert type(status) is str, 'Status must be a string'
-            assert len(status) >= 4, 'Status must be at least 4 characters'
-            assert int(status[:3]), 'Status must begin with 3-digit code'
-            assert status[3] == ' ', 'Status must have a space after code'
-            assert type(response_headers) is list, 'Headers must be a list'
-            if __debug__:
-                for name,val in response_headers:
-                    assert type(name) is str, 'Header names must be strings'
-                    assert type(val) is str, 'Header values must be strings'
-
-            headers_set[:] = [status, response_headers]
-            return write
-
-        if not self.multithreaded:
-            self._appLock.acquire()
-        try:
-            try:
-                result = self.application(environ, start_response)
-                try:
-                    for data in result:
-                        if data:
-                            write(data)
-                    if not headers_sent:
-                        write('') # in case body was empty
-                finally:
-                    if hasattr(result, 'close'):
-                        result.close()
-            except socket.error, e:
-                if e[0] != errno.EPIPE:
-                    raise # Don't let EPIPE propagate beyond server
-        finally:
-            if not self.multithreaded:
-                self._appLock.release()
-
-    def _sanitizeEnv(self, environ):
-        """Fill-in/deduce missing values in environ."""
-        # Namely SCRIPT_NAME/PATH_INFO
-        value = environ['REQUEST_URI']
-        scriptName = environ.get('WSGI_SCRIPT_NAME', self.scriptName)
-        if not value.startswith(scriptName):
-            self.logger.warning('scriptName does not match request URI')
-
-        environ['PATH_INFO'] = value[len(scriptName):]
-        environ['SCRIPT_NAME'] = scriptName
-
-        reqUri = None
-        if environ.has_key('REQUEST_URI'):
-            reqUri = environ['REQUEST_URI'].split('?', 1)
-
-        if not environ.has_key('QUERY_STRING') or not environ['QUERY_STRING']:
-            if reqUri is not None and len(reqUri) > 1:
-                environ['QUERY_STRING'] = reqUri[1]
-            else:
-                environ['QUERY_STRING'] = ''
-
-    def error(self, request):
-        """
-        Override to provide custom error handling. Ideally, however,
-        all errors should be caught at the application level.
-        """
-        if self.debug:
-            request.startResponse(200, 'OK', [('Content-Type', 'text/html')])
-            import cgitb
-            request.write(cgitb.html(sys.exc_info()))
-        else:
-            errorpage = """<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
-<html><head>
-<title>Unhandled Exception</title>
-</head><body>
-<h1>Unhandled Exception</h1>
-<p>An unhandled exception was thrown by the application.</p>
-</body></html>
-"""
-            request.startResponse(200, 'OK', [('Content-Type', 'text/html')])
-            request.write(errorpage)
--- a/contrib/flup-server/flup/server/ajp_fork.py	Wed Aug 13 21:06:45 2008 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,199 +0,0 @@
-# Copyright (c) 2005, 2006 Allan Saddi <allan@saddi.com>
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions
-# are met:
-# 1. Redistributions of source code must retain the above copyright
-#    notice, this list of conditions and the following disclaimer.
-# 2. Redistributions in binary form must reproduce the above copyright
-#    notice, this list of conditions and the following disclaimer in the
-#    documentation and/or other materials provided with the distribution.
-#
-# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
-# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
-# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-# SUCH DAMAGE.
-#
-# $Id$
-
-"""
-ajp - an AJP 1.3/WSGI gateway.
-
-For more information about AJP and AJP connectors for your web server, see
-<http://jakarta.apache.org/tomcat/connectors-doc/>.
-
-For more information about the Web Server Gateway Interface, see
-<http://www.python.org/peps/pep-0333.html>.
-
-Example usage:
-
-  #!/usr/bin/env python
-  import sys
-  from myapplication import app # Assume app is your WSGI application object
-  from ajp import WSGIServer
-  ret = WSGIServer(app).run()
-  sys.exit(ret and 42 or 0)
-
-See the documentation for WSGIServer for more information.
-
-About the bit of logic at the end:
-Upon receiving SIGHUP, the python script will exit with status code 42. This
-can be used by a wrapper script to determine if the python script should be
-re-run. When a SIGINT or SIGTERM is received, the script exits with status
-code 0, possibly indicating a normal exit.
-
-Example wrapper script:
-
-  #!/bin/sh
-  STATUS=42
-  while test $STATUS -eq 42; do
-    python "$@" that_script_above.py
-    STATUS=$?
-  done
-
-Example workers.properties (for mod_jk):
-
-  worker.list=foo
-  worker.foo.port=8009
-  worker.foo.host=localhost
-  worker.foo.type=ajp13
-
-Example httpd.conf (for mod_jk):
-
-  JkWorkersFile /path/to/workers.properties
-  JkMount /* foo
-
-Note that if you mount your ajp application anywhere but the root ("/"), you
-SHOULD specifiy scriptName to the WSGIServer constructor. This will ensure
-that SCRIPT_NAME/PATH_INFO are correctly deduced.
-"""
-
-__author__ = 'Allan Saddi <allan@saddi.com>'
-__version__ = '$Revision$'
-
-import socket
-import logging
-
-from flup.server.ajp_base import BaseAJPServer, Connection
-from flup.server.preforkserver import PreforkServer
-
-__all__ = ['WSGIServer']
-
-class WSGIServer(BaseAJPServer, PreforkServer):
-    """
-    AJP1.3/WSGI server. Runs your WSGI application as a persistant program
-    that understands AJP1.3. Opens up a TCP socket, binds it, and then
-    waits for forwarded requests from your webserver.
-
-    Why AJP? Two good reasons are that AJP provides load-balancing and
-    fail-over support. Personally, I just wanted something new to
-    implement. :)
-
-    Of course you will need an AJP1.3 connector for your webserver (e.g.
-    mod_jk) - see <http://jakarta.apache.org/tomcat/connectors-doc/>.
-    """
-    def __init__(self, application, scriptName='', environ=None,
-                 bindAddress=('localhost', 8009), allowedServers=None,
-                 loggingLevel=logging.INFO, debug=True, **kw):
-        """
-        scriptName is the initial portion of the URL path that "belongs"
-        to your application. It is used to determine PATH_INFO (which doesn't
-        seem to be passed in). An empty scriptName means your application
-        is mounted at the root of your virtual host.
-
-        environ, which must be a dictionary, can contain any additional
-        environment variables you want to pass to your application.
-
-        bindAddress is the address to bind to, which must be a tuple of
-        length 2. The first element is a string, which is the host name
-        or IPv4 address of a local interface. The 2nd element is the port
-        number.
-
-        allowedServers must be None or a list of strings representing the
-        IPv4 addresses of servers allowed to connect. None means accept
-        connections from anywhere.
-
-        loggingLevel sets the logging level of the module-level logger.
-        """
-        BaseAJPServer.__init__(self, application,
-                               scriptName=scriptName,
-                               environ=environ,
-                               multithreaded=False,
-                               multiprocess=True,
-                               bindAddress=bindAddress,
-                               allowedServers=allowedServers,
-                               loggingLevel=loggingLevel,
-                               debug=debug)
-        for key in ('multithreaded', 'multiprocess', 'jobClass', 'jobArgs'):
-            if kw.has_key(key):
-                del kw[key]
-        PreforkServer.__init__(self, jobClass=Connection, jobArgs=(self,), **kw)
-
-    def run(self):
-        """
-        Main loop. Call this after instantiating WSGIServer. SIGHUP, SIGINT,
-        SIGQUIT, SIGTERM cause it to cleanup and return. (If a SIGHUP
-        is caught, this method returns True. Returns False otherwise.)
-        """
-        self.logger.info('%s starting up', self.__class__.__name__)
-
-        try:
-            sock = self._setupSocket()
-        except socket.error, e:
-            self.logger.error('Failed to bind socket (%s), exiting', e[1])
-            return False
-
-        ret = PreforkServer.run(self, sock)
-
-        self._cleanupSocket(sock)
-
-        self.logger.info('%s shutting down%s', self.__class__.__name__,
-                         self._hupReceived and ' (reload requested)' or '')
-
-        return ret
-
-def factory(global_conf, host=None, port=None, **local):
-    import paste_factory
-    return paste_factory.helper(WSGIServer, global_conf, host, port, **local)
-
-if __name__ == '__main__':
-    def test_app(environ, start_response):
-        """Probably not the most efficient example."""
-        import cgi
-        start_response('200 OK', [('Content-Type', 'text/html')])
-        yield '<html><head><title>Hello World!</title></head>\n' \
-              '<body>\n' \
-              '<p>Hello World!</p>\n' \
-              '<table border="1">'
-        names = environ.keys()
-        names.sort()
-        for name in names:
-            yield '<tr><td>%s</td><td>%s</td></tr>\n' % (
-                name, cgi.escape(`environ[name]`))
-
-        form = cgi.FieldStorage(fp=environ['wsgi.input'], environ=environ,
-                                keep_blank_values=1)
-        if form.list:
-            yield '<tr><th colspan="2">Form data</th></tr>'
-
-        for field in form.list:
-            yield '<tr><td>%s</td><td>%s</td></tr>\n' % (
-                field.name, field.value)
-
-        yield '</table>\n' \
-              '</body></html>\n'
-
-    from wsgiref import validate
-    test_app = validate.validator(test_app)
-    # Explicitly set bindAddress to *:8009 for testing.
-    WSGIServer(test_app,
-               bindAddress=('', 8009), allowedServers=None,
-               loggingLevel=logging.DEBUG).run()
--- a/contrib/flup-server/flup/server/cgi.py	Wed Aug 13 21:06:45 2008 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,71 +0,0 @@
-# Taken from <http://www.python.org/dev/peps/pep-0333/>
-# which was placed in the public domain.
-
-import os, sys
-
-
-__all__ = ['WSGIServer']
-
-
-class WSGIServer(object):
-
-    def __init__(self, application):
-        self.application = application
-
-    def run(self):
-
-        environ = dict(os.environ.items())
-        environ['wsgi.input']        = sys.stdin
-        environ['wsgi.errors']       = sys.stderr
-        environ['wsgi.version']      = (1,0)
-        environ['wsgi.multithread']  = False
-        environ['wsgi.multiprocess'] = True
-        environ['wsgi.run_once']     = True
-
-        if environ.get('HTTPS','off') in ('on','1'):
-            environ['wsgi.url_scheme'] = 'https'
-        else:
-            environ['wsgi.url_scheme'] = 'http'
-
-        headers_set = []
-        headers_sent = []
-
-        def write(data):
-            if not headers_set:
-                 raise AssertionError("write() before start_response()")
-
-            elif not headers_sent:
-                 # Before the first output, send the stored headers
-                 status, response_headers = headers_sent[:] = headers_set
-                 sys.stdout.write('Status: %s\r\n' % status)
-                 for header in response_headers:
-                     sys.stdout.write('%s: %s\r\n' % header)
-                 sys.stdout.write('\r\n')
-
-            sys.stdout.write(data)
-            sys.stdout.flush()
-
-        def start_response(status,response_headers,exc_info=None):
-            if exc_info:
-                try:
-                    if headers_sent:
-                        # Re-raise original exception if headers sent
-                        raise exc_info[0], exc_info[1], exc_info[2]
-                finally:
-                    exc_info = None     # avoid dangling circular ref
-            elif headers_set:
-                raise AssertionError("Headers already set!")
-
-            headers_set[:] = [status,response_headers]
-            return write
-
-        result = self.application(environ, start_response)
-        try:
-            for data in result:
-                if data:    # don't send headers until body appears
-                    write(data)
-            if not headers_sent:
-                write('')   # send headers now if body was empty
-        finally:
-            if hasattr(result,'close'):
-                result.close()
--- a/contrib/flup-server/flup/server/fcgi.py	Wed Aug 13 21:06:45 2008 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,152 +0,0 @@
-# Copyright (c) 2005, 2006 Allan Saddi <allan@saddi.com>
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions
-# are met:
-# 1. Redistributions of source code must retain the above copyright
-#    notice, this list of conditions and the following disclaimer.
-# 2. Redistributions in binary form must reproduce the above copyright
-#    notice, this list of conditions and the following disclaimer in the
-#    documentation and/or other materials provided with the distribution.
-#
-# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
-# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
-# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-# SUCH DAMAGE.
-#
-# $Id$
-
-"""
-fcgi - a FastCGI/WSGI gateway.
-
-For more information about FastCGI, see <http://www.fastcgi.com/>.
-
-For more information about the Web Server Gateway Interface, see
-<http://www.python.org/peps/pep-0333.html>.
-
-Example usage:
-
-  #!/usr/bin/env python
-  from myapplication import app # Assume app is your WSGI application object
-  from fcgi import WSGIServer
-  WSGIServer(app).run()
-
-See the documentation for WSGIServer for more information.
-
-On most platforms, fcgi will fallback to regular CGI behavior if run in a
-non-FastCGI context. If you want to force CGI behavior, set the environment
-variable FCGI_FORCE_CGI to "Y" or "y".
-"""
-
-__author__ = 'Allan Saddi <allan@saddi.com>'
-__version__ = '$Revision$'
-
-import os
-
-from flup.server.fcgi_base import BaseFCGIServer, FCGI_RESPONDER
-from flup.server.threadedserver import ThreadedServer
-
-__all__ = ['WSGIServer']
-
-class WSGIServer(BaseFCGIServer, ThreadedServer):
-    """
-    FastCGI server that supports the Web Server Gateway Interface. See
-    <http://www.python.org/peps/pep-0333.html>.
-    """
-    def __init__(self, application, environ=None,
-                 multithreaded=True, multiprocess=False,
-                 bindAddress=None, umask=None, multiplexed=False,
-                 debug=True, roles=(FCGI_RESPONDER,), **kw):
-        """
-        environ, if present, must be a dictionary-like object. Its
-        contents will be copied into application's environ. Useful
-        for passing application-specific variables.
-
-        bindAddress, if present, must either be a string or a 2-tuple. If
-        present, run() will open its own listening socket. You would use
-        this if you wanted to run your application as an 'external' FastCGI
-        app. (i.e. the webserver would no longer be responsible for starting
-        your app) If a string, it will be interpreted as a filename and a UNIX
-        socket will be opened. If a tuple, the first element, a string,
-        is the interface name/IP to bind to, and the second element (an int)
-        is the port number.
-        """
-        BaseFCGIServer.__init__(self, application,
-                                environ=environ,
-                                multithreaded=multithreaded,
-                                multiprocess=multiprocess,
-                                bindAddress=bindAddress,
-                                umask=umask,
-                                multiplexed=multiplexed,
-                                debug=debug,
-                                roles=roles)
-        for key in ('jobClass', 'jobArgs'):
-            if kw.has_key(key):
-                del kw[key]
-        ThreadedServer.__init__(self, jobClass=self._connectionClass,
-                                jobArgs=(self,), **kw)
-
-    def _isClientAllowed(self, addr):
-        return self._web_server_addrs is None or \
-               (len(addr) == 2 and addr[0] in self._web_server_addrs)
-
-    def run(self):
-        """
-        The main loop. Exits on SIGHUP, SIGINT, SIGTERM. Returns True if
-        SIGHUP was received, False otherwise.
-        """
-        self._web_server_addrs = os.environ.get('FCGI_WEB_SERVER_ADDRS')
-        if self._web_server_addrs is not None:
-            self._web_server_addrs = map(lambda x: x.strip(),
-                                         self._web_server_addrs.split(','))
-
-        sock = self._setupSocket()
-
-        ret = ThreadedServer.run(self, sock)
-
-        self._cleanupSocket(sock)
-
-        return ret
-
-def factory(global_conf, host=None, port=None, **local):
-    import paste_factory
-    return paste_factory.helper(WSGIServer, global_conf, host, port, **local)
-
-if __name__ == '__main__':
-    def test_app(environ, start_response):
-        """Probably not the most efficient example."""
-        import cgi
-        start_response('200 OK', [('Content-Type', 'text/html')])
-        yield '<html><head><title>Hello World!</title></head>\n' \
-              '<body>\n' \
-              '<p>Hello World!</p>\n' \
-              '<table border="1">'
-        names = environ.keys()
-        names.sort()
-        for name in names:
-            yield '<tr><td>%s</td><td>%s</td></tr>\n' % (
-                name, cgi.escape(`environ[name]`))
-
-        form = cgi.FieldStorage(fp=environ['wsgi.input'], environ=environ,
-                                keep_blank_values=1)
-        if form.list:
-            yield '<tr><th colspan="2">Form data</th></tr>'
-
-        for field in form.list:
-            yield '<tr><td>%s</td><td>%s</td></tr>\n' % (
-                field.name, field.value)
-
-        yield '</table>\n' \
-              '</body></html>\n'
-
-    from wsgiref import validate
-    test_app = validate.validator(test_app)
-    WSGIServer(test_app).run()
--- a/contrib/flup-server/flup/server/fcgi_base.py	Wed Aug 13 21:06:45 2008 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1186 +0,0 @@
-# Copyright (c) 2002, 2003, 2005, 2006 Allan Saddi <allan@saddi.com>
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions
-# are met:
-# 1. Redistributions of source code must retain the above copyright
-#    notice, this list of conditions and the following disclaimer.
-# 2. Redistributions in binary form must reproduce the above copyright
-#    notice, this list of conditions and the following disclaimer in the
-#    documentation and/or other materials provided with the distribution.
-#
-# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
-# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
-# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-# SUCH DAMAGE.
-#
-# $Id$
-
-__author__ = 'Allan Saddi <allan@saddi.com>'
-__version__ = '$Revision$'
-
-import sys
-import os
-import signal
-import struct
-import cStringIO as StringIO
-import select
-import socket
-import errno
-import traceback
-
-try:
-    import thread
-    import threading
-    thread_available = True
-except ImportError:
-    import dummy_thread as thread
-    import dummy_threading as threading
-    thread_available = False
-
-# Apparently 2.3 doesn't define SHUT_WR? Assume it is 1 in this case.
-if not hasattr(socket, 'SHUT_WR'):
-    socket.SHUT_WR = 1
-
-__all__ = ['BaseFCGIServer']
-
-# Constants from the spec.
-FCGI_LISTENSOCK_FILENO = 0
-
-FCGI_HEADER_LEN = 8
-
-FCGI_VERSION_1 = 1
-
-FCGI_BEGIN_REQUEST = 1
-FCGI_ABORT_REQUEST = 2
-FCGI_END_REQUEST = 3
-FCGI_PARAMS = 4
-FCGI_STDIN = 5
-FCGI_STDOUT = 6
-FCGI_STDERR = 7
-FCGI_DATA = 8
-FCGI_GET_VALUES = 9
-FCGI_GET_VALUES_RESULT = 10
-FCGI_UNKNOWN_TYPE = 11
-FCGI_MAXTYPE = FCGI_UNKNOWN_TYPE
-
-FCGI_NULL_REQUEST_ID = 0
-
-FCGI_KEEP_CONN = 1
-
-FCGI_RESPONDER = 1
-FCGI_AUTHORIZER = 2
-FCGI_FILTER = 3
-
-FCGI_REQUEST_COMPLETE = 0
-FCGI_CANT_MPX_CONN = 1
-FCGI_OVERLOADED = 2
-FCGI_UNKNOWN_ROLE = 3
-
-FCGI_MAX_CONNS = 'FCGI_MAX_CONNS'
-FCGI_MAX_REQS = 'FCGI_MAX_REQS'
-FCGI_MPXS_CONNS = 'FCGI_MPXS_CONNS'
-
-FCGI_Header = '!BBHHBx'
-FCGI_BeginRequestBody = '!HB5x'
-FCGI_EndRequestBody = '!LB3x'
-FCGI_UnknownTypeBody = '!B7x'
-
-FCGI_EndRequestBody_LEN = struct.calcsize(FCGI_EndRequestBody)
-FCGI_UnknownTypeBody_LEN = struct.calcsize(FCGI_UnknownTypeBody)
-
-if __debug__:
-    import time
-
-    # Set non-zero to write debug output to a file.
-    DEBUG = 0
-    DEBUGLOG = '/tmp/fcgi.log'
-
-    def _debug(level, msg):
-        if DEBUG < level:
-            return
-
-        try:
-            f = open(DEBUGLOG, 'a')
-            f.write('%sfcgi: %s\n' % (time.ctime()[4:-4], msg))
-            f.close()
-        except:
-            pass
-
-class InputStream(object):
-    """
-    File-like object representing FastCGI input streams (FCGI_STDIN and
-    FCGI_DATA). Supports the minimum methods required by WSGI spec.
-    """
-    def __init__(self, conn):
-        self._conn = conn
-
-        # See Server.
-        self._shrinkThreshold = conn.server.inputStreamShrinkThreshold
-
-        self._buf = ''
-        self._bufList = []
-        self._pos = 0 # Current read position.
-        self._avail = 0 # Number of bytes currently available.
-
-        self._eof = False # True when server has sent EOF notification.
-
-    def _shrinkBuffer(self):
-        """Gets rid of already read data (since we can't rewind)."""
-        if self._pos >= self._shrinkThreshold:
-            self._buf = self._buf[self._pos:]
-            self._avail -= self._pos
-            self._pos = 0
-
-            assert self._avail >= 0
-
-    def _waitForData(self):
-        """Waits for more data to become available."""
-        self._conn.process_input()
-
-    def read(self, n=-1):
-        if self._pos == self._avail and self._eof:
-            return ''
-        while True:
-            if n < 0 or (self._avail - self._pos) < n:
-                # Not enough data available.
-                if self._eof:
-                    # And there's no more coming.
-                    newPos = self._avail
-                    break
-                else:
-                    # Wait for more data.
-                    self._waitForData()
-                    continue
-            else:
-                newPos = self._pos + n
-                break
-        # Merge buffer list, if necessary.
-        if self._bufList:
-            self._buf += ''.join(self._bufList)
-            self._bufList = []
-        r = self._buf[self._pos:newPos]
-        self._pos = newPos
-        self._shrinkBuffer()
-        return r
-
-    def readline(self, length=None):
-        if self._pos == self._avail and self._eof:
-            return ''
-        while True:
-            # Unfortunately, we need to merge the buffer list early.
-            if self._bufList:
-                self._buf += ''.join(self._bufList)
-                self._bufList = []
-            # Find newline.
-            i = self._buf.find('\n', self._pos)
-            if i < 0:
-                # Not found?
-                if self._eof:
-                    # No more data coming.
-                    newPos = self._avail
-                    break
-                else:
-                    if length is not None and len(self._buf) >= length + self._pos:
-                        newPos = self._pos + length
-                        break
-                    # Wait for more to come.
-                    self._waitForData()
-                    continue
-            else:
-                newPos = i + 1
-                break
-        r = self._buf[self._pos:newPos]
-        self._pos = newPos
-        self._shrinkBuffer()
-        return r
-
-    def readlines(self, sizehint=0):
-        total = 0
-        lines = []
-        line = self.readline()
-        while line:
-            lines.append(line)
-            total += len(line)
-            if 0 < sizehint <= total:
-                break
-            line = self.readline()
-        return lines
-
-    def __iter__(self):
-        return self
-
-    def next(self):
-        r = self.readline()
-        if not r:
-            raise StopIteration
-        return r
-
-    def add_data(self, data):
-        if not data:
-            self._eof = True
-        else:
-            self._bufList.append(data)
-            self._avail += len(data)
-
-class MultiplexedInputStream(InputStream):
-    """
-    A version of InputStream meant to be used with MultiplexedConnections.
-    Assumes the MultiplexedConnection (the producer) and the Request
-    (the consumer) are running in different threads.
-    """
-    def __init__(self, conn):
-        super(MultiplexedInputStream, self).__init__(conn)
-
-        # Arbitrates access to this InputStream (it's used simultaneously
-        # by a Request and its owning Connection object).
-        lock = threading.RLock()
-
-        # Notifies Request thread that there is new data available.
-        self._lock = threading.Condition(lock)
-
-    def _waitForData(self):
-        # Wait for notification from add_data().
-        self._lock.wait()
-
-    def read(self, n=-1):
-        self._lock.acquire()
-        try:
-            return super(MultiplexedInputStream, self).read(n)
-        finally:
-            self._lock.release()
-
-    def readline(self, length=None):
-        self._lock.acquire()
-        try:
-            return super(MultiplexedInputStream, self).readline(length)
-        finally:
-            self._lock.release()
-
-    def add_data(self, data):
-        self._lock.acquire()
-        try:
-            super(MultiplexedInputStream, self).add_data(data)
-            self._lock.notify()
-        finally:
-            self._lock.release()
-
-class OutputStream(object):
-    """
-    FastCGI output stream (FCGI_STDOUT/FCGI_STDERR). By default, calls to
-    write() or writelines() immediately result in Records being sent back
-    to the server. Buffering should be done in a higher level!
-    """
-    def __init__(self, conn, req, type, buffered=False):
-        self._conn = conn
-        self._req = req
-        self._type = type
-        self._buffered = buffered
-        self._bufList = [] # Used if buffered is True
-        self.dataWritten = False
-        self.closed = False
-
-    def _write(self, data):
-        length = len(data)
-        while length:
-            toWrite = min(length, self._req.server.maxwrite - FCGI_HEADER_LEN)
-
-            rec = Record(self._type, self._req.requestId)
-            rec.contentLength = toWrite
-            rec.contentData = data[:toWrite]
-            self._conn.writeRecord(rec)
-
-            data = data[toWrite:]
-            length -= toWrite
-
-    def write(self, data):
-        assert not self.closed
-
-        if not data:
-            return
-
-        self.dataWritten = True
-
-        if self._buffered:
-            self._bufList.append(data)
-        else:
-            self._write(data)
-
-    def writelines(self, lines):
-        assert not self.closed
-
-        for line in lines:
-            self.write(line)
-
-    def flush(self):
-        # Only need to flush if this OutputStream is actually buffered.
-        if self._buffered:
-            data = ''.join(self._bufList)
-            self._bufList = []
-            self._write(data)
-
-    # Though available, the following should NOT be called by WSGI apps.
-    def close(self):
-        """Sends end-of-stream notification, if necessary."""
-        if not self.closed and self.dataWritten:
-            self.flush()
-            rec = Record(self._type, self._req.requestId)
-            self._conn.writeRecord(rec)
-            self.closed = True
-
-class TeeOutputStream(object):
-    """
-    Simple wrapper around two or more output file-like objects that copies
-    written data to all streams.
-    """
-    def __init__(self, streamList):
-        self._streamList = streamList
-
-    def write(self, data):
-        for f in self._streamList:
-            f.write(data)
-
-    def writelines(self, lines):
-        for line in lines:
-            self.write(line)
-
-    def flush(self):
-        for f in self._streamList:
-            f.flush()
-
-class StdoutWrapper(object):
-    """
-    Wrapper for sys.stdout so we know if data has actually been written.
-    """
-    def __init__(self, stdout):
-        self._file = stdout
-        self.dataWritten = False
-
-    def write(self, data):
-        if data:
-            self.dataWritten = True
-        self._file.write(data)
-
-    def writelines(self, lines):
-        for line in lines:
-            self.write(line)
-
-    def __getattr__(self, name):
-        return getattr(self._file, name)
-
-def decode_pair(s, pos=0):
-    """
-    Decodes a name/value pair.
-
-    The number of bytes decoded as well as the name/value pair
-    are returned.
-    """
-    nameLength = ord(s[pos])
-    if nameLength & 128:
-        nameLength = struct.unpack('!L', s[pos:pos+4])[0] & 0x7fffffff
-        pos += 4
-    else:
-        pos += 1
-
-    valueLength = ord(s[pos])
-    if valueLength & 128:
-        valueLength = struct.unpack('!L', s[pos:pos+4])[0] & 0x7fffffff
-        pos += 4
-    else:
-        pos += 1
-
-    name = s[pos:pos+nameLength]
-    pos += nameLength
-    value = s[pos:pos+valueLength]
-    pos += valueLength
-
-    return (pos, (name, value))
-
-def encode_pair(name, value):
-    """
-    Encodes a name/value pair.
-
-    The encoded string is returned.
-    """
-    nameLength = len(name)
-    if nameLength < 128:
-        s = chr(nameLength)
-    else:
-        s = struct.pack('!L', nameLength | 0x80000000L)
-
-    valueLength = len(value)
-    if valueLength < 128:
-        s += chr(valueLength)
-    else:
-        s += struct.pack('!L', valueLength | 0x80000000L)
-
-    return s + name + value
-    
-class Record(object):
-    """
-    A FastCGI Record.
-
-    Used for encoding/decoding records.
-    """
-    def __init__(self, type=FCGI_UNKNOWN_TYPE, requestId=FCGI_NULL_REQUEST_ID):
-        self.version = FCGI_VERSION_1
-        self.type = type
-        self.requestId = requestId
-        self.contentLength = 0
-        self.paddingLength = 0
-        self.contentData = ''
-
-    def _recvall(sock, length):
-        """
-        Attempts to receive length bytes from a socket, blocking if necessary.
-        (Socket may be blocking or non-blocking.)
-        """
-        dataList = []
-        recvLen = 0
-        while length:
-            try:
-                data = sock.recv(length)
-            except socket.error, e:
-                if e[0] == errno.EAGAIN:
-                    select.select([sock], [], [])
-                    continue
-                else:
-                    raise
-            if not data: # EOF
-                break
-            dataList.append(data)
-            dataLen = len(data)
-            recvLen += dataLen
-            length -= dataLen
-        return ''.join(dataList), recvLen
-    _recvall = staticmethod(_recvall)
-
-    def read(self, sock):
-        """Read and decode a Record from a socket."""
-        try:
-            header, length = self._recvall(sock, FCGI_HEADER_LEN)
-        except:
-            raise EOFError
-
-        if length < FCGI_HEADER_LEN:
-            raise EOFError
-        
-        self.version, self.type, self.requestId, self.contentLength, \
-                      self.paddingLength = struct.unpack(FCGI_Header, header)
-
-        if __debug__: _debug(9, 'read: fd = %d, type = %d, requestId = %d, '
-                             'contentLength = %d' %
-                             (sock.fileno(), self.type, self.requestId,
-                              self.contentLength))
-        
-        if self.contentLength:
-            try:
-                self.contentData, length = self._recvall(sock,
-                                                         self.contentLength)
-            except:
-                raise EOFError
-
-            if length < self.contentLength:
-                raise EOFError
-
-        if self.paddingLength:
-            try:
-                self._recvall(sock, self.paddingLength)
-            except:
-                raise EOFError
-
-    def _sendall(sock, data):
-        """
-        Writes data to a socket and does not return until all the data is sent.
-        """
-        length = len(data)
-        while length:
-            try:
-                sent = sock.send(data)
-            except socket.error, e:
-                if e[0] == errno.EAGAIN:
-                    select.select([], [sock], [])
-                    continue
-                else:
-                    raise
-            data = data[sent:]
-            length -= sent
-    _sendall = staticmethod(_sendall)
-
-    def write(self, sock):
-        """Encode and write a Record to a socket."""
-        self.paddingLength = -self.contentLength & 7
-
-        if __debug__: _debug(9, 'write: fd = %d, type = %d, requestId = %d, '
-                             'contentLength = %d' %
-                             (sock.fileno(), self.type, self.requestId,
-                              self.contentLength))
-
-        header = struct.pack(FCGI_Header, self.version, self.type,
-                             self.requestId, self.contentLength,
-                             self.paddingLength)
-        self._sendall(sock, header)
-        if self.contentLength:
-            self._sendall(sock, self.contentData)
-        if self.paddingLength:
-            self._sendall(sock, '\x00'*self.paddingLength)
-            
-class Request(object):
-    """
-    Represents a single FastCGI request.
-
-    These objects are passed to your handler and is the main interface
-    between your handler and the fcgi module. The methods should not
-    be called by your handler. However, server, params, stdin, stdout,
-    stderr, and data are free for your handler's use.
-    """
-    def __init__(self, conn, inputStreamClass):
-        self._conn = conn
-
-        self.server = conn.server
-        self.params = {}
-        self.stdin = inputStreamClass(conn)
-        self.stdout = OutputStream(conn, self, FCGI_STDOUT)
-        self.stderr = OutputStream(conn, self, FCGI_STDERR, buffered=True)
-        self.data = inputStreamClass(conn)
-
-    def run(self):
-        """Runs the handler, flushes the streams, and ends the request."""
-        try:
-            protocolStatus, appStatus = self.server.handler(self)
-        except:
-            traceback.print_exc(file=self.stderr)
-            self.stderr.flush()
-            if not self.stdout.dataWritten:
-                self.server.error(self)
-
-            protocolStatus, appStatus = FCGI_REQUEST_COMPLETE, 0
-
-        if __debug__: _debug(1, 'protocolStatus = %d, appStatus = %d' %
-                             (protocolStatus, appStatus))
-
-        try:
-            self._flush()
-            self._end(appStatus, protocolStatus)
-        except socket.error, e:
-            if e[0] != errno.EPIPE:
-                raise
-
-    def _end(self, appStatus=0L, protocolStatus=FCGI_REQUEST_COMPLETE):
-        self._conn.end_request(self, appStatus, protocolStatus)
-        
-    def _flush(self):
-        self.stdout.close()
-        self.stderr.close()
-
-class CGIRequest(Request):
-    """A normal CGI request disguised as a FastCGI request."""
-    def __init__(self, server):
-        # These are normally filled in by Connection.
-        self.requestId = 1
-        self.role = FCGI_RESPONDER
-        self.flags = 0
-        self.aborted = False
-        
-        self.server = server
-        self.params = dict(os.environ)
-        self.stdin = sys.stdin
-        self.stdout = StdoutWrapper(sys.stdout) # Oh, the humanity!
-        self.stderr = sys.stderr
-        self.data = StringIO.StringIO()
-        
-    def _end(self, appStatus=0L, protocolStatus=FCGI_REQUEST_COMPLETE):
-        sys.exit(appStatus)
-
-    def _flush(self):
-        # Not buffered, do nothing.
-        pass
-
-class Connection(object):
-    """
-    A Connection with the web server.
-
-    Each Connection is associated with a single socket (which is
-    connected to the web server) and is responsible for handling all
-    the FastCGI message processing for that socket.
-    """
-    _multiplexed = False
-    _inputStreamClass = InputStream
-
-    def __init__(self, sock, addr, server):
-        self._sock = sock
-        self._addr = addr
-        self.server = server
-
-        # Active Requests for this Connection, mapped by request ID.
-        self._requests = {}
-
-    def _cleanupSocket(self):
-        """Close the Connection's socket."""
-        try:
-            self._sock.shutdown(socket.SHUT_WR)
-        except:
-            return
-        try:
-            while True:
-                r, w, e = select.select([self._sock], [], [])
-                if not r or not self._sock.recv(1024):
-                    break
-        except:
-            pass
-        self._sock.close()
-        
-    def run(self):
-        """Begin processing data from the socket."""
-        self._keepGoing = True
-        while self._keepGoing:
-            try:
-                self.process_input()
-            except (EOFError, KeyboardInterrupt):
-                break
-            except (select.error, socket.error), e:
-                if e[0] == errno.EBADF: # Socket was closed by Request.
-                    break
-                raise
-
-        self._cleanupSocket()
-
-    def process_input(self):
-        """Attempt to read a single Record from the socket and process it."""
-        # Currently, any children Request threads notify this Connection
-        # that it is no longer needed by closing the Connection's socket.
-        # We need to put a timeout on select, otherwise we might get
-        # stuck in it indefinitely... (I don't like this solution.)
-        while self._keepGoing:
-            try:
-                r, w, e = select.select([self._sock], [], [], 1.0)
-            except ValueError:
-                # Sigh. ValueError gets thrown sometimes when passing select
-                # a closed socket.
-                raise EOFError
-            if r: break
-        if not self._keepGoing:
-            return
-        rec = Record()
-        rec.read(self._sock)
-
-        if rec.type == FCGI_GET_VALUES:
-            self._do_get_values(rec)
-        elif rec.type == FCGI_BEGIN_REQUEST:
-            self._do_begin_request(rec)
-        elif rec.type == FCGI_ABORT_REQUEST:
-            self._do_abort_request(rec)
-        elif rec.type == FCGI_PARAMS:
-            self._do_params(rec)
-        elif rec.type == FCGI_STDIN:
-            self._do_stdin(rec)
-        elif rec.type == FCGI_DATA:
-            self._do_data(rec)
-        elif rec.requestId == FCGI_NULL_REQUEST_ID:
-            self._do_unknown_type(rec)
-        else:
-            # Need to complain about this.
-            pass
-
-    def writeRecord(self, rec):
-        """
-        Write a Record to the socket.
-        """
-        rec.write(self._sock)
-
-    def end_request(self, req, appStatus=0L,
-                    protocolStatus=FCGI_REQUEST_COMPLETE, remove=True):
-        """
-        End a Request.
-
-        Called by Request objects. An FCGI_END_REQUEST Record is
-        sent to the web server. If the web server no longer requires
-        the connection, the socket is closed, thereby ending this
-        Connection (run() returns).
-        """
-        rec = Record(FCGI_END_REQUEST, req.requestId)
-        rec.contentData = struct.pack(FCGI_EndRequestBody, appStatus,
-                                      protocolStatus)
-        rec.contentLength = FCGI_EndRequestBody_LEN
-        self.writeRecord(rec)
-
-        if remove:
-            del self._requests[req.requestId]
-
-        if __debug__: _debug(2, 'end_request: flags = %d' % req.flags)
-
-        if not (req.flags & FCGI_KEEP_CONN) and not self._requests:
-            self._cleanupSocket()
-            self._keepGoing = False
-
-    def _do_get_values(self, inrec):
-        """Handle an FCGI_GET_VALUES request from the web server."""
-        outrec = Record(FCGI_GET_VALUES_RESULT)
-
-        pos = 0
-        while pos < inrec.contentLength:
-            pos, (name, value) = decode_pair(inrec.contentData, pos)
-            cap = self.server.capability.get(name)
-            if cap is not None:
-                outrec.contentData += encode_pair(name, str(cap))
-
-        outrec.contentLength = len(outrec.contentData)
-        self.writeRecord(outrec)
-
-    def _do_begin_request(self, inrec):
-        """Handle an FCGI_BEGIN_REQUEST from the web server."""
-        role, flags = struct.unpack(FCGI_BeginRequestBody, inrec.contentData)
-
-        req = self.server.request_class(self, self._inputStreamClass)
-        req.requestId, req.role, req.flags = inrec.requestId, role, flags
-        req.aborted = False
-
-        if not self._multiplexed and self._requests:
-            # Can't multiplex requests.
-            self.end_request(req, 0L, FCGI_CANT_MPX_CONN, remove=False)
-        else:
-            self._requests[inrec.requestId] = req
-
-    def _do_abort_request(self, inrec):
-        """
-        Handle an FCGI_ABORT_REQUEST from the web server.
-
-        We just mark a flag in the associated Request.
-        """
-        req = self._requests.get(inrec.requestId)
-        if req is not None:
-            req.aborted = True
-
-    def _start_request(self, req):
-        """Run the request."""
-        # Not multiplexed, so run it inline.
-        req.run()
-
-    def _do_params(self, inrec):
-        """
-        Handle an FCGI_PARAMS Record.
-
-        If the last FCGI_PARAMS Record is received, start the request.
-        """
-        req = self._requests.get(inrec.requestId)
-        if req is not None:
-            if inrec.contentLength:
-                pos = 0
-                while pos < inrec.contentLength:
-                    pos, (name, value) = decode_pair(inrec.contentData, pos)
-                    req.params[name] = value
-            else:
-                self._start_request(req)
-
-    def _do_stdin(self, inrec):
-        """Handle the FCGI_STDIN stream."""
-        req = self._requests.get(inrec.requestId)
-        if req is not None:
-            req.stdin.add_data(inrec.contentData)
-
-    def _do_data(self, inrec):
-        """Handle the FCGI_DATA stream."""
-        req = self._requests.get(inrec.requestId)
-        if req is not None:
-            req.data.add_data(inrec.contentData)
-
-    def _do_unknown_type(self, inrec):
-        """Handle an unknown request type. Respond accordingly."""
-        outrec = Record(FCGI_UNKNOWN_TYPE)
-        outrec.contentData = struct.pack(FCGI_UnknownTypeBody, inrec.type)
-        outrec.contentLength = FCGI_UnknownTypeBody_LEN
-        self.writeRecord(rec)
-        
-class MultiplexedConnection(Connection):
-    """
-    A version of Connection capable of handling multiple requests
-    simultaneously.
-    """
-    _multiplexed = True
-    _inputStreamClass = MultiplexedInputStream
-
-    def __init__(self, sock, addr, server):
-        super(MultiplexedConnection, self).__init__(sock, addr, server)
-
-        # Used to arbitrate access to self._requests.
-        lock = threading.RLock()
-
-        # Notification is posted everytime a request completes, allowing us
-        # to quit cleanly.
-        self._lock = threading.Condition(lock)
-
-    def _cleanupSocket(self):
-        # Wait for any outstanding requests before closing the socket.
-        self._lock.acquire()
-        while self._requests:
-            self._lock.wait()
-        self._lock.release()
-
-        super(MultiplexedConnection, self)._cleanupSocket()
-        
-    def writeRecord(self, rec):
-        # Must use locking to prevent intermingling of Records from different
-        # threads.
-        self._lock.acquire()
-        try:
-            # Probably faster than calling super. ;)
-            rec.write(self._sock)
-        finally:
-            self._lock.release()
-
-    def end_request(self, req, appStatus=0L,
-                    protocolStatus=FCGI_REQUEST_COMPLETE, remove=True):
-        self._lock.acquire()
-        try:
-            super(MultiplexedConnection, self).end_request(req, appStatus,
-                                                           protocolStatus,
-                                                           remove)
-            self._lock.notify()
-        finally:
-            self._lock.release()
-
-    def _do_begin_request(self, inrec):
-        self._lock.acquire()
-        try:
-            super(MultiplexedConnection, self)._do_begin_request(inrec)
-        finally:
-            self._lock.release()
-
-    def _do_abort_request(self, inrec):
-        self._lock.acquire()
-        try:
-            super(MultiplexedConnection, self)._do_abort_request(inrec)
-        finally:
-            self._lock.release()
-
-    def _start_request(self, req):
-        thread.start_new_thread(req.run, ())
-
-    def _do_params(self, inrec):
-        self._lock.acquire()
-        try:
-            super(MultiplexedConnection, self)._do_params(inrec)
-        finally:
-            self._lock.release()
-
-    def _do_stdin(self, inrec):
-        self._lock.acquire()
-        try:
-            super(MultiplexedConnection, self)._do_stdin(inrec)
-        finally:
-            self._lock.release()
-
-    def _do_data(self, inrec):
-        self._lock.acquire()
-        try:
-            super(MultiplexedConnection, self)._do_data(inrec)
-        finally:
-            self._lock.release()
-        
-class BaseFCGIServer(object):
-    request_class = Request
-    cgirequest_class = CGIRequest
-
-    # The maximum number of bytes (per Record) to write to the server.
-    # I've noticed mod_fastcgi has a relatively small receive buffer (8K or
-    # so).
-    maxwrite = 8192
-
-    # Limits the size of the InputStream's string buffer to this size + the
-    # server's maximum Record size. Since the InputStream is not seekable,
-    # we throw away already-read data once this certain amount has been read.
-    inputStreamShrinkThreshold = 102400 - 8192
-
-    def __init__(self, application, environ=None,
-                 multithreaded=True, multiprocess=False,
-                 bindAddress=None, umask=None, multiplexed=False,
-                 debug=True, roles=(FCGI_RESPONDER,)):
-        """
-        bindAddress, if present, must either be a string or a 2-tuple. If
-        present, run() will open its own listening socket. You would use
-        this if you wanted to run your application as an 'external' FastCGI
-        app. (i.e. the webserver would no longer be responsible for starting
-        your app) If a string, it will be interpreted as a filename and a UNIX
-        socket will be opened. If a tuple, the first element, a string,
-        is the interface name/IP to bind to, and the second element (an int)
-        is the port number.
-
-        If binding to a UNIX socket, umask may be set to specify what
-        the umask is to be changed to before the socket is created in the
-        filesystem. After the socket is created, the previous umask is
-        restored.
-        
-        Set multiplexed to True if you want to handle multiple requests
-        per connection. Some FastCGI backends (namely mod_fastcgi) don't
-        multiplex requests at all, so by default this is off (which saves
-        on thread creation/locking overhead). If threads aren't available,
-        this keyword is ignored; it's not possible to multiplex requests
-        at all.
-        """
-        if environ is None:
-            environ = {}
-
-        self.application = application
-        self.environ = environ
-        self.multithreaded = multithreaded
-        self.multiprocess = multiprocess
-        self.debug = debug
-        self.roles = roles
-
-        self._bindAddress = bindAddress
-        self._umask = umask
-        
-        # Used to force single-threadedness
-        self._appLock = thread.allocate_lock()
-
-        if thread_available:
-            try:
-                import resource
-                # Attempt to glean the maximum number of connections
-                # from the OS.
-                maxConns = resource.getrlimit(resource.RLIMIT_NOFILE)[0]
-            except ImportError:
-                maxConns = 100 # Just some made up number.
-            maxReqs = maxConns
-            if multiplexed:
-                self._connectionClass = MultiplexedConnection
-                maxReqs *= 5 # Another made up number.
-            else:
-                self._connectionClass = Connection
-            self.capability = {
-                FCGI_MAX_CONNS: maxConns,
-                FCGI_MAX_REQS: maxReqs,
-                FCGI_MPXS_CONNS: multiplexed and 1 or 0
-                }
-        else:
-            self._connectionClass = Connection
-            self.capability = {
-                # If threads aren't available, these are pretty much correct.
-                FCGI_MAX_CONNS: 1,
-                FCGI_MAX_REQS: 1,
-                FCGI_MPXS_CONNS: 0
-                }
-
-    def _setupSocket(self):
-        if self._bindAddress is None: # Run as a normal FastCGI?
-            isFCGI = True
-
-            sock = socket.fromfd(FCGI_LISTENSOCK_FILENO, socket.AF_INET,
-                                 socket.SOCK_STREAM)
-            try:
-                sock.getpeername()
-            except socket.error, e:
-                if e[0] == errno.ENOTSOCK:
-                    # Not a socket, assume CGI context.
-                    isFCGI = False
-                elif e[0] != errno.ENOTCONN:
-                    raise
-
-            # FastCGI/CGI discrimination is broken on Mac OS X.
-            # Set the environment variable FCGI_FORCE_CGI to "Y" or "y"
-            # if you want to run your app as a simple CGI. (You can do
-            # this with Apache's mod_env [not loaded by default in OS X
-            # client, ha ha] and the SetEnv directive.)
-            if not isFCGI or \
-               os.environ.get('FCGI_FORCE_CGI', 'N').upper().startswith('Y'):
-                req = self.cgirequest_class(self)
-                req.run()
-                sys.exit(0)
-        else:
-            # Run as a server
-            oldUmask = None
-            if type(self._bindAddress) is str:
-                # Unix socket
-                sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
-                try:
-                    os.unlink(self._bindAddress)
-                except OSError:
-                    pass
-                if self._umask is not None:
-                    oldUmask = os.umask(self._umask)
-            else:
-                # INET socket
-                assert type(self._bindAddress) is tuple
-                assert len(self._bindAddress) == 2
-                sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
-                sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
-
-            sock.bind(self._bindAddress)
-            sock.listen(socket.SOMAXCONN)
-
-            if oldUmask is not None:
-                os.umask(oldUmask)
-                
-        return sock
-
-    def _cleanupSocket(self, sock):
-        """Closes the main socket."""
-        sock.close()
-
-    def handler(self, req):
-        """Special handler for WSGI."""
-        if req.role not in self.roles:
-            return FCGI_UNKNOWN_ROLE, 0
-
-        # Mostly taken from example CGI gateway.
-        environ = req.params
-        environ.update(self.environ)
-
-        environ['wsgi.version'] = (1,0)
-        environ['wsgi.input'] = req.stdin
-        if self._bindAddress is None:
-            stderr = req.stderr
-        else:
-            stderr = TeeOutputStream((sys.stderr, req.stderr))
-        environ['wsgi.errors'] = stderr
-        environ['wsgi.multithread'] = not isinstance(req, CGIRequest) and \
-                                      thread_available and self.multithreaded
-        environ['wsgi.multiprocess'] = isinstance(req, CGIRequest) or \
-                                       self.multiprocess
-        environ['wsgi.run_once'] = isinstance(req, CGIRequest)
-
-        if environ.get('HTTPS', 'off') in ('on', '1'):
-            environ['wsgi.url_scheme'] = 'https'
-        else:
-            environ['wsgi.url_scheme'] = 'http'
-
-        self._sanitizeEnv(environ)
-
-        headers_set = []
-        headers_sent = []
-        result = None
-
-        def write(data):
-            assert type(data) is str, 'write() argument must be string'
-            assert headers_set, 'write() before start_response()'
-
-            if not headers_sent:
-                status, responseHeaders = headers_sent[:] = headers_set
-                found = False
-                for header,value in responseHeaders:
-                    if header.lower() == 'content-length':
-                        found = True
-                        break
-                if not found and result is not None:
-                    try:
-                        if len(result) == 1:
-                            responseHeaders.append(('Content-Length',
-                                                    str(len(data))))
-                    except:
-                        pass
-                s = 'Status: %s\r\n' % status
-                for header in responseHeaders:
-                    s += '%s: %s\r\n' % header
-                s += '\r\n'
-                req.stdout.write(s)
-
-            req.stdout.write(data)
-            req.stdout.flush()
-
-        def start_response(status, response_headers, exc_info=None):
-            if exc_info:
-                try:
-                    if headers_sent:
-                        # Re-raise if too late
-                        raise exc_info[0], exc_info[1], exc_info[2]
-                finally:
-                    exc_info = None # avoid dangling circular ref
-            else:
-                assert not headers_set, 'Headers already set!'
-
-            assert type(status) is str, 'Status must be a string'
-            assert len(status) >= 4, 'Status must be at least 4 characters'
-            assert int(status[:3]), 'Status must begin with 3-digit code'
-            assert status[3] == ' ', 'Status must have a space after code'
-            assert type(response_headers) is list, 'Headers must be a list'
-            if __debug__:
-                for name,val in response_headers:
-                    assert type(name) is str, 'Header names must be strings'
-                    assert type(val) is str, 'Header values must be strings'
-
-            headers_set[:] = [status, response_headers]
-            return write
-
-        if not self.multithreaded:
-            self._appLock.acquire()
-        try:
-            try:
-                result = self.application(environ, start_response)
-                try:
-                    for data in result:
-                        if data:
-                            write(data)
-                    if not headers_sent:
-                        write('') # in case body was empty
-                finally:
-                    if hasattr(result, 'close'):
-                        result.close()
-            except socket.error, e:
-                if e[0] != errno.EPIPE:
-                    raise # Don't let EPIPE propagate beyond server
-        finally:
-            if not self.multithreaded:
-                self._appLock.release()
-
-        return FCGI_REQUEST_COMPLETE, 0
-
-    def _sanitizeEnv(self, environ):
-        """Ensure certain values are present, if required by WSGI."""
-        if not environ.has_key('SCRIPT_NAME'):
-            environ['SCRIPT_NAME'] = ''
-
-        reqUri = None
-        if environ.has_key('REQUEST_URI'):
-            reqUri = environ['REQUEST_URI'].split('?', 1)
-
-        if not environ.has_key('PATH_INFO') or not environ['PATH_INFO']:
-            if reqUri is not None:
-                environ['PATH_INFO'] = reqUri[0]
-            else:
-                environ['PATH_INFO'] = ''
-        if not environ.has_key('QUERY_STRING') or not environ['QUERY_STRING']:
-            if reqUri is not None and len(reqUri) > 1:
-                environ['QUERY_STRING'] = reqUri[1]
-            else:
-                environ['QUERY_STRING'] = ''
-
-        # If any of these are missing, it probably signifies a broken
-        # server...
-        for name,default in [('REQUEST_METHOD', 'GET'),
-                             ('SERVER_NAME', 'localhost'),
-                             ('SERVER_PORT', '80'),
-                             ('SERVER_PROTOCOL', 'HTTP/1.0')]:
-            if not environ.has_key(name):
-                environ['wsgi.errors'].write('%s: missing FastCGI param %s '
-                                             'required by WSGI!\n' %
-                                             (self.__class__.__name__, name))
-                environ[name] = default
-            
-    def error(self, req):
-        """
-        Called by Request if an exception occurs within the handler. May and
-        should be overridden.
-        """
-        if self.debug:
-            import cgitb
-            req.stdout.write('Content-Type: text/html\r\n\r\n' +
-                             cgitb.html(sys.exc_info()))
-        else:
-            errorpage = """<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
-<html><head>
-<title>Unhandled Exception</title>
-</head><body>
-<h1>Unhandled Exception</h1>
-<p>An unhandled exception was thrown by the application.</p>
-</body></html>
-"""
-            req.stdout.write('Content-Type: text/html\r\n\r\n' +
-                             errorpage)
--- a/contrib/flup-server/flup/server/fcgi_fork.py	Wed Aug 13 21:06:45 2008 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,170 +0,0 @@
-# Copyright (c) 2005, 2006 Allan Saddi <allan@saddi.com>
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions
-# are met:
-# 1. Redistributions of source code must retain the above copyright
-#    notice, this list of conditions and the following disclaimer.
-# 2. Redistributions in binary form must reproduce the above copyright
-#    notice, this list of conditions and the following disclaimer in the
-#    documentation and/or other materials provided with the distribution.
-#
-# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
-# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
-# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-# SUCH DAMAGE.
-#
-# $Id$
-
-"""
-fcgi - a FastCGI/WSGI gateway.
-
-For more information about FastCGI, see <http://www.fastcgi.com/>.
-
-For more information about the Web Server Gateway Interface, see
-<http://www.python.org/peps/pep-0333.html>.
-
-Example usage:
-
-  #!/usr/bin/env python
-  from myapplication import app # Assume app is your WSGI application object
-  from fcgi import WSGIServer
-  WSGIServer(app).run()
-
-See the documentation for WSGIServer for more information.
-
-On most platforms, fcgi will fallback to regular CGI behavior if run in a
-non-FastCGI context. If you want to force CGI behavior, set the environment
-variable FCGI_FORCE_CGI to "Y" or "y".
-"""
-
-__author__ = 'Allan Saddi <allan@saddi.com>'
-__version__ = '$Revision$'
-
-import os
-
-from flup.server.fcgi_base import BaseFCGIServer, \
-     FCGI_MAX_CONNS, FCGI_MAX_REQS, FCGI_MPXS_CONNS
-from flup.server.preforkserver import PreforkServer
-
-__all__ = ['WSGIServer']
-
-class WSGIServer(BaseFCGIServer, PreforkServer):
-    """
-    FastCGI server that supports the Web Server Gateway Interface. See
-    <http://www.python.org/peps/pep-0333.html>.
-    """
-    def __init__(self, application, environ=None,
-                 bindAddress=None, umask=None, multiplexed=False,
-                 debug=True, **kw):
-        """
-        environ, if present, must be a dictionary-like object. Its
-        contents will be copied into application's environ. Useful
-        for passing application-specific variables.
-
-        bindAddress, if present, must either be a string or a 2-tuple. If
-        present, run() will open its own listening socket. You would use
-        this if you wanted to run your application as an 'external' FastCGI
-        app. (i.e. the webserver would no longer be responsible for starting
-        your app) If a string, it will be interpreted as a filename and a UNIX
-        socket will be opened. If a tuple, the first element, a string,
-        is the interface name/IP to bind to, and the second element (an int)
-        is the port number.
-        """
-        BaseFCGIServer.__init__(self, application,
-                                environ=environ,
-                                multithreaded=False,
-                                multiprocess=True,
-                                bindAddress=bindAddress,
-                                umask=umask,
-                                multiplexed=multiplexed,
-                                debug=debug)
-        for key in ('multithreaded', 'multiprocess', 'jobClass', 'jobArgs'):
-            if kw.has_key(key):
-                del kw[key]
-        PreforkServer.__init__(self, jobClass=self._connectionClass,
-                               jobArgs=(self,), **kw)
-
-        try:
-            import resource
-            # Attempt to glean the maximum number of connections
-            # from the OS.
-            try:
-                maxProcs = resource.getrlimit(resource.RLIMIT_NPROC)[0]
-                maxConns = resource.getrlimit(resource.RLIMIT_NOFILE)[0]
-                maxConns = min(maxConns, maxProcs)
-            except AttributeError:
-                maxConns = resource.getrlimit(resource.RLIMIT_NOFILE)[0]
-        except ImportError:
-            maxConns = 100 # Just some made up number.
-        maxReqs = maxConns
-        self.capability = {
-            FCGI_MAX_CONNS: maxConns,
-            FCGI_MAX_REQS: maxReqs,
-            FCGI_MPXS_CONNS: 0
-            }
-
-    def _isClientAllowed(self, addr):
-        return self._web_server_addrs is None or \
-               (len(addr) == 2 and addr[0] in self._web_server_addrs)
-
-    def run(self):
-        """
-        The main loop. Exits on SIGHUP, SIGINT, SIGTERM. Returns True if
-        SIGHUP was received, False otherwise.
-        """
-        self._web_server_addrs = os.environ.get('FCGI_WEB_SERVER_ADDRS')
-        if self._web_server_addrs is not None:
-            self._web_server_addrs = map(lambda x: x.strip(),
-                                         self._web_server_addrs.split(','))
-
-        sock = self._setupSocket()
-
-        ret = PreforkServer.run(self, sock)
-
-        self._cleanupSocket(sock)
-
-        return ret
-
-def factory(global_conf, host=None, port=None, **local):
-    import paste_factory
-    return paste_factory.helper(WSGIServer, global_conf, host, port, **local)
-
-if __name__ == '__main__':
-    def test_app(environ, start_response):
-        """Probably not the most efficient example."""
-        import cgi
-        start_response('200 OK', [('Content-Type', 'text/html')])
-        yield '<html><head><title>Hello World!</title></head>\n' \
-              '<body>\n' \
-              '<p>Hello World!</p>\n' \
-              '<table border="1">'
-        names = environ.keys()
-        names.sort()
-        for name in names:
-            yield '<tr><td>%s</td><td>%s</td></tr>\n' % (
-                name, cgi.escape(`environ[name]`))
-
-        form = cgi.FieldStorage(fp=environ['wsgi.input'], environ=environ,
-                                keep_blank_values=1)
-        if form.list:
-            yield '<tr><th colspan="2">Form data</th></tr>'
-
-        for field in form.list:
-            yield '<tr><td>%s</td><td>%s</td></tr>\n' % (
-                field.name, field.value)
-
-        yield '</table>\n' \
-              '</body></html>\n'
-
-    from wsgiref import validate
-    test_app = validate.validator(test_app)
-    WSGIServer(test_app).run()
--- a/contrib/flup-server/flup/server/fcgi_single.py	Wed Aug 13 21:06:45 2008 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,157 +0,0 @@
-# Copyright (c) 2005, 2006 Allan Saddi <allan@saddi.com>
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions
-# are met:
-# 1. Redistributions of source code must retain the above copyright
-#    notice, this list of conditions and the following disclaimer.
-# 2. Redistributions in binary form must reproduce the above copyright
-#    notice, this list of conditions and the following disclaimer in the
-#    documentation and/or other materials provided with the distribution.
-#
-# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
-# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
-# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-# SUCH DAMAGE.
-#
-# $Id$
-
-"""
-fcgi - a FastCGI/WSGI gateway.
-
-For more information about FastCGI, see <http://www.fastcgi.com/>.
-
-For more information about the Web Server Gateway Interface, see
-<http://www.python.org/peps/pep-0333.html>.
-
-Example usage:
-
-  #!/usr/bin/env python
-  from myapplication import app # Assume app is your WSGI application object
-  from fcgi import WSGIServer
-  WSGIServer(app).run()
-
-See the documentation for WSGIServer for more information.
-
-On most platforms, fcgi will fallback to regular CGI behavior if run in a
-non-FastCGI context. If you want to force CGI behavior, set the environment
-variable FCGI_FORCE_CGI to "Y" or "y".
-"""
-
-__author__ = 'Allan Saddi <allan@saddi.com>'
-__version__ = '$Revision$'
-
-import os
-
-from flup.server.fcgi_base import BaseFCGIServer, FCGI_RESPONDER, \
-     FCGI_MAX_CONNS, FCGI_MAX_REQS, FCGI_MPXS_CONNS
-from flup.server.singleserver import SingleServer
-
-__all__ = ['WSGIServer']
-
-class WSGIServer(BaseFCGIServer, SingleServer):
-    """
-    FastCGI server that supports the Web Server Gateway Interface. See
-    <http://www.python.org/peps/pep-0333.html>.
-    """
-    def __init__(self, application, environ=None,
-                 bindAddress=None, umask=None, multiplexed=False,
-                 debug=True, roles=(FCGI_RESPONDER,), **kw):
-        """
-        environ, if present, must be a dictionary-like object. Its
-        contents will be copied into application's environ. Useful
-        for passing application-specific variables.
-
-        bindAddress, if present, must either be a string or a 2-tuple. If
-        present, run() will open its own listening socket. You would use
-        this if you wanted to run your application as an 'external' FastCGI
-        app. (i.e. the webserver would no longer be responsible for starting
-        your app) If a string, it will be interpreted as a filename and a UNIX
-        socket will be opened. If a tuple, the first element, a string,
-        is the interface name/IP to bind to, and the second element (an int)
-        is the port number.
-        """
-        BaseFCGIServer.__init__(self, application,
-                                environ=environ,
-                                multithreaded=False,
-                                multiprocess=False,
-                                bindAddress=bindAddress,
-                                umask=umask,
-                                multiplexed=multiplexed,
-                                debug=debug,
-                                roles=roles)
-        for key in ('jobClass', 'jobArgs'):
-            if kw.has_key(key):
-                del kw[key]
-        SingleServer.__init__(self, jobClass=self._connectionClass,
-                              jobArgs=(self,), **kw)
-        self.capability = {
-            FCGI_MAX_CONNS: 1,
-            FCGI_MAX_REQS: 1,
-            FCGI_MPXS_CONNS: 0
-            }
-
-    def _isClientAllowed(self, addr):
-        return self._web_server_addrs is None or \
-               (len(addr) == 2 and addr[0] in self._web_server_addrs)
-
-    def run(self):
-        """
-        The main loop. Exits on SIGHUP, SIGINT, SIGTERM. Returns True if
-        SIGHUP was received, False otherwise.
-        """
-        self._web_server_addrs = os.environ.get('FCGI_WEB_SERVER_ADDRS')
-        if self._web_server_addrs is not None:
-            self._web_server_addrs = map(lambda x: x.strip(),
-                                         self._web_server_addrs.split(','))
-
-        sock = self._setupSocket()
-
-        ret = SingleServer.run(self, sock)
-
-        self._cleanupSocket(sock)
-
-        return ret
-
-def factory(global_conf, host=None, port=None, **local):
-    import paste_factory
-    return paste_factory.helper(WSGIServer, global_conf, host, port, **local)
-
-if __name__ == '__main__':
-    def test_app(environ, start_response):
-        """Probably not the most efficient example."""
-        import cgi
-        start_response('200 OK', [('Content-Type', 'text/html')])
-        yield '<html><head><title>Hello World!</title></head>\n' \
-              '<body>\n' \
-              '<p>Hello World!</p>\n' \
-              '<table border="1">'
-        names = environ.keys()
-        names.sort()
-        for name in names:
-            yield '<tr><td>%s</td><td>%s</td></tr>\n' % (
-                name, cgi.escape(`environ[name]`))
-
-        form = cgi.FieldStorage(fp=environ['wsgi.input'], environ=environ,
-                                keep_blank_values=1)
-        if form.list:
-            yield '<tr><th colspan="2">Form data</th></tr>'
-
-        for field in form.list:
-            yield '<tr><td>%s</td><td>%s</td></tr>\n' % (
-                field.name, field.value)
-
-        yield '</table>\n' \
-              '</body></html>\n'
-
-    from wsgiref import validate
-    test_app = validate.validator(test_app)
-    WSGIServer(test_app).run()
--- a/contrib/flup-server/flup/server/paste_factory.py	Wed Aug 13 21:06:45 2008 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,16 +0,0 @@
-def helper(wsgiServerClass, global_conf, host, port, **local_conf):
-    # I think I can't write a tuple for bindAddress in .ini file
-    host = host or global_conf.get('host', 'localhost')
-    port = port or global_conf.get('port', 4000)
-
-    if 'socket' in local_conf:
-        local_conf['bindAddress'] = local_conf['socket']
-        del local_conf['socket']
-    else:
-        local_conf['bindAddress'] = (host, int(port))
-    
-    def server(application):
-        server = wsgiServerClass(application, **local_conf)
-        server.run()
-
-    return server
--- a/contrib/flup-server/flup/server/preforkserver.py	Wed Aug 13 21:06:45 2008 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,414 +0,0 @@
-# Copyright (c) 2005 Allan Saddi <allan@saddi.com>
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions
-# are met:
-# 1. Redistributions of source code must retain the above copyright
-#    notice, this list of conditions and the following disclaimer.
-# 2. Redistributions in binary form must reproduce the above copyright
-#    notice, this list of conditions and the following disclaimer in the
-#    documentation and/or other materials provided with the distribution.
-#
-# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
-# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
-# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-# SUCH DAMAGE.
-#
-# $Id$
-
-__author__ = 'Allan Saddi <allan@saddi.com>'
-__version__ = '$Revision$'
-
-import sys
-import os
-import socket
-import select
-import errno
-import signal
-
-try:
-    import fcntl
-except ImportError:
-    def setCloseOnExec(sock):
-        pass
-else:
-    def setCloseOnExec(sock):
-        fcntl.fcntl(sock.fileno(), fcntl.F_SETFD, fcntl.FD_CLOEXEC)
-
-# If running Python < 2.4, require eunuchs module for socket.socketpair().
-# See <http://www.inoi.fi/open/trac/eunuchs>.
-if not hasattr(socket, 'socketpair'):
-    try:
-        import eunuchs.socketpair
-    except ImportError:
-        # TODO: Other alternatives? Perhaps using os.pipe()?
-        raise ImportError, 'Requires eunuchs module for Python < 2.4'
-
-    def socketpair():
-        s1, s2 = eunuchs.socketpair.socketpair()
-        p, c = (socket.fromfd(s1, socket.AF_UNIX, socket.SOCK_STREAM),
-                socket.fromfd(s2, socket.AF_UNIX, socket.SOCK_STREAM))
-        os.close(s1)
-        os.close(s2)
-        return p, c
-
-    socket.socketpair = socketpair
-
-class PreforkServer(object):
-    """