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, reactor
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,
56
'UNIX': lambda: address.UNIXAddress(tup[1])}[addrdesc]()
58
pb.RemoteCopy.setCopyableState(self, state)
59
# Emulate the local request interface --
60
self.content = cStringIO.StringIO(self.content_data)
61
self.write = self.remote.remoteMethod('write')
62
self.finish = self.remote.remoteMethod('finish')
63
self.setHeader = self.remote.remoteMethod('setHeader')
64
self.addCookie = self.remote.remoteMethod('addCookie')
65
self.setETag = self.remote.remoteMethod('setETag')
66
self.setResponseCode = self.remote.remoteMethod('setResponseCode')
67
self.setLastModified = self.remote.remoteMethod('setLastModified')
69
def registerProducer(self, producer, streaming):
70
self.remote.callRemote("registerProducer",
71
_ReferenceableProducerWrapper(producer),
72
streaming).addErrback(self.fail)
74
def unregisterProducer(self):
75
self.remote.callRemote("unregisterProducer").addErrback(self.fail)
77
def fail(self, failure):
81
pb.setCopierForClass(server.Request, Request)
84
def __init__(self, request):
85
self.request = request
87
def finished(self, result):
88
if result != NOT_DONE_YET:
89
assert isinstance(result, types.StringType),\
90
"return value not a string"
91
self.request.write(result)
94
def failed(self, failure):
96
failure = str(failure)
98
error.ErrorPage(http.INTERNAL_SERVER_ERROR,
99
"Server Connection Lost",
100
"Connection to distributed server lost:" +
102
render(self.request))
103
self.request.finish()
107
class ResourceSubscription(resource.Resource):
110
def __init__(self, host, port):
111
resource.Resource.__init__(self)
115
self.publisher = None
117
def __getstate__(self):
118
"""Get persistent state for this ResourceSubscription.
120
# When I unserialize,
121
state = copy.copy(self.__dict__)
122
# Publisher won't be connected...
123
state['publisher'] = None
124
# I won't be making a connection
126
# There will be no pending requests.
127
state['pending'] = []
130
def connected(self, publisher):
131
"""I've connected to a publisher; I'll now send all my requests.
133
log.msg('connected to publisher')
134
publisher.broker.notifyOnDisconnect(self.booted)
135
self.publisher = publisher
137
for request in self.pending:
141
def notConnected(self, msg):
142
"""I can't connect to a publisher; I'll now reply to all pending
145
log.msg("could not connect to distributed web service: %s" % msg)
147
self.publisher = None
148
for request in self.pending:
149
request.write("Unable to connect to distributed server.")
154
self.notConnected("connection dropped")
156
def render(self, request):
157
"""Render this request, from my server.
159
This will always be asynchronous, and therefore return NOT_DONE_YET.
160
It spins off a request to the pb client, and either adds it to the list
161
of pending issues or requests it immediately, depending on if the
162
client is already connected.
164
if not self.publisher:
165
self.pending.append(request)
168
bf = pb.PBClientFactory()
170
if self.host == "unix":
171
reactor.connectUNIX(self.port, bf, timeout)
173
reactor.connectTCP(self.host, self.port, bf, timeout)
174
d = bf.getRootObject()
175
d.addCallbacks(self.connected, self.notConnected)
179
self.publisher.callRemote('request', request).addCallbacks(i.finished, i.failed)
182
class ResourcePublisher(pb.Root, styles.Versioned):
183
def __init__(self, site):
186
persistenceVersion = 2
188
def upgradeToVersion2(self):
189
self.application.authorizer.removeIdentity("web")
190
del self.application.services[self.serviceName]
193
del self.perspectiveName
195
def getPerspectiveNamed(self, name):
198
def remote_request(self, request):
199
res = self.site.getResourceFor(request)
201
return res.render(request)
203
class UserDirectory(page.Page):
204
userDirName = 'public_html'
205
userSocketName = '.twistd-web-pb'
210
<title>twisted.web.distrib.UserDirectory</title>
215
font-family: Lucida, Verdana, Helvetica, Arial, sans-serif;
217
text-decoration: none;
222
font-family: Lucida, Verdana, Helvetica, Arial, sans-serif;
224
text-decoration: none;
230
font-family: "Courier New", Courier, monospace;
233
p, body, td, ol, ul, menu, blockquote, div
235
font-family: Lucida, Verdana, Helvetica, Arial, sans-serif;
240
<base view="Attributes" model="base" />
244
<h1>twisted.web.distrib.UserDirectory</h1>
246
<ul view="List" model="directory">
247
<li pattern="listItem"><a view="Link" /> </li>
253
def wmfactory_base(self, request):
254
return {'href':request.prePathURL()}
256
def wmfactory_directory(self, request):
258
for user in pwd.getpwall():
259
pw_name, pw_passwd, pw_uid, pw_gid, pw_gecos, pw_dir, pw_shell \
261
realname = string.split(pw_gecos,',')[0]
264
if os.path.exists(os.path.join(pw_dir, self.userDirName)):
266
'href':'%s/'%pw_name,
267
'text':'%s (file)'%realname
269
twistdsock = os.path.join(pw_dir, self.userSocketName)
270
if os.path.exists(twistdsock):
271
linknm = '%s.twistd' % pw_name
274
'text':'%s (twistd)'%realname})
277
def getChild(self, name, request):
283
if name[-len(td):] == td:
284
username = name[:-len(td)]
290
pw_name, pw_passwd, pw_uid, pw_gid, pw_gecos, pw_dir, pw_shell \
291
= pwd.getpwnam(username)
293
return error.NoResource()
295
twistdsock = os.path.join(pw_dir, self.userSocketName)
296
rs = ResourceSubscription('unix',twistdsock)
297
self.putChild(name, rs)
300
path = os.path.join(pw_dir, self.userDirName)
301
if not os.path.exists(path):
302
return error.NoResource()
303
return static.File(path)