This is a simple chatroom using Play and Websockets with the Scala API.
This project makes use of dynamic streams from Akka Streams, notably BroadcastHub
and MergeHub
. By combining MergeHub and BroadcastHub, you can get publish/subscribe functionality.
The flow is defined once in the controller, and used everywhere from the chat
action:
import javax.inject._
import play.api.mvc._
import akka.stream.scaladsl._
import scala.concurrent._
class HomeController @Inject()(val controllerComponents: ControllerComponents) extends BaseController {
private type WSMessage = String
// chat room many clients -> merge hub -> broadcasthub -> many clients
private val (chatSink, chatSource) = {
// Don't log MergeHub$ProducerFailed as error if the client disconnects.
// recoverWithRetries -1 is essentially "recoverWith"
val source = MergeHub.source[WSMessage]
.log("source")
.recoverWithRetries(-1, { case _: Exception => Source.empty })
val sink = BroadcastHub.sink[WSMessage]
source.toMat(sink)(Keep.both).run()
}
private val userFlow: Flow[WSMessage, WSMessage, _] = {
Flow[WSMessage].via(Flow.fromSinkAndSource(chatSink, chatSource)).log("userFlow")
}
def chat: WebSocket = {
WebSocket.acceptOrResult[WSMessage, WSMessage] {
case rh if sameOriginCheck(rh) =>
Future.successful(userFlow).map { flow =>
Right(flow)
}.recover {
case e: Exception =>
Left(InternalServerError("Cannot create websocket"))
}
case rejected =>
Future.successful {
Left(Forbidden("forbidden"))
}
}
}
}
You will need JDK 1.8 and sbt installed.
sbt run
Go to http://localhost:9000 and open it in two different browsers. Typing into one browser will cause it to show up in another browser.
This project is originally taken from Johan Andrén's Akka-HTTP version:
Johan also has a blog post explaining dynamic streams in more detail: