tapir
tapir copied to clipboard
[BUG] ZioHttp Websocket get rejected immediately with RSV != 0 and no extension negotiated, RSV:4
Tapir version: 1.9.9
Scala version: 3.3.1
ZIO version: 2.0.21
Sttp version: 3.9.3
Describe the bug
I tried to get Websocket connections working with ZioHttp + ZioStream and Tapir but Netty always resets the connection immediately with the following error message:
12:21:07.001 [KQueueEventLoopGroup-2-2] DEBUG io.netty.handler.codec.http.websocketx.WebSocketServerHandshaker -- WebSocket version 13 server handshake key: phIT2q2DxlxU88VnDYnLZg==, response: T4lGj6WkaEa5P4Sfls/EBzGhmiE=
timestamp=2024-02-21T11:21:07.167283Z level=WARN thread=#zio-fiber-56 message="Fatal exception in Netty" cause="Exception in thread "zio-fiber-" io.netty.handler.codec.http.websocketx.CorruptedWebSocketFrameException: RSV != 0 and no extension negotiated, RSV:4
at io.netty.handler.codec.http.websocketx.WebSocket08FrameDecoder.protocolViolation(WebSocket08FrameDecoder.java:427)
at io.netty.handler.codec.http.websocketx.WebSocket08FrameDecoder.protocolViolation(WebSocket08FrameDecoder.java:423)
at io.netty.handler.codec.http.websocketx.WebSocket08FrameDecoder.decode(WebSocket08FrameDecoder.java:197)
at io.netty.handler.codec.ByteToMessageDecoder.decodeRemovalReentryProtection(ByteToMessageDecoder.java:529)
at io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:468)
at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:290)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:444)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420)
at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412)
at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1410)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:440)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420)
at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:919)
at io.netty.channel.kqueue.AbstractKQueueStreamChannel$KQueueStreamUnsafe.readReady(AbstractKQueueStreamChannel.java:544)
at io.netty.channel.kqueue.AbstractKQueueChannel$AbstractKQueueUnsafe.readReady(AbstractKQueueChannel.java:387)
at io.netty.channel.kqueue.KQueueEventLoop.processReady(KQueueEventLoop.java:218)
at io.netty.channel.kqueue.KQueueEventLoop.run(KQueueEventLoop.java:296)
at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:997)
at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
at java.base/java.lang.Thread.run(Thread.java:1623)"
Sometimes a few messages make it through and I get "Hi" a few times but the connection still crashes almost instantly.
How to reproduce? I created a minimal setup to reproduce the error:
import zio.*
import zio.stream.*
import sttp.capabilities.zio.ZioStreams
import sttp.capabilities.WebSockets
import sttp.tapir.CodecFormat
import sttp.tapir.server.ziohttp.{ZioHttpInterpreter, ZioHttpServerOptions}
import zio.http.{Server, WebSocketConfig}
import sttp.tapir.ztapir.*
object TapirWSTest extends ZIOAppDefault {
val webSocketEndpoint = endpoint
.get
.in("subscription")
.in(isWebSocket)
.out(webSocketBody[String, CodecFormat.TextPlain, String, CodecFormat.TextPlain](ZioStreams))
val testPipeline: (Stream[Throwable, String]) => Stream[Throwable, String] = (
(clientStream: Stream[Throwable, String]) => ZStream.repeatWithSchedule("Hi", Schedule.fixed(2.millis)))
val webSocket: ZServerEndpoint[Any, Any & ZioStreams & WebSockets] = webSocketEndpoint.zServerLogic({
_ => ZIO.succeed(testPipeline)
})
val app = for {
_ <- Server.serve(
ZioHttpInterpreter().toHttp(List(webSocket))
)
} yield ()
override def run: ZIO[Any with ZIOAppArgs with Scope, Any, Any] = app.provide(Server.default)
}
I use PieSocket Websocket Tester or Pythons websocket to test the connection.
Additional information
It actually seems to be a zio-http issue as I could recreate the bug without tapir: https://github.com/zio/zio-http/issues/2691
Thanks! Let's see what happens with the ZIO issue then :)
Could solve the issue. Had nothing to do with tapir or zio-http or even netty but instead with parental controls being activated on my Mac. See the zio-http issue for my solution. Cheers!