From 7533df48cfd41f40c36cb9969478cc475e9320ec Mon Sep 17 00:00:00 2001 From: Patrik Nordwall Date: Tue, 18 Aug 2015 17:27:42 +0200 Subject: [PATCH] !clt #13523 Don't expose sender in cluster client replies --- .../akka/cluster/client/ClusterClient.scala | 11 +++---- .../cluster/client/ClusterClientSpec.scala | 30 ++++++++++--------- akka-docs/rst/java/cluster-client.rst | 7 +++-- .../project/migration-guide-2.3.x-2.4.x.rst | 11 +++++++ akka-docs/rst/scala/cluster-client.rst | 7 +++-- 5 files changed, 41 insertions(+), 25 deletions(-) diff --git a/akka-cluster-tools/src/main/scala/akka/cluster/client/ClusterClient.scala b/akka-cluster-tools/src/main/scala/akka/cluster/client/ClusterClient.scala index 6f976d49a8..abc68a9256 100644 --- a/akka-cluster-tools/src/main/scala/akka/cluster/client/ClusterClient.scala +++ b/akka-cluster-tools/src/main/scala/akka/cluster/client/ClusterClient.scala @@ -524,7 +524,7 @@ object ClusterReceptionist { def receive = { case Ping ⇒ // keep alive from client case ReceiveTimeout ⇒ context stop self - case msg ⇒ client forward msg + case msg ⇒ client.tell(msg, Actor.noSender) } } } @@ -546,10 +546,11 @@ object ClusterReceptionist { * * Response messages from the destination actor are tunneled via the receptionist * to avoid inbound connections from other cluster nodes to the client, i.e. - * the `sender`, as seen by the destination actor, is not the client itself. - * The `sender` of the response messages, as seen by the client, is preserved - * as the original sender, so the client can choose to send subsequent messages - * directly to the actor in the cluster. + * the `sender()`, as seen by the destination actor, is not the client itself. + * The `sender()` of the response messages, as seen by the client, is `deadLetters` + * since the client should normally send subsequent messages via the `ClusterClient`. + * It is possible to pass the the original sender inside the reply messages if + * the client is supposed to communicate directly to the actor in the cluster. * */ final class ClusterReceptionist(pubSubMediator: ActorRef, settings: ClusterReceptionistSettings) diff --git a/akka-cluster-tools/src/multi-jvm/scala/akka/cluster/client/ClusterClientSpec.scala b/akka-cluster-tools/src/multi-jvm/scala/akka/cluster/client/ClusterClientSpec.scala index 9c1f0625f5..19d97bb07f 100644 --- a/akka-cluster-tools/src/multi-jvm/scala/akka/cluster/client/ClusterClientSpec.scala +++ b/akka-cluster-tools/src/multi-jvm/scala/akka/cluster/client/ClusterClientSpec.scala @@ -41,11 +41,13 @@ object ClusterClientSpec extends MultiNodeConfig { testTransport(on = true) + case class Reply(msg: Any, node: Address) + class TestService(testActor: ActorRef) extends Actor { def receive = { case msg ⇒ testActor forward msg - sender() ! msg + "-ack" + sender() ! Reply(msg + "-ack", Cluster(context.system).selfAddress) } } @@ -116,7 +118,7 @@ class ClusterClientSpec extends MultiNodeSpec(ClusterClientSpec) with STMultiNod val c = system.actorOf(ClusterClient.props( ClusterClientSettings(system).withInitialContacts(initialContacts)), "client1") c ! ClusterClient.Send("/user/testService", "hello", localAffinity = true) - expectMsg("hello-ack") + expectMsgType[Reply].msg should be("hello-ack") system.stop(c) } runOn(fourth) { @@ -190,18 +192,18 @@ class ClusterClientSpec extends MultiNodeSpec(ClusterClientSpec) with STMultiNod ClusterClientSettings(system).withInitialContacts(initialContacts)), "client2") c ! ClusterClient.Send("/user/service2", "bonjour", localAffinity = true) - expectMsg("bonjour-ack") - val lastSenderAddress = lastSender.path.address - val receptionistRoleName = roleName(lastSenderAddress) match { + val reply = expectMsgType[Reply] + reply.msg should be("bonjour-ack") + val receptionistRoleName = roleName(reply.node) match { case Some(r) ⇒ r - case None ⇒ fail("unexpected missing roleName: " + lastSender.path.address) + case None ⇒ fail("unexpected missing roleName: " + reply.node) } testConductor.exit(receptionistRoleName, 0).await remainingServerRoleNames -= receptionistRoleName within(remaining - 3.seconds) { awaitAssert { c ! ClusterClient.Send("/user/service2", "hi again", localAffinity = true) - expectMsg(1 second, "hi again-ack") + expectMsgType[Reply](1 second).msg should be("hi again-ack") } } system.stop(c) @@ -220,11 +222,11 @@ class ClusterClientSpec extends MultiNodeSpec(ClusterClientSpec) with STMultiNod ClusterClientSettings(system).withInitialContacts(initialContacts)), "client3") c ! ClusterClient.Send("/user/service2", "bonjour2", localAffinity = true) - expectMsg("bonjour2-ack") - val lastSenderAddress = lastSender.path.address - val receptionistRoleName = roleName(lastSenderAddress) match { + val reply = expectMsgType[Reply] + reply.msg should be("bonjour2-ack") + val receptionistRoleName = roleName(reply.node) match { case Some(r) ⇒ r - case None ⇒ fail("unexpected missing roleName: " + lastSender.path.address) + case None ⇒ fail("unexpected missing roleName: " + reply.node) } // shutdown all but the one that the client is connected to remainingServerRoleNames.foreach { r ⇒ @@ -244,9 +246,9 @@ class ClusterClientSpec extends MultiNodeSpec(ClusterClientSpec) with STMultiNod val expectedAddress = node(receptionistRoleName).address awaitAssert { c ! ClusterClient.Send("/user/service2", "bonjour3", localAffinity = true) - expectMsg(1 second, "bonjour3-ack") - val lastSenderAddress = lastSender.path.address - lastSenderAddress should be(expectedAddress) + val reply = expectMsgType[Reply](1 second) + reply.msg should be("bonjour3-ack") + reply.node should be(expectedAddress) } system.stop(c) } diff --git a/akka-docs/rst/java/cluster-client.rst b/akka-docs/rst/java/cluster-client.rst index 90f7ec715d..add40c0c73 100644 --- a/akka-docs/rst/java/cluster-client.rst +++ b/akka-docs/rst/java/cluster-client.rst @@ -54,9 +54,10 @@ to the named topic. Response messages from the destination actor are tunneled via the receptionist to avoid inbound connections from other cluster nodes to the client, i.e. the ``sender()``, as seen by the destination actor, is not the client itself. -The ``sender()`` of the response messages, as seen by the client, is preserved -as the original sender(), so the client can choose to send subsequent messages -directly to the actor in the cluster. +The ``sender()`` of the response messages, as seen by the client, is ``deadLetters`` +since the client should normally send subsequent messages via the ``ClusterClient``. +It is possible to pass the the original sender inside the reply messages if +the client is supposed to communicate directly to the actor in the cluster. While establishing a connection to a receptionist the ``ClusterClient`` will buffer messages and send them when the connection is established. If the buffer is full diff --git a/akka-docs/rst/project/migration-guide-2.3.x-2.4.x.rst b/akka-docs/rst/project/migration-guide-2.3.x-2.4.x.rst index f961fb3ff8..c238a1f406 100644 --- a/akka-docs/rst/project/migration-guide-2.3.x-2.4.x.rst +++ b/akka-docs/rst/project/migration-guide-2.3.x-2.4.x.rst @@ -297,6 +297,17 @@ is now started as a ``system`` actor instead of a ``user`` actor, i.e. the defau the ``ClusterClient`` initial contacts has changed to ``"akka.tcp://system@hostname:port/system/receptionist"``. +ClusterClient sender +==================== + +In 2.3 the ``sender()`` of the response messages, as seen by the client, was the +actor in cluster. + +In 2.4 the ``sender()`` of the response messages, as seen by the client, is ``deadLetters`` +since the client should normally send subsequent messages via the ``ClusterClient``. +It is possible to pass the the original sender inside the reply messages if +the client is supposed to communicate directly to the actor in the cluster. + Akka Persistence ================ diff --git a/akka-docs/rst/scala/cluster-client.rst b/akka-docs/rst/scala/cluster-client.rst index bc28e61451..ecec0cd354 100644 --- a/akka-docs/rst/scala/cluster-client.rst +++ b/akka-docs/rst/scala/cluster-client.rst @@ -54,9 +54,10 @@ to the named topic. Response messages from the destination actor are tunneled via the receptionist to avoid inbound connections from other cluster nodes to the client, i.e. the ``sender()``, as seen by the destination actor, is not the client itself. -The ``sender()`` of the response messages, as seen by the client, is preserved -as the original sender(), so the client can choose to send subsequent messages -directly to the actor in the cluster. +The ``sender()`` of the response messages, as seen by the client, is ``deadLetters`` +since the client should normally send subsequent messages via the ``ClusterClient``. +It is possible to pass the the original sender inside the reply messages if +the client is supposed to communicate directly to the actor in the cluster. While establishing a connection to a receptionist the ``ClusterClient`` will buffer messages and send them when the connection is established. If the buffer is full