Search code examples
scalawebsocketakkaakka-streamakka-http

watchTermination is not triggered in an akka-http flow


I am currently trying to build an akka-http websocket connection that can:

  • broadcast to all connected clients
  • answer to certain requests of the clients

This is how I create my flow so far:


// keeps a list of all actors so I can broadcast to them
var actors: List[ActorRef] = Nil

private def wsFlow(implicit materializer: ActorMaterializer): Flow[ws.Message, ws.Message, NotUsed] = {
    val (actor, source) = Source.actorRef[String](10, akka.stream.OverflowStrategy.dropTail)
      .toMat(BroadcastHub.sink[String])(Keep.both)
      .run()

    // this never triggers
    source.watchTermination() { (m, f) =>
      f.onComplete(r => println("TERMINATION: " + r.toString))
      actors = actors diff actor :: Nil
      m
    }

    actors = actor :: actors

    val wsHandler: Flow[ws.Message, ws.Message, NotUsed] =
      Flow[ws.Message]
        .merge(source)
        .map {
          case TextMessage.Strict(tm) => handleMessage(actor, tm)
          case _ => TextMessage.Strict("Ignored message!")
        }
    wsHandler
  }

  def broadcast(msg: String): Unit = {
    actors.foreach(_ ! TextMessage.Strict(msg))
  }

The - hopefully - last problem I am encountering is that the watchTermination callback never triggers (I am never getting a "TERMINATION: ..." message on my console). Why is that? And how is it possible to detect when a client is leaving (so I can remove him from my actors list)?


Solution

  • I figured out how to do it:

    val wsHandler: Flow[ws.Message, ws.Message, NotUsed] = Flow[ws.Message]
      .watchTermination() { (m, f) =>
        f.onComplete(r => {
          println("Client left: " + r.toString)
          actors = actors diff actor :: Nil
          }
        )
        m
      }
      .merge(source)
      .map {
        case TextMessage.Strict(tm) => handleMessage(actor, tm)
        case _ => TextMessage.Strict("Ignored message!")
      }