[ws] Any effect you like, as long as it's IO
See original GitHub issueWe 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:
- Created 2 years ago
- Comments:9 (9 by maintainers)
Top 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 >Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start FreeTop Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found
Top GitHub Comments
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