1
# -*- test-case-name: twisted.web.test.test_web -*-
3
# Copyright (c) 2001-2004 Twisted Matrix Laboratories.
4
# See LICENSE for details.
7
"""Distributed web servers.
9
This is going to have to be refactored so that argument parsing is done
10
by each subprocess and not by the main web server (i.e. GET, POST etc.).
14
import types, os, copy, string, cStringIO
15
if (os.sys.platform != 'win32') and (os.name != 'java'):
19
from twisted.spread import pb
20
from twisted.web import http
21
from twisted.python import log
22
from twisted.persisted import styles
23
from twisted.web.woven import page
24
from twisted.internet import address
32
from server import NOT_DONE_YET
34
class _ReferenceableProducerWrapper(pb.Referenceable):
35
def __init__(self, producer):
36
self.producer = producer
38
def remote_resumeProducing(self):
39
self.producer.resumeProducing()
41
def remote_pauseProducing(self):
42
self.producer.pauseProducing()
44
def remote_stopProducing(self):
45
self.producer.stopProducing()
48
class Request(pb.RemoteCopy, server.Request):
49
def setCopyableState(self, state):
50
for k in 'host', 'client':
52
addrdesc = {'INET': 'TCP', 'UNIX': 'UNIX'}[tup[0]]
53
addr = {'TCP': lambda: address.IPv4Address(addrdesc, tup[1], tup[2], _bwHack='INET'),
54
'UNIX': lambda: address.UNIXAddress(tup[1])}[addrdesc]()
56
pb.RemoteCopy.setCopyableState(self, state)
57
# Emulate the local request interface --
58
self.content = cStringIO.StringIO(self.content_data)
59
self.write = self.remote.remoteMethod('write')
60
self.finish = self.remote.remoteMethod('finish')
61
self.setHeader = self.remote.remoteMethod('setHeader')
62
self.addCookie = self.remote.remoteMethod('addCookie')
63
self.setETag = self.remote.remoteMethod('setETag')
64
self.setResponseCode = self.remote.remoteMethod('setResponseCode')
65
self.setLastModified = self.remote.remoteMethod('setLastModified')
67
def registerProducer(self, producer, streaming):
68
self.remote.callRemote("registerProducer",
69
_ReferenceableProducerWrapper(producer),
70
streaming).addErrback(self.fail)
72
def unregisterProducer(self):
73
self.remote.callRemote("unregisterProducer").addErrback(self.fail)
75
def fail(self, failure):
79
pb.setCopierForClass(server.Request, Request)
82
def __init__(self, request):
83
self.request = request
85
def finished(self, result):
86
if result != NOT_DONE_YET:
87
assert isinstance(result, types.StringType),\
88
"return value not a string"
89
self.request.write(result)
92
def failed(self, failure):
94
failure = str(failure)
96
error.ErrorPage(http.INTERNAL_SERVER_ERROR,
97
"Server Connection Lost",
98
"Connection to distributed server lost:" +
100
render(self.request))
101
self.request.finish()
105
class ResourceSubscription(resource.Resource):
108
def __init__(self, host, port):
109
resource.Resource.__init__(self)
113
self.publisher = None
115
def __getstate__(self):
116
"""Get persistent state for this ResourceSubscription.
118
# When I unserialize,
119
state = copy.copy(self.__dict__)
120
# Publisher won't be connected...
121
state['publisher'] = None
122
# I won't be making a connection
124
# There will be no pending requests.
125
state['pending'] = []
128
def connected(self, publisher):
129
"""I've connected to a publisher; I'll now send all my requests.
131
log.msg('connected to publisher')
132
publisher.broker.notifyOnDisconnect(self.booted)
133
self.publisher = publisher
135
for request in self.pending:
139
def notConnected(self, msg):
140
"""I can't connect to a publisher; I'll now reply to all pending requests.
142
log.msg("could not connect to distributed web service: %s" % msg)
144
self.publisher = None
145
for request in self.pending:
146
request.write("Unable to connect to distributed server.")
151
self.notConnected("connection dropped")
153
def render(self, request):
154
"""Render this request, from my server.
156
This will always be asynchronous, and therefore return NOT_DONE_YET.
157
It spins off a request to the pb client, and either adds it to the list
158
of pending issues or requests it immediately, depending on if the
159
client is already connected.
161
if not self.publisher:
162
self.pending.append(request)
165
pb.getObjectAt(self.host, self.port, 10).addCallbacks(self.connected, self.notConnected)
169
self.publisher.callRemote('request', request).addCallbacks(i.finished, i.failed)
172
class ResourcePublisher(pb.Root, styles.Versioned):
173
def __init__(self, site):
176
persistenceVersion = 2
178
def upgradeToVersion2(self):
179
self.application.authorizer.removeIdentity("web")
180
del self.application.services[self.serviceName]
183
del self.perspectiveName
185
def getPerspectiveNamed(self, name):
188
def remote_request(self, request):
189
res = self.site.getResourceFor(request)
191
return res.render(request)
193
class UserDirectory(page.Page):
194
userDirName = 'public_html'
195
userSocketName = '.twistd-web-pb'
200
<title>twisted.web.distrib.UserDirectory</title>
205
font-family: Lucida, Verdana, Helvetica, Arial, sans-serif;
207
text-decoration: none;
212
font-family: Lucida, Verdana, Helvetica, Arial, sans-serif;
214
text-decoration: none;
220
font-family: "Courier New", Courier, monospace;
223
p, body, td, ol, ul, menu, blockquote, div
225
font-family: Lucida, Verdana, Helvetica, Arial, sans-serif;
230
<base view="Attributes" model="base" />
234
<h1>twisted.web.distrib.UserDirectory</h1>
236
<ul view="List" model="directory">
237
<li pattern="listItem"><a view="Link" /> </li>
243
def wmfactory_base(self, request):
244
return {'href':request.prePathURL()}
246
def wmfactory_directory(self, request):
248
for user in pwd.getpwall():
249
pw_name, pw_passwd, pw_uid, pw_gid, pw_gecos, pw_dir, pw_shell \
251
realname = string.split(pw_gecos,',')[0]
254
if os.path.exists(os.path.join(pw_dir, self.userDirName)):
256
'href':'%s/'%pw_name,
257
'text':'%s (file)'%realname
259
twistdsock = os.path.join(pw_dir, self.userSocketName)
260
if os.path.exists(twistdsock):
261
linknm = '%s.twistd' % pw_name
264
'text':'%s (twistd)'%realname})
267
def getChild(self, name, request):
273
if name[-len(td):] == td:
274
username = name[:-len(td)]
280
pw_name, pw_passwd, pw_uid, pw_gid, pw_gecos, pw_dir, pw_shell \
281
= pwd.getpwnam(username)
283
return error.NoResource()
285
twistdsock = os.path.join(pw_dir, self.userSocketName)
286
rs = ResourceSubscription('unix',twistdsock)
287
self.putChild(name, rs)
290
return static.File(os.path.join(pw_dir, self.userDirName))