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 Route
s 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 =>
}
}