question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

[ws] Any effect you like, as long as it's IO

See original GitHub issue

We seem to make an assumption that we’re running in IO, based on the hardcoded key here.

This can be exposed if your routes are defined in an effect that differs from the effect the server is running in, as via the liftR operation provided by natchez-http4s, which turns an HttpRoutes[Kleisli[F, Span[F], *]] into an HttpRoutes[F] via a pair of natural transformations. A simplified version here exhibits the same problem:

package test

import cats._
import cats.data.Kleisli
import cats.effect._
import org.http4s.HttpRoutes
import org.http4s.blaze.server.BlazeServerBuilder
import org.http4s.dsl.Http4sDsl
import org.http4s.implicits._
import org.http4s.server.websocket.WebSocketBuilder
import org.http4s.websocket.WebSocketFrame.Text
import scala.concurrent.ExecutionContext

object Test extends IOApp.Simple {

  type K[A] = Kleisli[IO,Int,A]

  // Our routes run in Kleisli
  val routes: HttpRoutes[K] = {
    object dsl extends Http4sDsl[K]; import dsl._
    HttpRoutes.of[K] {
      case GET -> Root / "ws" =>
        WebSocketBuilder[K].build(_.map(f => Text(s"You said $f")))
    }
  }

  // We can unlift our routes back to IO
  def unlift(routes: HttpRoutes[K]): HttpRoutes[IO] =
    Kleisli { ioReq =>
      val io2k: IO ~> K = Kleisli.liftK
      val k2io: K ~> IO = Kleisli.applyK(42)
      routes.run(ioReq.mapK(io2k)).mapK(k2io).map(_.mapK(k2io))
    }

  // Main app and server are in IO
  def run: IO[Unit] =
    BlazeServerBuilder[IO](ExecutionContext.global)
      .withHttpApp(unlift(routes).orNotFound)
      .bindHttp(8080, "0.0.0.0")
      .resource
      .use(_ => IO.never)

}

Using websocat to connect and send a message yields

$ websocat ws://localhost:8080/ws
hi

websocat: WebSocketError: I/O failure
websocat: error running
$

And over on the console we see

[INFO] org.http4s.blaze.channel.nio1.NIO1SocketServerGroup - Service bound to address /0:0:0:0:0:0:0:0:8080
[INFO] org.http4s.blaze.server.BlazeServerBuilder -
  _   _   _        _ _
 | |_| |_| |_ _ __| | | ___
 | ' \  _|  _| '_ \_  _(_-<
 |_||_\__|\__| .__/ |_|/__/
             |_|
[INFO] org.http4s.blaze.server.BlazeServerBuilder - http4s v0.15.1 on blaze v0.15.1 started at http://[::]:8080/
[INFO] org.http4s.blaze.channel.nio1.NIO1SocketServerGroup - Accepted connection from /0:0:0:0:0:0:0:1:55117
[ERROR] org.http4s.blaze.server.WSFrameAggregator - Exception caught when attempting inbound command
java.lang.ClassCastException: class cats.data.Kleisli cannot be cast to class cats.effect.IO (cats.data.Kleisli and cats.effect.IO are in unnamed module of loader 'app')
	at cats.effect.IO$$anon$2.attempt(IO.scala:1268)
	at cats.syntax.ApplicativeErrorOps$.attempt$extension(applicativeError.scala:52)
	at org.http4s.blazecore.websocket.Http4sWSStage.stageStartup(Http4sWSStage.scala:174)
	at org.http4s.blaze.pipeline.Stage.inboundCommand(Stages.scala:85)
	at org.http4s.blaze.pipeline.Stage.inboundCommand$(Stages.scala:83)
	at org.http4s.blazecore.websocket.Http4sWSStage.inboundCommand(Http4sWSStage.scala:44)
	at org.http4s.blaze.pipeline.Head.sendInboundCommand(Stages.scala:268)
	at org.http4s.blaze.pipeline.Head.sendInboundCommand$(Stages.scala:264)
	at org.http4s.blaze.server.WSFrameAggregator.sendInboundCommand(WSFrameAggregator.scala:32)
	at org.http4s.blaze.pipeline.Head.inboundCommand(Stages.scala:282)
	at org.http4s.blaze.pipeline.Head.inboundCommand$(Stages.scala:280)
	at org.http4s.blaze.server.WSFrameAggregator.inboundCommand(WSFrameAggregator.scala:32)
	at org.http4s.blaze.pipeline.Head.sendInboundCommand(Stages.scala:268)
	at org.http4s.blaze.pipeline.Head.sendInboundCommand$(Stages.scala:264)
	at org.http4s.blaze.server.WebSocketDecoder.sendInboundCommand(WebSocketDecoder.scala:25)
	at org.http4s.blaze.pipeline.Head.inboundCommand(Stages.scala:282)
	at org.http4s.blaze.pipeline.Head.inboundCommand$(Stages.scala:280)
	at org.http4s.blaze.server.WebSocketDecoder.inboundCommand(WebSocketDecoder.scala:25)
	at org.http4s.blaze.pipeline.Head.sendInboundCommand(Stages.scala:268)
	at org.http4s.blaze.pipeline.Head.sendInboundCommand$(Stages.scala:264)
	at org.http4s.blazecore.IdleTimeoutStage.sendInboundCommand(IdleTimeoutStage.scala:29)
	at org.http4s.blaze.pipeline.Tail.replaceTail(Stages.scala:193)
	at org.http4s.blaze.pipeline.Tail.replaceTail$(Stages.scala:172)
	at org.http4s.blaze.server.Http1ServerStage.replaceTail(Http1ServerStage.scala:86)
	at org.http4s.blaze.server.WebSocketSupport.$anonfun$renderResponse$7(WebSocketSupport.scala:109)
	at scala.concurrent.impl.Promise$Transformation.run(Promise.scala:448)
	at java.base/java.util.concurrent.ForkJoinTask$RunnableExecuteAction.exec(ForkJoinTask.java:1426)
	at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:290)
	at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1020)
	at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1656)
	at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1594)
	at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:183)
