~landscape/zope3/newer-from-ztk

« back to all changes in this revision

Viewing changes to src/twisted/web2/compat.py

  • Committer: Thomas Hervé
  • Date: 2009-07-08 13:52:04 UTC
  • Revision ID: thomas@canonical.com-20090708135204-df5eesrthifpylf8
Remove twisted copy

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
from __future__ import generators
2
 
 
3
 
from urllib import quote, string
4
 
 
5
 
import UserDict, math, time
6
 
from cStringIO import StringIO
7
 
 
8
 
from twisted.web2 import http_headers, iweb, stream, responsecode
9
 
from twisted.internet import defer, address
10
 
from twisted.python import components
11
 
from twisted.spread import pb
12
 
 
13
 
from zope.interface import implements
14
 
 
15
 
class HeaderAdapter(UserDict.DictMixin):
16
 
    def __init__(self, headers):
17
 
        self._headers = headers
18
 
        
19
 
    def __getitem__(self, name):
20
 
        raw = self._headers.getRawHeaders(name)
21
 
        if raw is None:
22
 
            raise KeyError(name)
23
 
        return ', '.join(raw)
24
 
 
25
 
    def __setitem__(self, name, value):
26
 
        self._headers.setRawHeaders([value])
27
 
        
28
 
    def __delitem__(self, name):
29
 
        if not self._headers.hasHeader(name):
30
 
            raise KeyError(name)
31
 
        self._headers.removeHeader(name)
32
 
 
33
 
    def iteritems(self):
34
 
        for k,v in self._headers.getAllRawHeaders():
35
 
            yield k, ', '.join(v)
36
 
 
37
 
    def keys(self):
38
 
        return [k for k, _ in self.iteritems()]
39
 
 
40
 
    def __iter__(self):
41
 
        for k, _ in self.iteritems():
42
 
            yield k
43
 
 
44
 
    def has_key(self, name):
45
 
        return self._headers.hasHeader(name)
46
 
 
47
 
def makeOldRequestAdapter(original):
48
 
    # Cache the adapter. Replace this with a more better generalized
49
 
    # mechanism when one becomes available.
50
 
    if not hasattr(original, '_oldRequest'):
51
 
        original._oldRequest = OldRequestAdapter(original)
52
 
    return original._oldRequest
53
 
 
54
 
def _addressToTuple(addr):
55
 
    if isinstance(addr, address.IPv4Address):
56
 
        return ('INET', addr.host, addr.port)
57
 
    elif isinstance(addr, address.UNIXAddress):
58
 
        return ('UNIX', addr.name)
59
 
    else:
60
 
        return tuple(addr)
61
 
 
62
 
class OldRequestAdapter(pb.Copyable, components.Componentized, object):
63
 
    """Adapt old requests to new request
64
 
    """
65
 
    implements(iweb.IOldRequest)
66
 
    
67
 
    def _getFrom(where, name):
68
 
        def _get(self):
69
 
            return getattr(getattr(self, where), name)
70
 
        return property(_get)
71
 
 
72
 
    def _getsetFrom(where, name):
73
 
        def _get(self):
74
 
            return getattr(getattr(self, where), name)
75
 
        def _set(self, new):
76
 
            setattr(getattr(self, where), name, new)
77
 
        def _del(self):
78
 
            delattr(getattr(self, where), name)
79
 
        return property(_get, _set, _del)
80
 
 
81
 
    def _getsetHeaders(where):
82
 
        def _get(self):
83
 
            headers = getattr(self, where).headers
84
 
            return HeaderAdapter(headers)
85
 
 
86
 
        def _set(self, newheaders):
87
 
            headers = http_headers.Headers()
88
 
            for n,v in newheaders.items():
89
 
                headers.setRawHeaders(n, (v,))
90
 
            newheaders = headers
91
 
            getattr(self, where).headers = newheaders
92
 
            
93
 
        return property(_get, _set)
94
 
    
95
 
    
96
 
    code = _getsetFrom('response', 'code')
97
 
    code_message = ""
98
 
    
99
 
    method = _getsetFrom('request', 'method')
100
 
    uri = _getsetFrom('request', 'uri')
101
 
    def _getClientproto(self):
102
 
        return "HTTP/%d.%d" % self.request.clientproto
103
 
    clientproto = property(_getClientproto)
104
 
    
105
 
    received_headers = _getsetHeaders('request')
106
 
    headers = _getsetHeaders('response')
107
 
    path = _getsetFrom('request', 'path')
108
 
    
109
 
    # cookies = # Do I need this?
