view MoinMoin/server/ @ 1529:76b4d2324d25

MoinMoin should not record the homepage links as pagelinks, fixes MoinMoinBugs/PageLinksIncludeUnrelatedLinks.
author Alexander Schremmer <alex AT alexanderweb DOT de>
date Tue, 05 Sep 2006 00:29:59 +0200
parents 65dc979761f0
children c92c8e5d68e3
line wrap: on
line source
# -*- coding: iso-8859-1 -*-

    Create standalone twisted based server.

    Minimal usage:

        from MoinMoin.server.twistedmoin 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 See moin_twisted script.

    @copyright: 2004 by Thomas Waldmann, Oliver Graf, Nir Soffer
    @license: GNU GPL, see COPYING for details.

from twisted.application import internet, service
from twisted.web import script, static, server, vhost, resource, util
from twisted.internet import threads, reactor

    from twisted.internet import ssl
except ImportError:
    ssl = None

# Enable threads
from twisted.python import threadable

# 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 '/wiki'
        if request.prepath == [] and name == 'wiki':
            return resource.Resource.getChild(self, name, request)

        # Serve special 'root' files from '/wiki'
        elif name in ['favicon.ico', 'robots.txt'] and request.postpath == []:
            return self.children['wiki'].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
            if config.memoryProfile:
            req = TWISTED.Request(request, name, reactor,
            if config.hotshotProfile:
            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)
            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

        Creates an extra member extended_args which also has
        filenames of file uploads ( FIELDNAME__filename__ ).
        import cgi, 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
            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 = =

        # create dummy env for cgi.FieldStorage
        env = {
            'REQUEST_METHOD': self.method,
            'QUERY_STRING': argstring,
        form = cgi.FieldStorage(fp=self.content,

        # Argument processing

        args = self.args
            keys = form.keys()
        except TypeError:
            for key in keys:
                values = form[key]
                if not isinstance(values, list):
                    values = [values]
                fixedResult = []
                for i in values:
                    if isinstance(i, cgi.MiniFieldStorage):
                    elif isinstance(i, cgi.FieldStorage):
                        # multiple uploads to same form field are stupid!
                        if i.filename:
                            args[key + '__filename__'] = i.filename
                args[key] = fixedResult


class MoinSite(server.Site):
    """ Moin site """
    requestFactory = MoinRequest

    def startFactory(self):
        """ Setup before starting """
        # Memory profile
        if config.memoryProfile:

        # hotshot profile
        if config.hotshotProfile:
            import hotshot
            config.hotshotProfile = hotshot.Profile(config.hotshotProfile)

    def stopFactory(self):
        """ Cleaup before stoping """
        if config.hotshotProfile:

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
    logPath = None
    virtualHosts = None
    memoryProfile = None
    hotshotProfile = None

    # sslcert = ('/whereever/cert/sitekey.pem', '/whereever/cert/sitecert.pem')
    sslcert = None

    def __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

    # The root of the HTTP hierarchy
    default = WikiRoot()

    # Here is where img and css and some special files come from
    default.putChild('wiki', static.File(

    # 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, timeout=2*60) # 2 minutes 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!
            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)
            s = internet.TCPServer(port, site, interface=interface)

    return application