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