110
 
    # received_cookies = # Do I need this?
111
 
    content = StringIO() #### FIXME
112
 
    args = _getsetFrom('request', 'args')
113
 
    # stack = # WTF is stack?
114
 
    prepath = _getsetFrom('request', 'prepath')
115
 
    postpath = _getsetFrom('request', 'postpath')
116
 
 
117
 
    def _getClient(self):
118
 
        return "WTF"
119
 
    client = property(_getClient)
120
 
    
121
 
    def _getHost(self):
122
 
        return address.IPv4Address("TCP", self.request.host, self.request.port)
123
 
    host = property(_getHost)
124
 
    
125
 
    def __init__(self, request):
126
 
        from twisted.web2 import http
127
 
        components.Componentized.__init__(self)
128
 
        self.request = request
129
 
        self.response = http.Response(stream=stream.ProducerStream())
130
 
        # This deferred will be fired by the first call to write on OldRequestAdapter
131
 
        # and will cause the headers to be output.
132
 
        self.deferredResponse = defer.Deferred()
133
 
 
134
 
    def getStateToCopyFor(self, issuer):
135
 
        # This is for distrib compatibility
136
 
        x = {}
137
 
 
138
 
        x['prepath'] = self.prepath
139
 
        x['postpath'] = self.postpath
140
 
        x['method'] = self.method
141
 
        x['uri'] = self.uri
142
 
 
143
 
        x['clientproto'] = self.clientproto
144
 
        self.content.seek(0, 0)
145
 
        x['content_data'] = self.content.read()
146
 
        x['remote'] = pb.ViewPoint(issuer, self)
147
 
 
148
 
        x['host'] = _addressToTuple(self.request.chanRequest.channel.transport.getHost())
149
 
        x['client'] = _addressToTuple(self.request.chanRequest.channel.transport.getPeer())
150
 
 
151
 
        return x
152
 
 
153
 
    def getTypeToCopy(self):
154
 
        # lie to PB so the ResourcePublisher doesn't have to know web2 exists
155
 
        # which is good because web2 doesn't exist.
156
 
        return 'twisted.web.server.Request'
157
 
 
158
 
    def registerProducer(self, producer, streaming):
159
 
        self.response.stream.registerProducer(producer, streaming)
160
 
        
161
 
    def unregisterProducer(self):
162
 
        self.response.stream.unregisterProducer()
163
 
        
164
 
    def finish(self):
165
 
        if self.deferredResponse is not None:
166
 
            d = self.deferredResponse
167
 
            self.deferredResponse = None
168
 
            d.callback(self.response)
169
 
        self.response.stream.finish()
170
 
        
171
 
    def write(self, data):
172
 
        if self.deferredResponse is not None:
173
 
            d = self.deferredResponse
174
 
            self.deferredResponse = None
175
 
            d.callback(self.response)
176
 
        self.response.stream.write(data)
177
 
        
178
 
    def getHeader(self, name):
179
 
        raw = self.request.headers.getRawHeaders(name)
180
 
        if raw is None:
181
 
            return None
182
 
        return ', '.join(raw)
183
 
 
184
 
    def setHeader(self, name, value):
185
 
        """Set an outgoing HTTP header.
186
 
        """
187
 
        self.response.headers.setRawHeaders(name, [value])
188
 
        
189
 
    def setResponseCode(self, code, message=None):
190
 
        # message ignored
191
 
        self.response.code = code
192
 
 
193
 
    def setLastModified(self, when):
194
 
        # Never returns CACHED -- can it and still be compliant?
195
 
        when = long(math.ceil(when))
196
 
        self.response.headers.setHeader('last-modified', when)
197
 
        return None
198
 
 
199
 
    def setETag(self, etag):
200
 
        self.response.headers.setRawHeaders('etag', [etag])
201
 
        return None
202
 
 
203
 
    def getAllHeaders(self):
204
 
        return dict(self.headers.iteritems())
205
 
 
206
 
    def getRequestHostname(self):
207
 
        return self.request.host
208
 
 
209
 
 
210
 
    def getCookie(self, key):
211
 
        for cookie in self.request.headers.getHeader('cookie', ()):
212
 
            if cookie.name == key:
213
 
                return cookie.value
214
 
            
215
 
        return None
216
 
 
217
 
    def addCookie(self, k, v, expires=None, domain=None, path=None, max_age=None, comment=None, secure=None):
218
 
        if expires is None and max_age is not None:
219
 
            expires=max_age-time.time()
