Skip to content

Instantly share code, notes, and snippets.

@dacr
Last active August 25, 2023 17:30
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save dacr/bcccd1f14f040417b54892f104fc7c88 to your computer and use it in GitHub Desktop.
Save dacr/bcccd1f14f040417b54892f104fc7c88 to your computer and use it in GitHub Desktop.
akka websocket echo service / published by https://github.com/dacr/code-examples-manager #4bf9d7aa-9945-4a59-b750-c5734358b975/90b035b53e142d0621784039766c8a9b606fd3bf
// summary : akka websocket echo service
// keywords : scala, actors, akka-http, http-server, websocket
// publish : gist
// authors : David Crosson
// license : Apache NON-AI License Version 2.0 (https://raw.githubusercontent.com/non-ai-licenses/non-ai-licenses/main/NON-AI-APACHE2)
// id : 4bf9d7aa-9945-4a59-b750-c5734358b975
// created-on : 2021-02-06T17:41:33Z
// managed-by : https://github.com/dacr/code-examples-manager
// run-with : scala-cli $file
// usage-example : scala-cli akka-http-server-websocket-echo.sc
// ---------------------
//> using scala "2.13.11"
//> using objectWrapper
//> using dep "com.typesafe.akka::akka-http:10.2.10"
//> using dep "com.typesafe.akka::akka-stream:2.6.21"
////> using dep "org.slf4j:slf4j-nop:2.0.7"
//> using dep "org.slf4j:slf4j-simple:2.0.7"
// ---------------------
import akka.http.scaladsl._
import akka.http.scaladsl.model.ws.{BinaryMessage, Message, TextMessage}
import akka.http.scaladsl.server.Directives._
import akka.stream.scaladsl.{Flow, Sink}
import scala.concurrent.Await
import scala.concurrent.duration.Duration
// ---------------------------------------------------------------------------------------------------------------------
// Just a helper function which can be removed as only used for some println
def lanAddresses(): List[String] = {
import scala.jdk.CollectionConverters._
java.net.NetworkInterface
.getNetworkInterfaces().asScala
.filterNot(_.isLoopback)
.filterNot(_.isVirtual)
.filter(_.isUp)
.toList
.flatMap { interface =>
val ips = interface
.getInetAddresses.asScala
.to(List)
.filterNot(_.isAnyLocalAddress)
.collect { case x: java.net.Inet4Address => x.getHostAddress }
ips.headOption
}
}
// ---------------------------------------------------------------------------------------------------------------------
val port = args.headOption.map(_.toInt).getOrElse(8080)
val interface = args.drop(1).headOption.getOrElse("0.0.0.0")
System.setProperty("akka.http.server.remote-address-header", "true")
System.setProperty("akka.http.server.remote-address-attribute", "true")
implicit val system:akka.actor.ActorSystem = akka.actor.ActorSystem("MySystem")
implicit val executionContext:scala.concurrent.ExecutionContextExecutor = system.dispatcher
val routes = pathEndOrSingleSlash {
extractClientIP { clientIP =>
val echoFlow = {
Flow[Message].mapConcat {
case tm: TextMessage =>
System.out.print(clientIP.toString())
System.out.print(">")
System.out.print(tm.getStrictText)
System.out.flush()
TextMessage(tm.textStream) :: Nil
case bm: BinaryMessage =>
bm.dataStream.runWith(Sink.ignore) // Force consume
Nil
}
}
val from = clientIP.toIP.map(_.ip.getHostAddress)
println(s"new connection from $from")
handleWebSocketMessages(echoFlow)
}
}
Http().newServerAt(interface, port).bind(routes).andThen { case _ =>
val addr = lanAddresses().head
println(s"Waiting for websocket clients on $interface:$port ")
println(s"Try this server by using such command :")
println(s"- scala-cli akka-wscat.sc -- ws://$addr:8080")
println(s"- scala-cli pekko-wscat.sc -- ws://$addr:8080")
println(s"- scala-cli akka-wscat-stream.sc -- ws://$addr:8080")
println(s"- scala-cli pekko-wscat-stream.sc -- ws://$addr:8080")
println(s"- docker run -it --rm solsson/websocat -v ws://$addr:8080")
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment