How can we send data to a websocket client side function? - scala

In play-scala websocket using Iteratee how can we send data to a websocket client side function? How can we specify the function in scala before doing the channel push?
My current websocket server code is as follows :
lazy val (out, channel) = Concurrent.broadcast[String] def connect = WebSocket.using[String] { request => val in = Iteratee.foreach[String] {
msg => println(msg)
channel push("I received your message: " + msg)
}
(in,out)
}
My current client code is as follow:
var ws = new WebSocket("ws://localhost:9000/connect.ws");
ws.onopen = function(){
console.log("Socket has been opened! ");
};
ws.onmessage = function(message) {
console.log(JSON.parse(message.data))
};
Every time I send a message using “channel push”, “ws.onmessage” is getting triggered. How can I emit/trigger the custom event in client side from server side?
Thank you very much in advance.

Related

Play Framework - edit Sec-WebSocket-Protocol in Web socket Response Header

Edit the Web socket header response sent from server to client.
I am creating a websocket server application using playframework. Right now the websocket response from the server is
taken care by Play. Following is the response header,
Request Header:
(UpgradeToWebSocket,),
(Host,localhost:8083),
(Connection,Upgrade),
(Upgrade,websocket),
(Sec-WebSocket-Version,13),
(Accept-Encoding,gzip, deflate, br),
(Accept-Language,en-US,en;q=0.9),
(Sec-WebSocket-Key,ZvfzpVo3EX4DFA4BRcgRIA==)
def chatSystem(): WebSocket = WebSocket.acceptOrResult[String, String] { request =>
Future.successful{
AuthenticationService.doBasicAuthentication(request.headers) match {
case Results.Ok => Right(ActorFlow.actorRef { out => ChatServiceActor.props(out) })
case _ => Left(Unauthorized)
}
}
}
I want to validate Sec-WebSocket-Protocol if it is present in the request header or add the same with value in the server response if it is not present.
I used the following code:
// Defined at http://tools.ietf.org/html/rfc6455#section-4.2.2
val MagicGuid = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
def websocketAcceptForKey(key: String): String = {
val sha1 = MessageDigest.getInstance("sha1")
val salted = key + MagicGuid
val hash = sha1.digest(salted.asciiBytes)
val acceptKey: String = Base64.rfc2045().encodeToString(hash, false)
acceptKey
}
Use it like the following:
val wsKey: Optional[HttpHeader] = request.getHeader("Sec-WebSocket-Key")
val wsAccept = if (wsKey.isPresent) Some(RawHeader("Sec-WebSocket-Accept", websocketAcceptForKey(wsKey.get.value()))) else None

How to send web socket message from server to client at particular time intervals?

I have implemented a web socket server using Play Framework. The server can take connections and respond to clients.
If the connections are idle for some time then the server automatically closes the connection .
I am not sure if there is any configuration to make the connections always alive.
So in order to monitor the connection status (connection is alive or not), the server needs to send PING message to the client at a particular time
intervals and it should receive PONG from the client.
Below is my server implementation
#Singleton
class RequestController #Inject()(cc: ControllerComponents)(implicit system: ActorSystem, mat: Materializer) extends AbstractController(cc) {
def ws = WebSocket.accept[String, String] {req =>
ActorFlow.actorRef { out =>
ParentActor.props(out)
}
}
}
object ParentActor {
def props(out: ActorRef) = Props(new ParentActor(out))
}
class ParentActor(out : ActorRef) extends Actor {
override def receive: Receive = {
case msg: String => out ! s"Echo from server $msg"
}
}
So how to send web socket ping message from server to client at particular time intervals?
You can use a scheduler in order to send message to client in certain interval. Following is one of the example that can be use to implement your scenario:
class ParentActor(out : ActorRef) extends Actor {
var isPongReceived = false
override def receive: Receive = {
case "START" =>
// Client started the connection, i.e. so, initially let mark client pong receive status as true.
isPongReceived = true
out ! s"Echo from server - Connection Started"
// This part schedules a scheduler that check in every 10 seconds if client is active/connected or not.
// Sends PING response if connected to client, otherwise terminate the actor.
context.system.scheduler.schedule(new DurationInt(10).seconds,
new DurationInt(10).seconds) {
if(isPongReceived) {
// Below ensures that server is waiting for client's PONG message in order to keep connection.
// That means, next time when this scheduler run after 10 seconds, if PONG is not received from client, the
// connection will be terminated.
isPongReceived = false
out ! "PING_RESPONSE"
}
else {
// Pong is not received from client / client is idle, terminate the connection.
out ! PoisonPill // Or directly terminate - context.stop(out)
}
}
case "PONG" => isPongReceived = true // Client sends PONG request, isPongReceived status is marked as true.
}
}

Javascript WebSocket not connecting to Scala server

I am trying to implement a real-time game server in Scala, but I have a problem connecting to it with javascript's WebSocket. I tryed connecting with websocket.org/echo.html using either ws://localhost:8888, ws://127.0.0.1:8888 or ws://[my ip]:8888 .
Here's my server code, whch is pretty much the example from Akka's doc:
class MyServer(port: Int) extends Actor with ActorLogging{
var address: InetSocketAddress = new InetSocketAddress("", port)
var socketServer: ServerHandle = IOManager(context.system).listen(address)
override def receive = {
//Fired when the server is listening
case Listening(server, add) =>
log.info(s"The server is now listening on $add.")
//When a new client connect to the server...
case NewClient(server) =>
log.info("A new client just connected !")
server.accept()
//When a message is received by the server
case Read(socket, bytes) =>
log.info("The server just received some data.")
log.info(bytes.decodeString("US-ASCII"))
//When a connection with a client is closed
case Closed(socket, cause) =>
log.info(s"A client just disconnected : $cause")
}
}
object GemsApplication extends App{
println("Starting Gems application !")
val port = Option(System.getenv("PORT")).map(_.toInt).getOrElse(8888)
println(s"Selected port is $port.")
ActorSystem().actorOf(Props( new MyServer(port) ))
}
The odd thing is that I can connect to my server using Python sockets. Since they are may more low-level I'm guessing that I'll have to implement the handshake response to make it work with WebSocket, but shouldn't I see the message from the web socket asking for upgrade ? or even just connecting ?
Thanks for any insight you might have
Robin

Scala with Play Framework 2.3.6: sending a message to all socket clients

I'm new to Scala and the Play Framework, so I'm experimenting a bit.
I've successfully created websockets, but I'd like to be able to send a message to multiple socket clients from a simple POST request.
For instance, I have 10 different random browsers connected to my socket (ws:// ... /websocket), and I can myself send a "POST: HELLO" to /newMessage. How do I make so this "HELLO" gets sent to each of the 10 clients ?
Here's the controller receiving the HELLO. Works fine and prints "Got: AnyContentAsText(HELLO)" :
def newMessage = Action { implicit request =>
println("Got: " + request.body)
/* add something here to send "request.body" to every socket client */
Ok("Got: " + request.body)
}
And here's my simple "Socket" controller that sends "Welcome" to connected clients :
object Socket extends Controller {
def txSocket = WebSocket.using[String] { request =>
// Log events to the console
val in = Iteratee.foreach[String](println).map { _ =>
println("Disconnected")
}
// Send a single 'Welcome!' message
val out = Enumerator("Welcome!")
(in, out)
}
}
How can I, from my "Message" controller, send request.body to the websocket ?
Thank you for your time !
Each websocket connection makes a new actor. You need to select the actors and send the message to them.
Like so.
object Application extends Controller {
def connect = WebSocket.acceptWithActor[JsValue, JsValue] { request => out =>
ClientActor.props(out)
}
def broadcast = Action { _ =>
system.actorSelection("akka://application/system/websockets/*/handler") ! "msg"
Ok
}
def system = play.api.libs.concurrent.Akka.system(play.api.Play.current)
}

PlayFramework 2.2 scala close WebSocket connection

How can I close a WebSocket connection? The example on the documentation works if you want to close it immediately.
But how about the following case: I want to close the connection when some condition occurs in the future. For instance, when I receive a certain message from the client.
def indexWS = WebSocket.using[String] {
request => {
var channel: Option[Concurrent.Channel[String]] = None
var outEnumerator: Enumerator[String] = Concurrent.unicast(c => channel = Some(c))
val myIteratee: Iteratee[String, Unit] = Iteratee.foreach[String] {gotString => {
// received a string from the client
if (gotString == "close_me") {
// outEnumerator = Enumerator.eof // doesn't work
// outEnumerator >>> Enumerator.eof // doesn't work
}
}}
(myIteratee, outEnumerator)
}
}
Thank you for your help!
I got it: I had to go through the channel that I opened at
var outEnumerator: Enumerator[String] = Concurrent.unicast(c => channel = Some(c))
and the commented out block would become
if (gotString == "close_me") {
channel.foreach(_.eofAndEnd())
}
which will push an EOF through the enumerator and close the connection.