changeset 2340:dbfe98af770a

Merge with main.
author Karol 'grzywacz' Nowak <grzywacz@sul.uni.lodz.pl>
date Thu, 05 Jul 2007 22:11:16 +0200
parents 1d85514968a2 (current diff) 505991ec14f3 (diff)
children 338334a0d38d 24242585fd70
files MoinMoin/request/CGI.py MoinMoin/request/CLI.py MoinMoin/request/FCGI.py MoinMoin/request/MODPYTHON.py MoinMoin/request/STANDALONE.py MoinMoin/request/TWISTED.py MoinMoin/request/WSGI.py MoinMoin/server/CGI.py MoinMoin/server/STANDALONE.py MoinMoin/server/TWISTED.py MoinMoin/server/WSGI.py MoinMoin/xmlrpc/_tests/test_multicall.py
diffstat 35 files changed, 1682 insertions(+), 1681 deletions(-) [+]
line wrap: on
line diff
--- a/MoinMoin/action/SubscribeUser.py	Wed Jul 04 12:12:00 2007 +0200
+++ b/MoinMoin/action/SubscribeUser.py	Thu Jul 05 22:11:16 2007 +0200
@@ -116,8 +116,8 @@
         request_url = "localhost/"
 
     # Setup MoinMoin environment
-    from MoinMoin.request import CLI
-    request = CLI.Request(url=request_url)
+    from MoinMoin.request import request_cli
+    request = request_cli.Request(url=request_url)
     request.form = request.args = request.setup_args()
 
     from MoinMoin.formatter.text_plain import Formatter
--- a/MoinMoin/auth/http.py	Wed Jul 04 12:12:00 2007 +0200
+++ b/MoinMoin/auth/http.py	Thu Jul 05 22:11:16 2007 +0200
@@ -12,8 +12,9 @@
                 2007 MoinMoin:JohannesBerg
     @license: GNU GPL, see COPYING for details.
 """
+
 from MoinMoin import config, user
-from MoinMoin.request import TWISTED, CLI, STANDALONE
+from MoinMoin.request import request_twisted, request_cli, request_standalone
 from MoinMoin.auth import BaseAuth
 from base64 import decodestring
 
@@ -33,7 +34,7 @@
 
         # for standalone, request authorization and verify it,
         # deny access if it isn't verified
-        if isinstance(request, STANDALONE.Request):
+        if isinstance(request, request_standalone.Request):
             request.setHttpHeader('WWW-Authenticate: Basic realm="MoinMoin"')
             auth = request.headers.get('Authorization')
             if auth:
@@ -45,14 +46,14 @@
             if not u:
                 request.makeForbidden(401, _('You need to log in.'))
         # for Twisted, just check
-        elif isinstance(request, TWISTED.Request):
+        elif isinstance(request, request_twisted.Request):
             username = request.twistd.getUser().decode(config.charset)
             password = request.twistd.getPassword().decode(config.charset)
             # when using Twisted http auth, we use username and password from
             # the moin user profile, so both can be changed by user.
             u = user.User(request, auth_username=username, password=password,
                           auth_method=self.name, auth_attribs=())
-        elif not isinstance(request, CLI.Request):
+        elif not isinstance(request, request_cli.Request):
             env = request.env
             auth_type = env.get('AUTH_TYPE', '')
             if auth_type in ['Basic', 'Digest', 'NTLM', 'Negotiate', ]:
--- a/MoinMoin/auth/sslclientcert.py	Wed Jul 04 12:12:00 2007 +0200
+++ b/MoinMoin/auth/sslclientcert.py	Thu Jul 05 22:11:16 2007 +0200
@@ -11,7 +11,7 @@
 """
 
 from MoinMoin import config, user
-from MoinMoin.request import TWISTED
+from MoinMoin.request import request_twisted
 from MoinMoin.auth import BaseAuth
 
 class SSLClientCertAuth(BaseAuth):
@@ -34,7 +34,7 @@
         u = None
         changed = False
         # check if we are running Twisted
-        if isinstance(request, TWISTED.Request):
+        if isinstance(request, request_twisted.Request):
             return user_obj, True # not supported if we run twisted
             # Addendum: this seems to need quite some twisted insight and coding.
             # A pointer i got on #twisted: divmod's vertex.sslverify
--- a/MoinMoin/conftest.py	Wed Jul 04 12:12:00 2007 +0200
+++ b/MoinMoin/conftest.py	Thu Jul 05 22:11:16 2007 +0200
@@ -71,13 +71,13 @@
 
 
 def init_test_request(static_state=[False]):
-    from MoinMoin.request import CLI
+    from MoinMoin.request import request_cli
     from MoinMoin.user import User
     from MoinMoin.formatter.text_html import Formatter as HtmlFormatter
     if not static_state[0]:
         maketestwiki.run(True)
         static_state[0] = True
-    request = CLI.Request()
+    request = request_cli.Request()
     request.form = request.args = request.setup_args()
     request.user = User(request)
     request.html_formatter = HtmlFormatter(request)
--- a/MoinMoin/mail/mailimport.py	Wed Jul 04 12:12:00 2007 +0200
+++ b/MoinMoin/mail/mailimport.py	Thu Jul 05 22:11:16 2007 +0200
@@ -17,7 +17,7 @@
 from MoinMoin.action.AttachFile import add_attachment, AttachmentAlreadyExists
 from MoinMoin.Page import Page
 from MoinMoin.PageEditor import PageEditor
-from MoinMoin.request.CLI import Request as RequestCLI
+from MoinMoin.request.request_cli import Request as RequestCLI
 # python, at least up to 2.4, ships a broken parser for headers
 from MoinMoin.support.HeaderFixed import decode_header
 
--- a/MoinMoin/packages.py	Wed Jul 04 12:12:00 2007 +0200
+++ b/MoinMoin/packages.py	Thu Jul 05 22:11:16 2007 +0200
@@ -533,8 +533,8 @@
         request_url = "localhost/"
 
     # Setup MoinMoin environment
-    from MoinMoin.request import CLI
-    request = CLI.Request(url='localhost/')
+    from MoinMoin.request import request_cli
+    request = request_cli.Request(url='localhost/')
     request.form = request.args = request.setup_args()
 
     package = ZipPackage(request, packagefile)
