~certify-web-dev/twisted/certify-trunk

« back to all changes in this revision

Viewing changes to twisted/web/distrib.py

  • Committer: Bazaar Package Importer
  • Author(s): Matthias Klose
  • Date: 2006-01-16 19:56:10 UTC
  • mfrom: (1.1.3 upstream)
  • Revision ID: james.westby@ubuntu.com-20060116195610-ykmxbia4mnnod9o2
Tags: 2.1.0-0ubuntu2
debian/copyright: Include copyright for python 2.3; some 2.3 files
are included in the upstream tarball, but not in the binary packages.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# -*- test-case-name: twisted.web.test.test_web -*-
2
 
 
3
 
# Copyright (c) 2001-2004 Twisted Matrix Laboratories.
4
 
# See LICENSE for details.
5
 
 
6
 
 
7
 
"""Distributed web servers.
8
 
 
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.).
11
 
"""
12
 
 
13
 
# System Imports
14
 
import types, os, copy, string, cStringIO
15
 
if (os.sys.platform != 'win32') and (os.name != 'java'):
16
 
    import pwd
17
 
 
18
 
# Twisted Imports
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
25
 
 
26
 
# Sibling Imports
27
 
import resource
28
 
import server
29
 
import error
30
 
import html
31
 
import static
32
 
from server import NOT_DONE_YET
33
 
 
34
 
class _ReferenceableProducerWrapper(pb.Referenceable):
35
 
    def __init__(self, producer):
36
 
        self.producer = producer
37
 
 
38
 
    def remote_resumeProducing(self):
39
 
        self.producer.resumeProducing()
40
 
 
41
 
    def remote_pauseProducing(self):
42
 
        self.producer.pauseProducing()
43
 
 
44
 
    def remote_stopProducing(self):
45
 
        self.producer.stopProducing()
46
 
 
47
 
 
48
 
class Request(pb.RemoteCopy, server.Request):
49
 
    def setCopyableState(self, state):
50
 
        for k in 'host', 'client':
51
 
            tup = state[k]
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]()
55
 
            state[k] = addr
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')
66
 
 
67
 
    def registerProducer(self, producer, streaming):
68
 
        self.remote.callRemote("registerProducer",
69
 
                               _ReferenceableProducerWrapper(producer),
70
 
                               streaming).addErrback(self.fail)
71
 
 
72
 
    def unregisterProducer(self):
73
 
        self.remote.callRemote("unregisterProducer").addErrback(self.fail)
74
 
 
75
 
    def fail(self, failure):
76
 
        log.err(failure)
77
 
 
78
 
 
79
 
pb.setCopierForClass(server.Request, Request)
80
 
 
81
 
class Issue:
82
 
    def __init__(self, request):
83
 
        self.request = request
84
 
 
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)
90
 
            self.request.finish()
91
 
 
92
 
    def failed(self, failure):
93
 
        #XXX: Argh. FIXME.
94
 
        failure = str(failure)
95
 
        self.request.write(
96
 
            error.ErrorPage(http.INTERNAL_SERVER_ERROR,
97
 
                            "Server Connection Lost",
98
 
                            "Connection to distributed server lost:" +
99
 
                            html.PRE(failure)).
100
 
            render(self.request))
101
 
        self.request.finish()
102
 
        log.msg(failure)
103
 
 
104
 
 
105
 
class ResourceSubscription(resource.Resource):
106
 
    isLeaf = 1
107
 
    waiting = 0
108
 
    def __init__(self, host, port):
109
 
        resource.Resource.__init__(self)
110
 
        self.host = host
111
 
        self.port = port
112
 
        self.pending = []
113
 
        self.publisher = None
114
 
 
115
 
    def __getstate__(self):
116
 
        """Get persistent state for this ResourceSubscription.
117
 
        """
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
123
 
        state['waiting'] = 0
124
 
        # There will be no pending requests.
125
 
        state['pending'] = []
126
 
        return state
127
 
 
128
 
    def connected(self, publisher):
129
 
        """I've connected to a publisher; I'll now send all my requests.
130
 
        """
131
 
        log.msg('connected to publisher')
132
 
        publisher.broker.notifyOnDisconnect(self.booted)
133
 
        self.publisher = publisher
134
 
        self.waiting = 0
135
 
        for request in self.pending:
136
 
            self.render(request)
137
 
        self.pending = []
138
 
 
139
 
    def notConnected(self, msg):
140
 
        """I can't connect to a publisher; I'll now reply to all pending requests.
141
 
        """
142
 
        log.msg("could not connect to distributed web service: %s" % msg)
143
 
        self.waiting = 0
144
 
        self.publisher = None
145
 
        for request in self.pending:
146
 
            request.write("Unable to connect to distributed server.")
147
 
            request.finish()
148
 
        self.pending = []
149
 
 
150
 
    def booted(self):
151
 
        self.notConnected("connection dropped")
152
 
 
153
 
    def render(self, request):
