~ubuntu-branches/ubuntu/natty/moin/natty-updates

« back to all changes in this revision

Viewing changes to MoinMoin/server/twistedmoin.py

  • Committer: Bazaar Package Importer
  • Author(s): Jonas Smedegaard
  • Date: 2008-06-22 21:17:13 UTC
  • mfrom: (0.9.1 upstream)
  • Revision ID: james.westby@ubuntu.com-20080622211713-fpo2zrq3s5dfecxg
Tags: 1.7.0-3
Simplify /etc/moin/wikilist format: "USER URL" (drop unneeded middle
CONFIG_DIR that was wrongly advertised as DATA_DIR).  Make
moin-mass-migrate handle both formats and warn about deprecation of
the old one.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# -*- coding: iso-8859-1 -*-
2
 
"""
3
 
    MoinMoin.server.twistedmoin
4
 
 
5
 
    Create standalone twisted based server.
6
 
 
7
 
    Minimal usage:
8
 
 
9
 
        from MoinMoin.server.twistedmoin import TwistedConfig, makeApp
10
 
        
11
 
        class Config(TwistedConfig):
12
 
            docs = '/usr/share/moin/wiki/htdocs'
13
 
            user = 'www-data'
14
 
            group = 'www-data'
15
 
            
16
 
        application = makeApp(Config)
17
 
 
18
 
    Then run this code with twistd -y yourcode.py. See moin_twisted script.
19
 
 
20
 
    @copyright: 2004 by Thomas Waldmann, Oliver Graf, Nir Soffer
21
 
    @license: GNU GPL, see COPYING for details.
22
 
"""
23
 
 
24
 
# Twisted imports
25
 
from twisted.application import internet, service
26
 
from twisted.web import script, static, server, vhost, resource, util
27
 
from twisted.internet import threads, reactor
28
 
 
29
 
try:
30
 
    from twisted.internet import ssl
31
 
except ImportError:
32
 
    ssl = None
33
 
 
34
 
# Enable threads
35
 
from twisted.python import threadable
36
 
threadable.init(1)
37
 
 
38
 
# MoinMoin imports
39
 
from MoinMoin.request import RequestTwisted
40
 
from MoinMoin.server import Config
41
 
 
42
 
# Set threads flag, so other code can use proper locking
43
 
from MoinMoin import config
44
 
config.use_threads = True
45
 
del config
46
 
 
47
 
# Server globals
48
 
config = None
49
 
 
50
 
    
51
 
class WikiResource(resource.Resource):
52
 
    """ Wiki resource """
53
 
    isLeaf = 1
54
 
    
55
 
    def render(self, request):
56
 
        return server.NOT_DONE_YET
57
 
 
58
 
 
59
 
class WikiRoot(resource.Resource):
60
 
    """ Wiki root resource """
61
 
    
62
 
    def getChild(self, name, request):
63
 
        # Serve images and css from '/wiki'
64
 
        if request.prepath == [] and name == 'wiki':
65
 
            return resource.Resource.getChild(self, name, request)
66
 
 
67
 
        # Serve special 'root' files from '/wiki'
68
 
        elif name in ['favicon.ico', 'robots.txt'] and request.postpath == []:
69
 
            return self.children['wiki'].getChild(name, request)
70
 
 
71
 
        # All other through moin
72
 
 
73
 
        # TODO: fix profile code to include the request init and ignore
74
 
        # first request. I'm not doing this now since its better done
75
 
        # with the new twisted code waiting in fix branch. --Nir
76
 
        else:
77
 
            if config.memoryProfile:
78
 
                config.memoryProfile.addRequest()
79
 
            req = RequestTwisted(request, name, reactor,
80
 
                                 properties=config.properties)
81
 
            if config.hotshotProfile:
82
 
                threads.deferToThread(config.hotshotProfile.runcall, req.run)
83
 
            else:
84
 
                threads.deferToThread(req.run)
85
 
            return WikiResource()
86
 
 
87
 
 
88
 
class MoinRequest(server.Request):
89
 
    """ MoinMoin request
90
 
 
91
 
    Enable passing of file-upload filenames
92
 
    """
93
 
 
94
 
    def requestReceived(self, command, path, version):
95
 
        """ Called by channel when all data has been received.
96
 
 
97
 
        Override server.Request method for POST requests, to fix file
98
 
        uploads issue.
99
 
        """
100
 
        if command == 'POST':
101
 
            self.requestReceivedPOST(path, version)
102
 
        else:
103
 
            server.Request.requestReceived(self, command, path, version)
104
 
 
105
 
    def requestReceivedPOST(self, path, version):
106
 
        """ Handle POST requests
107
 
 
108
 
        This is a modified copy of server.Request.requestRecived,
109
 
        modified to use cgi.FieldStorage to handle file uploads
110
 
        correctly.
111
 
 
112
 
        Creates an extra member extended_args which also has
113
 
        filenames of file uploads ( FIELDNAME__filename__ ).
114
 
        """
115
 
        import cgi
116
 
        
117
 
        self.content.seek(0,0)
118
 
        self.args = {}
119
 
        self.extended_args = {}
120
 
        self.stack = []
121
 
 
122
 
        self.method = 'POST'
123
 
        self.uri = path
124
 
        self.clientproto = version
125
 
        x = self.uri.split('?')
126
 
 
127
 
        argstring = ""
128
 
        if len(x) == 1:
129
 
            self.path = self.uri
130
 
        else:
131
 
            if len(x) != 2:
132
 
                from twisted.python import log
133
 
                log.msg("May ignore parts of this invalid URI: %s"
134
 
                        % repr(self.uri))
135
 
            self.path, argstring = x[0], x[1]
136
 
 
137
 
        # cache the client and server information, we'll need this later to be
138
 
        # serialized and sent with the request so CGIs will work remotely
139
 
        self.client = self.channel.transport.getPeer()
140
 
        self.host = self.channel.transport.getHost()
141
 
 
142
 
        # create dummy env for cgi.FieldStorage
143
 
        env = {
144
 
            'REQUEST_METHOD': self.method,
145
 
            'QUERY_STRING': argstring,
146
 
            }
147
 
        form = cgi.FieldStorage(fp=self.content,
148
 
                                environ=env,
149
 
                                headers=self.received_headers)
150
 
 
151
 
        # Argument processing
152
 
 
153
 
        args = self.args
154
 
        try:
155
 
            keys = form.keys()
156
 
        except TypeError:
157
 
            pass
158
 
        else:
159
 
            for key in keys:
160
 
                values = form[key]
161
 
                if not isinstance(values, list):
162
 
                    values = [values]
163
 
                fixedResult = []
164
 
                for i in values:
165
 
                    if isinstance(i, cgi.MiniFieldStorage):
166
 
                        fixedResult.append(i.value)
167
 
                    elif isinstance(i, cgi.FieldStorage):
168
 
                        fixedResult.append(i.value)
169
 
                        # multiple uploads to same form field are stupid!
170
 
                        if i.filename:
171
 
                            args[key + '__filename__'] = i.filename
172
 
                args[key] = fixedResult
173
 
        
174
 
        self.process()
175
 
 
176
 
 
177
 
class MoinSite(server.Site):
178
 
    """ Moin site """
179
 
    requestFactory = MoinRequest
180
 
 
181
 
    def startFactory(self):
182
 
        """ Setup before starting """
183
 
        # Memory profile
184
 
        if config.memoryProfile:
185
 
            config.memoryProfile.sample()
186
 
 
187
 
        # hotshot profile
188
 
        if config.hotshotProfile:
189
 
            import hotshot
190
 
            config.hotshotProfile = hotshot.Profile(config.hotshotProfile)
191
 
        server.Site.startFactory(self)
192
 
 
193
 
    def stopFactory(self):
194
 
        """ Cleaup before stoping """
195
 
        server.Site.stopFactory(self)
196
 
        if config.hotshotProfile:
197
 
            config.hotshotProfile.close()        
198
 
 
199
 
 
200
 
class TwistedConfig(Config):
201
 
    """ Twisted server default config """
202
 
 
203
 
    name = 'mointwisted'
204
 
    properties = {}
205
 
    docs = '/usr/share/moin/htdocs'
206
 
    user = 'www-data'
207
 
    group = 'www-data'
208
 
    port = 8080
209
 
    interfaces = ['']
210
 
    threads = 10
211
 
    timeout = 15*60 # 15 minutes
212
 
    logPath = None
213
 
    virtualHosts = None
214
 
    memoryProfile = None
215
 
    hotshotProfile = None
216
 
    
217
 
    # sslcert = ('/whereever/cert/sitekey.pem', '/whereever/cert/sitecert.pem')
218
 
    sslcert = None 
219
 
 
220
 
    def __init__(self):
221
 
        Config.__init__(self)
222
 
 
223
 
        # Check for '' in interfaces, then ignore other
224
 
        if '' in self.interfaces:
225
 
            self.interfaces = ['']
226
 
 
227
 
 
228
 
def makeApp(ConfigClass):
229
 
    """ Generate and return an application
230
 
 
231
 
    See MoinMoin.server.Config for config options
232
 
 
233
 
    @param ConfigClass: config class
234
 
    @rtype: application object
235
 
    @return twisted application, needed by twistd
236
 
    """
237
 
    # Create config instance (raise RuntimeError if config invalid)
238
 
    global config
239
 
    config = ConfigClass()
240
 
        
241
 
    # Set number of threads
242
 
    reactor.suggestThreadPoolSize(config.threads)
243
 
    
244
 
    # The root of the HTTP hierarchy
245
 
    default = WikiRoot()
246
 
 
247
 
    # Here is where img and css and some special files come from
248
 
    default.putChild('wiki', static.File(config.docs))
249
 
 
250
 
    # Generate the Site factory
251
 
    # TODO: Maybe we can use WikiRoot instead of this
252
 
    # ----------------------------------------------
253
 
    root = vhost.NameVirtualHost()
254
 
    root.default = default
255
 
    # ----------------------------------------------
256
 
    site = MoinSite(root, logPath=config.logPath_twisted, timeout=config.timeout)
257
 
 
258
 
    # Make application
259
 
    application = service.Application("web", uid=config.uid, gid=config.gid)
260
 
    sc = service.IServiceCollection(application)
261
 
 
262
 
    # Listen to all interfaces in config.interfaces
263
 
    for entry in config.interfaces:
264
 
        # Add a TCPServer for each interface.
265
 
 
266
 
        # This is an hidden experimantal feature: each entry in
267
 
        # interface may contain a port, using 'ip:port'.
268
 
        # Note: the format is subject to change!
269
 
        try:
270
 
            interface, port = entry.split(':', 1)
271
 
        except ValueError:
272
 
            interface, port = entry, config.port
273
 
            
274
 
        # Might raise ValueError if not integer.
275
 
        # TODO: check if we can use string port, like 'http'
276
 
        port = int(port)                       
277
 
 
278
 
        if port == 443 and ssl and ssl.supported and config.sslcert:
279
 
            sslContext = ssl.DefaultOpenSSLContextFactory(*config.sslcert)
280
 
            s = internet.SSLServer(port, site, sslContext, interface=interface)
281
 
        else:
282
 
            s = internet.TCPServer(port, site, interface=interface)
283
 
        s.setServiceParent(sc)
284
 
 
285
 
    return application
286