view MoinMoin/server/ @ 0:77665d8e2254

tag of nonpublic@localhost--archive/moin--enterprise--1.5--base-0 (automatically generated log message) imported from: moin--main--1.5--base-0
author Thomas Waldmann <>
date Thu, 22 Sep 2005 15:09:50 +0000
children 7f314352e723
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.

# Twisted imports
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 RequestTwisted
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 = RequestTwisted(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
        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=10*60) # 10 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