view MoinMoin/server/ @ 3105:80e1a910a2f1

make logging more configurable and flexible, packages/modules shall call MoinMoin.server.getLogger
author Thomas Waldmann <tw AT waldmann-edv DOT de>
date Sat, 23 Feb 2008 21:43:47 +0100
parents 806cf4814612
children 6fe1ea4d9d1a
line wrap: on
line source

# -*- coding: iso-8859-1 -*-

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

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

# Enable threads
from twisted.python import threadable

# 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
            if config.memoryProfile:
            req = request_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
    timeout = 15 * 60 # 15 minutes
    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):

        # 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(config.url_prefix_static[1:], 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_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!
            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