# Copyright (c) The PyAMF Project. # See LICENSE.txt for details. """ AMF3 RemoteObject support. @see: U{RemoteObject on LiveDocs } @since: 0.1.0 """ import calendar import time import uuid import sys import pyamf.python from pyamf import remoting from pyamf.flex import messaging class BaseServerError(pyamf.BaseError): """ Base server error. """ class ServerCallFailed(BaseServerError): """ A catchall error. """ _amf_code = 'Server.Call.Failed' def generate_random_id(): return str(uuid.uuid4()) def generate_acknowledgement(request=None): ack = messaging.AcknowledgeMessage() ack.messageId = generate_random_id() ack.clientId = generate_random_id() ack.timestamp = calendar.timegm(time.gmtime()) if request: ack.correlationId = request.messageId return ack def generate_error(request, cls, e, tb, include_traceback=False): """ Builds an L{ErrorMessage} based on the last traceback and the request that was sent. """ import traceback if hasattr(cls, '_amf_code'): code = cls._amf_code else: code = cls.__name__ details = None rootCause = None if include_traceback: details = traceback.format_exception(cls, e, tb) rootCause = e faultDetail = None faultString = None if hasattr(e, 'message'): faultString = unicode(e.message) elif hasattr(e, 'args') and e.args: if isinstance(e.args[0], pyamf.python.str_types): faultString = unicode(e.args[0]) if details: faultDetail = unicode(details) return messaging.ErrorMessage( messageId=generate_random_id(), clientId=generate_random_id(), timestamp=calendar.timegm(time.gmtime()), correlationId=request.messageId, faultCode=code, faultString=faultString, faultDetail=faultDetail, extendedData=details, rootCause=rootCause) class RequestProcessor(object): def __init__(self, gateway): self.gateway = gateway def buildErrorResponse(self, request, error=None): """ Builds an error response. @param request: The AMF request @type request: L{Request} @return: The AMF response @rtype: L{Response} """ if error is not None: cls, e, tb = error else: cls, e, tb = sys.exc_info() return generate_error(request, cls, e, tb, self.gateway.debug) def _getBody(self, amf_request, ro_request, **kwargs): """ @raise ServerCallFailed: Unknown request. """ if isinstance(ro_request, messaging.CommandMessage): return self._processCommandMessage(amf_request, ro_request, **kwargs) elif isinstance(ro_request, messaging.RemotingMessage): return self._processRemotingMessage(amf_request, ro_request, **kwargs) elif isinstance(ro_request, messaging.AsyncMessage): return self._processAsyncMessage(amf_request, ro_request, **kwargs) else: raise ServerCallFailed("Unknown request: %s" % ro_request) def _processCommandMessage(self, amf_request, ro_request, **kwargs): """ @raise ServerCallFailed: Unknown Command operation. @raise ServerCallFailed: Authorization is not supported in RemoteObject. """ ro_response = generate_acknowledgement(ro_request) if ro_request.operation == messaging.CommandMessage.PING_OPERATION: ro_response.body = True return remoting.Response(ro_response) elif ro_request.operation == messaging.CommandMessage.LOGIN_OPERATION: raise ServerCallFailed("Authorization is not supported in RemoteObject") elif ro_request.operation == messaging.CommandMessage.DISCONNECT_OPERATION: return remoting.Response(ro_response) else: raise ServerCallFailed("Unknown Command operation %s" % ro_request.operation) def _processAsyncMessage(self, amf_request, ro_request, **kwargs): ro_response = generate_acknowledgement(ro_request) ro_response.body = True return remoting.Response(ro_response) def _processRemotingMessage(self, amf_request, ro_request, **kwargs): ro_response = generate_acknowledgement(ro_request) service_name = ro_request.operation if hasattr(ro_request, 'destination') and ro_request.destination: service_name = '%s.%s' % (ro_request.destination, service_name) service_request = self.gateway.getServiceRequest(amf_request, service_name) # fire the preprocessor (if there is one) self.gateway.preprocessRequest(service_request, *ro_request.body, **kwargs) ro_response.body = self.gateway.callServiceRequest(service_request, *ro_request.body, **kwargs) return remoting.Response(ro_response) def __call__(self, amf_request, **kwargs): """ Processes an AMF3 Remote Object request. @param amf_request: The request to be processed. @type amf_request: L{Request} @return: The response to the request. @rtype: L{Response} """ ro_request = amf_request.body[0] try: return self._getBody(amf_request, ro_request, **kwargs) except (KeyboardInterrupt, SystemExit): raise except: return remoting.Response(self.buildErrorResponse(ro_request), status=remoting.STATUS_ERROR)