!clt #13523 Don't expose sender in cluster client replies

This commit is contained in:
Patrik Nordwall 2015-08-18 17:27:42 +02:00
parent e19d3cb3e8
commit 7533df48cf
5 changed files with 41 additions and 25 deletions

View file

@ -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)

View file

@ -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)
}

View file

@ -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

View file

@ -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
================

View file

@ -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