220
 
        cookie = http_headers.Cookie(k,v, expires=expires, domain=domain, path=path, comment=comment, secure=secure)
221
 
        self.response.headers.setHeader('set-cookie', self.request.headers.getHeader('set-cookie', ())+(cookie,))
222
 
 
223
 
    def notifyFinish(self):
224
 
        ### FIXME
225
 
        return None
226
 
#        return self.request.notifyFinish()
227
 
    
228
 
    def getHost(self):
229
 
        return self.host
230
 
    
231
 
    def setHost(self, host, port, ssl=0):
232
 
        self.request.host = host
233
 
        self.request.port = port
234
 
        self.request.scheme = ssl and 'https' or 'http'
235
 
 
236
 
    def isSecure(self):
237
 
        return self.request.scheme == 'https'
238
 
    
239
 
    def getClientIP(self):
240
 
        if isinstance(self.request.chanRequest.getRemoteHost(), address.IPv4Address):
241
 
            return self.client.host
242
 
        else:
243
 
            return None
244
 
        return self.request.chanRequest.getRemoteHost()
245
 
        return "127.0.0.1"
246
 
 
247
 
    def getClient(self):
248
 
        return "127.0.0.1"
249
 
 
250
 
### FIXME:
251
 
    def getUser(self):
252
 
        return ""
253
 
 
254
 
    def getPassword(self):
255
 
        return ""
256
 
 
257
 
# Identical to original methods -- hopefully these don't have to change
258
 
    def sibLink(self, name):
259
 
        "Return the text that links to a sibling of the requested resource."
260
 
        if self.postpath:
261
 
            return (len(self.postpath)*"../") + name
262
 
        else:
263
 
            return name
264
 
 
265
 
    def childLink(self, name):
266
 
        "Return the text that links to a child of the requested resource."
267
 
        lpp = len(self.postpath)
268
 
        if lpp > 1:
269
 
            return ((lpp-1)*"../") + name
270
 
        elif lpp == 1:
271
 
            return name
272
 
        else: # lpp == 0
273
 
            if len(self.prepath) and self.prepath[-1]:
274
 
                return self.prepath[-1] + '/' + name
275
 
            else:
276
 
                return name
277
 
 
278
 
    def redirect(self, url):
279
 
        """Utility function that does a redirect.
280
 
        
281
 
        The request should have finish() called after this.
282
 
        """
283
 
        self.setResponseCode(responsecode.FOUND)
284
 
        self.setHeader("location", url)
285
 
    
286
 
    def prePathURL(self):
287
 
        port = self.getHost().port
288
 
        if self.isSecure():
289
 
            default = 443
290
 
        else:
291
 
            default = 80
292
 
        if port == default:
293
 
            hostport = ''
294
 
        else:
295
 
            hostport = ':%d' % port
296
 
        return quote('http%s://%s%s/%s' % (
297
 
            self.isSecure() and 's' or '',
298
 
            self.getRequestHostname(),
299
 
            hostport,
300
 
            string.join(self.prepath, '/')), "/:")
301
 
 
302
 
#     def URLPath(self):
303
 
#         from twisted.python import urlpath
304
 
#         return urlpath.URLPath.fromRequest(self)
305
 
 
306
 
# But nevow wants it to look like this... :(
307
 
    def URLPath(self):
308
 
        from nevow import url
309
 
        return url.URL.fromContext(self)
310
 
 
311
 
    def rememberRootURL(self, url=None):
312
 
        """
313
 
        Remember the currently-processed part of the URL for later
314
 
        recalling.
315
 
        """
316
 
        if url is None:
317
 
            url = self.prePathURL()
318
 
            # remove one segment
319
 
            self.appRootURL = url[:url.rindex("/")]
320
 
        else:
321
 
            self.appRootURL = url
322
 
 
323
 
    def getRootURL(self):
324
 
        """
325
 
        Get a previously-remembered URL.
326
 
        """
327
 
        return self.appRootURL
328
 
 
329
 
    
330
 
    session = None
331
 
 
332
 
    def getSession(self, sessionInterface = None):
333
 
        # Session management
334
 
        if not self.session:
335
 
            # FIXME: make sitepath be something
336
 
            cookiename = string.join(['TWISTED_SESSION'] + self.sitepath, "_")
337
 
            sessionCookie = self.getCookie(cookiename)
338
 
            if sessionCookie:
339
 
                try:
340
 
                    self.session = self.site.getSession(sessionCookie)
341
 
                except KeyError:
342
 
                    pass
343
 
            # if it still hasn't been set, fix it up.
