Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Error during WebSocket handshake in Chrome #4900

Closed
lucaro opened this issue May 21, 2020 · 2 comments
Closed

Error during WebSocket handshake in Chrome #4900

lucaro opened this issue May 21, 2020 · 2 comments

Comments

@lucaro
Copy link

lucaro commented May 21, 2020

Jetty version

jetty-9.4.25.v20191220; built: 2019-12-20T17:00:00.294Z; git: a9729c7

Java version

jvm 11.0.6+8-b765.40

OS type/version

Windows 10 version 19.09

Description

(Note: I first posted this issue in the Javalin project since I observed it in that context, see javalin/javalin#975. Some further investigation indicated however that the issue is independent of Javalin, so I'm posting it here again.)

The problem is that I'm unable to establish a wss connection to a javalin endpoint using Chrome. Wss works as expected in Firefox and http, https and ws connections work without any issue in both browsers. Chrome does however always return the following error message: Error during WebSocket handshake: Invalid status line

The error is independent of the used keystore, I tried it with a self-signed cert via localhost and via a cert validated through Let's Encrypt and a proper domain, both with the exact same behavior. It works fine without SSL. It also does not work without HTTP2 and with SSL, the other way around is not testable since Chrome does not do HTTP2 over unencrypted connections. The error does however change to Connection closed before receiving a handshake response when not using HTTP2. Below is a minimal working example (in Kotlin) to reproduce the problem. I used https://www.websocket.org/echo.html to test the websocket.

import org.eclipse.jetty.alpn.server.ALPNServerConnectionFactory
import org.eclipse.jetty.http2.HTTP2Cipher
import org.eclipse.jetty.http2.server.HTTP2ServerConnectionFactory
import org.eclipse.jetty.server.*
import org.eclipse.jetty.util.ssl.SslContextFactory
import org.eclipse.jetty.websocket.api.Session
import org.eclipse.jetty.websocket.api.annotations.*
import org.eclipse.jetty.websocket.server.WebSocketHandler
import org.eclipse.jetty.websocket.servlet.WebSocketServletFactory
import java.io.IOException


object WSTestJetty {

    @JvmStatic
    fun main(args: Array<String>) {

        val server = setupHttpServer()
        val wsHandler: WebSocketHandler = object : WebSocketHandler() {
            override fun configure(factory: WebSocketServletFactory) {
                factory.register(MyWebSocketHandler::class.java)
            }
        }
        server.handler = wsHandler
        server.start()
        server.join()

    }

    private fun setupHttpServer(): Server {

        val httpConfig = HttpConfiguration().apply {
            sendServerVersion = false
            sendXPoweredBy = false
            secureScheme = "https"
            securePort = 443
        }

        val httpsConfig = HttpConfiguration(httpConfig).apply {
            addCustomizer(SecureRequestCustomizer())
        }

        val alpn = ALPNServerConnectionFactory().apply {
            defaultProtocol = "http" //"h2"
        }

        val sslContextFactory = SslContextFactory.Server().apply {
            keyStorePath = "keystore.jks"
            setKeyStorePassword("password")
            //cipherComparator = HTTP2Cipher.COMPARATOR
            provider = "Conscrypt"
        }

        val ssl = SslConnectionFactory(sslContextFactory, alpn.protocol)

        val http2 = HTTP2ServerConnectionFactory(httpsConfig)

        val fallback = HttpConnectionFactory(httpsConfig)


        return Server().apply {

            addConnector(ServerConnector(server, HttpConnectionFactory(httpConfig), HTTP2ServerConnectionFactory(httpConfig)).apply {
                port = 80
            })

            addConnector(ServerConnector(server, ssl, alpn, http2, fallback).apply {
                port = 443
            })
        }
    }
}

@WebSocket
class MyWebSocketHandler {
    @OnWebSocketClose
    fun onClose(statusCode: Int, reason: String) {
        println("Close: statusCode=$statusCode, reason=$reason")
    }

    @OnWebSocketError
    fun onError(t: Throwable) {
        println("Error: " + t.message)
    }

    @OnWebSocketConnect
    fun onConnect(session: Session) {
        println("Connect: ${session.remoteAddress.address}")
        try {
            session.getRemote().sendString("Hello Webbrowser")
        } catch (e: IOException) {
            e.printStackTrace()
        }
    }

    @OnWebSocketMessage
    fun onMessage(message: String) {
        println("Message: $message")
    }
}
@joakime
Copy link
Contributor

joakime commented May 21, 2020

Bootstrapping WebSocket over HTTP/2 (RFC8441) was added to Jetty 10.0.x

See #3537

Jetty 10.0.0.alpha2 contains support for WebSocket over HTTP/2.

Jetty 9.4.x does not.

@lucaro
Copy link
Author

lucaro commented May 21, 2020

Thanks for the quick reply, explicitly setting the protocol to http/1.1 appears to have solved the problem.

@joakime joakime closed this as completed May 21, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants