changeset 2338:b902f2397c68

rename server and request Implementations by adding a prefix server_, request_
author Reimar Bauer <rb.proj AT googlemail DOT com>
date Thu, 05 Jul 2007 21:23:29 +0200
parents 49188b1e803c
children 505991ec14f3
files MoinMoin/action/SubscribeUser.py MoinMoin/auth/http.py MoinMoin/auth/sslclientcert.py MoinMoin/conftest.py MoinMoin/mail/mailimport.py MoinMoin/packages.py MoinMoin/request/request_cgi.py MoinMoin/request/request_cli.py MoinMoin/request/request_fcgi.py MoinMoin/request/request_modpython.py MoinMoin/request/request_standalone.py MoinMoin/request/request_twisted.py MoinMoin/request/request_wsgi.py MoinMoin/script/__init__.py MoinMoin/search/builtin.py MoinMoin/server/server_cgi.py MoinMoin/server/server_standalone.py MoinMoin/server/server_twisted.py MoinMoin/server/server_wsgi.py MoinMoin/xmlrpc/_tests/test_multicall.py contrib/phpwiki_migration/phpwiki2moinmoin.py moin.py wiki/server/moinmodpy.py wiki/server/mointwisted.py wiki/server/moinwsgi.py
diffstat 25 files changed, 1683 insertions(+), 25 deletions(-) [+]
line wrap: on
line diff
--- a/MoinMoin/action/SubscribeUser.py	Tue Jul 03 08:58:33 2007 +0200
+++ b/MoinMoin/action/SubscribeUser.py	Thu Jul 05 21:23:29 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	Tue Jul 03 08:58:33 2007 +0200
+++ b/MoinMoin/auth/http.py	Thu Jul 05 21:23:29 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	Tue Jul 03 08:58:33 2007 +0200
+++ b/MoinMoin/auth/sslclientcert.py	Thu Jul 05 21:23:29 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	Tue Jul 03 08:58:33 2007 +0200
+++ b/MoinMoin/conftest.py	Thu Jul 05 21:23:29 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	Tue Jul 03 08:58:33 2007 +0200
+++ b/MoinMoin/mail/mailimport.py	Thu Jul 05 21:23:29 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	Tue Jul 03 08:58:33 2007 +0200
+++ b/MoinMoin/packages.py	Thu Jul 05 21:23:29 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)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MoinMoin/request/request_cgi.py	Thu Jul 05 21:23:29 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 21:23:29 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 21:23:29 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 21:23:29 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 21:23:29 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 21:23:29 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 21:23:29 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	Tue Jul 03 08:58:33 2007 +0200
+++ b/MoinMoin/script/__init__.py	Thu Jul 05 21:23:29 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	Tue Jul 03 08:58:33 2007 +0200
+++ b/MoinMoin/search/builtin.py	Thu Jul 05 21:23:29 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):
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MoinMoin/server/server_cgi.py	Thu Jul 05 21:23:29 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 21:23:29 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 21:23:29 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 21:23:29 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/MoinMoin/xmlrpc/_tests/test_multicall.py	Tue Jul 03 08:58:33 2007 +0200
+++ b/MoinMoin/xmlrpc/_tests/test_multicall.py	Thu Jul 05 21:23:29 2007 +0200
@@ -7,7 +7,7 @@
 """
 
 from MoinMoin.xmlrpc import XmlRpcBase
-from MoinMoin.request.CLI import Request
+from MoinMoin.request.request_cli import Request
 from xmlrpclib import Fault
 
 def xmlrpc_return_fault():
--- a/contrib/phpwiki_migration/phpwiki2moinmoin.py	Tue Jul 03 08:58:33 2007 +0200
+++ b/contrib/phpwiki_migration/phpwiki2moinmoin.py	Thu Jul 05 21:23:29 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	Tue Jul 03 08:58:33 2007 +0200
+++ b/moin.py	Thu Jul 05 21:23:29 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	Tue Jul 03 08:58:33 2007 +0200
+++ b/wiki/server/moinmodpy.py	Thu Jul 05 21:23:29 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	Tue Jul 03 08:58:33 2007 +0200
+++ b/wiki/server/mointwisted.py	Thu Jul 05 21:23:29 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	Tue Jul 03 08:58:33 2007 +0200
+++ b/wiki/server/moinwsgi.py	Thu Jul 05 21:23:29 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__':