1
2
3
4 """
5 Twisted server implementation.
6
7 This gateway allows you to expose functions in Twisted to AMF
8 clients and servers.
9
10 @see: U{Twisted homepage (external)<http://twistedmatrix.com>}
11
12 @author: U{Thijs Triemstra<mailto:info@collab.nl>}
13 @author: U{Nick Joyce<mailto:nick@boxdesign.co.uk>}
14
15 @since: 0.1.0
16 """
17
18 import sys, os.path
19
20 try:
21 sys.path.remove('')
22 except ValueError:
23 pass
24
25 try:
26 sys.path.remove(os.path.dirname(os.path.abspath(__file__)))
27 except ValueError:
28 pass
29
30 twisted = __import__('twisted')
31 __import__('twisted.internet.defer')
32 __import__('twisted.internet.threads')
33 __import__('twisted.web.resource')
34 __import__('twisted.web.server')
35
36 defer = twisted.internet.defer
37 threads = twisted.internet.threads
38 resource = twisted.web.resource
39 server = twisted.web.server
40
41 import pyamf
42 from pyamf import remoting
43 from pyamf.remoting import gateway, amf0, amf3
44
45 __all__ = ['TwistedGateway']
46
48 """
49 A Twisted friendly implementation of
50 L{amf0.RequestProcessor<pyamf.remoting.amf0.RequestProcessor>}
51 """
52
53 - def __call__(self, request, *args, **kwargs):
54 """
55 Calls the underlying service method.
56
57 @return: A C{Deferred} that will contain the AMF L{Response}.
58 @rtype: C{twisted.internet.defer.Deferred}
59 """
60 try:
61 service_request = self.gateway.getServiceRequest(request, request.target)
62 except gateway.UnknownServiceError, e:
63 return defer.succeed(self.buildErrorResponse(request))
64
65 response = remoting.Response(None)
66 deferred_response = defer.Deferred()
67
68 def eb(failure):
69 self.gateway.logger.debug(failure.printTraceback())
70 deferred_response.callback(self.buildErrorResponse(
71 request, (failure.type, failure.value, failure.tb)))
72
73 def response_cb(result):
74 self.gateway.logger.debug("AMF Response: %r" % result)
75 response.body = result
76 deferred_response.callback(response)
77
78 def preprocess_cb(result):
79 d = defer.maybeDeferred(self._getBody, request, response, service_request, **kwargs)
80 d.addCallback(response_cb).addErrback(eb)
81
82 def auth_cb(result):
83 if result is not True:
84 response.status = remoting.STATUS_ERROR
85 response.body = remoting.ErrorFault(code='AuthenticationError',
86 description='Authentication failed')
87
88 deferred_response.callback(response)
89
90 return
91
92 d = defer.maybeDeferred(self.gateway.preprocessRequest, service_request, *args, **kwargs)
93 d.addCallback(preprocess_cb).addErrback(eb)
94
95
96 d = defer.maybeDeferred(self.authenticateRequest, request, service_request, **kwargs)
97 d.addCallback(auth_cb).addErrback(eb)
98
99 return deferred_response
100
102 """
103 A Twisted friendly implementation of
104 L{amf3.RequestProcessor<pyamf.remoting.amf3.RequestProcessor>}
105 """
106
108 ro_response = amf3.generate_acknowledgement(ro_request)
109 amf_response = remoting.Response(ro_response, status=remoting.STATUS_OK)
110
111 try:
112 service_name = ro_request.operation
113
114 if hasattr(ro_request, 'destination') and ro_request.destination:
115 service_name = '%s.%s' % (ro_request.destination, service_name)
116
117 service_request = self.gateway.getServiceRequest(amf_request, service_name)
118 except gateway.UnknownServiceError, e:
119 return defer.succeed(remoting.Response(self.buildErrorResponse(ro_request), status=remoting.STATUS_ERROR))
120
121 deferred_response = defer.Deferred()
122
123 def eb(failure):
124 self.gateway.logger.debug(failure.printTraceback())
125 ro_response = self.buildErrorResponse(ro_request, (failure.type, failure.value, failure.tb))
126 deferred_response.callback(remoting.Response(ro_response, status=remoting.STATUS_ERROR))
127
128 def response_cb(result):
129 self.gateway.logger.debug("AMF Response: %r" % result)
130 ro_response.body = result
131 deferred_response.callback(remoting.Response(ro_response))
132
133 def process_cb(result):
134 d = defer.maybeDeferred(self.gateway.callServiceRequest, service_request, *ro_request.body, **kwargs)
135 d.addCallback(response_cb).addErrback(eb)
136
137 d = defer.maybeDeferred(self.gateway.preprocessRequest, service_request, *ro_request.body, **kwargs)
138 d.addCallback(process_cb).addErrback(eb)
139
140 return deferred_response
141
142 - def __call__(self, amf_request, **kwargs):
143 """
144 Calls the underlying service method.
145
146 @return: A C{deferred} that will contain the AMF L{Response}.
147 @rtype: C{Deferred<twisted.internet.defer.Deferred>}
148 """
149 deferred_response = defer.Deferred()
150 ro_request = amf_request.body[0]
151
152 def cb(amf_response):
153 deferred_response.callback(amf_response)
154
155 def eb(failure):
156 self.gateway.logger.debug(failure.printTraceback())
157 deferred_response.callback(self.buildErrorResponse(ro_request,
158 (failure.type, failure.value, failure.tb)))
159
160 d = defer.maybeDeferred(self._getBody, amf_request, ro_request, **kwargs)
161 d.addCallback(cb).addErrback(eb)
162
163 return deferred_response
164
166 """
167 Twisted Remoting gateway for C{twisted.web}.
168
169 @ivar expose_request: Forces the underlying HTTP request to be the first
170 argument to any service call.
171 @type expose_request: C{bool}
172 """
173
174 allowedMethods = ('POST',)
175
177 if 'expose_request' not in kwargs:
178 kwargs['expose_request'] = True
179
180 gateway.BaseGateway.__init__(self, *args, **kwargs)
181 resource.Resource.__init__(self)
182
184 """
185 Finalises the request.
186
187 @param request: The HTTP Request.
188 @type request: C{http.Request}
189 @param status: The HTTP status code.
190 @type status: C{int}
191 @param content: The content of the response.
192 @type content: C{str}
193 @param mimetype: The MIME type of the request.
194 @type mimetype: C{str}
195 """
196 request.setResponseCode(status)
197
198 request.setHeader("Content-Type", mimetype)
199 request.setHeader("Content-Length", str(len(content)))
200
201 request.write(content)
202 request.finish()
203
204 - def render_POST(self, request):
205 """
206 Read remoting request from the client.
207
208 @type request: The HTTP Request.
209 @param request: C{twisted.web.http.Request}
210 """
211 def handleDecodeError(failure):
212 """
213 Return HTTP 400 Bad Request.
214 """
215 self.logger.debug(failure.printDetailedTraceback())
216
217 body = "400 Bad Request\n\nThe request body was unable to " \
218 "be successfully decoded."
219
220 if self.debug:
221 body += "\n\nTraceback:\n\n%s" % failure.printTraceback()
222
223 self._finaliseRequest(request, 400, body)
224
225 request.content.seek(0, 0)
226 context = pyamf.get_context(pyamf.AMF0)
227
228 d = threads.deferToThread(remoting.decode, request.content.read(), context)
229
230 def cb(amf_request):
231 self.logger.debug("AMF Request: %r" % amf_request)
232 x = self.getResponse(request, amf_request)
233
234 x.addCallback(self.sendResponse, request, context)
235
236
237 d.addCallback(cb).addErrback(handleDecodeError)
238
239 return server.NOT_DONE_YET
240
245
246 def eb(failure):
247 """
248 Return 500 Internal Server Error.
249 """
250 self.logger.debug(failure.printDetailedTraceback())
251
252 body = "500 Internal Server Error\n\nThere was an error encoding" \
253 " the response."
254
255 if self.debug:
256 body += "\n\nTraceback:\n\n%s" % failure.printTraceback()
257
258 self._finaliseRequest(request, 500, body)
259
260 d = threads.deferToThread(remoting.encode, amf_response, context)
261
262 d.addCallback(cb).addErrback(eb)
263
265 """
266 Determines the request processor, based on the request.
267
268 @param request: The AMF message.
269 @type request: L{Request<pyamf.remoting.Request>}
270 """
271 if request.target == 'null':
272 return AMF3RequestProcessor(self)
273
274 return AMF0RequestProcessor(self)
275
277 """
278 Processes the AMF request, returning an AMF L{Response}.
279
280 @param http_request: The underlying HTTP Request
281 @type http_request: C{twisted.web.http.Request}
282 @param amf_request: The AMF Request.
283 @type amf_request: L{Envelope<pyamf.remoting.Envelope>}
284 """
285 response = remoting.Envelope(amf_request.amfVersion, amf_request.clientType)
286 dl = []
287
288 def cb(body, name):
289 response[name] = body
290
291 for name, message in amf_request:
292 processor = self.getProcessor(message)
293
294 d = defer.maybeDeferred(processor, message, http_request=http_request)
295 d.addCallback(cb, name)
296
297 dl.append(d)
298
299 def cb2(result):
300 return response
301
302 d = defer.DeferredList(dl)
303
304 return d.addCallback(cb2)
305
307 """
308 Processes an authentication request. If no authenticator is supplied,
309 then authentication succeeds.
310
311 @return: C{Deferred}.
312 @rtype: C{twisted.internet.defer.Deferred}
313 """
314 authenticator = self.getAuthenticator(service_request)
315 self.logger.debug('Authenticator expands to: %r' % authenticator)
316
317 if authenticator is None:
318 return defer.succeed(True)
319
320 args = (username, password)
321
322 if hasattr(authenticator, '_pyamf_expose_request'):
323 http_request = kwargs.get('http_request', None)
324 args = (http_request,) + args
325
326 return defer.maybeDeferred(authenticator, *args)
327
329 """
330 Preprocesses a request.
331 """
332 processor = self.getPreprocessor(service_request)
333 self.logger.debug('Preprocessor expands to: %r' % processor)
334
335 if processor is None:
336 return
337
338 args = (service_request,) + args
339
340 if hasattr(processor, '_pyamf_expose_request'):
341 http_request = kwargs.get('http_request', None)
342 args = (http_request,) + args
343
344 return defer.maybeDeferred(processor, *args)
345