2
from zope.interface import implements
4
from twisted.python import usage, reflect
5
from twisted.application import internet, service, strports
6
from twisted.scripts.mktap import IServiceMaker
7
from twisted.plugin import IPlugin
9
from twisted.web2 import static, iweb, log, server, channel, vhost
10
from twisted.web2.dav import static as dav_static
12
class Options(usage.Options):
13
optParameters = [["port", "p", "8080",
14
"Port to start the server on."],
15
["logfile", "l", None,
16
("Common Access Logging Format file to write to "
17
"if unspecified access log information will be "
18
"written to the standard twisted log file.")],
20
"Port to listen on for Secure HTTP."],
21
["certificate", "c", "server.pem",
22
"SSL certificate to use for HTTPS."],
23
["privkey", "k", "server.pem",
24
"SSL certificate to use for HTTPS."]]
26
zsh_actions = {"certificate" : "_files -g '*.pem'",
27
"privkey" : "_files -g '*.pem'"}
30
This creates a web2.tap file that can be used by twistd.
34
To serve a static directory or file:
36
mktap web2 --path=/tmp/
38
To serve a dav collection:
40
mktap web2 --dav=/tmp/
42
To serve a dynamic resource:
44
mktap web2 --class=fully.qualified.ClassName
46
To serve a directory of the form:
51
mktap web2 --vhost-path=/var/www/
53
All the above options are incompatible as they all specify the
54
root resource. However you can use the following options in
55
conjunction with --vhost-path
57
To serve a specific host name as a static file:
59
mktap web2 --vhost-static=domain3=/some/other/root/domain3
61
Or to serve a specific host name as a dynamic resource:
63
mktap web2 --vhost-class=domain4=fully.qualified.ClassName
68
usage.Options.__init__(self)
72
def opt_index(self, indexName):
73
"""Add the name of a file used to check for directory indexes.
74
[default: index, index.html]
76
self['indexes'].append(indexName)
80
def opt_path(self, path):
81
"""A path that will be used to serve the root resource as a raw file
86
raise usage.UsageError("You may only have one root resource.")
88
self['root'] = static.File(os.path.abspath(path))
90
def opt_processor(self, proc):
91
"""`ext=class' where `class' is added as a Processor for files ending
94
if not isinstance(self['root'], static.File):
95
raise usage.UsageError("You can only use --processor after --path.")
96
ext, klass = proc.split('=', 1)
97
self['root'].processors[ext] = reflect.namedClass(klass)
99
def opt_class(self, className):
100
"""A class that will be used to serve the root resource. Must implement twisted.web2.iweb.IResource and take no arguments.
103
raise usage.UsageError("You may only have one root resource.")
105
classObj = reflect.namedClass(className)
106
self['root'] = iweb.IResource(classObj())
108
def opt_allow_ignore_ext(self):
109
"""Specify whether or not a request for 'foo' should return 'foo.ext'"""
110
if not isinstance(self['root'], static.File):
111
raise usage.UsageError("You can only use --allow_ignore_ext "
113
self['root'].ignoreExt('*')
115
def opt_ignore_ext(self, ext):
116
"""Specify an extension to ignore. These will be processed in order.
118
if not isinstance(self['root'], static.File):
119
raise usage.UsageError("You can only use --ignore_ext "
121
self['root'].ignoreExt(ext)
123
def opt_mimetype(self, mimetype):
124
"""Mapping from file extension to MIME Type in the form of 'ext=type'.
125
Example: html=text/html
128
if not isinstance(self['root'], static.File):
129
raise usage.UsageError("You can only use --mimetype "
132
ext, mimetype = mimetype.split('=', 1)
134
# this is really gross, there should be a public api for this.
136
self['root']._sharedContentTypes.update({ext: mimetype})
138
def opt_vhost_path(self, path):
139
"""Specify a directory to use for automatic named virtual hosts.
140
It is assumed that this directory contains a series of
141
subdirectories each representing a virtual host domain name
142
and containing the files to be served at that domain.
146
if not isintance(self['root'], vhost.NameVirtualHost):
147
raise usage.UsageError("You may only have one root resource")
149
self['root'] = vhost.NameVirtualHost()
151
path = os.path.abspath(path)
153
for name in os.listdir(path):
154
fullname = os.path.join(path, name)
155
self['root'].addHost(name,
156
static.File(fullname))
158
def opt_vhost_static(self, virtualHost):
159
"""Specify a virtual host in the form of domain=path to be served as
160
raw directory or file.
162
if (self['root'] and not \
163
isinstance(self['root'], vhost.NameVirtualHost)):
165
raise usage.UsageError("You can only use --vhost-static alone "
166
"or with --vhost-class and --vhost-path")
168
domain, path = virtualHost.split('=', 1)
171
self['root'] = vhost.NameVirtualHost()
173
self['root'].addHost(domain, static.File(os.path.abspath(path)))
175
def opt_vhost_class(self, virtualHost):
176
"""Specify a virtual host in the form of domain=class,
177
where class can be adapted to an iweb.IResource and has a
178
zero-argument constructor.
180
if (self['root'] and not \
181
isinstance(self['root'], vhost.NameVirtualHost)):
183
raise usage.UsageError("You can not use --vhost-class with "
184
"--path or --class.")
186
domain, className = virtualHost.split('=', 1)
189
self['root'] = vhost.NameVirtualHost()
191
classObj = reflect.namedClass(className)
192
self['root'].addHost(domain, iweb.IResource(classObj()))
194
def opt_vhost_dav(self, virtualHost):
195
"""Specify a virtual host in the form of domain=path,
196
to have path served as a DAV collection at the root of
200
if (self['root'] and not \
201
isinstance(self['root'], vhost.NameVirtualHost)):
203
raise usage.UsageError("You can only use --vhost-static alone "
204
"or with --vhost-class and --vhost-path")
206
domain, path = virtualHost.split('=', 1)
209
self['root'] = vhost.NameVirtualHost()
211
self['root'].addHost(domain, dav_static.DAVFile(os.path.abspath(path)))
213
def opt_dav(self, path):
214
"""A path that will be used to serve the root resource as a DAV Collection.
218
raise usage.UsageError("You may only have one root resource")
220
self['root'] = dav_static.DAVFile(os.path.abspath(path))
222
def postOptions(self):
225
from twisted.internet.ssl import DefaultOpenSSLContextFactory
227
raise usage.UsageError("SSL support not installed")
230
class Web2Service(service.MultiService):
231
def __init__(self, logObserver):
232
self.logObserver = logObserver
233
service.MultiService.__init__(self)
235
def startService(self):
236
service.MultiService.startService(self)
237
self.logObserver.start()
239
def stopService(self):
240
service.MultiService.stopService(self)
241
self.logObserver.stop()
244
def makeService(config):
245
if config['logfile']:
246
logObserver = log.FileAccessLoggingObserver(config['logfile'])
248
logObserver = log.DefaultCommonAccessLoggingObserver()
251
if config['indexes']:
252
config['root'].indexNames = config['indexes']
254
root = log.LogWrapperResource(config['root'])
257
s = Web2Service(logObserver)
259
site = server.Site(root)
260
chan = channel.HTTPFactory(site)
263
from twisted.internet.ssl import DefaultOpenSSLContextFactory
264
i = internet.SSLServer(int(config['https']), chan,
265
DefaultOpenSSLContextFactory(config['privkey'],
266
config['certificate']))
267
i.setServiceParent(s)
269
strports.service(config['port'], chan
270
).setServiceParent(s)