Search code examples
kotlinsocketswebsocketnetty

Netty wss socket client drops connection


Trying to setup basic wss client. Channel is activated, but then immediately disconnected without any exception.

Client:

class WebSocketClient(val uri: String) {

    lateinit var ch: Channel

    fun connect() {
        val bootstrap = Bootstrap()
        val uri: URI = URI.create(uri)
        val handler = WebSocketClientHandler(WebSocketClientHandshakerFactory.newHandshaker(uri, WebSocketVersion.V13, null, false, HttpHeaders.EMPTY_HEADERS, 1280000))

        bootstrap.group(NioEventLoopGroup())
                .channel(NioSocketChannel::class.java)
                .handler(object : ChannelInitializer<SocketChannel>() {
                    override fun initChannel(ch: SocketChannel) {
                        val pipeline = ch.pipeline()
                        pipeline.addLast("http-codec", HttpClientCodec())
                        pipeline.addLast("aggregator", HttpObjectAggregator(65536))
                        pipeline.addLast("ws-handler", handler)
                    }
                })
        ch = bootstrap.connect(uri.host, 443).sync().channel()
        handler.channelPromise.sync()
    }
}

Handler:

class WebSocketClientHandler(val handShaker: WebSocketClientHandshaker) : SimpleChannelInboundHandler<Any>() {

    lateinit var channelPromise: ChannelPromise

    override fun handlerAdded(ctx: ChannelHandlerContext) {
        channelPromise = ctx.newPromise()
    }

    override fun channelActive(ctx: ChannelHandlerContext) {
        handShaker.handshake(ctx.channel())
    }

    override fun channelRead0(ctx: ChannelHandlerContext, msg: Any) {
        val ch = ctx.channel()
        if (!handShaker.isHandshakeComplete) {
            handShaker.finishHandshake(ch, msg as FullHttpResponse)
            channelPromise.setSuccess()
            return
        }

        val frame = msg as WebSocketFrame
        if (frame is TextWebSocketFrame) {
            println("text message: $frame")
        } else if (frame is PongWebSocketFrame) {
            println("pont message")
        } else if (frame is CloseWebSocketFrame) {
            ch.close()
        } else {
            println("unhandled frame: $frame")
        }
    }
}

The flow of handler calls:

handleAdded
channelRegistered
channelActive
channelReadComplete
channelInactive
channelUnregistered
handlerRemoved

Is there something I miss?


Solution

  • You forgot to add a SSLHandler, this handler is needed because you are connecting to the https port (443), so the remote server expects all the traffic to be encrypted. Sending a unencrypted message to the https port has undefined behaviour, some server will shut down your connection, other servers will send a redirect back to https.

    You can add an sslhandler using the following way:

    java:

    final SslContext sslCtx = SslContextBuilder.forClient()
    //  .trustManager(InsecureTrustManagerFactory.INSTANCE)
        .build();
    
    pipeline.addLast("ssl-handler", sslCtx.newHandler(ch.alloc(), url.getHost(), 443));
    
    // Your remaining code....
    pipeline.addLast("http-codec", new HttpClientCodec())