1
# Copyright (c) 2001-2010 Twisted Matrix Laboratories.
2
# See LICENSE for details.
5
Support for creating a service which runs a web server.
11
from twisted.web import server, static, twcgi, script, demo, distrib, wsgi
12
from twisted.internet import interfaces, reactor
13
from twisted.python import usage, reflect, threadpool
14
from twisted.spread import pb
15
from twisted.application import internet, service, strports
18
class Options(usage.Options):
20
Define the options accepted by the I{twistd web} plugin.
22
synopsis = "[web options]"
24
optParameters = [["port", "p", None, "strports description of the port to "
25
"start the server on."],
26
["logfile", "l", None, "Path to web CLF (Combined Log Format) log file."],
27
["https", None, None, "Port to listen on for Secure HTTP."],
28
["certificate", "c", "server.pem", "SSL certificate to use for HTTPS. "],
29
["privkey", "k", "server.pem", "SSL certificate to use for HTTPS."],
32
optFlags = [["personal", "",
33
"Instead of generating a webserver, generate a "
34
"ResourcePublisher which listens on the port given by "
35
"--port, or ~/%s " % (distrib.UserDirectory.userSocketName,) +
36
"if --port is not specified."],
37
["notracebacks", "n", "Do not display tracebacks in broken web pages. " +
38
"Displaying tracebacks to users may be security risk!"],
41
zsh_actions = {"logfile" : "_files -g '*.log'", "certificate" : "_files -g '*.pem'",
42
"privkey" : "_files -g '*.pem'"}
46
This starts a webserver. If you specify no arguments, it will be a
47
demo webserver that has the Test class from twisted.web.demo in it."""
50
usage.Options.__init__(self)
54
def opt_index(self, indexName):
56
Add the name of a file used to check for directory indexes.
57
[default: index, index.html]
59
self['indexes'].append(indexName)
65
Makes a server with ~/public_html and ~/.twistd-web-pb support for
68
self['root'] = distrib.UserDirectory()
72
def opt_path(self, path):
74
<path> is either a specific file or a directory to be set as the root
75
of the web server. Use this if you have a directory full of HTML, cgi,
76
php3, epy, or rpy files or any other files that you want to be served
79
def trp(*args, **kwargs):
80
# Help avoid actually importing twisted.web.trp until it is really
81
# needed. This avoids getting a deprecation warning if you're not
82
# using deprecated functionality.
83
from twisted.web import trp
84
return trp.ResourceUnpickler(*args, **kwargs)
86
self['root'] = static.File(os.path.abspath(path))
87
self['root'].processors = {
88
'.cgi': twcgi.CGIScript,
89
'.php3': twcgi.PHP3Script,
90
'.php': twcgi.PHPScript,
91
'.epy': script.PythonScript,
92
'.rpy': script.ResourceScript,
96
def opt_processor(self, proc):
98
`ext=class' where `class' is added as a Processor for files ending
101
if not isinstance(self['root'], static.File):
102
raise usage.UsageError("You can only use --processor after --path.")
103
ext, klass = proc.split('=', 1)
104
self['root'].processors[ext] = reflect.namedClass(klass)
106
def opt_class(self, className):
108
Create a Resource subclass with a zero-argument constructor.
110
classObj = reflect.namedClass(className)
111
self['root'] = classObj()
114
def opt_resource_script(self, name):
116
An .rpy file to be used as the root resource of the webserver.
118
self['root'] = script.ResourceScriptWrapper(name)
121
def opt_wsgi(self, name):
123
The FQPN of a WSGI application object to serve as the root resource of
126
pool = threadpool.ThreadPool()
127
reactor.callWhenRunning(pool.start)
128
reactor.addSystemEventTrigger('after', 'shutdown', pool.stop)
130
application = reflect.namedAny(name)
131
except (AttributeError, ValueError):
132
raise usage.UsageError("No such WSGI application: %r" % (name,))
133
self['root'] = wsgi.WSGIResource(reactor, pool, application)
136
def opt_mime_type(self, defaultType):
138
Specify the default mime-type for static files.
140
if not isinstance(self['root'], static.File):
141
raise usage.UsageError("You can only use --mime_type after --path.")
142
self['root'].defaultType = defaultType
143
opt_m = opt_mime_type
146
def opt_allow_ignore_ext(self):
148
Specify whether or not a request for 'foo' should return 'foo.ext'
150
if not isinstance(self['root'], static.File):
151
raise usage.UsageError("You can only use --allow_ignore_ext "
153
self['root'].ignoreExt('*')
155
def opt_ignore_ext(self, ext):
157
Specify an extension to ignore. These will be processed in order.
159
if not isinstance(self['root'], static.File):
160
raise usage.UsageError("You can only use --ignore_ext "
162
self['root'].ignoreExt(ext)
164
def postOptions(self):
166
Set up conditional defaults and check for dependencies.
168
If SSL is not available but an HTTPS server was configured, raise a
169
L{UsageError} indicating that this is not possible.
171
If no server port was supplied, select a default appropriate for the
172
other options supplied.
176
from twisted.internet.ssl import DefaultOpenSSLContextFactory
178
raise usage.UsageError("SSL support not installed")
179
if self['port'] is None:
181
path = os.path.expanduser(
182
os.path.join('~', distrib.UserDirectory.userSocketName))
183
self['port'] = 'unix:' + path
185
self['port'] = 'tcp:8080'
189
def makePersonalServerFactory(site):
191
Create and return a factory which will respond to I{distrib} requests
192
against the given site.
194
@type site: L{twisted.web.server.Site}
195
@rtype: L{twisted.internet.protocol.Factory}
197
return pb.PBServerFactory(distrib.ResourcePublisher(site))
201
def makeService(config):
202
s = service.MultiService()
204
root = config['root']
205
if config['indexes']:
206
config['root'].indexNames = config['indexes']
208
# This really ought to be web.Admin or something
211
if isinstance(root, static.File):
212
root.registry.setComponent(interfaces.IServiceCollection, s)
214
if config['logfile']:
215
site = server.Site(root, logPath=config['logfile'])
217
site = server.Site(root)
219
site.displayTracebacks = not config["notracebacks"]
221
if config['personal']:
222
personal = strports.service(
223
config['port'], makePersonalServerFactory(site))
224
personal.setServiceParent(s)
227
from twisted.internet.ssl import DefaultOpenSSLContextFactory
228
i = internet.SSLServer(int(config['https']), site,
229
DefaultOpenSSLContextFactory(config['privkey'],
230
config['certificate']))
231
i.setServiceParent(s)
232
strports.service(config['port'], site).setServiceParent(s)