--- a/MoinMoin/request/CGI.py	Wed Jul 04 12:12:00 2007 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,65 +0,0 @@
-# -*- coding: iso-8859-1 -*-
-"""
-    MoinMoin - CGI Request Implementation for std. CGI web servers
-    like Apache or IIS or others.
-
-    @copyright: 2001-2003 by Juergen Hermann <jh@web.de>,
-                2003-2006 MoinMoin:ThomasWaldmann
-    @license: GNU GPL, see COPYING for details.
-"""
-import sys, os, cgi
-
-from MoinMoin.request import RequestBase
-
-class Request(RequestBase):
-    """ specialized on CGI requests """
-
-    def __init__(self, properties={}):
-        try:
-            # force input/output to binary
-            if sys.platform == "win32":
-                import msvcrt
-                msvcrt.setmode(sys.stdin.fileno(), os.O_BINARY)
-                msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)
-
-            self._setup_vars_from_std_env(os.environ)
-            RequestBase.__init__(self, properties)
-
-        except Exception, err:
-            self.fail(err)
-
-    def _setup_args_from_cgi_form(self):
-        """ Override to create cgi form """
-        form = cgi.FieldStorage()
-        return RequestBase._setup_args_from_cgi_form(self, form)
-
-    def read(self, n=None):
-        """ Read from input stream. """
-        if n is None:
-            return sys.stdin.read()
-        else:
-            return sys.stdin.read(n)
-
-    def write(self, *data):
-        """ Write to output stream. """
-        sys.stdout.write(self.encode(data))
-
-    def flush(self):
-        sys.stdout.flush()
-
-    def finish(self):
-        RequestBase.finish(self)
-        # flush the output, ignore errors caused by the user closing the socket
-        try:
-            sys.stdout.flush()
-        except IOError, ex:
-            import errno
-            if ex.errno != errno.EPIPE:
-                raise
-
-    def _emit_http_headers(self, headers):
-        """ private method to send out preprocessed list of HTTP headers """
-        for header in headers:
-            self.write("%s\r\n" % header)
-        self.write("\r\n")
-
--- a/MoinMoin/request/CLI.py	Wed Jul 04 12:12:00 2007 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,95 +0,0 @@
-# -*- coding: iso-8859-1 -*-
-"""
-    MoinMoin - CLI Request Implementation for commandline usage.
-
-    @copyright: 2001-2003 Juergen Hermann <jh@web.de>,
-                2003-2006 MoinMoin:ThomasWaldmann
-    @license: GNU GPL, see COPYING for details.
-"""
-import sys
-
-from MoinMoin.request import RequestBase
-
-class Request(RequestBase):
-    """ specialized on command line interface and script requests """
-
-    def __init__(self, url='CLI', pagename='', properties={}):
-        self.saved_cookie = ''
-        self.path_info = '/' + pagename
-        self.query_string = ''
-        self.remote_addr = '127.0.0.1'
-        self.is_ssl = 0
-        self.http_user_agent = 'CLI/Script'
-        self.url = url
-        self.request_method = 'GET'
-        self.request_uri = '/' + pagename # TODO check if /pagename works as URI for CLI usage
-        self.http_host = 'localhost'
-        self.http_referer = ''
-        self.script_name = '.'
-        self.if_modified_since = None
-        self.if_none_match = None
-        RequestBase.__init__(self, properties)
-        self.cfg.caching_formats = [] # don't spoil the cache
-        self.initTheme() # usually request.run() does this, but we don't use it
-
-    def _setup_args_from_cgi_form(self):
-        """ Override to create cli form """
-        #form = cgi.FieldStorage()
-        #return RequestBase._setup_args_from_cgi_form(self, form)
-        return {}
-
-    def read(self, n=None):
-        """ Read from input stream. """
-        if n is None:
-            return sys.stdin.read()
-        else:
-            return sys.stdin.read(n)
-
-    def write(self, *data):
-        """ Write to output stream. """
-        sys.stdout.write(self.encode(data))
-
-    def flush(self):
-        sys.stdout.flush()
-
-    def finish(self):
-        RequestBase.finish(self)
-        # flush the output, ignore errors caused by the user closing the socket
-        try:
-            sys.stdout.flush()
-        except IOError, ex:
-            import errno
-            if ex.errno != errno.EPIPE:
-                raise
-
-    def isForbidden(self):
-        """ Nothing is forbidden """
-        return 0
-
-    # Accessors --------------------------------------------------------
-
-    def getQualifiedURL(self, uri=None):
-        """ Return a full URL starting with schema and host
-
-        TODO: does this create correct pages when you render wiki pages
-              within a cli request?!
-        """
-        return uri
-
-    # Headers ----------------------------------------------------------
-
-    def setHttpHeader(self, header):
-        pass
-
-    def _emit_http_headers(self, headers):
-        """ private method to send out preprocessed list of HTTP headers """
-        pass
-
-    def http_redirect(self, url):
-        """ Redirect to a fully qualified, or server-rooted URL
-
-        TODO: Does this work for rendering redirect pages?
-        """
-        raise Exception("Redirect not supported for command line tools!")
-
-
--- a/MoinMoin/request/FCGI.py	Wed Jul 04 12:12:00 2007 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,62 +0,0 @@
-# -*- coding: iso-8859-1 -*-
-"""
-    MoinMoin - FastCGI Request Implementation for fastcgi and Apache
-    (and maybe others).
-
-    @copyright: 2001-2003 Juergen Hermann <jh@web.de>,
-                2003-2006 MoinMoin:ThomasWaldmann
-    @license: GNU GPL, see COPYING for details.
-"""
-from MoinMoin.request import RequestBase
-
-class Request(RequestBase):
-    """ specialized on FastCGI requests """
-
-    def __init__(self, fcgRequest, env, form, properties={}):
-        """ Initializes variables from FastCGI environment and saves
-            FastCGI request and form for further use.
-
-            @param fcgRequest: the FastCGI request instance.
-            @param env: environment passed by FastCGI.
-            @param form: FieldStorage passed by FastCGI.
-        """
-        try:
-            self.fcgreq = fcgRequest
-            self.fcgenv = env
-            self.fcgform = form
-            self._setup_vars_from_std_env(env)
-            RequestBase.__init__(self, properties)
-
-        except Exception, err:
-            self.fail(err)
-
-    def _setup_args_from_cgi_form(self):
-        """ Override to use FastCGI form """
-        return RequestBase._setup_args_from_cgi_form(self, self.fcgform)
-
-    def read(self, n=None):
-        """ Read from input stream. """
-        if n is None:
-            return self.fcgreq.stdin.read()
-        else:
-            return self.fcgreq.stdin.read(n)
-
-    def write(self, *data):
-        """ Write to output stream. """
-        self.fcgreq.out.write(self.encode(data))
-
-    def flush(self):
-        """ Flush output stream. """
-        self.fcgreq.flush_out()
-
-    def finish(self):
-        """ Call finish method of FastCGI request to finish handling of this request. """
-        RequestBase.finish(self)
-        self.fcgreq.finish()
-
-    def _emit_http_headers(self, headers):
-        """ private method to send out preprocessed list of HTTP headers """
-        for header in headers:
-            self.write("%s\r\n" % header)
-        self.write("\r\n")
-
--- a/MoinMoin/request/MODPYTHON.py	Wed Jul 04 12:12:00 2007 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,151 +0,0 @@
-# -*- coding: iso-8859-1 -*-
-"""
-    MoinMoin - mod_python Request Implementation for Apache and mod_python.
-
-    @copyright: 2001-2003 Juergen Hermann <jh@web.de>,
-                2003-2006 MoinMoin:ThomasWaldmann
-    @license: GNU GPL, see COPYING for details.
-"""
-from MoinMoin import wikiutil
-from MoinMoin.request import RequestBase
-
-class Request(RequestBase):
-    """ specialized on mod_python requests """
-
-    def __init__(self, req):
-        """ Saves mod_pythons request and sets basic variables using
-            the req.subprocess_env, cause this provides a standard
-            way to access the values we need here.
-
-            @param req: the mod_python request instance
-        """
-        try:
-            # flags if headers sent out contained content-type or status
-            self._have_ct = 0
-            self._have_status = 0
-
-            req.add_common_vars()
-            self.mpyreq = req
-            # some mod_python 2.7.X has no get method for table objects,
-            # so we make a real dict out of it first.
-            if not hasattr(req.subprocess_env, 'get'):
-                env = dict(req.subprocess_env)
-            else:
-                env = req.subprocess_env
-            self._setup_vars_from_std_env(env)
-            RequestBase.__init__(self)
-
-        except Exception, err:
-            self.fail(err)
-
-    def fixURI(self, env):
-        """ Fix problems with script_name and path_info using
-        PythonOption directive to rewrite URI.
-
-        This is needed when using Apache 1 or other server which does
-        not support adding custom headers per request. With mod_python we
-        can use the PythonOption directive:
-
-            <Location /url/to/mywiki/>
-                PythonOption X-Moin-Location /url/to/mywiki/
-            </location>
-
-        Note that *neither* script_name *nor* path_info can be trusted
-        when Moin is invoked as a mod_python handler with apache1, so we
-        must build both using request_uri and the provided PythonOption.
-        """
-        # Be compatible with release 1.3.5 "Location" option
-        # TODO: Remove in later release, we should have one option only.
-        old_location = 'Location'
-        options_table = self.mpyreq.get_options()
-        if not hasattr(options_table, 'get'):
-            options = dict(options_table)
-        else:
-            options = options_table
-        location = options.get(self.moin_location) or options.get(old_location)
-        if location:
-            env[self.moin_location] = location
-            # Try to recreate script_name and path_info from request_uri.
-            import urlparse
-            scriptAndPath = urlparse.urlparse(self.request_uri)[2]
-            self.script_name = location.rstrip('/')
-            path = scriptAndPath.replace(self.script_name, '', 1)
-            self.path_info = wikiutil.url_unquote(path, want_unicode=False)
-
-        RequestBase.fixURI(self, env)
-
-    def _setup_args_from_cgi_form(self):
-        """ Override to use mod_python.util.FieldStorage
-
-        It's little different from cgi.FieldStorage, so we need to
-        duplicate the conversion code.
-        """
-        from mod_python import util
-        form = util.FieldStorage(self.mpyreq)
-
-        args = {}
-        for key in form:
-            if key is None:
-                continue
-            values = form[key]
-            if not isinstance(values, list):
-                values = [values]
-            fixedResult = []
-
-            for item in values:
-                # Remember filenames with a name hack
-                if hasattr(item, 'filename') and item.filename:
-                    args[key + '__filename__'] = item.filename
-                # mod_python 2.7 might return strings instead of Field
-                # objects.
-                if hasattr(item, 'value'):
-                    item = item.value
-                fixedResult.append(item)
-            args[key] = fixedResult
-
-        return self.decodeArgs(args)
-
-    def run(self, req):
-        """ mod_python calls this with its request object. We don't
-            need it cause its already passed to __init__. So ignore
-            it and just return RequestBase.run.
-
-            @param req: the mod_python request instance
-        """
-        return RequestBase.run(self)
-
-    def read(self, n=None):
-        """ Read from input stream. """
-        if n is None:
-            return self.mpyreq.read()
-        else:
-            return self.mpyreq.read(n)
-
-    def write(self, *data):
-        """ Write to output stream. """
-        self.mpyreq.write(self.encode(data))
-
-    def flush(self):
-        """ We can't flush it, so do nothing. """
-        pass
-
-    def finish(self):
-        """ Just return apache.OK. Status is set in req.status. """
-        RequestBase.finish(self)
-        # is it possible that we need to return something else here?
-        from mod_python import apache
-        return apache.OK
-
-    def _emit_http_headers(self, headers):
-        """ private method to send out preprocessed list of HTTP headers """
-        st_header, ct_header, other_headers = headers[0], headers[1], headers[2:]
-        status = st_header.split(':', 1)[1].lstrip()
-        self.mpyreq.status = int(status.split(' ', 1)[0])
-        self.mpyreq.content_type = ct_header.split(':', 1)[1].lstrip()
-        for header in other_headers:
-            key, value = header.split(':', 1)
-            value = value.lstrip()
-            self.mpyreq.headers_out[key] = value
-        # this is for mod_python 2.7.X, for 3.X it's a NOP
-        self.mpyreq.send_http_header()
-
--- a/MoinMoin/request/STANDALONE.py	Wed Jul 04 12:12:00 2007 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,106 +0,0 @@
-# -*- coding: iso-8859-1 -*-
-"""
-    MoinMoin - Standalone Moin Server Request Implementation
-
-    @copyright: 2001-2003 Juergen Hermann <jh@web.de>,
-                2003-2006 MoinMoin:ThomasWaldmann
-    @license: GNU GPL, see COPYING for details.
-"""
-import cgi
-
-from MoinMoin.request import RequestBase
-
-class Request(RequestBase):
-    """ specialized on StandAlone Server (MoinMoin.server.standalone) requests """
-    script_name = ''
-
-    def __init__(self, sa, properties={}):
-        """
-        @param sa: stand alone server object
-        @param properties: ...
-        """
-        try:
-            self.sareq = sa
-            self.wfile = sa.wfile
-            self.rfile = sa.rfile
-            self.headers = sa.headers
-            self.is_ssl = 0
-
-            # Copy headers
-            self.http_accept_language = (sa.headers.getheader('accept-language')
-                                         or self.http_accept_language)
-            self.http_user_agent = sa.headers.getheader('user-agent', '')
-            co = [c for c in sa.headers.getheaders('cookie') if c]
-            self.saved_cookie = ', '.join(co) or ''
-            self.if_modified_since = sa.headers.getheader('if-modified-since')
-            self.if_none_match = sa.headers.getheader('if-none-match')
-
-            # Copy rest from standalone request
-            self.server_name = sa.server.server_name
-            self.server_port = str(sa.server.server_port)
-            self.request_method = sa.command
-            self.request_uri = sa.path
-            self.remote_addr = sa.client_address[0]
-
-            # Values that need more work
-            self.path_info, self.query_string = self.splitURI(sa.path)
-            self.setHttpReferer(sa.headers.getheader('referer'))
-            self.setHost(sa.headers.getheader('host'))
-            self.setURL(sa.headers)
-
-            ##self.debugEnvironment(sa.headers)
-
-            RequestBase.__init__(self, properties)
-
-        except Exception, err:
-            self.fail(err)
-
-    def _setup_args_from_cgi_form(self):
-        """ Override to create standalone form """
-        form = cgi.FieldStorage(self.rfile, headers=self.headers, environ={'REQUEST_METHOD': 'POST'})
-        return RequestBase._setup_args_from_cgi_form(self, form)
-
-    def read(self, n=None):
-        """ Read from input stream
-
-        Since self.rfile.read() will block, content-length will be used instead.
-
-        TODO: test with n > content length, or when calling several times
-        with smaller n but total over content length.
-        """
-        if n is None:
-            try:
-                n = int(self.headers.get('content-length'))
-            except (TypeError, ValueError):
-                import warnings
-                warnings.warn("calling request.read() when content-length is "
-                              "not available will block")
-                return self.rfile.read()
-        return self.rfile.read(n)
-
-    def write(self, *data):
-        """ Write to output stream. """
-        self.wfile.write(self.encode(data))
-
-    def flush(self):
-        self.wfile.flush()
-
-    def finish(self):
-        RequestBase.finish(self)
-        self.wfile.flush()
-
-    # Headers ----------------------------------------------------------
-
-    def _emit_http_headers(self, headers):
-        """ private method to send out preprocessed list of HTTP headers """
-        st_header, other_headers = headers[0], headers[1:]
-        status = st_header.split(':', 1)[1].lstrip()
-        status_code, status_msg = status.split(' ', 1)
-        status_code = int(status_code)
-        self.sareq.send_response(status_code, status_msg)
-        for header in other_headers:
-            key, value = header.split(':', 1)
-            value = value.lstrip()
-            self.sareq.send_header(key, value)
-        self.sareq.end_headers()
-
--- a/MoinMoin/request/TWISTED.py	Wed Jul 04 12:12:00 2007 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,132 +0,0 @@
-# -*- coding: iso-8859-1 -*-
-"""
-    MoinMoin - Twisted Request Implementation
-
-    @copyright: 2001-2003 Juergen Hermann <jh@web.de>,
-                2003-2006 MoinMoin:ThomasWaldmann
-    @license: GNU GPL, see COPYING for details.
-"""
-
-from MoinMoin.request import RequestBase, MoinMoinFinish
-
-class Request(RequestBase):
-    """ specialized on Twisted requests """
-
-    def __init__(self, twistedRequest, pagename, reactor, properties={}):
-        try:
-            self.twistd = twistedRequest
-            self.reactor = reactor
-
-            # Copy headers
-            self.http_accept_language = self.twistd.getHeader('Accept-Language')
-            self.saved_cookie = self.twistd.getHeader('Cookie')
-            self.http_user_agent = self.twistd.getHeader('User-Agent')
-            self.if_modified_since = self.twistd.getHeader('If-Modified-Since')
-            self.if_none_match = self.twistd.getHeader('If-None-Match')
-
-            # Copy values from twisted request
-            self.server_protocol = self.twistd.clientproto
-            self.server_name = self.twistd.getRequestHostname().split(':')[0]
-            self.server_port = str(self.twistd.getHost()[2])
-            self.is_ssl = self.twistd.isSecure()
-            self.path_info = '/' + '/'.join([pagename] + self.twistd.postpath)
-            self.request_method = self.twistd.method
-            self.remote_addr = self.twistd.getClientIP()
-            self.request_uri = self.twistd.uri
-            self.script_name = "/" + '/'.join(self.twistd.prepath[:-1])
-
-            # Values that need more work
-            self.query_string = self.splitURI(self.twistd.uri)[1]
-            self.setHttpReferer(self.twistd.getHeader('Referer'))
-            self.setHost()
-            self.setURL(self.twistd.getAllHeaders())
-
-            ##self.debugEnvironment(twistedRequest.getAllHeaders())
-
-            RequestBase.__init__(self, properties)
-
-        except MoinMoinFinish: # might be triggered by http_redirect
-            self.emit_http_headers() # send headers (important for sending MOIN_ID cookie)
-            self.finish()
-
-        except Exception, err:
-            # Wrap err inside an internal error if needed
-            from MoinMoin import error
-            if isinstance(err, error.FatalError):
-                self.delayedError = err
-            else:
-                self.delayedError = error.InternalError(str(err))
-
-    def run(self):
-        """ Handle delayed errors then invoke base class run """
-        if hasattr(self, 'delayedError'):
-            self.fail(self.delayedError)
-            return self.finish()
-        RequestBase.run(self)
-
-    def setup_args(self):
-        """ Return args dict
-
-        Twisted already parsed args, including __filename__ hacking,
-        but did not decode the values.
-        """
-        # TODO: check if for a POST this included query_string args (needed for
-        # TwikiDraw's action=AttachFile&do=savedrawing)
-        return self.decodeArgs(self.twistd.args)
-
-    def read(self, n=None):
-        """ Read from input stream. """
-        # XXX why is that wrong?:
-        #rd = self.reactor.callFromThread(self.twistd.read)
-
-        # XXX do we need self.reactor.callFromThread with that?
-        # XXX if yes, why doesn't it work?
-        self.twistd.content.seek(0, 0)
-        if n is None:
-            rd = self.twistd.content.read()
-        else:
-            rd = self.twistd.content.read(n)
-        #print "request.RequestTwisted.read: data=\n" + str(rd)
-        return rd
-
-    def write(self, *data):
-        """ Write to output stream. """
-        #print "request.RequestTwisted.write: data=\n" + wd
-        self.reactor.callFromThread(self.twistd.write, self.encode(data))
-
-    def flush(self):
-        pass # XXX is there a flush in twisted?
-
-    def finish(self):
-        RequestBase.finish(self)
-        self.reactor.callFromThread(self.twistd.finish)
-
-    # Headers ----------------------------------------------------------
-
-    def _emit_http_headers(self, headers):
-        """ private method to send out preprocessed list of HTTP headers """
-        st_header, other_headers = headers[0], headers[1:]
-        status = st_header.split(':', 1)[1].lstrip()
-        status_code, status_msg = status.split(' ', 1)
-        self.twistd.setResponseCode(int(status_code), status_msg)
-        for header in other_headers:
-            key, value = header.split(':', 1)
-            value = value.lstrip()
-            if key.lower() == 'set-cookie':
-                key, value = value.split('=', 1)
-                self.twistd.addCookie(key, value)
-            else:
-                self.twistd.setHeader(key, value)
-
-    def http_redirect(self, url):
-        """ Redirect to a fully qualified, or server-rooted URL
-
-        @param url: relative or absolute url, ascii using url encoding.
-        """
-        url = self.getQualifiedURL(url)
-        self.twistd.redirect(url)
-        # calling finish here will send the rest of the data to the next
-        # request. leave the finish call to run()
-        #self.twistd.finish()
-        raise MoinMoinFinish
-
--- a/MoinMoin/request/WSGI.py	Wed Jul 04 12:12:00 2007 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,67 +0,0 @@
-# -*- coding: iso-8859-1 -*-
-"""
-    MoinMoin - WSGI Request Implementation for std. WSGI web servers.
-
-    @copyright: 2001-2003 Juergen Hermann <jh@web.de>,
-                2003-2006 MoinMoin:ThomasWaldmann
-    @license: GNU GPL, see COPYING for details.
-"""
-import cgi, StringIO
-
-from MoinMoin.request import RequestBase
-
-class Request(RequestBase):
-    """ specialized on WSGI requests """
-    def __init__(self, env):
-        try:
-            self.env = env
-            self.hasContentType = False
-
-            self.stdin = env['wsgi.input']
-            self.stdout = StringIO.StringIO()
-
-            # used by MoinMoin.server.wsgi:
-            self.status = '200 OK'
-            self.headers = []
-
-            self._setup_vars_from_std_env(env)
-            RequestBase.__init__(self, {})
-
-        except Exception, err:
-            self.fail(err)
-
-    def setup_args(self):
-        # TODO: does this include query_string args for POST requests?
-        # see also how CGI works now
-        form = cgi.FieldStorage(fp=self.stdin, environ=self.env, keep_blank_values=1)
-        return RequestBase._setup_args_from_cgi_form(self, form)
-
-    def read(self, n=None):
-        if n is None:
-            return self.stdin.read()
-        else:
-            return self.stdin.read(n)
-
-    def write(self, *data):
-        self.stdout.write(self.encode(data))
-
-    def reset_output(self):
-        self.stdout = StringIO.StringIO()
-
-    def _emit_http_headers(self, headers):
-        """ private method to send out preprocessed list of HTTP headers """
-        st_header, other_headers = headers[0], headers[1:]
-        self.status = st_header.split(':', 1)[1].lstrip()
-        for header in other_headers:
-            key, value = header.split(':', 1)
-            value = value.lstrip()
-            self.headers.append((key, value))
-
-    def flush(self):
-        pass
-
-    def output(self):
-        # called by MoinMoin.server.wsgi
-        return self.stdout.getvalue()
-
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MoinMoin/request/request_cgi.py	Thu Jul 05 22:11:16 2007 +0200
@@ -0,0 +1,65 @@
+# -*- coding: iso-8859-1 -*-
+"""
+    MoinMoin - CGI Request Implementation for std. CGI web servers
+    like Apache or IIS or others.
+
+    @copyright: 2001-2003 by Juergen Hermann <jh@web.de>,
+                2003-2006 MoinMoin:ThomasWaldmann
+    @license: GNU GPL, see COPYING for details.
+"""
+import sys, os, cgi
+
+from MoinMoin.request import RequestBase
+
+class Request(RequestBase):
+    """ specialized on CGI requests """
+
+    def __init__(self, properties={}):
+        try:
+            # force input/output to binary
+            if sys.platform == "win32":
+                import msvcrt
+                msvcrt.setmode(sys.stdin.fileno(), os.O_BINARY)
+                msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)
+
+            self._setup_vars_from_std_env(os.environ)
+            RequestBase.__init__(self, properties)
+
+        except Exception, err:
+            self.fail(err)
+
+    def _setup_args_from_cgi_form(self):
+        """ Override to create cgi form """
+        form = cgi.FieldStorage()
+        return RequestBase._setup_args_from_cgi_form(self, form)
+
+    def read(self, n=None):
+        """ Read from input stream. """
+        if n is None:
+            return sys.stdin.read()
+        else:
+            return sys.stdin.read(n)
+
+    def write(self, *data):
+        """ Write to output stream. """
+        sys.stdout.write(self.encode(data))
+
+    def flush(self):
+        sys.stdout.flush()
+
+    def finish(self):
+        RequestBase.finish(self)
+        # flush the output, ignore errors caused by the user closing the socket
+        try:
+            sys.stdout.flush()
+        except IOError, ex:
+            import errno
+            if ex.errno != errno.EPIPE:
+                raise
+
+    def _emit_http_headers(self, headers):
+        """ private method to send out preprocessed list of HTTP headers """
+        for header in headers:
+            self.write("%s\r\n" % header)
+        self.write("\r\n")
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MoinMoin/request/request_cli.py	Thu Jul 05 22:11:16 2007 +0200
@@ -0,0 +1,95 @@
+# -*- coding: iso-8859-1 -*-
+"""
+    MoinMoin - CLI Request Implementation for commandline usage.
+
+    @copyright: 2001-2003 Juergen Hermann <jh@web.de>,
+                2003-2006 MoinMoin:ThomasWaldmann
+    @license: GNU GPL, see COPYING for details.
+"""
+import sys
+
+from MoinMoin.request import RequestBase
+
+class Request(RequestBase):
+    """ specialized on command line interface and script requests """
+
+    def __init__(self, url='CLI', pagename='', properties={}):
+        self.saved_cookie = ''
+        self.path_info = '/' + pagename
+        self.query_string = ''
+        self.remote_addr = '127.0.0.1'
+        self.is_ssl = 0
+        self.http_user_agent = 'CLI/Script'
+        self.url = url
+        self.request_method = 'GET'
+        self.request_uri = '/' + pagename # TODO check if /pagename works as URI for CLI usage
+        self.http_host = 'localhost'
+        self.http_referer = ''
+        self.script_name = '.'
+        self.if_modified_since = None
+        self.if_none_match = None
+        RequestBase.__init__(self, properties)
+        self.cfg.caching_formats = [] # don't spoil the cache
+        self.initTheme() # usually request.run() does this, but we don't use it
+
+    def _setup_args_from_cgi_form(self):
+        """ Override to create cli form """
+        #form = cgi.FieldStorage()
+        #return RequestBase._setup_args_from_cgi_form(self, form)
+        return {}
+
+    def read(self, n=None):
+        """ Read from input stream. """
+        if n is None:
+            return sys.stdin.read()
+        else:
+            return sys.stdin.read(n)
+
+    def write(self, *data):
+        """ Write to output stream. """
+        sys.stdout.write(self.encode(data))
+
+    def flush(self):
+        sys.stdout.flush()
+
+    def finish(self):
+        RequestBase.finish(self)
+        # flush the output, ignore errors caused by the user closing the socket
+        try:
+            sys.stdout.flush()
+        except IOError, ex:
+            import errno
+            if ex.errno != errno.EPIPE:
+                raise
+
+    def isForbidden(self):
+        """ Nothing is forbidden """
+        return 0
+
+    # Accessors --------------------------------------------------------
+
+    def getQualifiedURL(self, uri=None):
+        """ Return a full URL starting with schema and host
+
+        TODO: does this create correct pages when you render wiki pages
+              within a cli request?!
+        """
+        return uri
+
+    # Headers ----------------------------------------------------------
+
+    def setHttpHeader(self, header):
+        pass
+
+    def _emit_http_headers(self, headers):
+        """ private method to send out preprocessed list of HTTP headers """
+        pass
+
+    def http_redirect(self, url):
+        """ Redirect to a fully qualified, or server-rooted URL
+
+        TODO: Does this work for rendering redirect pages?
+        """
+        raise Exception("Redirect not supported for command line tools!")
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MoinMoin/request/request_fcgi.py	Thu Jul 05 22:11:16 2007 +0200
@@ -0,0 +1,62 @@
+# -*- coding: iso-8859-1 -*-
+"""
+    MoinMoin - FastCGI Request Implementation for fastcgi and Apache
+    (and maybe others).
+
+    @copyright: 2001-2003 Juergen Hermann <jh@web.de>,
+                2003-2006 MoinMoin:ThomasWaldmann
+    @license: GNU GPL, see COPYING for details.
+"""
+from MoinMoin.request import RequestBase
+
+class Request(RequestBase):
+    """ specialized on FastCGI requests """
+
+    def __init__(self, fcgRequest, env, form, properties={}):
+        """ Initializes variables from FastCGI environment and saves
+            FastCGI request and form for further use.
+
+            @param fcgRequest: the FastCGI request instance.
+            @param env: environment passed by FastCGI.
+            @param form: FieldStorage passed by FastCGI.
+        """
+        try:
+            self.fcgreq = fcgRequest
+            self.fcgenv = env
+            self.fcgform = form
+            self._setup_vars_from_std_env(env)
+            RequestBase.__init__(self, properties)
+
+        except Exception, err:
+            self.fail(err)
+
+    def _setup_args_from_cgi_form(self):
+        """ Override to use FastCGI form """
+        return RequestBase._setup_args_from_cgi_form(self, self.fcgform)
+
+    def read(self, n=None):
+        """ Read from input stream. """
+        if n is None:
+            return self.fcgreq.stdin.read()
+        else:
+            return self.fcgreq.stdin.read(n)
+
+    def write(self, *data):
+        """ Write to output stream. """
+        self.fcgreq.out.write(self.encode(data))
+
+    def flush(self):
+        """ Flush output stream. """
+        self.fcgreq.flush_out()
+
+    def finish(self):
+        """ Call finish method of FastCGI request to finish handling of this request. """
+        RequestBase.finish(self)
+        self.fcgreq.finish()
+
+    def _emit_http_headers(self, headers):
+        """ private method to send out preprocessed list of HTTP headers """
+        for header in headers:
+            self.write("%s\r\n" % header)
+        self.write("\r\n")
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MoinMoin/request/request_modpython.py	Thu Jul 05 22:11:16 2007 +0200
@@ -0,0 +1,151 @@
+# -*- coding: iso-8859-1 -*-
+"""
+    MoinMoin - mod_python Request Implementation for Apache and mod_python.
+
+    @copyright: 2001-2003 Juergen Hermann <jh@web.de>,
+                2003-2006 MoinMoin:ThomasWaldmann
+    @license: GNU GPL, see COPYING for details.
+"""
+from MoinMoin import wikiutil
+from MoinMoin.request import RequestBase
+
+class Request(RequestBase):
+    """ specialized on mod_python requests """
+
+    def __init__(self, req):
+        """ Saves mod_pythons request and sets basic variables using
+            the req.subprocess_env, cause this provides a standard
+            way to access the values we need here.
+
+            @param req: the mod_python request instance
+        """
+        try:
+            # flags if headers sent out contained content-type or status
+            self._have_ct = 0
+            self._have_status = 0
+
+            req.add_common_vars()
+            self.mpyreq = req
+            # some mod_python 2.7.X has no get method for table objects,
+            # so we make a real dict out of it first.
+            if not hasattr(req.subprocess_env, 'get'):
+                env = dict(req.subprocess_env)
+            else:
+                env = req.subprocess_env
+            self._setup_vars_from_std_env(env)
+            RequestBase.__init__(self)
+
+        except Exception, err:
+            self.fail(err)
+
+    def fixURI(self, env):
+        """ Fix problems with script_name and path_info using
+        PythonOption directive to rewrite URI.
+
+        This is needed when using Apache 1 or other server which does
+        not support adding custom headers per request. With mod_python we
+        can use the PythonOption directive:
+
+            <Location /url/to/mywiki/>
+                PythonOption X-Moin-Location /url/to/mywiki/
+            </location>
+
+        Note that *neither* script_name *nor* path_info can be trusted
+        when Moin is invoked as a mod_python handler with apache1, so we
+        must build both using request_uri and the provided PythonOption.
+        """
+        # Be compatible with release 1.3.5 "Location" option
+        # TODO: Remove in later release, we should have one option only.
+        old_location = 'Location'
+        options_table = self.mpyreq.get_options()
+        if not hasattr(options_table, 'get'):
+            options = dict(options_table)
+        else:
+            options = options_table
+        location = options.get(self.moin_location) or options.get(old_location)
+        if location:
+            env[self.moin_location] = location
+            # Try to recreate script_name and path_info from request_uri.
+            import urlparse
+            scriptAndPath = urlparse.urlparse(self.request_uri)[2]
+            self.script_name = location.rstrip('/')
+            path = scriptAndPath.replace(self.script_name, '', 1)
+            self.path_info = wikiutil.url_unquote(path, want_unicode=False)
+
+        RequestBase.fixURI(self, env)
+
+    def _setup_args_from_cgi_form(self):
+        """ Override to use mod_python.util.FieldStorage
+
+        It's little different from cgi.FieldStorage, so we need to
+        duplicate the conversion code.
+        """
+        from mod_python import util
+        form = util.FieldStorage(self.mpyreq)
+
+        args = {}
+        for key in form:
+            if key is None:
+                continue
+            values = form[key]
+            if not isinstance(values, list):
+                values = [values]
+            fixedResult = []
+
+            for item in values:
+                # Remember filenames with a name hack
+                if hasattr(item, 'filename') and item.filename:
+                    args[key + '__filename__'] = item.filename
+                # mod_python 2.7 might return strings instead of Field
+                # objects.
+                if hasattr(item, 'value'):
+                    item = item.value
+                fixedResult.append(item)
+            args[key] = fixedResult
+
+        return self.decodeArgs(args)
+
+    def run(self, req):
+        """ mod_python calls this with its request object. We don't
+            need it cause its already passed to __init__. So ignore
+            it and just return RequestBase.run.
+
+            @param req: the mod_python request instance
+        """
+        return RequestBase.run(self)
+
+    def read(self, n=None):
+        """ Read from input stream. """
+        if n is None:
+            return self.mpyreq.read()
+        else:
+            return self.mpyreq.read(n)
+
+    def write(self, *data):
+        """ Write to output stream. """
+        self.mpyreq.write(self.encode(data))
+
+    def flush(self):
+        """ We can't flush it, so do nothing. """
+        pass
+
+    def finish(self):
+        """ Just return apache.OK. Status is set in req.status. """
+        RequestBase.finish(self)
+        # is it possible that we need to return something else here?
+        from mod_python import apache
+        return apache.OK
+
+    def _emit_http_headers(self, headers):
+        """ private method to send out preprocessed list of HTTP headers """
+        st_header, ct_header, other_headers = headers[0], headers[1], headers[2:]
+        status = st_header.split(':', 1)[1].lstrip()
+        self.mpyreq.status = int(status.split(' ', 1)[0])
+        self.mpyreq.content_type = ct_header.split(':', 1)[1].lstrip()
+        for header in other_headers:
+            key, value = header.split(':', 1)
+            value = value.lstrip()
+            self.mpyreq.headers_out[key] = value
+        # this is for mod_python 2.7.X, for 3.X it's a NOP
+        self.mpyreq.send_http_header()
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MoinMoin/request/request_standalone.py	Thu Jul 05 22:11:16 2007 +0200
@@ -0,0 +1,106 @@
+# -*- coding: iso-8859-1 -*-
+"""
+    MoinMoin - Standalone Moin Server Request Implementation
+
+    @copyright: 2001-2003 Juergen Hermann <jh@web.de>,
+                2003-2006 MoinMoin:ThomasWaldmann
+    @license: GNU GPL, see COPYING for details.
+"""
+import cgi
+
+from MoinMoin.request import RequestBase
+
+class Request(RequestBase):
+    """ specialized on StandAlone Server (MoinMoin.server.standalone) requests """
+    script_name = ''
+
+    def __init__(self, sa, properties={}):
+        """
+        @param sa: stand alone server object
+        @param properties: ...
+        """
+        try:
+            self.sareq = sa
+            self.wfile = sa.wfile
+            self.rfile = sa.rfile
+            self.headers = sa.headers
+            self.is_ssl = 0
+
+            # Copy headers
+            self.http_accept_language = (sa.headers.getheader('accept-language')
+                                         or self.http_accept_language)
+            self.http_user_agent = sa.headers.getheader('user-agent', '')
+            co = [c for c in sa.headers.getheaders('cookie') if c]
+            self.saved_cookie = ', '.join(co) or ''
+            self.if_modified_since = sa.headers.getheader('if-modified-since')
+            self.if_none_match = sa.headers.getheader('if-none-match')
+
+            # Copy rest from standalone request
+            self.server_name = sa.server.server_name
+            self.server_port = str(sa.server.server_port)
+            self.request_method = sa.command
+            self.request_uri = sa.path
+            self.remote_addr = sa.client_address[0]
+
+            # Values that need more work
+            self.path_info, self.query_string = self.splitURI(sa.path)
+            self.setHttpReferer(sa.headers.getheader('referer'))
+            self.setHost(sa.headers.getheader('host'))
+            self.setURL(sa.headers)
+
+            ##self.debugEnvironment(sa.headers)
+
+            RequestBase.__init__(self, properties)
+
+        except Exception, err:
+            self.fail(err)
+
+    def _setup_args_from_cgi_form(self):
+        """ Override to create standalone form """
+        form = cgi.FieldStorage(self.rfile, headers=self.headers, environ={'REQUEST_METHOD': 'POST'})
+        return RequestBase._setup_args_from_cgi_form(self, form)
+
+    def read(self, n=None):
+        """ Read from input stream
+
+        Since self.rfile.read() will block, content-length will be used instead.
+
+        TODO: test with n > content length, or when calling several times
+        with smaller n but total over content length.
+        """
+        if n is None:
+            try:
+                n = int(self.headers.get('content-length'))
+            except (TypeError, ValueError):
+                import warnings
+                warnings.warn("calling request.read() when content-length is "
+                              "not available will block")
+                return self.rfile.read()
+        return self.rfile.read(n)
+
+    def write(self, *data):
+        """ Write to output stream. """
+        self.wfile.write(self.encode(data))
+
+    def flush(self):
+        self.wfile.flush()
+
+    def finish(self):
+        RequestBase.finish(self)
+        self.wfile.flush()
+
+    # Headers ----------------------------------------------------------
+
+    def _emit_http_headers(self, headers):
+        """ private method to send out preprocessed list of HTTP headers """
+        st_header, other_headers = headers[0], headers[1:]
+        status = st_header.split(':', 1)[1].lstrip()
+        status_code, status_msg = status.split(' ', 1)
+        status_code = int(status_code)
+        self.sareq.send_response(status_code, status_msg)
+        for header in other_headers:
+            key, value = header.split(':', 1)
+            value = value.lstrip()
+            self.sareq.send_header(key, value)
+        self.sareq.end_headers()
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MoinMoin/request/request_twisted.py	Thu Jul 05 22:11:16 2007 +0200
@@ -0,0 +1,132 @@
+# -*- coding: iso-8859-1 -*-
+"""
+    MoinMoin - Twisted Request Implementation
+
+    @copyright: 2001-2003 Juergen Hermann <jh@web.de>,
+                2003-2006 MoinMoin:ThomasWaldmann
+    @license: GNU GPL, see COPYING for details.
+"""
+
+from MoinMoin.request import RequestBase, MoinMoinFinish
+
+class Request(RequestBase):
+    """ specialized on Twisted requests """
+
+    def __init__(self, twistedRequest, pagename, reactor, properties={}):
+        try:
+            self.twistd = twistedRequest
+            self.reactor = reactor
+
+            # Copy headers
+            self.http_accept_language = self.twistd.getHeader('Accept-Language')
+            self.saved_cookie = self.twistd.getHeader('Cookie')
+            self.http_user_agent = self.twistd.getHeader('User-Agent')
+            self.if_modified_since = self.twistd.getHeader('If-Modified-Since')
+            self.if_none_match = self.twistd.getHeader('If-None-Match')
+
+            # Copy values from twisted request
+            self.server_protocol = self.twistd.clientproto
+            self.server_name = self.twistd.getRequestHostname().split(':')[0]
+            self.server_port = str(self.twistd.getHost()[2])
+            self.is_ssl = self.twistd.isSecure()
+            self.path_info = '/' + '/'.join([pagename] + self.twistd.postpath)
+            self.request_method = self.twistd.method
+            self.remote_addr = self.twistd.getClientIP()
+            self.request_uri = self.twistd.uri
+            self.script_name = "/" + '/'.join(self.twistd.prepath[:-1])
+
+            # Values that need more work
+            self.query_string = self.splitURI(self.twistd.uri)[1]
+            self.setHttpReferer(self.twistd.getHeader('Referer'))
+            self.setHost()
+            self.setURL(self.twistd.getAllHeaders())
+
+            ##self.debugEnvironment(twistedRequest.getAllHeaders())
+
+            RequestBase.__init__(self, properties)
+
+        except MoinMoinFinish: # might be triggered by http_redirect
+            self.emit_http_headers() # send headers (important for sending MOIN_ID cookie)
+            self.finish()
+
+        except Exception, err:
+            # Wrap err inside an internal error if needed
+            from MoinMoin import error
+            if isinstance(err, error.FatalError):
+                self.delayedError = err
+            else:
+                self.delayedError = error.InternalError(str(err))
+
+    def run(self):
+        """ Handle delayed errors then invoke base class run """
+        if hasattr(self, 'delayedError'):
+            self.fail(self.delayedError)
+            return self.finish()
+        RequestBase.run(self)
+
+    def setup_args(self):
+        """ Return args dict
+
+        Twisted already parsed args, including __filename__ hacking,
+        but did not decode the values.
+        """
+        # TODO: check if for a POST this included query_string args (needed for
+        # TwikiDraw's action=AttachFile&do=savedrawing)
+        return self.decodeArgs(self.twistd.args)
+
+    def read(self, n=None):
+        """ Read from input stream. """
+        # XXX why is that wrong?:
+        #rd = self.reactor.callFromThread(self.twistd.read)
+
+        # XXX do we need self.reactor.callFromThread with that?
+        # XXX if yes, why doesn't it work?
+        self.twistd.content.seek(0, 0)
+        if n is None:
+            rd = self.twistd.content.read()
+        else:
+            rd = self.twistd.content.read(n)
+        #print "request.RequestTwisted.read: data=\n" + str(rd)
+        return rd
+
+    def write(self, *data):
+        """ Write to output stream. """
+        #print "request.RequestTwisted.write: data=\n" + wd
+        self.reactor.callFromThread(self.twistd.write, self.encode(data))
+
+    def flush(self):
+        pass # XXX is there a flush in twisted?
+
+    def finish(self):
+        RequestBase.finish(self)
+        self.reactor.callFromThread(self.twistd.finish)
+
+    # Headers ----------------------------------------------------------
+
+    def _emit_http_headers(self, headers):
+        """ private method to send out preprocessed list of HTTP headers """
+        st_header, other_headers = headers[0], headers[1:]
+        status = st_header.split(':', 1)[1].lstrip()
+        status_code, status_msg = status.split(' ', 1)
+        self.twistd.setResponseCode(int(status_code), status_msg)
+        for header in other_headers:
+            key, value = header.split(':', 1)
+            value = value.lstrip()
+            if key.lower() == 'set-cookie':
+                key, value = value.split('=', 1)
+                self.twistd.addCookie(key, value)
+            else:
+                self.twistd.setHeader(key, value)
+
+    def http_redirect(self, url):
+        """ Redirect to a fully qualified, or server-rooted URL
+
+        @param url: relative or absolute url, ascii using url encoding.
+        """
+        url = self.getQualifiedURL(url)
+        self.twistd.redirect(url)
+        # calling finish here will send the rest of the data to the next
+        # request. leave the finish call to run()
+        #self.twistd.finish()
+        raise MoinMoinFinish
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MoinMoin/request/request_wsgi.py	Thu Jul 05 22:11:16 2007 +0200
@@ -0,0 +1,67 @@
+# -*- coding: iso-8859-1 -*-
+"""
+    MoinMoin - WSGI Request Implementation for std. WSGI web servers.
+
+    @copyright: 2001-2003 Juergen Hermann <jh@web.de>,
+                2003-2006 MoinMoin:ThomasWaldmann
+    @license: GNU GPL, see COPYING for details.
+"""
+import cgi, StringIO
+
+from MoinMoin.request import RequestBase
+
+class Request(RequestBase):
+    """ specialized on WSGI requests """
+    def __init__(self, env):
+        try:
+            self.env = env
+            self.hasContentType = False
+
+            self.stdin = env['wsgi.input']
+            self.stdout = StringIO.StringIO()
+
+            # used by MoinMoin.server.wsgi:
+            self.status = '200 OK'
+            self.headers = []
+
+            self._setup_vars_from_std_env(env)
+            RequestBase.__init__(self, {})
+
+        except Exception, err:
+            self.fail(err)
+
+    def setup_args(self):
+        # TODO: does this include query_string args for POST requests?
+        # see also how CGI works now
+        form = cgi.FieldStorage(fp=self.stdin, environ=self.env, keep_blank_values=1)
+        return RequestBase._setup_args_from_cgi_form(self, form)
+
+    def read(self, n=None):
+        if n is None:
+            return self.stdin.read()
+        else:
+            return self.stdin.read(n)
+
+    def write(self, *data):
+        self.stdout.write(self.encode(data))
+
+    def reset_output(self):
+        self.stdout = StringIO.StringIO()
+
+    def _emit_http_headers(self, headers):
+        """ private method to send out preprocessed list of HTTP headers """
+        st_header, other_headers = headers[0], headers[1:]
+        self.status = st_header.split(':', 1)[1].lstrip()
+        for header in other_headers:
+            key, value = header.split(':', 1)
+            value = value.lstrip()
+            self.headers.append((key, value))
+
+    def flush(self):
+        pass
+
+    def output(self):
+        # called by MoinMoin.server.wsgi
+        return self.stdout.getvalue()
+
+
--- a/MoinMoin/script/__init__.py	Wed Jul 04 12:12:00 2007 +0200
+++ b/MoinMoin/script/__init__.py	Thu Jul 05 22:11:16 2007 +0200
@@ -45,7 +45,7 @@
 
 class ScriptRequestCLI(ScriptRequest):
     """ When a script runs directly on the shell, we just use the CLI request
-        object (see MoinMoin.request.CLI) to do I/O (which will use stdin/out/err).
+        object (see MoinMoin.request.request_cli) to do I/O (which will use stdin/out/err).
     """
     def __init__(self, request):
         self.request = request
@@ -172,11 +172,11 @@
 
     def init_request(self):
         """ create request """
-        from MoinMoin.request import CLI
+        from MoinMoin.request import request_cli
         if self.options.wiki_url:
-            self.request = CLI.Request(self.options.wiki_url, self.options.page)
+            self.request = request_cli.Request(self.options.wiki_url, self.options.page)
         else:
-            self.request = CLI.Request(pagename=self.options.page)
+            self.request = request_cli.Request(pagename=self.options.page)
 
     def mainloop(self):
         # Insert config dir or the current directory to the start of the path.
--- a/MoinMoin/search/builtin.py	Wed Jul 04 12:12:00 2007 +0200
+++ b/MoinMoin/search/builtin.py	Thu Jul 05 22:11:16 2007 +0200
@@ -399,7 +399,7 @@
 
         @param request: current request
         """
-        from MoinMoin.request.CLI import Request
+        from MoinMoin.request.request_cli import Request
         from MoinMoin.security import Permissions
         request = Request(request.url)
         class SecurityPolicy(Permissions):
--- a/MoinMoin/server/CGI.py	Wed Jul 04 12:12:00 2007 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,65 +0,0 @@
-# -*- coding: iso-8859-1 -*-
-"""
-    MoinMoin - CGI pseudo Server
-
-    This is not really a server, it is just so that CGI stuff (the real
-    server is likely Apache or IIS or some other std. CGI server) looks
-    similar to what we have for Twisted and standalone server.
-
-    Minimal usage:
-
-        from MoinMoin.server.CGI import CgiConfig, run
-
-        class Config(CgiConfig):
-            pass
-
-        run(Config)
-
-    See more options in CgiConfig class.
-
-    @copyright: 2006 MoinMoin:ThomasWaldmann
-    @license: GNU GPL, see COPYING for details.
-"""
-
-from MoinMoin.server import Config
-from MoinMoin.request import CGI
-
-# Server globals
-config = None
-
-# ------------------------------------------------------------------------
-# Public interface
-
-class CgiConfig(Config):
-    """ CGI default config """
-
-    name = 'moin'
-    properties = {}
-    logPath = None
-
-    # Development options
-    hotshotProfile = None # e.g. "moin.prof"
-
-
-def run(configClass):
-    """ Create and run a Cgi Request
-
-    See CgiConfig for available options
-
-    @param configClass: config class
-    """
-
-    config = configClass()
-
-    if config.hotshotProfile:
-        import hotshot
-        config.hotshotProfile = hotshot.Profile(config.hotshotProfile)
-        config.hotshotProfile.start()
-
-    request = CGI.Request(properties=config.properties)
-    request.run()
-
-    if config.hotshotProfile:
-        config.hotshotProfile.close()
-
-
--- a/MoinMoin/server/STANDALONE.py	Wed Jul 04 12:12:00 2007 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,614 +0,0 @@
-# -*- coding: iso-8859-1 -*-
-"""
-    MoinMoin - Stand-alone HTTP Server
-
-    This is a simple, fast and very easy to install server. Its
-    recommended for personal wikis or public wikis with little load.
-
-    It is not well tested in public wikis with heavy load. In these case
-    you might want to use twisted, fast cgi or mod python, or if you
-    can't use those, cgi.
-
-    Minimal usage:
-
-        from MoinMoin.server.STANDALONE import StandaloneConfig, run
-
-        class Config(StandaloneConfig):
-            docs = '/usr/share/moin/wiki/htdocs'
-            user = 'www-data'
-            group = 'www-data'
-
-        run(Config)
-
-    See more options in StandaloneConfig class.
-
-    For security, the server will not run as root. If you try to run it
-    as root, it will run as the user and group in the config. If you run
-    it as a normal user, it will run with your regular user and group.
-
-    Significant contributions to this module by R. Church <rc@ghostbitch.org>
-
-    @copyright: 2001-2004 MoinMoin:JuergenHermann,
-                2005 MoinMoin:AlexanderSchremmer,
-                2005 MoinMoin:NirSoffer
-    @license: GNU GPL, see COPYING for details.
-"""
-
-import os, sys, time, socket, errno, shutil, logging
-import BaseHTTPServer, SimpleHTTPServer, SocketServer
-
-from MoinMoin import version, wikiutil
-from MoinMoin.server import Config, switchUID
-from MoinMoin.request import STANDALONE
-from MoinMoin.util import timefuncs
-
-# Server globals
-httpd = None
-config = None
-
-
-class SimpleServer(BaseHTTPServer.HTTPServer):
-    """ Simplest server, serving one request after another
-
-    This server is good for personal wiki, or when lowest memory
-    footprint is needed.
-    """
-    use_threads = False
-
-    def __init__(self, config):
-        self.htdocs = config.docs
-        self.request_queue_size = config.requestQueueSize
-        self._abort = 0
-        address = (config.interface, config.port)
-        BaseHTTPServer.HTTPServer.__init__(self, address, MoinRequestHandler)
-
-    def server_activate(self):
-        BaseHTTPServer.HTTPServer.server_activate(self)
-        logging.info("Serving on %s:%d" % self.server_address)
-
-    def serve_forever(self):
-        """Handle one request at a time until we die """
-        while not self._abort:
-            self.handle_request()
-
-    def die(self):
-        """Abort this server instance's serving loop """
-        # Close hotshot profiler
-        if config.hotshotProfile:
-            config.hotshotProfile.close()
-
-        # Set abort flag, then make request to wake the server
-        self._abort = 1
-        try:
-            import httplib
-            addr = self.server_address
-            if not addr[0]:
-                addr = ("localhost", addr[1])
-            req = httplib.HTTP('%s:%d' % addr)
-            req.connect()
-            req.putrequest('DIE', '/')
-            req.endheaders()
-            del req
-        except socket.error, err:
-            # Ignore certain errors
-            if err.args[0] not in [errno.EADDRNOTAVAIL, ]:
-                raise
-
-
-class ThreadingServer(SimpleServer):
-    """ Serve each request in a new thread
-
-    This server is used since release 1.3 and seems to work nice with
-    little load.
-
-    From release 1.3.5 there is a thread limit, that should help to
-    limit the load on the server.
-    """
-    use_threads = True
-
-    def __init__(self, config):
-        self.thread_limit = config.threadLimit
-        from threading import Condition
-        self.lock = Condition()
-        SimpleServer.__init__(self, config)
-
-    def process_request(self, request, client_address):
-        """ Start a new thread to process the request
-
-        If the thread limit has been reached, wait on the lock. The
-        next thread will notify when finished.
-        """
-        from threading import Thread, activeCount
-        self.lock.acquire()
-        try:
-            if activeCount() > self.thread_limit:
-                self.lock.wait()
-            if self._abort:
-                return
-            t = Thread(target=self.process_request_thread,
-                       args=(request, client_address))
-            t.start()
-        finally:
-            self.lock.release()
-
-    def process_request_thread(self, request, client_address):
-        """ Called for each request on a new thread
-
-        Notify the main thread on the end of each request.
-        """
-        try:
-            self.finish_request(request, client_address)
-        except:
-            self.handle_error(request, client_address)
-        self.close_request(request)
-        self.lock.acquire()
-        try:
-            # Main thread might be waiting
-            self.lock.notify()
-        finally:
-            self.lock.release()
-
-
-class ThreadPoolServer(SimpleServer):
-    """ Threading server using a pool of threads
-
-    This is a new experimental server, using a pool of threads instead
-    of creating new thread for each request. This is similar to Apache
-    worker mpm, with a simpler constant thread pool.
-
-    This server is 5 times faster than ThreadingServer for static
-    files, and about the same for wiki pages.
-
-    TODO: sometimes the server won't exit on Conrol-C, and continue to
-    run with few threads (you can kill it with kill -9). Same problem
-    exist with the twisted server. When the problem is finally solved,
-    remove the commented debug prints.
-    """
-    use_threads = True
-
-    def __init__(self, config):
-        self.queue = []
-        # The size of the queue need more testing
-        self.queueSize = config.threadLimit * 2
-        self.poolSize = config.threadLimit
-        from threading import Condition
-        self.lock = Condition()
-        SimpleServer.__init__(self, config)
-
-    def serve_forever(self):
-        """ Create a thread pool then invoke base class method """
-        from threading import Thread
-        for dummy in range(self.poolSize):
-            t = Thread(target=self.serve_forever_thread)
-            t.start()
-        SimpleServer.serve_forever(self)
-
-    def process_request(self, request, client_address):
-        """ Called for each request
-
-        Insert the request into the queue. If the queue is full, wait
-        until one of the request threads pop a request. During the wait,
-        new connections might be dropped.
-        """
-        self.lock.acquire()
-        try:
-            if len(self.queue) >= self.queueSize:
-                self.lock.wait()
-            if self._abort:
-                return
-            self.queue.insert(0, (request, client_address))
-            self.lock.notify()
-        finally:
-            self.lock.release()
-
-    def serve_forever_thread(self):
-        """ The main loop of request threads
-
-        Pop a request from the queue and process it.
-        """
-        while not self._abort:
-            request, client_address = self.pop_request()
-            try:
-                self.finish_request(request, client_address)
-            except:
-                self.handle_error(request, client_address)
-            self.close_request(request)
-        # sys.stderr.write('thread exiting...\n')
-
-    def pop_request(self):
-        """ Pop a request from the queue
-
-        If the queue is empty, wait for notification. If the queue was
-        full, notify the main thread which may be waiting.
-        """
-        self.lock.acquire()
-        try:
-            while not self._abort:
-                try:
-                    item = self.queue.pop()
-                    if len(self.queue) == self.queueSize - 1:
-                        # Queue was full - main thread might be waiting
-                        self.lock.notify()
-                    return item
-                except IndexError:
-                    self.lock.wait()
-        finally:
-            self.lock.release()
-        # sys.stderr.write('thread exiting...\n')
-        sys.exit()
-
-    def die(self):
-        """ Wake all threads then invoke base class die
-
-        Threads should exist when _abort is True.
-        """
-        self._abort = True
-        self.wake_all_threads()
-        time.sleep(0.1)
-        SimpleServer.die(self)
-
-    def wake_all_threads(self):
-        self.lock.acquire()
-        try:
-            # sys.stderr.write('waking up all threads...\n')
-            self.lock.notifyAll()
-        finally:
-            self.lock.release()
-
-
-class ForkingServer(SocketServer.ForkingMixIn, SimpleServer):
-    """ Serve each request in a new process
-
-    This is new untested server, first tests show rather pathetic cgi
-    like performance. No data is cached between requests.
-
-    The mixin has its own process limit.
-    """
-    max_children = 10
-
-
-class MoinRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
-
-    bufferSize = 8 * 1024 # used to serve static files
-    staticExpire = 365 * 24 * 3600 # 1 year expiry for static files
-
-    def __init__(self, request, client_address, server):
-        self.server_version = "MoinMoin %s %s %s" % (version.release,
-                                                     version.revision,
-                                                     server.__class__.__name__)
-        self.expires = 0
-        SimpleHTTPServer.SimpleHTTPRequestHandler.__init__(self, request,
-            client_address, server)
-
-    def log_message(self, format, *args):
-        logging.info("%s %s" % (self.address_string(), format % args))
-
-    # -------------------------------------------------------------------
-    # do_METHOD dispatchers - called for each request
-
-    def do_DIE(self):
-        if self.server._abort:
-            self.log_error("Shutting down")
-
-    def do_ALL(self):
-        """ Handle requests (request type GET/HEAD/POST is in self.command)
-
-        Separate between wiki pages and css and image url by similar
-        system as cgi and twisted, the url_prefix_static prefix.
-        """
-        prefix = config.url_prefix_static
-        if self.path.startswith(prefix + '/'):
-            self.path = self.path[len(prefix):]
-            self.serve_static_file()
-        elif self.path in ['/favicon.ico', '/robots.txt']:
-            self.serve_static_file()
-        else:
-            self.serve_moin()
-
-    do_POST = do_ALL
-    do_GET = do_ALL
-    do_HEAD = do_ALL
-
-    # -------------------------------------------------------------------
-    # Serve methods
-
-    def serve_static_file(self):
-        """ Serve files from the htdocs directory """
-        self.expires = self.staticExpire
-        path = self.path.split("?", 1)
-        if len(path) > 1:
-            self.path = path[0] # XXX ?params
-
-        try:
-            fn = getattr(SimpleHTTPServer.SimpleHTTPRequestHandler, 'do_' + self.command)
-            fn(self)
-        except socket.error, err:
-            # Ignore certain errors
-            if err.args[0] not in [errno.EPIPE, errno.ECONNABORTED]:
-                raise
-
-    def serve_moin(self):
-        """ Serve a request using moin """
-        # don't make an Expires header for wiki pages
-        self.expires = 0
-
-        try:
-            req = STANDALONE.Request(self, properties=config.properties)
-            req.run()
-        except socket.error, err:
-            # Ignore certain errors
-            if err.args[0] not in [errno.EPIPE, errno.ECONNABORTED]:
-                raise
-
-    def translate_path(self, uri):
-        """ Translate a /-separated PATH to the local filename syntax.
-
-        Components that mean special things to the local file system
-        (e.g. drive or directory names) are ignored.
-        """
-        path = wikiutil.url_unquote(uri, want_unicode=False)
-        path = path.replace('\\', '/')
-        words = path.split('/')
-        words = filter(None, words)
-
-        path = self.server.htdocs
-        bad_uri = 0
-        for word in words:
-            drive, word = os.path.splitdrive(word)
-            if drive:
-                bad_uri = 1
-            head, word = os.path.split(word)
-            if word in (os.curdir, os.pardir):
-                bad_uri = 1
-                continue
-            path = os.path.join(path, word)
-
-        if bad_uri:
-            self.log_error("Detected bad request URI '%s', translated to '%s'"
-                           % (uri, path, ))
-        return path
-
-    def end_headers(self):
-        """overload the default end_headers, inserting expires header"""
-        if self.expires:
-            now = time.time()
-            expires = now + self.expires
-            self.send_header('Expires', timefuncs.formathttpdate(expires))
-        SimpleHTTPServer.SimpleHTTPRequestHandler.end_headers(self)
-
-    def copyfile(self, source, outputfile):
-        """Copy all data between two file objects.
-
-        Modify the base class method to change the buffer size. Test
-        shows that for the typical static files we serve, 8K buffer is
-        faster than the default 16K buffer.
-        """
-        shutil.copyfileobj(source, outputfile, length=self.bufferSize)
-
-    def address_string(self):
-        """We don't want to do reverse DNS lookups, just return IP address."""
-        return self.client_address[0]
-
-
-try:
-    from tlslite.api import TLSSocketServerMixIn, X509, X509CertChain, SessionCache, parsePEMKey
-    from tlslite.TLSConnection import TLSConnection
-except ImportError:
-    pass
-else:
-    class SecureRequestRedirect(BaseHTTPServer.BaseHTTPRequestHandler):
-        def handle(self):
-            self.close_connection = 1
-            try:
-                self.raw_requestline = self.rfile.readline()
-            except socket.error:
-                return
-            if self.parse_request():
-                host = self.headers.get('Host', socket.gethostname())
-                path = self.path
-            else:
-                host = '%s:%s' % (socket.gethostname(),
-                        self.request.getsockname()[1])
-                path = '/'
-
-            self.requestline = 'ERROR: Redirecting to https://%s%s' % (host, path)
-            self.request_version = 'HTTP/1.1'
-            self.command = 'GET'
-            self.send_response(301, 'Document Moved')
-            self.send_header('Date', self.date_time_string())
-            self.send_header('Location', 'https://%s%s' % (host, path))
-            self.send_header('Connection', 'close')
-            self.send_header('Content-Length', '0')
-            self.wfile.write('\r\n')
-
-    class SecureThreadPoolServer(TLSSocketServerMixIn, ThreadPoolServer):
-        def __init__(self, config):
-            ThreadPoolServer.__init__(self, config)
-
-            cert = open(config.ssl_certificate).read()
-            x509 = X509()
-            x509.parse(cert)
-            self.certChain = X509CertChain([x509])
-
-            priv = open(config.ssl_privkey).read()
-            self.privateKey = parsePEMKey(priv, private=True)
-
-            self.sessionCache = SessionCache()
-
-        def finish_request(self, sock, client_address):
-            # Peek into the packet, if it starts with GET or POS(T) then
-            # redirect, otherwise let TLSLite handle the connection.
-            peek = sock.recv(3, socket.MSG_PEEK).lower()
-            if peek == 'get' or peek == 'pos':
-                SecureRequestRedirect(sock, client_address, self)
-                return
-            tls_connection = TLSConnection(sock)
-            if self.handshake(tls_connection):
-                self.RequestHandlerClass(tls_connection, client_address, self)
-            else:
-                # This will probably fail because the TLSConnection has
-                # already written SSL stuff to the socket. But not sure what
-                # else we should do.
-                SecureRequestRedirect(sock, client_address, self)
-
-        def handshake(self, tls_connection):
-            try:
-                tls_connection.handshakeServer(certChain=self.certChain,
-                                               privateKey=self.privateKey,
-                                               sessionCache=self.sessionCache)
-                tls_connection.ignoreAbruptClose = True
-                return True
-            except:
-                return False
-
-
-def memoryProfileDecorator(func, profile):
-    """ Return a profiled function """
-    def profiledFunction(*args, **kw):
-        profile.addRequest()
-        return func(*args, **kw)
-    return profiledFunction
-
-
-def hotshotProfileDecorator(func, profile):
-    """ Return a profiled function """
-    profile.moin_requests_done = 0
-    def profiledFunction(*args, **kw):
-        profile.moin_requests_done += 1
-        if profile.moin_requests_done == 1:
-            # Don't profile first request, its not interesting
-            return func(*args, **kw)
-        return profile.runcall(func, *args, **kw)
-
-    return profiledFunction
-
-
-def quit(signo, stackframe):
-    """ Signal handler for aborting signals """
-    global httpd, config
-    logging.info("Thanks for using MoinMoin!")
-
-    fname = config.pycallgraph_output
-    if fname:
-        import pycallgraph
-        if fname.endswith('.png'):
-            pycallgraph.make_dot_graph(fname)
-        elif fname.endswith('.dot'):
-            pycallgraph.save_dot(fname)
-
-    if httpd:
-        httpd.die()
-
-
-def registerSignalHandlers(func):
-    """ Register signal handlers on platforms that support it """
-    try:
-        import signal
-        signal.signal(signal.SIGABRT, func)
-        signal.signal(signal.SIGINT, func)
-        signal.signal(signal.SIGTERM, func)
-    except ImportError:
-        pass
-
-
-def makeServer(config):
-    """ Create a new server, based on the the platform capabilities
-
-    Try to create the server class specified in the config. If threads
-    are not available, fallback to ForkingServer. If fork is not
-    available, fallback to a SimpleServer.
-    """
-    serverClass = globals()[config.serverClass]
-    if serverClass.use_threads:
-        try:
-            import threading
-        except ImportError:
-            serverClass = ForkingServer
-    if serverClass is ForkingServer and not hasattr(os, "fork"):
-        serverClass = SimpleServer
-    if serverClass.__name__ != config.serverClass:
-        logging.error('%s is not available on this platform, falling back '
-                      'to %s\n' % (config.serverClass, serverClass.__name__))
-
-    from MoinMoin import config as _config
-    _config.use_threads = serverClass.use_threads
-    return serverClass(config)
-
-# ------------------------------------------------------------------------
-# Public interface
-
-class StandaloneConfig(Config):
-    """ Standalone server default config """
-
-    name = 'moin'
-    properties = {}
-    docs = '/usr/share/moin/htdocs'
-    user = 'www-data'
-    group = 'www-data'
-    port = 8000
-    interface = 'localhost'
-    logPath = None
-
-    # Advanced options
-    serverClass = 'ThreadPoolServer'
-    threadLimit = 10
-    # The size of the listen backlog. Twisted uses a default of 50.
-    # Tests on Mac OS X show many failed request with backlog of 5 or 10.
-    requestQueueSize = 50
-
-    # Development options
-    memoryProfile = None
-    hotshotProfile = None
-    pycallgraph_output = None
-
-def run(configClass):
-    """ Create and run a moin server
-
-    See StandaloneConfig for available options
-
-    @param configClass: config class
-    """
-    # Run only once!
-    global httpd, config
-    if httpd is not None:
-        raise RuntimeError("You can run only one server per process!")
-
-    config = configClass()
-
-    # Install hotshot profiled serve_moin method. To compare with other
-    # servers, we profile the part that create and run the request.
-    if config.hotshotProfile:
-        import hotshot
-        config.hotshotProfile = hotshot.Profile(config.hotshotProfile)
-        MoinRequestHandler.serve_moin = hotshotProfileDecorator(
-            MoinRequestHandler.serve_moin, config.hotshotProfile)
-
-    # Install a memory profiled serve_moin method
-    if config.memoryProfile:
-        config.memoryProfile.sample()
-        MoinRequestHandler.serve_moin = memoryProfileDecorator(
-            MoinRequestHandler.serve_moin, config.memoryProfile)
-
-    # initialize pycallgraph, if wanted
-    if config.pycallgraph_output:
-        try:
-            import pycallgraph
-            pycallgraph.settings['include_stdlib'] = False
-            pcg_filter = pycallgraph.GlobbingFilter(exclude=['pycallgraph.*',
-                                                             'unknown.*',
-                                                    ],
-                                                    max_depth=9999)
-            pycallgraph.start_trace(reset=True, filter_func=pcg_filter)
-        except ImportError:
-            config.pycallgraph_output = None
-
-
-    registerSignalHandlers(quit)
-    httpd = makeServer(config)
-
-    # Run as a safe user (posix only)
-    if os.name == 'posix' and os.getuid() == 0:
-        switchUID(config.uid, config.gid)
-
-    httpd.serve_forever()
-
--- a/MoinMoin/server/TWISTED.py	Wed Jul 04 12:12:00 2007 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,285 +0,0 @@
-# -*- coding: iso-8859-1 -*-
-"""
-    MoinMoin.server.twistedmoin
-
-    Create standalone twisted based server.
-
-    Minimal usage:
-
-        from MoinMoin.server.TWISTED import TwistedConfig, makeApp
-
-        class Config(TwistedConfig):
-            docs = '/usr/share/moin/wiki/htdocs'
-            user = 'www-data'
-            group = 'www-data'
-
-        application = makeApp(Config)
-
-    Then run this code with twistd -y yourcode.py. See moin_twisted script.
-
-    @copyright: 2004 Thomas Waldmann, Oliver Graf, Nir Soffer
-    @license: GNU GPL, see COPYING for details.
-"""
-
-from twisted.application import internet, service
-from twisted.web import static, server, vhost, resource
-from twisted.internet import threads, reactor
-
-try:
-    from twisted.internet import ssl
-except ImportError:
-    ssl = None
-
-# Enable threads
-from twisted.python import threadable
-threadable.init(1)
-
-# MoinMoin imports
-from MoinMoin.request import TWISTED
-from MoinMoin.server import Config
-
-# Set threads flag, so other code can use proper locking
-from MoinMoin import config
-config.use_threads = True
-del config
-
-# Server globals
-config = None
-
-
-class WikiResource(resource.Resource):
-    """ Wiki resource """
-    isLeaf = 1
-
-    def render(self, request):
-        return server.NOT_DONE_YET
-
-
-class WikiRoot(resource.Resource):
-    """ Wiki root resource """
-
-    def getChild(self, name, request):
-        # Serve images and css from url_prefix_static
-        if request.prepath == [] and name == config.url_prefix_static[1:]:
-            return resource.Resource.getChild(self, name, request)
-
-        # Serve special 'root' files from url_prefix_static
-        elif name in ['favicon.ico', 'robots.txt'] and request.postpath == []:
-            return self.children[config.url_prefix_static[1:]].getChild(name, request)
-
-        # All other through moin
-
-        # TODO: fix profile code to include the request init and ignore
-        # first request. I'm not doing this now since its better done
-        # with the new twisted code waiting in fix branch. --Nir
-        else:
-            if config.memoryProfile:
-                config.memoryProfile.addRequest()
-            req = TWISTED.Request(request, name, reactor, properties=config.properties)
-            if config.hotshotProfile:
-                threads.deferToThread(config.hotshotProfile.runcall, req.run)
-            else:
-                threads.deferToThread(req.run)
-            return WikiResource()
-
-
-class MoinRequest(server.Request):
-    """ MoinMoin request
-
-    Enable passing of file-upload filenames
-    """
-
-    def requestReceived(self, command, path, version):
-        """ Called by channel when all data has been received.
-
-        Override server.Request method for POST requests, to fix file
-        uploads issue.
-        """
-        if command == 'POST':
-            self.requestReceivedPOST(path, version)
-        else:
-            server.Request.requestReceived(self, command, path, version)
-
-    def requestReceivedPOST(self, path, version):
-        """ Handle POST requests
-
-        This is a modified copy of server.Request.requestRecived,
-        modified to use cgi.FieldStorage to handle file uploads
-        correctly.
-
-        Creates an extra member extended_args which also has
-        filenames of file uploads ( FIELDNAME__filename__ ).
-        """
-        import cgi
-
-        self.content.seek(0, 0)
-        self.args = {}
-        self.extended_args = {}
-        self.stack = []
-
-        self.method = 'POST'
-        self.uri = path
-        self.clientproto = version
-        x = self.uri.split('?')
-
-        argstring = ""
-        if len(x) == 1:
-            self.path = self.uri
-        else:
-            if len(x) != 2:
-                from twisted.python import log
-                log.msg("May ignore parts of this invalid URI: %s"
-                        % repr(self.uri))
-            self.path, argstring = x[0], x[1]
-
-        # cache the client and server information, we'll need this later to be
-        # serialized and sent with the request so CGIs will work remotely
-        self.client = self.channel.transport.getPeer()
-        self.host = self.channel.transport.getHost()
-
-        # create dummy env for cgi.FieldStorage
-        env = {
-            'REQUEST_METHOD': self.method,
-            'QUERY_STRING': argstring,
-            }
-        form = cgi.FieldStorage(fp=self.content,
-                                environ=env,
-                                headers=self.received_headers)
-
-        # Argument processing
-
-        args = self.args
-        try:
-            keys = form.keys()
-        except TypeError:
-            pass
-        else:
-            for key in keys:
-                values = form[key]
-                if not isinstance(values, list):
-                    values = [values]
-                fixedResult = []
-                for i in values:
-                    if isinstance(i, cgi.MiniFieldStorage):
-                        fixedResult.append(i.value)
-                    elif isinstance(i, cgi.FieldStorage):
-                        fixedResult.append(i.value)
-                        # multiple uploads to same form field are stupid!
-                        if i.filename:
-                            args[key + '__filename__'] = i.filename
-                args[key] = fixedResult
-
-        self.process()
-
-
-class MoinSite(server.Site):
-    """ Moin site """
-    requestFactory = MoinRequest
-
-    def startFactory(self):
-        """ Setup before starting """
-        # Memory profile
-        if config.memoryProfile:
-            config.memoryProfile.sample()
-
-        # hotshot profile
-        if config.hotshotProfile:
-            import hotshot
-            config.hotshotProfile = hotshot.Profile(config.hotshotProfile)
-        server.Site.startFactory(self)
-
-    def stopFactory(self):
-        """ Cleaup before stoping """
-        server.Site.stopFactory(self)
-        if config.hotshotProfile:
-            config.hotshotProfile.close()
-
-
-class TwistedConfig(Config):
-    """ Twisted server default config """
-
-    name = 'mointwisted'
-    properties = {}
-    docs = '/usr/share/moin/htdocs'
-    user = 'www-data'
-    group = 'www-data'
-    port = 8080
-    interfaces = ['']
-    threads = 10
-    timeout = 15*60 # 15 minutes
-    logPath = None # moin log file
-    logPath_twisted = None # Twisted log file
-    virtualHosts = None
-    memoryProfile = None
-    hotshotProfile = None
-
-    # sslcert = ('/whereever/cert/sitekey.pem', '/whereever/cert/sitecert.pem')
-    sslcert = None
-
-    def __init__(self):
-        Config.__init__(self)
-
-        # Check for '' in interfaces, then ignore other
-        if '' in self.interfaces:
-            self.interfaces = ['']
-
-
-def makeApp(ConfigClass):
-    """ Generate and return an application
-
-    See MoinMoin.server.Config for config options
-
-    @param ConfigClass: config class
-    @rtype: application object
-    @return twisted application, needed by twistd
-    """
-    # Create config instance (raise RuntimeError if config invalid)
-    global config
-    config = ConfigClass()
-
-    # Set number of threads
-    reactor.suggestThreadPoolSize(config.threads)
-
-    # The root of the HTTP hierarchy
-    default = WikiRoot()
-
-    # Here is where img and css and some special files come from
-    default.putChild(config.url_prefix_static[1:], static.File(config.docs))
-
-    # Generate the Site factory
-    # TODO: Maybe we can use WikiRoot instead of this
-    # ----------------------------------------------
-    root = vhost.NameVirtualHost()
-    root.default = default
-    # ----------------------------------------------
-    site = MoinSite(root, logPath=config.logPath_twisted, timeout=config.timeout)
-
-    # Make application
-    application = service.Application("web", uid=config.uid, gid=config.gid)
-    sc = service.IServiceCollection(application)
-
-    # Listen to all interfaces in config.interfaces
-    for entry in config.interfaces:
-        # Add a TCPServer for each interface.
-
-        # This is an hidden experimantal feature: each entry in
-        # interface may contain a port, using 'ip:port'.
-        # Note: the format is subject to change!
-        try:
-            interface, port = entry.split(':', 1)
-        except ValueError:
-            interface, port = entry, config.port
-
-        # Might raise ValueError if not integer.
-        # TODO: check if we can use string port, like 'http'
-        port = int(port)
-
-        if port == 443 and ssl and ssl.supported and config.sslcert:
-            sslContext = ssl.DefaultOpenSSLContextFactory(*config.sslcert)
-            s = internet.SSLServer(port, site, sslContext, interface=interface)
-        else:
-            s = internet.TCPServer(port, site, interface=interface)
-        s.setServiceParent(sc)
-
-    return application
-
--- a/MoinMoin/server/WSGI.py	Wed Jul 04 12:12:00 2007 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,15 +0,0 @@
-"""
-    MoinMoin - WSGI application
-
-    @copyright: 2005 Anakim Border <akborder@gmail.com>
-    @license: GNU GPL, see COPYING for details.
-"""
-
-from MoinMoin.request import WSGI
-
-def moinmoinApp(environ, start_response):
-    request = WSGI.Request(environ)
-    request.run()
-    start_response(request.status, request.headers)
-    return [request.output()]
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MoinMoin/server/server_cgi.py	Thu Jul 05 22:11:16 2007 +0200
@@ -0,0 +1,65 @@
+# -*- coding: iso-8859-1 -*-
+"""
+    MoinMoin - CGI pseudo Server
+
+    This is not really a server, it is just so that CGI stuff (the real
+    server is likely Apache or IIS or some other std. CGI server) looks
+    similar to what we have for Twisted and standalone server.
+
+    Minimal usage:
+
+        from MoinMoin.server.server_cgi import CgiConfig, run
+
+        class Config(CgiConfig):
+            pass
+
+        run(Config)
+
+    See more options in CgiConfig class.
+
+    @copyright: 2006 MoinMoin:ThomasWaldmann
+    @license: GNU GPL, see COPYING for details.
+"""
+
+from MoinMoin.server import Config
+from MoinMoin.request import request_cgi
+
+# Server globals
+config = None
+
+# ------------------------------------------------------------------------
+# Public interface
+
+class CgiConfig(Config):
+    """ CGI default config """
+
+    name = 'moin'
+    properties = {}
+    logPath = None
+
+    # Development options
+    hotshotProfile = None # e.g. "moin.prof"
+
+
+def run(configClass):
+    """ Create and run a Cgi Request
+
+    See CgiConfig for available options
+
+    @param configClass: config class
+    """
+
+    config = configClass()
+
+    if config.hotshotProfile:
+        import hotshot
+        config.hotshotProfile = hotshot.Profile(config.hotshotProfile)
+        config.hotshotProfile.start()
+
+    request = request_cgi.Request(properties=config.properties)
+    request.run()
+
+    if config.hotshotProfile:
+        config.hotshotProfile.close()
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MoinMoin/server/server_standalone.py	Thu Jul 05 22:11:16 2007 +0200
@@ -0,0 +1,614 @@
+# -*- coding: iso-8859-1 -*-
+"""
+    MoinMoin - Stand-alone HTTP Server
+
+    This is a simple, fast and very easy to install server. Its
+    recommended for personal wikis or public wikis with little load.
+
+    It is not well tested in public wikis with heavy load. In these case
+    you might want to use twisted, fast cgi or mod python, or if you
+    can't use those, cgi.
+
+    Minimal usage:
+
+        from MoinMoin.server.server_standalone import StandaloneConfig, run
+
+        class Config(StandaloneConfig):
+            docs = '/usr/share/moin/wiki/htdocs'
+            user = 'www-data'
+            group = 'www-data'
+
+        run(Config)
+
+    See more options in StandaloneConfig class.
+
+    For security, the server will not run as root. If you try to run it
+    as root, it will run as the user and group in the config. If you run
+    it as a normal user, it will run with your regular user and group.
+
+    Significant contributions to this module by R. Church <rc@ghostbitch.org>
+
+    @copyright: 2001-2004 MoinMoin:JuergenHermann,
+                2005 MoinMoin:AlexanderSchremmer,
+                2005 MoinMoin:NirSoffer
+    @license: GNU GPL, see COPYING for details.
+"""
+
+import os, sys, time, socket, errno, shutil, logging
+import BaseHTTPServer, SimpleHTTPServer, SocketServer
+
+from MoinMoin import version, wikiutil
+from MoinMoin.server import Config, switchUID
+from MoinMoin.request import request_standalone
+from MoinMoin.util import timefuncs
+
+# Server globals
+httpd = None
+config = None
+
+
+class SimpleServer(BaseHTTPServer.HTTPServer):
+    """ Simplest server, serving one request after another
+
+    This server is good for personal wiki, or when lowest memory
+    footprint is needed.
+    """
+    use_threads = False
+
+    def __init__(self, config):
+        self.htdocs = config.docs
+        self.request_queue_size = config.requestQueueSize
+        self._abort = 0
+        address = (config.interface, config.port)
+        BaseHTTPServer.HTTPServer.__init__(self, address, MoinRequestHandler)
+
+    def server_activate(self):
+        BaseHTTPServer.HTTPServer.server_activate(self)
+        logging.info("Serving on %s:%d" % self.server_address)
+
+    def serve_forever(self):
+        """Handle one request at a time until we die """
+        while not self._abort:
+            self.handle_request()
+
+    def die(self):
+        """Abort this server instance's serving loop """
+        # Close hotshot profiler
+        if config.hotshotProfile:
+            config.hotshotProfile.close()
+
+        # Set abort flag, then make request to wake the server
+        self._abort = 1
+        try:
+            import httplib
+            addr = self.server_address
+            if not addr[0]:
+                addr = ("localhost", addr[1])
+            req = httplib.HTTP('%s:%d' % addr)
+            req.connect()
+            req.putrequest('DIE', '/')
+            req.endheaders()
+            del req
+        except socket.error, err:
+            # Ignore certain errors
+            if err.args[0] not in [errno.EADDRNOTAVAIL, ]:
+                raise
+
+
+class ThreadingServer(SimpleServer):
+    """ Serve each request in a new thread
+
+    This server is used since release 1.3 and seems to work nice with
+    little load.
+
+    From release 1.3.5 there is a thread limit, that should help to
+    limit the load on the server.
+    """
+    use_threads = True
+
+    def __init__(self, config):
+        self.thread_limit = config.threadLimit
+        from threading import Condition
+        self.lock = Condition()
+        SimpleServer.__init__(self, config)
+
+    def process_request(self, request, client_address):
+        """ Start a new thread to process the request
+
+        If the thread limit has been reached, wait on the lock. The
+        next thread will notify when finished.
+        """
+        from threading import Thread, activeCount
+        self.lock.acquire()
+        try:
+            if activeCount() > self.thread_limit:
+                self.lock.wait()
+            if self._abort:
+                return
+            t = Thread(target=self.process_request_thread,
+                       args=(request, client_address))
+            t.start()
+        finally:
+            self.lock.release()
+
+    def process_request_thread(self, request, client_address):
+        """ Called for each request on a new thread
+
+        Notify the main thread on the end of each request.
+        """
+        try:
+            self.finish_request(request, client_address)
+        except:
+            self.handle_error(request, client_address)
+        self.close_request(request)
+        self.lock.acquire()
+        try:
+            # Main thread might be waiting
+            self.lock.notify()
+        finally:
+            self.lock.release()
+
+
+class ThreadPoolServer(SimpleServer):
+    """ Threading server using a pool of threads
+
+    This is a new experimental server, using a pool of threads instead
+    of creating new thread for each request. This is similar to Apache
+    worker mpm, with a simpler constant thread pool.
+
+    This server is 5 times faster than ThreadingServer for static
+    files, and about the same for wiki pages.
+
+    TODO: sometimes the server won't exit on Conrol-C, and continue to
+    run with few threads (you can kill it with kill -9). Same problem
+    exist with the twisted server. When the problem is finally solved,
+    remove the commented debug prints.
+    """
+    use_threads = True
+
+    def __init__(self, config):
+        self.queue = []
+        # The size of the queue need more testing
+        self.queueSize = config.threadLimit * 2
+        self.poolSize = config.threadLimit
+        from threading import Condition
+        self.lock = Condition()
+        SimpleServer.__init__(self, config)
+
+    def serve_forever(self):
+        """ Create a thread pool then invoke base class method """
+        from threading import Thread
+        for dummy in range(self.poolSize):
+            t = Thread(target=self.serve_forever_thread)
+            t.start()
+        SimpleServer.serve_forever(self)
+
+    def process_request(self, request, client_address):
+        """ Called for each request
+
+        Insert the request into the queue. If the queue is full, wait
+        until one of the request threads pop a request. During the wait,
+        new connections might be dropped.
+        """
+        self.lock.acquire()
+        try:
+            if len(self.queue) >= self.queueSize:
+                self.lock.wait()
+            if self._abort:
+                return
+            self.queue.insert(0, (request, client_address))
+            self.lock.notify()
+        finally:
+            self.lock.release()
+
+    def serve_forever_thread(self):
+        """ The main loop of request threads
+
+        Pop a request from the queue and process it.
+        """
+        while not self._abort:
+            request, client_address = self.pop_request()
+            try:
+                self.finish_request(request, client_address)
+            except:
+                self.handle_error(request, client_address)
+            self.close_request(request)
+        # sys.stderr.write('thread exiting...\n')
+
+    def pop_request(self):
+        """ Pop a request from the queue
+
+        If the queue is empty, wait for notification. If the queue was
+        full, notify the main thread which may be waiting.
+        """
+        self.lock.acquire()
+        try:
+            while not self._abort:
+                try:
+                    item = self.queue.pop()
+                    if len(self.queue) == self.queueSize - 1:
+                        # Queue was full - main thread might be waiting
+                        self.lock.notify()
+                    return item
+                except IndexError:
+                    self.lock.wait()
+        finally:
+            self.lock.release()
+        # sys.stderr.write('thread exiting...\n')
+        sys.exit()
+
+    def die(self):
+        """ Wake all threads then invoke base class die
+
+        Threads should exist when _abort is True.
+        """
+        self._abort = True
+        self.wake_all_threads()
+        time.sleep(0.1)
+        SimpleServer.die(self)
+
+    def wake_all_threads(self):
+        self.lock.acquire()
+        try:
+            # sys.stderr.write('waking up all threads...\n')
+            self.lock.notifyAll()
+        finally:
+            self.lock.release()
+
+
+class ForkingServer(SocketServer.ForkingMixIn, SimpleServer):
+    """ Serve each request in a new process
+
+    This is new untested server, first tests show rather pathetic cgi
+    like performance. No data is cached between requests.
+
+    The mixin has its own process limit.
+    """
+    max_children = 10
+
+
+class MoinRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
+
+    bufferSize = 8 * 1024 # used to serve static files
+    staticExpire = 365 * 24 * 3600 # 1 year expiry for static files
+
+    def __init__(self, request, client_address, server):
+        self.server_version = "MoinMoin %s %s %s" % (version.release,
+                                                     version.revision,
+                                                     server.__class__.__name__)
+        self.expires = 0
+        SimpleHTTPServer.SimpleHTTPRequestHandler.__init__(self, request,
+            client_address, server)
+
+    def log_message(self, format, *args):
+        logging.info("%s %s" % (self.address_string(), format % args))
+
+    # -------------------------------------------------------------------
+    # do_METHOD dispatchers - called for each request
+
+    def do_DIE(self):
+        if self.server._abort:
+            self.log_error("Shutting down")
+
+    def do_ALL(self):
+        """ Handle requests (request type GET/HEAD/POST is in self.command)
+
+        Separate between wiki pages and css and image url by similar
+        system as cgi and twisted, the url_prefix_static prefix.
+        """
+        prefix = config.url_prefix_static
+        if self.path.startswith(prefix + '/'):
+            self.path = self.path[len(prefix):]
+            self.serve_static_file()
+        elif self.path in ['/favicon.ico', '/robots.txt']:
+            self.serve_static_file()
+        else:
+            self.serve_moin()
+
+    do_POST = do_ALL
+    do_GET = do_ALL
+    do_HEAD = do_ALL
+
+    # -------------------------------------------------------------------
+    # Serve methods
+
+    def serve_static_file(self):
+        """ Serve files from the htdocs directory """
+        self.expires = self.staticExpire
+        path = self.path.split("?", 1)
+        if len(path) > 1:
+            self.path = path[0] # XXX ?params
+
+        try:
+            fn = getattr(SimpleHTTPServer.SimpleHTTPRequestHandler, 'do_' + self.command)
+            fn(self)
+        except socket.error, err:
+            # Ignore certain errors
+            if err.args[0] not in [errno.EPIPE, errno.ECONNABORTED]:
+                raise
+
+    def serve_moin(self):
+        """ Serve a request using moin """
+        # don't make an Expires header for wiki pages
+        self.expires = 0
+
+        try:
+            req = request_standalone.Request(self, properties=config.properties)
+            req.run()
+        except socket.error, err:
+            # Ignore certain errors
+            if err.args[0] not in [errno.EPIPE, errno.ECONNABORTED]:
+                raise
+
+    def translate_path(self, uri):
+        """ Translate a /-separated PATH to the local filename syntax.
+
+        Components that mean special things to the local file system
+        (e.g. drive or directory names) are ignored.
+        """
+        path = wikiutil.url_unquote(uri, want_unicode=False)
+        path = path.replace('\\', '/')
+        words = path.split('/')
+        words = filter(None, words)
+
+        path = self.server.htdocs
+        bad_uri = 0
+        for word in words:
+            drive, word = os.path.splitdrive(word)
+            if drive:
+                bad_uri = 1
+            head, word = os.path.split(word)
+            if word in (os.curdir, os.pardir):
+                bad_uri = 1
+                continue
+            path = os.path.join(path, word)
+
+        if bad_uri:
+            self.log_error("Detected bad request URI '%s', translated to '%s'"
+                           % (uri, path, ))
+        return path
+
+    def end_headers(self):
+        """overload the default end_headers, inserting expires header"""
+        if self.expires:
+            now = time.time()
+            expires = now + self.expires
+            self.send_header('Expires', timefuncs.formathttpdate(expires))
+        SimpleHTTPServer.SimpleHTTPRequestHandler.end_headers(self)
+
+    def copyfile(self, source, outputfile):
+        """Copy all data between two file objects.
+
+        Modify the base class method to change the buffer size. Test
+        shows that for the typical static files we serve, 8K buffer is
+        faster than the default 16K buffer.
+        """
+        shutil.copyfileobj(source, outputfile, length=self.bufferSize)
+
+    def address_string(self):
+        """We don't want to do reverse DNS lookups, just return IP address."""
+        return self.client_address[0]
+
+
+try:
+    from tlslite.api import TLSSocketServerMixIn, X509, X509CertChain, SessionCache, parsePEMKey
+    from tlslite.TLSConnection import TLSConnection
+except ImportError:
+    pass
+else:
+    class SecureRequestRedirect(BaseHTTPServer.BaseHTTPRequestHandler):
+        def handle(self):
+            self.close_connection = 1
+            try:
+                self.raw_requestline = self.rfile.readline()
+            except socket.error:
+                return
+            if self.parse_request():
+                host = self.headers.get('Host', socket.gethostname())
+                path = self.path
+            else:
+                host = '%s:%s' % (socket.gethostname(),
+                        self.request.getsockname()[1])
+                path = '/'
+
+            self.requestline = 'ERROR: Redirecting to https://%s%s' % (host, path)
+            self.request_version = 'HTTP/1.1'
+            self.command = 'GET'
+            self.send_response(301, 'Document Moved')
+            self.send_header('Date', self.date_time_string())
+            self.send_header('Location', 'https://%s%s' % (host, path))
+            self.send_header('Connection', 'close')
+            self.send_header('Content-Length', '0')
+            self.wfile.write('\r\n')
+
+    class SecureThreadPoolServer(TLSSocketServerMixIn, ThreadPoolServer):
+        def __init__(self, config):
+            ThreadPoolServer.__init__(self, config)
+
+            cert = open(config.ssl_certificate).read()
+            x509 = X509()
+            x509.parse(cert)
+            self.certChain = X509CertChain([x509])
+
+            priv = open(config.ssl_privkey).read()
+            self.privateKey = parsePEMKey(priv, private=True)
+
+            self.sessionCache = SessionCache()
+
+        def finish_request(self, sock, client_address):
+            # Peek into the packet, if it starts with GET or POS(T) then
+            # redirect, otherwise let TLSLite handle the connection.
+            peek = sock.recv(3, socket.MSG_PEEK).lower()
+            if peek == 'get' or peek == 'pos':
+                SecureRequestRedirect(sock, client_address, self)
+                return
+            tls_connection = TLSConnection(sock)
+            if self.handshake(tls_connection):
+                self.RequestHandlerClass(tls_connection, client_address, self)
+            else:
+                # This will probably fail because the TLSConnection has
+                # already written SSL stuff to the socket. But not sure what
+                # else we should do.
+                SecureRequestRedirect(sock, client_address, self)
+
+        def handshake(self, tls_connection):
+            try:
+                tls_connection.handshakeServer(certChain=self.certChain,
+                                               privateKey=self.privateKey,
+                                               sessionCache=self.sessionCache)
+                tls_connection.ignoreAbruptClose = True
+                return True
+            except:
+                return False
+
+
+def memoryProfileDecorator(func, profile):
+    """ Return a profiled function """
+    def profiledFunction(*args, **kw):
+        profile.addRequest()
+        return func(*args, **kw)
+    return profiledFunction
+
+
+def hotshotProfileDecorator(func, profile):
+    """ Return a profiled function """
+    profile.moin_requests_done = 0
+    def profiledFunction(*args, **kw):
+        profile.moin_requests_done += 1
+        if profile.moin_requests_done == 1:
+            # Don't profile first request, its not interesting
+            return func(*args, **kw)
+        return profile.runcall(func, *args, **kw)
+
+    return profiledFunction
+
+
+def quit(signo, stackframe):
+    """ Signal handler for aborting signals """
+    global httpd, config
+    logging.info("Thanks for using MoinMoin!")
+
+    fname = config.pycallgraph_output
+    if fname:
+        import pycallgraph
+        if fname.endswith('.png'):
+            pycallgraph.make_dot_graph(fname)
+        elif fname.endswith('.dot'):
+            pycallgraph.save_dot(fname)
+
+    if httpd:
+        httpd.die()
+
+
+def registerSignalHandlers(func):
+    """ Register signal handlers on platforms that support it """
+    try:
+        import signal
+        signal.signal(signal.SIGABRT, func)
+        signal.signal(signal.SIGINT, func)
+        signal.signal(signal.SIGTERM, func)
+    except ImportError:
+        pass
+
+
+def makeServer(config):
+    """ Create a new server, based on the the platform capabilities
+
+    Try to create the server class specified in the config. If threads
+    are not available, fallback to ForkingServer. If fork is not
+    available, fallback to a SimpleServer.
+    """
+    serverClass = globals()[config.serverClass]
+    if serverClass.use_threads:
+        try:
+            import threading
+        except ImportError:
+            serverClass = ForkingServer
+    if serverClass is ForkingServer and not hasattr(os, "fork"):
+        serverClass = SimpleServer
+    if serverClass.__name__ != config.serverClass:
+        logging.error('%s is not available on this platform, falling back '
+                      'to %s\n' % (config.serverClass, serverClass.__name__))
+
+    from MoinMoin import config as _config
+    _config.use_threads = serverClass.use_threads
+    return serverClass(config)
+
+# ------------------------------------------------------------------------
+# Public interface
+
+class StandaloneConfig(Config):
+    """ Standalone server default config """
+
+    name = 'moin'
+    properties = {}
+    docs = '/usr/share/moin/htdocs'
+    user = 'www-data'
+    group = 'www-data'
+    port = 8000
+    interface = 'localhost'
+    logPath = None
+
+    # Advanced options
+    serverClass = 'ThreadPoolServer'
+    threadLimit = 10
+    # The size of the listen backlog. Twisted uses a default of 50.
+    # Tests on Mac OS X show many failed request with backlog of 5 or 10.
+    requestQueueSize = 50
+
+    # Development options
+    memoryProfile = None
+    hotshotProfile = None
+    pycallgraph_output = None
+
+def run(configClass):
+    """ Create and run a moin server
+
+    See StandaloneConfig for available options
+
+    @param configClass: config class
+    """
+    # Run only once!
+    global httpd, config
+    if httpd is not None:
+        raise RuntimeError("You can run only one server per process!")
+
+    config = configClass()
+
+    # Install hotshot profiled serve_moin method. To compare with other
+    # servers, we profile the part that create and run the request.
+    if config.hotshotProfile:
+        import hotshot
+        config.hotshotProfile = hotshot.Profile(config.hotshotProfile)
+        MoinRequestHandler.serve_moin = hotshotProfileDecorator(
+            MoinRequestHandler.serve_moin, config.hotshotProfile)
+
+    # Install a memory profiled serve_moin method
+    if config.memoryProfile:
+        config.memoryProfile.sample()
+        MoinRequestHandler.serve_moin = memoryProfileDecorator(
+            MoinRequestHandler.serve_moin, config.memoryProfile)
+
+    # initialize pycallgraph, if wanted
+    if config.pycallgraph_output:
+        try:
+            import pycallgraph
+            pycallgraph.settings['include_stdlib'] = False
+            pcg_filter = pycallgraph.GlobbingFilter(exclude=['pycallgraph.*',
+                                                             'unknown.*',
+                                                    ],
+                                                    max_depth=9999)
+            pycallgraph.start_trace(reset=True, filter_func=pcg_filter)
+        except ImportError:
+            config.pycallgraph_output = None
+
+
+    registerSignalHandlers(quit)
+    httpd = makeServer(config)
+
+    # Run as a safe user (posix only)
+    if os.name == 'posix' and os.getuid() == 0:
+        switchUID(config.uid, config.gid)
+
+    httpd.serve_forever()
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MoinMoin/server/server_twisted.py	Thu Jul 05 22:11:16 2007 +0200
@@ -0,0 +1,285 @@
+# -*- coding: iso-8859-1 -*-
+"""
+    MoinMoin.server.twistedmoin
+
+    Create standalone twisted based server.
+
+    Minimal usage:
+
+        from MoinMoin.server.server_twisted import TwistedConfig, makeApp
+
+        class Config(TwistedConfig):
+            docs = '/usr/share/moin/wiki/htdocs'
+            user = 'www-data'
+            group = 'www-data'
+
+        application = makeApp(Config)
+
+    Then run this code with twistd -y yourcode.py. See moin_twisted script.
+
+    @copyright: 2004 Thomas Waldmann, Oliver Graf, Nir Soffer
+    @license: GNU GPL, see COPYING for details.
+"""
+
+from twisted.application import internet, service
+from twisted.web import static, server, vhost, resource
+from twisted.internet import threads, reactor
+
+try:
+    from twisted.internet import ssl
+except ImportError:
+    ssl = None
+
+# Enable threads
+from twisted.python import threadable
+threadable.init(1)
+
+# MoinMoin imports
+from MoinMoin.request import request_twisted
+from MoinMoin.server import Config
+
+# Set threads flag, so other code can use proper locking
+from MoinMoin import config
+config.use_threads = True
+del config
+
+# Server globals
+config = None
+
+
+class WikiResource(resource.Resource):
+    """ Wiki resource """
+    isLeaf = 1
+
+    def render(self, request):
+        return server.NOT_DONE_YET
+
+
+class WikiRoot(resource.Resource):
+    """ Wiki root resource """
+
+    def getChild(self, name, request):
+        # Serve images and css from url_prefix_static
+        if request.prepath == [] and name == config.url_prefix_static[1:]:
+            return resource.Resource.getChild(self, name, request)
+
+        # Serve special 'root' files from url_prefix_static
+        elif name in ['favicon.ico', 'robots.txt'] and request.postpath == []:
+            return self.children[config.url_prefix_static[1:]].getChild(name, request)
+
+        # All other through moin
+
+        # TODO: fix profile code to include the request init and ignore
+        # first request. I'm not doing this now since its better done
+        # with the new twisted code waiting in fix branch. --Nir
+        else:
+            if config.memoryProfile:
+                config.memoryProfile.addRequest()
+            req = request_twisted.Request(request, name, reactor, properties=config.properties)
+            if config.hotshotProfile:
+                threads.deferToThread(config.hotshotProfile.runcall, req.run)
+            else:
+                threads.deferToThread(req.run)
+            return WikiResource()
+
+
+class MoinRequest(server.Request):
+    """ MoinMoin request
+
+    Enable passing of file-upload filenames
+    """
+
+    def requestReceived(self, command, path, version):
+        """ Called by channel when all data has been received.
+
+        Override server.Request method for POST requests, to fix file
+        uploads issue.
+        """
+        if command == 'POST':
+            self.requestReceivedPOST(path, version)
+        else:
+            server.Request.requestReceived(self, command, path, version)
+
+    def requestReceivedPOST(self, path, version):
+        """ Handle POST requests
+
+        This is a modified copy of server.Request.requestRecived,
+        modified to use cgi.FieldStorage to handle file uploads
+        correctly.
+
+        Creates an extra member extended_args which also has
+        filenames of file uploads ( FIELDNAME__filename__ ).
+        """
+        import cgi
+
+        self.content.seek(0, 0)
+        self.args = {}
+        self.extended_args = {}
+        self.stack = []
+
+        self.method = 'POST'
+        self.uri = path
+        self.clientproto = version
+        x = self.uri.split('?')
+
+        argstring = ""
+        if len(x) == 1:
+            self.path = self.uri
+        else:
+            if len(x) != 2:
+                from twisted.python import log
+                log.msg("May ignore parts of this invalid URI: %s"
+                        % repr(self.uri))
+            self.path, argstring = x[0], x[1]
+
+        # cache the client and server information, we'll need this later to be
+        # serialized and sent with the request so CGIs will work remotely
+        self.client = self.channel.transport.getPeer()
+        self.host = self.channel.transport.getHost()
+
+        # create dummy env for cgi.FieldStorage
+        env = {
+            'REQUEST_METHOD': self.method,
+            'QUERY_STRING': argstring,
+            }
+        form = cgi.FieldStorage(fp=self.content,
+                                environ=env,
+                                headers=self.received_headers)
+
+        # Argument processing
+
+        args = self.args
+        try:
+            keys = form.keys()
+        except TypeError:
+            pass
+        else:
+            for key in keys:
+                values = form[key]
+                if not isinstance(values, list):
+                    values = [values]
+                fixedResult = []
+                for i in values:
+                    if isinstance(i, cgi.MiniFieldStorage):
+                        fixedResult.append(i.value)
+                    elif isinstance(i, cgi.FieldStorage):
+                        fixedResult.append(i.value)
+                        # multiple uploads to same form field are stupid!
+                        if i.filename:
+                            args[key + '__filename__'] = i.filename
+                args[key] = fixedResult
+
+        self.process()
+
+
+class MoinSite(server.Site):
+    """ Moin site """
+    requestFactory = MoinRequest
+
+    def startFactory(self):
+        """ Setup before starting """
+        # Memory profile
+        if config.memoryProfile:
+            config.memoryProfile.sample()
+
+        # hotshot profile
+        if config.hotshotProfile:
+            import hotshot
+            config.hotshotProfile = hotshot.Profile(config.hotshotProfile)
+        server.Site.startFactory(self)
+
+    def stopFactory(self):
+        """ Cleaup before stoping """
+        server.Site.stopFactory(self)
+        if config.hotshotProfile:
+            config.hotshotProfile.close()
+
+
+class TwistedConfig(Config):
+    """ Twisted server default config """
+
+    name = 'mointwisted'
+    properties = {}
+    docs = '/usr/share/moin/htdocs'
+    user = 'www-data'
+    group = 'www-data'
+    port = 8080
+    interfaces = ['']
+    threads = 10
+    timeout = 15*60 # 15 minutes
+    logPath = None # moin log file
+    logPath_twisted = None # Twisted log file
+    virtualHosts = None
+    memoryProfile = None
+    hotshotProfile = None
+
+    # sslcert = ('/whereever/cert/sitekey.pem', '/whereever/cert/sitecert.pem')
+    sslcert = None
+
+    def __init__(self):
+        Config.__init__(self)
+
+        # Check for '' in interfaces, then ignore other
+        if '' in self.interfaces:
+            self.interfaces = ['']
+
+
+def makeApp(ConfigClass):
+    """ Generate and return an application
+
+    See MoinMoin.server.Config for config options
+
+    @param ConfigClass: config class
+    @rtype: application object
+    @return twisted application, needed by twistd
+    """
+    # Create config instance (raise RuntimeError if config invalid)
+    global config
+    config = ConfigClass()
+
+    # Set number of threads
+    reactor.suggestThreadPoolSize(config.threads)
+
+    # The root of the HTTP hierarchy
+    default = WikiRoot()
+
+    # Here is where img and css and some special files come from
+    default.putChild(config.url_prefix_static[1:], static.File(config.docs))
+
+    # Generate the Site factory
+    # TODO: Maybe we can use WikiRoot instead of this
+    # ----------------------------------------------
+    root = vhost.NameVirtualHost()
+    root.default = default
+    # ----------------------------------------------
+    site = MoinSite(root, logPath=config.logPath_twisted, timeout=config.timeout)
+
+    # Make application
+    application = service.Application("web", uid=config.uid, gid=config.gid)
+    sc = service.IServiceCollection(application)
+
+    # Listen to all interfaces in config.interfaces
+    for entry in config.interfaces:
+        # Add a TCPServer for each interface.
+
+        # This is an hidden experimantal feature: each entry in
+        # interface may contain a port, using 'ip:port'.
+        # Note: the format is subject to change!
+        try:
+            interface, port = entry.split(':', 1)
+        except ValueError:
+            interface, port = entry, config.port
+
+        # Might raise ValueError if not integer.
+        # TODO: check if we can use string port, like 'http'
+        port = int(port)
+
+        if port == 443 and ssl and ssl.supported and config.sslcert:
+            sslContext = ssl.DefaultOpenSSLContextFactory(*config.sslcert)
+            s = internet.SSLServer(port, site, sslContext, interface=interface)
+        else:
+            s = internet.TCPServer(port, site, interface=interface)
+        s.setServiceParent(sc)
+
+    return application
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MoinMoin/server/server_wsgi.py	Thu Jul 05 22:11:16 2007 +0200
@@ -0,0 +1,15 @@
+"""
+    MoinMoin - WSGI application
+
+    @copyright: 2005 Anakim Border <akborder@gmail.com>
+    @license: GNU GPL, see COPYING for details.
+"""
+
+from MoinMoin.request import request_wsgi
+
+def moinmoinApp(environ, start_response):
+    request = request_wsgi.Request(environ)
+    request.run()
+    start_response(request.status, request.headers)
+    return [request.output()]
+
--- a/contrib/phpwiki_migration/phpwiki2moinmoin.py	Wed Jul 04 12:12:00 2007 +0200
+++ b/contrib/phpwiki_migration/phpwiki2moinmoin.py	Thu Jul 05 22:11:16 2007 +0200
@@ -110,7 +110,7 @@
         sys.path.append(wikipath)
 
 from MoinMoin.PageEditor import PageEditor
-from MoinMoin.request.CLI import Request
+from MoinMoin.request.request_cli import Request
 
 # the block parser deals with the whole text to be converted
 #
--- a/moin.py	Wed Jul 04 12:12:00 2007 +0200
+++ b/moin.py	Thu Jul 05 22:11:16 2007 +0200
@@ -44,7 +44,7 @@
 ## import os
 ## os.environ['MOIN_DEBUG'] = '1'
 
-from MoinMoin.server.STANDALONE import StandaloneConfig, run
+from MoinMoin.server.server_standalone import StandaloneConfig, run
 from MoinMoin.version import project, release, revision
 
 print "%s - %s [%s]" % (project, release, revision)
--- a/wiki/server/moinmodpy.py	Wed Jul 04 12:12:00 2007 +0200
+++ b/wiki/server/moinmodpy.py	Thu Jul 05 22:11:16 2007 +0200
@@ -56,9 +56,9 @@
 del config
 
 
-from MoinMoin.request import MODPYTHON
+from MoinMoin.request import request_modpython
 
 def handler(request):
-    moinreq = MODPYTHON.Request(request)
+    moinreq = request_modpython.Request(request)
     return moinreq.run(request)
 
--- a/wiki/server/mointwisted.py	Wed Jul 04 12:12:00 2007 +0200
+++ b/wiki/server/mointwisted.py	Thu Jul 05 22:11:16 2007 +0200
@@ -27,7 +27,7 @@
 ## import os
 ## os.environ['MOIN_DEBUG'] = '1'
 
-from MoinMoin.server.TWISTED import TwistedConfig, makeApp
+from MoinMoin.server.server_twisted import TwistedConfig, makeApp
 
 
 class Config(TwistedConfig):
--- a/wiki/server/moinwsgi.py	Wed Jul 04 12:12:00 2007 +0200
+++ b/wiki/server/moinwsgi.py	Thu Jul 05 22:11:16 2007 +0200
@@ -14,7 +14,7 @@
 del config
 
 from flup.server.fcgi import WSGIServer
-from MoinMoin.server.WSGI import moinmoinApp
+from MoinMoin.server.server_wsgi import moinmoinApp
 import os
 
 if __name__ == '__main__':