TLDR; Akka server checks headers to upgrade connections to WebSocket, isn’t able to find them
I am trying to set up a server that accepts WebSockets requests from the client using Akka HTTP. I am using this as a guide so most of the code is really copy-pasted:
import akka.actor.ActorSystem
import akka.stream.ActorMaterializer
import akka.http.scaladsl.server.Directives._
import akka.http.scaladsl.model.HttpMethods._
import akka.http.scaladsl.model._
import akka.http.scaladsl.Http
import scala.io.StdIn
import akka.stream.scaladsl.Sink
import scala.concurrent.Future
import akka.http.scaladsl.model.ws.UpgradeToWebSocket
import akka.stream.scaladsl.{Flow, Source}
import akka.http.scaladsl.model.ws.{ Message, TextMessage, BinaryMessage }
object WebServer extends App {
implicit val system = ActorSystem("websockets-prototype")
implicit val materializer = ActorMaterializer()
implicit val ec = system.dispatcher
val server = Http().bind(interface = "localhost", port = 9191)
val greeterWebSocketService = Flow[Message]
.mapConcat {
// we match but don't actually consume the text message here,
// rather we simply stream it back as the tail of the response
// this means we might start sending the response even before the
// end of the incoming message has been received
case tm: TextMessage => TextMessage(Source.single("Hello ") ++ tm.textStream) :: Nil
case bm: BinaryMessage =>
// ignore binary messages but drain content to avoid the stream being clogged
bm.dataStream.runWith(Sink.ignore)
Nil
}
val requestHandler: HttpRequest => HttpResponse = {
case HttpRequest(GET, Uri.Path("/ping"), _, _, _) => HttpResponse(entity = "PONG!")
case req @ HttpRequest(GET, Uri.Path("/greeter"), _, _, _) => req.header[UpgradeToWebSocket] match {
case Some(upgrade) => {
println("I recognized the WebSocket")
HttpResponse(200, entity = "YES!")
// println(upgrade.toString());
// upgrade.handleMessages(greeterWebSocketService)
}
case None =>
println("Hit the WebSocket endpoint, but didn't recognize the headers")
println(req.headers.toString())
HttpResponse(400, entity = "Something didn't work!")
}
}
val bind: Future[Http.ServerBinding] = server.to(Sink.foreach {
conn => println("Accepting new connection")
conn.handleWithSyncHandler(requestHandler)
}).run()
}
I test the WebSocket server using the following cURL command:
curl -i -N -H "Connection: Upgrade" -H "Upgrade: websocket" -H "Host: localhost:9191" -H "Origin: http://localhost:9191" http://localhost:9191/greeter
Whenever I run this I hit the 400
expected when the request to /greeter
cannot find the Upgrade headers. This is the headers printed to console:
List(Timeout-Access: <function1>, Host: localhost:91, User-Agent: curl/7.68.0, Accept: */*, Connection: Upgrade, Upgrade: websocket, Origin: http://localhost:91)
According to Mozilla, this is supposed to be the correct headers info to upgrade to a WebSockets connection, but it doesn’t seem to work here.
Note that the same command (with URLs switched, of course) worked to test the endpoint on the WebSockets.org website. I don’t know why the header[UpgradeToWebSocket] returns
None`.