1
from __future__ import generators
3
from urllib import quote, string
5
import UserDict, math, time
6
from cStringIO import StringIO
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
13
from zope.interface import implements
15
class HeaderAdapter(UserDict.DictMixin):
16
def __init__(self, headers):
17
self._headers = headers
19
def __getitem__(self, name):
20
raw = self._headers.getRawHeaders(name)
25
def __setitem__(self, name, value):
26
self._headers.setRawHeaders([value])
28
def __delitem__(self, name):
29
if not self._headers.hasHeader(name):
31
self._headers.removeHeader(name)
34
for k,v in self._headers.getAllRawHeaders():
38
return [k for k, _ in self.iteritems()]
41
for k, _ in self.iteritems():
44
def has_key(self, name):
45
return self._headers.hasHeader(name)
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
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)
62
class OldRequestAdapter(pb.Copyable, components.Componentized, object):
63
"""Adapt old requests to new request
65
implements(iweb.IOldRequest)
67
def _getFrom(where, name):
69
return getattr(getattr(self, where), name)
72
def _getsetFrom(where, name):
74
return getattr(getattr(self, where), name)
76
setattr(getattr(self, where), name, new)
78
delattr(getattr(self, where), name)
79
return property(_get, _set, _del)
81
def _getsetHeaders(where):
83
headers = getattr(self, where).headers
84
return HeaderAdapter(headers)
86
def _set(self, newheaders):
87
headers = http_headers.Headers()
88
for n,v in newheaders.items():
89
headers.setRawHeaders(n, (v,))
91
getattr(self, where).headers = newheaders
93
return property(_get, _set)
96
code = _getsetFrom('response', 'code')
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)
105
received_headers = _getsetHeaders('request')
106
headers = _getsetHeaders('response')
107
path = _getsetFrom('request', 'path')
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')
117
def _getClient(self):
119
client = property(_getClient)
122
return address.IPv4Address("TCP", self.request.host, self.request.port)
123
host = property(_getHost)
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()
134
def getStateToCopyFor(self, issuer):
135
# This is for distrib compatibility
138
x['prepath'] = self.prepath
139
x['postpath'] = self.postpath
140
x['method'] = self.method
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)
148
x['host'] = _addressToTuple(self.request.chanRequest.channel.transport.getHost())
149
x['client'] = _addressToTuple(self.request.chanRequest.channel.transport.getPeer())
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'
158
def registerProducer(self, producer, streaming):
159
self.response.stream.registerProducer(producer, streaming)
161
def unregisterProducer(self):
162
self.response.stream.unregisterProducer()
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()
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)
178
def getHeader(self, name):
179
raw = self.request.headers.getRawHeaders(name)
182
return ', '.join(raw)
184
def setHeader(self, name, value):
185
"""Set an outgoing HTTP header.
187
self.response.headers.setRawHeaders(name, [value])
189
def setResponseCode(self, code, message=None):
191
self.response.code = code
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)
199
def setETag(self, etag):
200
self.response.headers.setRawHeaders('etag', [etag])
203
def getAllHeaders(self):
204
return dict(self.headers.iteritems())
206
def getRequestHostname(self):
207
return self.request.host
210
def getCookie(self, key):
211
for cookie in self.request.headers.getHeader('cookie', ()):
212
if cookie.name == key:
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,))
223
def notifyFinish(self):
226
# return self.request.notifyFinish()
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'
237
return self.request.scheme == 'https'
239
def getClientIP(self):
240
if isinstance(self.request.chanRequest.getRemoteHost(), address.IPv4Address):
241
return self.client.host
244
return self.request.chanRequest.getRemoteHost()
254
def getPassword(self):
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."
261
return (len(self.postpath)*"../") + name
265
def childLink(self, name):
266
"Return the text that links to a child of the requested resource."
267
lpp = len(self.postpath)
269
return ((lpp-1)*"../") + name
273
if len(self.prepath) and self.prepath[-1]:
274
return self.prepath[-1] + '/' + name
278
def redirect(self, url):
279
"""Utility function that does a redirect.
281
The request should have finish() called after this.
283
self.setResponseCode(responsecode.FOUND)
284
self.setHeader("location", url)
286
def prePathURL(self):
287
port = self.getHost().port
295
hostport = ':%d' % port
296
return quote('http%s://%s%s/%s' % (
297
self.isSecure() and 's' or '',
298
self.getRequestHostname(),
300
string.join(self.prepath, '/')), "/:")
303
# from twisted.python import urlpath
304
# return urlpath.URLPath.fromRequest(self)
306
# But nevow wants it to look like this... :(
308
from nevow import url
309
return url.URL.fromContext(self)
311
def rememberRootURL(self, url=None):
313
Remember the currently-processed part of the URL for later
317
url = self.prePathURL()
319
self.appRootURL = url[:url.rindex("/")]
321
self.appRootURL = url
323
def getRootURL(self):
325
Get a previously-remembered URL.
327
return self.appRootURL
332
def getSession(self, sessionInterface = None):
335
# FIXME: make sitepath be something
336
cookiename = string.join(['TWISTED_SESSION'] + self.sitepath, "_")
337
sessionCookie = self.getCookie(cookiename)
340
self.session = self.site.getSession(sessionCookie)
343
# if it still hasn't been set, fix it up.
345
self.session = self.site.makeSession()
346
self.addCookie(cookiename, self.session.uid, path='/')
349
return self.session.getComponent(sessionInterface)
353
class OldNevowResourceAdapter(object):
354
implements(iweb.IResource)
356
def __init__(self, original):
357
# Can't use self.__original= because of __setattr__.
358
self.__dict__['_OldNevowResourceAdapter__original']=original
360
def __getattr__(self, name):
361
return getattr(self.__original, name)
363
def __setattr__(self, name, value):
364
setattr(self.__original, name, value)
366
def __delattr__(self, name):
367
delattr(self.__original, name)
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)
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)
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)
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
398
def __finish(self, data, ctx):
399
oldRequest = iweb.IOldRequest(ctx)
400
oldRequest.write(data)
404
class OldResourceAdapter(object):
405
implements(iweb.IOldNevowResource)
407
def __init__(self, original):
408
self.original = original
411
return "<%s @ 0x%x adapting %r>" % (self.__class__.__name__, id(self), self.original)
413
def locateChild(self, req, segments):
415
request = iweb.IOldRequest(req)
416
if self.original.isLeaf:
417
return self, server.StopTraversal
422
request.prepath.append(request.postpath.pop(0))
423
res = self.original.getChildWithDefault(name, request)
424
request.postpath.insert(0, request.prepath.pop())
426
if isinstance(res, defer.Deferred):
427
return res.addCallback(lambda res: (res, segments[1:]))
429
return res, segments[1:]
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()
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)