def testCustomHandshake(self): conn = ConnectionMock() class CustomHandshakeDaemon(Pyro4.core.Daemon): def validateHandshake(self, conn, data): return ["sure", "have", "fun"] def annotations(self): ann = super(CustomHandshakeDaemon, self).annotations() ann["XYZZ"] = b"custom annotation set by daemon" return ann with CustomHandshakeDaemon(port=0) as d: corr_id = uuid.uuid4() self.sendHandshakeMessage(conn, correlation_id=corr_id) self.assertNotEqual(corr_id, current_context.correlation_id) success = d._handshake(conn) self.assertTrue(success) self.assertEqual(corr_id, current_context.correlation_id) msg = Pyro4.message.Message.recv(conn, hmac_key=d._pyroHmacKey) self.assertEqual(Pyro4.message.MSG_CONNECTOK, msg.type) self.assertEqual(99, msg.seq) self.assertEqual(2, len(msg.annotations)) self.assertEqual(corr_id.bytes, msg.annotations["CORR"]) self.assertEqual(b"custom annotation set by daemon", msg.annotations["XYZZ"]) ser = get_serializer_by_id(msg.serializer_id) data = ser.deserializeData(msg.data, msg.flags & Pyro4.message.FLAGS_COMPRESSED) self.assertEqual(["sure", "have", "fun"], data)
def _sendExceptionResponse(self, connection, seq, serializer_id, exc_value, tbinfo): """send an exception back including the local traceback info""" exc_value._pyroTraceback = tbinfo if sys.platform == "cli": util.fixIronPythonExceptionForPickle(exc_value, True) # piggyback attributes serializer = util.get_serializer_by_id(serializer_id) try: data, compressed = serializer.serializeData(exc_value) except: # the exception object couldn't be serialized, use a generic PyroError instead xt, xv, tb = sys.exc_info() msg = "Error serializing exception: %s. Original exception: %s: %s" % ( str(xv), type(exc_value), str(exc_value)) exc_value = errors.PyroError(msg) exc_value._pyroTraceback = tbinfo if sys.platform == "cli": util.fixIronPythonExceptionForPickle( exc_value, True) # piggyback attributes data, compressed = serializer.serializeData(exc_value) flags = Pyro4.message.FLAGS_EXCEPTION if compressed: flags |= Pyro4.message.FLAGS_COMPRESSED if Pyro4.config.LOGWIRE: log.debug( "daemon wiredata sending (error response): msgtype=%d flags=0x%x ser=%d seq=%d data=%r" % (Pyro4.message.MSG_RESULT, flags, serializer.serializer_id, seq, data)) msg = Message(Pyro4.message.MSG_RESULT, data, serializer.serializer_id, flags, seq) connection.send(msg.to_bytes())
def testMetaSerialization(self): with Pyro4.core.Daemon() as d: daemon_obj = d.objectsById[Pyro4.constants.DAEMON_NAME] meta = daemon_obj.get_metadata(Pyro4.constants.DAEMON_NAME) for ser_id in [Pyro4.message.SERIALIZER_JSON, Pyro4.message.SERIALIZER_MARSHAL, Pyro4.message.SERIALIZER_PICKLE, Pyro4.message.SERIALIZER_SERPENT]: serializer = get_serializer_by_id(ser_id) data = serializer.dumps(meta) _ = serializer.loads(data)
def testMetaSerialization(self): with Pyro4.core.Daemon() as d: daemon_obj = d.objectsById[Pyro4.constants.DAEMON_NAME] meta = daemon_obj.get_metadata(Pyro4.constants.DAEMON_NAME) for ser_id in [Pyro4.message.SERIALIZER_JSON, Pyro4.message.SERIALIZER_MARSHAL, Pyro4.message.SERIALIZER_PICKLE, Pyro4.message.SERIALIZER_SERPENT]: serializer = get_serializer_by_id(ser_id) data = serializer.dumps(meta) _ = serializer.loads(data) try: serializer = get_serializer_by_id(Pyro4.message.SERIALIZER_DILL) except SerializeError: # dill is optional for ironpython alone if sys.platform != "cli": raise else: data = serializer.dumps(meta) _ = serializer.loads(data)
def sendHandshakeMessage(self, conn, correlation_id=None): ser = get_serializer_by_id(Pyro4.message.SERIALIZER_MARSHAL) data, _ = ser.serializeData({"handshake": "hello", "object": Pyro4.constants.DAEMON_NAME}, False) annotations = {"CORR": correlation_id.bytes} if correlation_id else None msg = Pyro4.message.Message( Pyro4.message.MSG_CONNECT, data, Pyro4.message.SERIALIZER_MARSHAL, 0, 99, annotations=annotations ) msg.send(conn)
def __pyroCreateConnection(self, replaceUri=False): """ Connects this proxy to the remote Pyro daemon. Does connection handshake. Returns true if a new connection was made, false if an existing one was already present. """ with self.__pyroConnLock: if self._pyroConnection is not None: return False # already connected from Pyro4.naming import resolve # don't import this globally because of cyclic dependancy uri=resolve(self._pyroUri) # socket connection (normal or Unix domain socket) conn=None log.debug("connecting to %s", uri) connect_location=uri.sockname if uri.sockname else (uri.host, uri.port) with self.__pyroLock: try: if self._pyroConnection is not None: return False # already connected sock=socketutil.createSocket(connect=connect_location, reuseaddr=Pyro4.config.SOCK_REUSE, timeout=self.__pyroTimeout) conn=socketutil.SocketConnection(sock, uri.object) # Do handshake. For now, no need to send anything. (message type CONNECT is not yet used) msg = Message.recv(conn, None) # any trailing data (dataLen>0) is an error message, if any except Exception: x=sys.exc_info()[1] if conn: conn.close() err="cannot connect: %s" % x log.error(err) if isinstance(x, errors.CommunicationError): raise else: ce = errors.CommunicationError(err) ce.__cause__ = x raise ce else: if msg.type==Pyro4.message.MSG_CONNECTFAIL: error="connection rejected" if msg.data: serializer = util.get_serializer_by_id(msg.serializer_id) data = serializer.deserializeData(msg.data, compressed=msg.flags & Pyro4.message.FLAGS_COMPRESSED) error += ", reason: " + data conn.close() log.error(error) raise errors.CommunicationError(error) elif msg.type==Pyro4.message.MSG_CONNECTOK: self._pyroConnection=conn if replaceUri: self._pyroUri=uri log.debug("connected to %s", self._pyroUri) return True else: conn.close() err="connect: invalid msg type %d received" % msg.type log.error(err) raise errors.ProtocolError(err)
def testMetaSerialization(self): with Pyro4.core.Daemon() as d: daemon_obj = d.objectsById[Pyro4.constants.DAEMON_NAME] meta = daemon_obj.get_metadata(Pyro4.constants.DAEMON_NAME) for ser_id in [Pyro4.message.SERIALIZER_JSON, Pyro4.message.SERIALIZER_MARSHAL, Pyro4.message.SERIALIZER_PICKLE, Pyro4.message.SERIALIZER_SERPENT, Pyro4.message.SERIALIZER_DILL]: import platform if platform.python_implementation() in ('IronPython', 'PyPy') and \ ser_id == Pyro4.message.SERIALIZER_DILL: continue serializer = get_serializer_by_id(ser_id) data = serializer.dumps(meta) _ = serializer.loads(data)
def testMetaSerialization(self): with Pyro4.core.Daemon() as d: daemon_obj = d.objectsById[Pyro4.constants.DAEMON_NAME] meta = daemon_obj.get_metadata(Pyro4.constants.DAEMON_NAME) for ser_id in [ Pyro4.message.SERIALIZER_JSON, Pyro4.message.SERIALIZER_MARSHAL, Pyro4.message.SERIALIZER_PICKLE, Pyro4.message.SERIALIZER_SERPENT ]: serializer = get_serializer_by_id(ser_id) data = serializer.dumps(meta) _ = serializer.loads(data)
def _pyro_remote_call(self, msg): log = logger.debug result = [] # Deserialize request_flags = msg.flags request_seq = msg.seq serializer = util.get_serializer_by_id(msg.serializer_id) objId, method, vargs, kwargs = serializer.deserializeCall(msg.data, compressed=msg.flags & Pyro4.message.FLAGS_COMPRESSED) del msg # invite GC to collect the object, don't wait for out-of-scope # Individual or batch log("Searching for object %s" % str(objId)) obj = self.factory.objectsById.get(objId) log("Found object with type %s" % str(type(obj))) if obj is not None: # Sanitize kwargs if kwargs and sys.version_info < (2, 6, 5) and os.name != "java": # Python before 2.6.5 doesn't accept unicode keyword arguments kwargs = dict((str(k), kwargs[k]) for k in kwargs) if request_flags & Pyro4.message.FLAGS_BATCH: for method, vargs, kwargs in vargs: log("Running call %s with vargs %s and kwargs %s agasint object %s" % (str(method), str(vargs), str(kwargs), str(obj))) response = yield self._pyro_run_call(obj, method, vargs, kwargs) if isinstance(response, Exception): response = _ExceptionWrapper(response) result.append(response) # Return the final value else: log("Running call %s with vargs %s and kwargs %s agasint object %s" % (str(method), str(vargs), str(kwargs), str(obj))) if method == "__getattr__": # special case for direct attribute access (only exposed @properties are accessible) result = util.get_exposed_property_value(obj, vargs[0], only_exposed=Pyro4.config.REQUIRE_EXPOSE) elif method == "__setattr__": # special case for direct attribute access (only exposed @properties are accessible) result = util.set_exposed_property_value(obj, vargs[0], vargs[1], only_exposed=Pyro4.config.REQUIRE_EXPOSE) else: result = yield self._pyro_run_call(obj, method, vargs, kwargs) if isinstance(result, Failure): exception = result.type(result.value) exception._pyroTraceback = result.tb result = exception log("Returning result %s from _remote_call" % pformat(result)) defer.returnValue(result) else: log("unknown object requested: %s", objId) raise Pyro4.core.errors.DaemonError("unknown object")
def sendHandshakeMessage(self, conn, correlation_id=None): ser = get_serializer_by_id(Pyro4.message.SERIALIZER_MARSHAL) data, _ = ser.serializeData( { "handshake": "hello", "object": Pyro4.constants.DAEMON_NAME }, False) annotations = { "CORR": correlation_id.bytes } if correlation_id else None msg = Pyro4.message.Message(Pyro4.message.MSG_CONNECT, data, Pyro4.message.SERIALIZER_MARSHAL, 0, 99, annotations=annotations) msg.send(conn)
def _build_response(self, result): log = logger.debug # Determine appropriate response type if self.request.type == message.MSG_PING: msg_type = message.MSG_PING elif self.request.type == message.MSG_INVOKE: msg_type = message.MSG_RESULT else: err = "Attempting to respond to invalid message type." log.exception(err) raise errors.ProtocolError(err) flags = 0 # Serialize and set flags serializer = util.get_serializer_by_id(self.request.serializer_id) try: data, compressed = serializer.serializeData(result) except: # the exception object couldn't be serialized, use a generic PyroError instead xt, xv, tb = sys.exc_info() msg = "Error serializing exception: %s. Original exception: %s: %s" % (str(xv), type(xv), str(xv)) exc_value = errors.PyroError(msg) exc_value._pyroTraceback = tb if sys.platform == "cli": util.fixIronPythonExceptionForPickle(exc_value, True) # piggyback attributes data, compressed = serializer.serializeData(exc_value) if compressed: flags |= message.FLAGS_COMPRESSED if self.request.flags & Pyro4.message.FLAGS_BATCH: flags |= Pyro4.message.FLAGS_BATCH if isinstance(result, Exception): flags = message.FLAGS_EXCEPTION if Pyro4.config.LOGWIRE: log("daemon wiredata sending (error response): msgtype=%d flags=0x%x ser=%d seq=%d data=%r" % (msg_type, flags, serializer.serializer_id, self.request.seq, data)) elif self.request.type == message.MSG_PING or self.request.type == message.MSG_INVOKE: if Pyro4.config.LOGWIRE: log("daemon wiredata sending: msgtype=%d flags=0x%x ser=%d seq=%d data=%r" % (msg_type, flags, serializer.serializer_id, self.request.seq, data)) return Message(msg_type, data, serializer.serializer_id, flags, self.request.seq)
def handleRequest(self, conn): """ Handle incoming Pyro request. Catches any exception that may occur and wraps it in a reply to the calling side, as to not make this server side loop terminate due to exceptions caused by remote invocations. """ request_flags = 0 request_seq = 0 request_serializer_id = util.MarshalSerializer.serializer_id wasBatched = False isCallback = False try: msg = Message.recv( conn, [Pyro4.message.MSG_INVOKE, Pyro4.message.MSG_PING]) request_flags = msg.flags request_seq = msg.seq request_serializer_id = msg.serializer_id if Pyro4.config.LOGWIRE: log.debug( "daemon wiredata received: msgtype=%d flags=0x%x ser=%d seq=%d data=%r" % (msg.type, msg.flags, msg.serializer_id, msg.seq, msg.data)) if msg.type == Pyro4.message.MSG_PING: # return same seq, but ignore any data (it's a ping, not an echo). Nothing is deserialized. msg = Message(Pyro4.message.MSG_PING, b"pong", msg.serializer_id, 0, msg.seq) if Pyro4.config.LOGWIRE: log.debug( "daemon wiredata sending: msgtype=%d flags=0x%x ser=%d seq=%d data=%r" % (msg.type, msg.flags, msg.serializer_id, msg.seq, msg.data)) conn.send(msg.to_bytes()) return if msg.serializer_id not in self.__serializer_ids: raise errors.ProtocolError( "message used serializer that is not accepted: %d" % msg.serializer_id) serializer = util.get_serializer_by_id(msg.serializer_id) objId, method, vargs, kwargs = serializer.deserializeCall( msg.data, compressed=msg.flags & Pyro4.message.FLAGS_COMPRESSED) del msg # invite GC to collect the object, don't wait for out-of-scope obj = self.objectsById.get(objId) if obj is not None: if kwargs and sys.version_info < (2, 6, 5) and os.name != "java": # Python before 2.6.5 doesn't accept unicode keyword arguments kwargs = dict((str(k), kwargs[k]) for k in kwargs) if request_flags & Pyro4.message.FLAGS_BATCH: # batched method calls, loop over them all and collect all results data = [] for method, vargs, kwargs in vargs: method = util.resolveDottedAttribute( obj, method, Pyro4.config.DOTTEDNAMES) try: result = method( *vargs, **kwargs ) # this is the actual method call to the Pyro object except Exception: xt, xv = sys.exc_info()[0:2] log.debug( "Exception occurred while handling batched request: %s", xv) xv._pyroTraceback = util.formatTraceback( detailed=Pyro4.config.DETAILED_TRACEBACK) if sys.platform == "cli": util.fixIronPythonExceptionForPickle( xv, True) # piggyback attributes data.append(futures._ExceptionWrapper(xv)) break # stop processing the rest of the batch else: data.append(result) wasBatched = True else: # normal single method call method = util.resolveDottedAttribute( obj, method, Pyro4.config.DOTTEDNAMES) if request_flags & Pyro4.message.FLAGS_ONEWAY and Pyro4.config.ONEWAY_THREADED: # oneway call to be run inside its own thread thread = threadutil.Thread(target=method, args=vargs, kwargs=kwargs) thread.setDaemon(True) thread.start() else: isCallback = getattr(method, "_pyroCallback", False) data = method( *vargs, **kwargs ) # this is the actual method call to the Pyro object else: log.debug("unknown object requested: %s", objId) raise errors.DaemonError("unknown object") if request_flags & Pyro4.message.FLAGS_ONEWAY: return # oneway call, don't send a response else: data, compressed = serializer.serializeData( data, compress=Pyro4.config.COMPRESSION) response_flags = 0 if compressed: response_flags |= Pyro4.message.FLAGS_COMPRESSED if wasBatched: response_flags |= Pyro4.message.FLAGS_BATCH if Pyro4.config.LOGWIRE: log.debug( "daemon wiredata sending: msgtype=%d flags=0x%x ser=%d seq=%d data=%r" % (Pyro4.message.MSG_RESULT, response_flags, serializer.serializer_id, request_seq, data)) msg = Message(Pyro4.message.MSG_RESULT, data, serializer.serializer_id, response_flags, request_seq) conn.send(msg.to_bytes()) except Exception: xt, xv = sys.exc_info()[0:2] if xt is not errors.ConnectionClosedError: log.debug("Exception occurred while handling request: %r", xv) if not request_flags & Pyro4.message.FLAGS_ONEWAY: # only return the error to the client if it wasn't a oneway call tblines = util.formatTraceback( detailed=Pyro4.config.DETAILED_TRACEBACK) self._sendExceptionResponse(conn, request_seq, request_serializer_id, xv, tblines) if isCallback or isinstance( xv, (errors.CommunicationError, errors.SecurityError)): raise # re-raise if flagged as callback, communication or security error.