154
 
        """Render this request, from my server.
155
 
 
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.
160
 
        """
161
 
        if not self.publisher:
162
 
            self.pending.append(request)
163
 
            if not self.waiting:
164
 
                self.waiting = 1
165
 
                pb.getObjectAt(self.host, self.port, 10).addCallbacks(self.connected, self.notConnected)
166
 
 
167
 
        else:
168
 
            i = Issue(request)
169
 
            self.publisher.callRemote('request', request).addCallbacks(i.finished, i.failed)
170
 
        return NOT_DONE_YET
171
 
 
172
 
class ResourcePublisher(pb.Root, styles.Versioned):
173
 
    def __init__(self, site):
174
 
        self.site = site
175
 
 
176
 
    persistenceVersion = 2
177
 
 
178
 
    def upgradeToVersion2(self):
179
 
        self.application.authorizer.removeIdentity("web")
180
 
        del self.application.services[self.serviceName]
181
 
        del self.serviceName
182
 
        del self.application
183
 
        del self.perspectiveName
184
 
 
185
 
    def getPerspectiveNamed(self, name):
186
 
        return self
187
 
 
188
 
    def remote_request(self, request):
189
 
        res = self.site.getResourceFor(request)
190
 
        log.msg( request )
191
 
        return res.render(request)
192
 
 
193
 
class UserDirectory(page.Page):
194
 
    userDirName = 'public_html'
195
 
    userSocketName = '.twistd-web-pb'
196
 
 
197
 
    template = """
198
 
<html>
199
 
    <head>
200
 
    <title>twisted.web.distrib.UserDirectory</title>
201
 
    <style>
202
 
    
203
 
    a
204
 
    {
205
 
        font-family: Lucida, Verdana, Helvetica, Arial, sans-serif;
206
 
        color: #369;
207
 
        text-decoration: none;
208
 
    }
209
 
 
210
 
    th
211
 
    {
212
 
        font-family: Lucida, Verdana, Helvetica, Arial, sans-serif;
213
 
        font-weight: bold;
214
 
        text-decoration: none;
215
 
        text-align: left;
216
 
    }
217
 
 
218
 
    pre, code
219
 
    {
220
 
        font-family: "Courier New", Courier, monospace;
221
 
    }
222
 
 
223
 
    p, body, td, ol, ul, menu, blockquote, div
224
 
    {
225
 
        font-family: Lucida, Verdana, Helvetica, Arial, sans-serif;
226
 
        color: #000;
227
 
    }
228
 
    
229
 
    </style>
230
 
    <base view="Attributes" model="base" />
231
 
    </head>
232
 
 
233
 
    <body>
234
 
    <h1>twisted.web.distrib.UserDirectory</h1>
235
 
 
236
 
    <ul view="List" model="directory">
237
 
            <li pattern="listItem"><a view="Link" /> </li>
238
 
    </ul>
239
 
</body>
240
 
</html>
241
 
    """
242
 
 
243
 
    def wmfactory_base(self, request):
244
 
        return {'href':request.prePathURL()}
245
 
 
246
 
    def wmfactory_directory(self, request):
247
 
        m = []
248
 
        for user in pwd.getpwall():
249
 
            pw_name, pw_passwd, pw_uid, pw_gid, pw_gecos, pw_dir, pw_shell \
250
 
                     = user
251
 
            realname = string.split(pw_gecos,',')[0]
252
 
            if not realname:
253
 
                realname = pw_name
254
 
            if os.path.exists(os.path.join(pw_dir, self.userDirName)):
255
 
                m.append({
256
 
                        'href':'%s/'%pw_name,
257
 
                        'text':'%s (file)'%realname
258
 
                })
259
 
            twistdsock = os.path.join(pw_dir, self.userSocketName)
260
 
            if os.path.exists(twistdsock):
261
 
                linknm = '%s.twistd' % pw_name
262
 
                m.append({
263
 
                        'href':'%s/'%linknm,
264
 
                        'text':'%s (twistd)'%realname})
265
 
        return m
266
 
 
267
 
    def getChild(self, name, request):
268
 
        if name == '':
269
 
            return self
270
 
 
271
 
        td = '.twistd'
272
 
 
273
 
        if name[-len(td):] == td:
274
 
            username = name[:-len(td)]
275
 
            sub = 1
276
 
        else:
277
 
            username = name
278
 
            sub = 0
279
 
        try:
280
 
            pw_name, pw_passwd, pw_uid, pw_gid, pw_gecos, pw_dir, pw_shell \
281
 
                     = pwd.getpwnam(username)
282
 
        except KeyError:
283
 
            return error.NoResource()
284
 
        if sub:
285
 
            twistdsock = os.path.join(pw_dir, self.userSocketName)
286
 
            rs = ResourceSubscription('unix',twistdsock)
287
 
            self.putChild(name, rs)
288
 
            return rs
289
 
        else:
290
 
            return static.File(os.path.join(pw_dir, self.userDirName))