[ERROR] org.http4s.blaze.channel.nio1.NIO1HeadStage - Abnormal NIO1HeadStage termination
java.lang.ClassCastException: class cats.data.Kleisli cannot be cast to class cats.effect.IO (cats.data.Kleisli and cats.effect.IO are in unnamed module of loader 'app')
	at cats.effect.IO$$anon$2.attempt(IO.scala:1268)
	at cats.syntax.ApplicativeErrorOps$.attempt$extension(applicativeError.scala:52)
	at org.http4s.blazecore.websocket.Http4sWSStage.stageStartup(Http4sWSStage.scala:174)
	at org.http4s.blaze.pipeline.Stage.inboundCommand(Stages.scala:85)
	at org.http4s.blaze.pipeline.Stage.inboundCommand$(Stages.scala:83)
	at org.http4s.blazecore.websocket.Http4sWSStage.inboundCommand(Http4sWSStage.scala:44)
	at org.http4s.blaze.pipeline.Head.sendInboundCommand(Stages.scala:268)
	at org.http4s.blaze.pipeline.Head.sendInboundCommand$(Stages.scala:264)
	at org.http4s.blaze.server.WSFrameAggregator.sendInboundCommand(WSFrameAggregator.scala:32)
	at org.http4s.blaze.pipeline.Head.inboundCommand(Stages.scala:282)
	at org.http4s.blaze.pipeline.Head.inboundCommand$(Stages.scala:280)
	at org.http4s.blaze.server.WSFrameAggregator.inboundCommand(WSFrameAggregator.scala:32)
	at org.http4s.blaze.pipeline.Head.sendInboundCommand(Stages.scala:268)
	at org.http4s.blaze.pipeline.Head.sendInboundCommand$(Stages.scala:264)
	at org.http4s.blaze.server.WebSocketDecoder.sendInboundCommand(WebSocketDecoder.scala:25)
	at org.http4s.blaze.pipeline.Head.inboundCommand(Stages.scala:282)
	at org.http4s.blaze.pipeline.Head.inboundCommand$(Stages.scala:280)
	at org.http4s.blaze.server.WebSocketDecoder.inboundCommand(WebSocketDecoder.scala:25)
	at org.http4s.blaze.pipeline.Head.sendInboundCommand(Stages.scala:268)
	at org.http4s.blaze.pipeline.Head.sendInboundCommand$(Stages.scala:264)
	at org.http4s.blazecore.IdleTimeoutStage.sendInboundCommand(IdleTimeoutStage.scala:29)
	at org.http4s.blaze.pipeline.Tail.replaceTail(Stages.scala:193)
	at org.http4s.blaze.pipeline.Tail.replaceTail$(Stages.scala:172)
	at org.http4s.blaze.server.Http1ServerStage.replaceTail(Http1ServerStage.scala:86)
	at org.http4s.blaze.server.WebSocketSupport.$anonfun$renderResponse$7(WebSocketSupport.scala:109)
	at scala.concurrent.impl.Promise$Transformation.run(Promise.scala:448)
	at java.base/java.util.concurrent.ForkJoinTask$RunnableExecuteAction.exec(ForkJoinTask.java:1426)
	at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:290)
	at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1020)
	at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1656)
	at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1594)
	at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:183)

Thanks @ChristopherDavenport for helping me track this down.

Issue Analytics

  • State:closed
  • Created 2 years ago
  • Comments:9 (9 by maintainers)

github_iconTop GitHub Comments

2reactions
RaasAhsancommented, Sep 21, 2021

I’ll start a github discussion sometime in the next few days. I’ve started work on the websocket client for ember and I think it’ll be worth looking at this holistically

1reaction
tpolecatcommented, Aug 26, 2021

image

Read more comments on GitHub >

github_iconTop Results From Across the Web

WebSockets vs. HTTP: Comparing pros and cons
An overview of the HTTP and WebSocket protocols, including their pros and cons, and the best use cases for each protocol.
Read more >
node.js - Socket IO The connection to ws://someAddress was ...
I cloned the project and it works now. I am not sure why I am getting that error message but it doesn't seem...
Read more >
socketio/engine.io - GitHub
The engine used in the Socket.IO JavaScript server, which manages the low-level transports such as HTTP long-polling and WebSocket.
Read more >
Client Initialization | Socket.IO
IO client library, you can now init the client. ... Note: You can use either https or wss (respectively, http or ws )....
Read more >
The Worksheet Class — XlsxWriter Documentation
As explained above, the write() method maps basic Python types to corresponding Excel types. If you want to write an unsupported type then...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found