I’m looking at the example at https://github.com/playframework/Play20/tree/master/samples/scala/websocket-chat
To make a websocket controller, you write something like:
def chat(username: String) = WebSocket.async[JsValue] { request =>
ChatRoom.join(username)
}
Chatroom.join returns a scala.concurrent.Future[(Iteratee[JsValue,_],Enumerator[JsValue])] . But where are the iteratee and the enumerator used within the Play! framework? The WebSocket class (WebSocket.scala) seems to ignore the inputs:
case class WebSocket[A](f: RequestHeader => (Enumerator[A], Iteratee[A, Unit]) => Unit) (implicit val frameFormatter: WebSocket.FrameFormatter[A]) extends Handler {
type FRAMES_TYPE = A
/**
* Returns itself, for better support in the routes file.
*
* @return itself
*/
def apply() = this
}
How does Play! manage the changing state of the iteratee as it consumes input?
It’s worth noting that the
WebSocketitself is just a dumb container. The magic happens in various classes withinplay.core.server.netty.To understand what that magic is, it’s instructive to look at the signature of f (the function that a
WebSocketcontains:This is a function that takes a
RequestHeader, anEnumerator, and anIteratee, and does something with them.So, at some point in the future, the framework will provide our
WebSocketwith aRequestHeader(which should be self explanatory), anEnumerator[A](Enumerators are sources, in this case, these are the messages being received from the client), and anIteratee[A, Unit](Iteratees are sinks, in this case, this is where we send messages to go back to the client).In the case of
WebSocket.adapter, the WebSocket will connect theEnumeratorto theIterateevia anEnumeratee. In the case ofWebSocket.using, the WebSocket will connect the remoteEnumeratorto a localIteratee, and connect the removeIterateeto a localEnumerator.Rather than defining WebSocket directly, it’s likely to be easier to use one of the convenience methods in the
WebSocketobject. The following code will echo the previous message received:Note that this code almost certainly has thread safety issues – in Scala, you should try to avoid mutable state whenever possible, or use actors or similar if not.
Or, try
WebSocket.using, and look at a pusheeEnumerator, in conjunction with a foreachIteratee, although it’s a little fiddler. Perhaps understandably, the pushee enumerator is deprecated in Play 2.1, as it’s being superseded by the new channels system.