Simple example of REST/WebSockets on Spray Can.
Available on maven central
This small project introduces convenience classes / patterns to set up a server that can handle both REST and WebSockets as well as the example server and client actors below.
spray-websocket by Wandou Labs
is an extension to spray adding the
WebSocket asynchronous upgrade
to HTTP. Use of spray-websocket is quite low level and requires some
boilerplate to get an example up and running.
An added advantage of using this pattern is that it automatically
provides the
net-a-porter actor-per-request
pattern (by accident). This is because an Actor is spawned to deal
with every incoming request and will serve a REST request unless a
stateful WebSocket Upgrade is obtained, at which point it switches
to serving async messages until closed.
Back-pressure on the HTTP Routes is provided by
spray-can
if enabled.
Back-pressure with wandoulabs WebSockets is an afterthought and must
be carefully crafted. We provide a sendWithAck(f: TextFrame) method
in the server which will allow us to receive an Ack. Backpressure
must therefore be written in the application tier using the become
principles outlined in
the akka-io documentation.
The documentation on sendWithAck provides more information about how
to implement acks/nacks for other types of frames.
The SimpleWebSocketComboWorker is simple to understand and implement
but if you want high performance, you will want to use the lower level
WebSocketComboWorker and share your runRoute between actors to
save on the initialisation costs per connection.
The following example is created and tested in
WebSocketsSpec.scala.
val Ping = TextFrame("PING")
val Pong = TextFrame("PONG")
class ExampleServer(conn: ActorRef) extends SimpleWebSocketComboWorker(conn) {
def websockets = {
case UpgradedToWebSocket =>
case Ping => sendWithAck(Pong)
case Ack => // useful for backpressure
}
def route = path("hello") {
get {
complete {
<h1>Greetings!</h1>
}
}
}
}
class ExampleClient extends WebSocketClient(host, port) {
def websockets: Receive = {
case UpgradedToWebSocket =>
connection ! Ping
case Pong =>
}
}