1
# -*- coding: iso-8859-1 -*-
3
MoinMoin.server.twistedmoin
5
Create standalone twisted based server.
9
from MoinMoin.server.twistedmoin import TwistedConfig, makeApp
11
class Config(TwistedConfig):
12
docs = '/usr/share/moin/wiki/htdocs'
16
application = makeApp(Config)
18
Then run this code with twistd -y yourcode.py. See moin_twisted script.
20
@copyright: 2004 by Thomas Waldmann, Oliver Graf, Nir Soffer
21
@license: GNU GPL, see COPYING for details.
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
30
from twisted.internet import ssl
35
from twisted.python import threadable
39
from MoinMoin.request import RequestTwisted
40
from MoinMoin.server import Config
42
# Set threads flag, so other code can use proper locking
43
from MoinMoin import config
44
config.use_threads = True
51
class WikiResource(resource.Resource):
55
def render(self, request):
56
return server.NOT_DONE_YET
59
class WikiRoot(resource.Resource):
60
""" Wiki root resource """
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)
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)
71
# All other through moin
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
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)
84
threads.deferToThread(req.run)
88
class MoinRequest(server.Request):
91
Enable passing of file-upload filenames
94
def requestReceived(self, command, path, version):
95
""" Called by channel when all data has been received.
97
Override server.Request method for POST requests, to fix file
100
if command == 'POST':
101
self.requestReceivedPOST(path, version)
103
server.Request.requestReceived(self, command, path, version)
105
def requestReceivedPOST(self, path, version):
106
""" Handle POST requests
108
This is a modified copy of server.Request.requestRecived,
109
modified to use cgi.FieldStorage to handle file uploads
112
Creates an extra member extended_args which also has
113
filenames of file uploads ( FIELDNAME__filename__ ).
117
self.content.seek(0,0)
119
self.extended_args = {}
124
self.clientproto = version
125
x = self.uri.split('?')
132
from twisted.python import log
133
log.msg("May ignore parts of this invalid URI: %s"
135
self.path, argstring = x[0], x[1]
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()
142
# create dummy env for cgi.FieldStorage
144
'REQUEST_METHOD': self.method,
145
'QUERY_STRING': argstring,
147
form = cgi.FieldStorage(fp=self.content,
149
headers=self.received_headers)
151
# Argument processing
161
if not isinstance(values, list):
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!
171
args[key + '__filename__'] = i.filename
172
args[key] = fixedResult
177
class MoinSite(server.Site):
179
requestFactory = MoinRequest
181
def startFactory(self):
182
""" Setup before starting """
184
if config.memoryProfile:
185
config.memoryProfile.sample()
188
if config.hotshotProfile:
190
config.hotshotProfile = hotshot.Profile(config.hotshotProfile)
191
server.Site.startFactory(self)
193
def stopFactory(self):
194
""" Cleaup before stoping """
195
server.Site.stopFactory(self)
196
if config.hotshotProfile:
197
config.hotshotProfile.close()
200
class TwistedConfig(Config):
201
""" Twisted server default config """
205
docs = '/usr/share/moin/htdocs'
211
timeout = 15*60 # 15 minutes
215
hotshotProfile = None
217
# sslcert = ('/whereever/cert/sitekey.pem', '/whereever/cert/sitecert.pem')
221
Config.__init__(self)
223
# Check for '' in interfaces, then ignore other
224
if '' in self.interfaces:
225
self.interfaces = ['']
228
def makeApp(ConfigClass):
229
""" Generate and return an application
231
See MoinMoin.server.Config for config options
233
@param ConfigClass: config class
234
@rtype: application object
235
@return twisted application, needed by twistd
237
# Create config instance (raise RuntimeError if config invalid)
239
config = ConfigClass()
241
# Set number of threads
242
reactor.suggestThreadPoolSize(config.threads)
244
# The root of the HTTP hierarchy
247
# Here is where img and css and some special files come from
248
default.putChild('wiki', static.File(config.docs))
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)
259
application = service.Application("web", uid=config.uid, gid=config.gid)
260
sc = service.IServiceCollection(application)
262
# Listen to all interfaces in config.interfaces
263
for entry in config.interfaces:
264
# Add a TCPServer for each interface.
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!
270
interface, port = entry.split(':', 1)
272
interface, port = entry, config.port
274
# Might raise ValueError if not integer.
275
# TODO: check if we can use string port, like 'http'
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)
282
s = internet.TCPServer(port, site, interface=interface)
283
s.setServiceParent(sc)