344
 
            if not self.session:
345
 
                self.session = self.site.makeSession()
346
 
                self.addCookie(cookiename, self.session.uid, path='/')
347
 
        self.session.touch()
348
 
        if sessionInterface:
349
 
            return self.session.getComponent(sessionInterface)
350
 
        return self.session
351
 
 
352
 
 
353
 
class OldNevowResourceAdapter(object):
354
 
    implements(iweb.IResource)
355
 
    
356
 
    def __init__(self, original):
357
 
        # Can't use self.__original= because of __setattr__.
358
 
        self.__dict__['_OldNevowResourceAdapter__original']=original
359
 
        
360
 
    def __getattr__(self, name):
361
 
        return getattr(self.__original, name)
362
 
 
363
 
    def __setattr__(self, name, value):
364
 
        setattr(self.__original, name, value)
365
 
 
366
 
    def __delattr__(self, name):
367
 
        delattr(self.__original, name)
368
 
 
369
 
    def locateChild(self, ctx, segments):
370
 
        from twisted.web2.server import parsePOSTData
371
 
        request = iweb.IRequest(ctx)
372
 
        if request.method == "POST":
373
 
            return parsePOSTData(request).addCallback(
374
 
                lambda x: self.__original.locateChild(ctx, segments))
375
 
        return self.__original.locateChild(ctx, segments)
376
 
    
377
 
    def renderHTTP(self, ctx):
378
 
        from twisted.web2.server import parsePOSTData
379
 
        request = iweb.IRequest(ctx)
380
 
        if request.method == "POST":
381
 
            return parsePOSTData(request).addCallback(self.__reallyRender, ctx)
382
 
        return self.__reallyRender(None, ctx)
383
 
 
384
 
    def __reallyRender(self, ignored, ctx):
385
 
        # This deferred will be called when our resource is _finished_
386
 
        # writing, and will make sure we write the rest of our data
387
 
        # and finish the connection.
388
 
        defer.maybeDeferred(self.__original.renderHTTP, ctx).addCallback(self.__finish, ctx)
389
 
 
390
 
        # Sometimes the __original.renderHTTP will write() before we
391
 
        # even get this far, and we don't want to return
392
 
        # oldRequest.deferred if it's already been set to None.
393
 
        oldRequest = iweb.IOldRequest(ctx)
394
 
        if oldRequest.deferredResponse is None:
395
 
            return oldRequest.response
396
 
        return oldRequest.deferredResponse
397
 
 
398
 
    def __finish(self, data, ctx):
399
 
        oldRequest = iweb.IOldRequest(ctx)
400
 
        oldRequest.write(data)
401
 
        oldRequest.finish()
402
 
 
403
 
 
404
 
class OldResourceAdapter(object):
405
 
    implements(iweb.IOldNevowResource)
406
 
 
407
 
    def __init__(self, original):
408
 
        self.original = original
409
 
 
410
 
    def __repr__(self):
411
 
        return "<%s @ 0x%x adapting %r>" % (self.__class__.__name__, id(self), self.original)
412
 
 
413
 
    def locateChild(self, req, segments):
414
 
        import server
415
 
        request = iweb.IOldRequest(req)
416
 
        if self.original.isLeaf:
417
 
            return self, server.StopTraversal
418
 
        name = segments[0]
419
 
        if name == '':
420
 
            res = self
421
 
        else:
422
 
            request.prepath.append(request.postpath.pop(0))
423
 
            res = self.original.getChildWithDefault(name, request)
424
 
            request.postpath.insert(0, request.prepath.pop())
425
 
            
426
 
            if isinstance(res, defer.Deferred):
427
 
                return res.addCallback(lambda res: (res, segments[1:]))
428
 
            
429
 
        return res, segments[1:]
430
 
 
431
 
    def _handle_NOT_DONE_YET(self, data, request):
432
 
        from twisted.web.server import NOT_DONE_YET
433
 
        if data == NOT_DONE_YET:
434
 
            # Return a deferred that will never fire, so the finish
435
 
            # callback doesn't happen. This is because, when returning
436
 
            # NOT_DONE_YET, the page is responsible for calling finish.
437
 
            return defer.Deferred()
438
 
        else:
439
 
            return data
440
 
 
441
 
    def renderHTTP(self, req):
442
 
        request = iweb.IOldRequest(req)
443
 
        result = defer.maybeDeferred(self.original.render, request).addCallback(
444
 
            self._handle_NOT_DONE_YET, request)
445
 
        return result
446
 
 
447
 
__all__ = []