Package pyamf :: Package remoting :: Package gateway :: Module twisted
[hide private]
[frames] | no frames]

Source Code for Module pyamf.remoting.gateway.twisted

  1  # Copyright (c) 2007-2008 The PyAMF Project. 
  2  # See LICENSE for details. 
  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   
47 -class AMF0RequestProcessor(amf0.RequestProcessor):
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 # we have a valid service, now attempt authentication 96 d = defer.maybeDeferred(self.authenticateRequest, request, service_request, **kwargs) 97 d.addCallback(auth_cb).addErrback(eb) 98 99 return deferred_response 100
101 -class AMF3RequestProcessor(amf3.RequestProcessor):
102 """ 103 A Twisted friendly implementation of 104 L{amf3.RequestProcessor<pyamf.remoting.amf3.RequestProcessor>} 105 """ 106
107 - def _processRemotingMessage(self, amf_request, ro_request, **kwargs):
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
165 -class TwistedGateway(gateway.BaseGateway, resource.Resource):
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
176 - def __init__(self, *args, **kwargs):
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
183 - def _finaliseRequest(self, request, status, content, mimetype='text/plain'):
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 # Process the request 237 d.addCallback(cb).addErrback(handleDecodeError) 238 239 return server.NOT_DONE_YET 240
241 - def sendResponse(self, amf_response, request, context):
242 def cb(result): 243 self._finaliseRequest(request, 200, result.getvalue(), 244 remoting.CONTENT_TYPE)
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
264 - def getProcessor(self, request):
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
276 - def getResponse(self, http_request, amf_request):
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
306 - def authenticateRequest(self, service_request, username, password, **kwargs):
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
328 - def preprocessRequest(self, service_request, *args, **kwargs):
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