eclipse-vertx / eclipse-vertx/vert.x

Manual Websocket Upgrade Fails Due to Client Headers w/ (inc. modern browsers)

Open
#4,511 0 comments 0 reactions 0 assignees View on GitHub
bug
Dominant language
Java
Stars
14.7k
Forks
2.1k
Avg merge
2d 7h
Merged PRs (30d)
28

Description

### Version
4.3.4

### Context

I have to validate a WebSocket request before the connection is fully opened. To do this I create a handler, pause the request, do my authentication, and then upgrade the request to a websocket.

This seems to work okay, but I am seeing (full stack trace at bottom):

```
io.netty.handler.codec.http.websocketx.WebSocketHandshakeException: Invalid connection header
```

This is due to:

```
private WebSocketServerHandshaker createHandshaker(Http1xServerRequest request) throws WebSocketHandshakeException {
// As a fun part, Firefox 6.0.2 supports Websockets protocol '7'. But,
// it doesn't send a normal 'Connection: Upgrade' header. Instead it
// sends: 'Connection: keep-alive, Upgrade'. Brilliant.
String connectionHeader = request.getHeader(io.vertx.core.http.HttpHeaders.CONNECTION);
if (connectionHeader == null || !connectionHeader.toLowerCase().contains("upgrade")) {
request.response()
.setStatusCode(BAD_REQUEST.code())
.end("\"Connection\" header must be \"Upgrade\".");
throw new WebSocketHandshakeException("Invalid connection header");
}
```

So it seems like we are validating the client headers on handshake. Makes sense. But I already know I want the connection to be a websocket. Is it possible to just force upgrade the connection?

The client does not look like some old out dated browser. It's not entirely clear to me why they would be omitted. Here's the headers from one failure:

```
headers: Pragma:no-cache
User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 Safari/537.36
Origin:https://oursite.com
Sec-WebSocket-Version:13
Accept-Encoding:gzip,deflate
Accept-Language:en-AU,en-GB;q=0.9,en-US;q=0.8,en;q=0.7
Sec-WebSocket-Key:randomstuffhere
Sec-WebSocket-Extensions:permessage-deflate; client_max_window_bits
Host:ws.oursite.com
Cache-Control:no-cache
Connection:keep-alive
```

The client is just doing `new Websocket()`.

There is no proxy that would be stripping the headers. The domain routes directly to the box right now, with vertx handling TLS.

### Do you have a reproducer?

Will do if confirmed is even possible to fix... Doing something like this:

```
router.get("/sub").handler((routeContext) -> {
routeContext.request().pause();
final var future = routeContext
.request().toWebSocket();

authenticateAsync(() -> {
future
.onFailure((e) -> {
StringBuilder headers = new StringBuilder();
var headersMap = request.headers();
for (var entry : headersMap) {
headers.append(entry.getKey()).append(":").append(entry.getValue()).append("\n");
}
System.err.println("Failure to handle websocket route? " + request.absoluteURI() + " headers: " + headers);
e.printStackTrace();
if (!routeContext.response().ended()) {
routeContext.end();
}
})
.onSuccess(websocket -> {});
})
});
```

### Steps to reproduce

1. Handle websocket connection via route.
2. Upgrade connection manually.
3. Sadness

### Extra

Server runs Debian Linux 11 and Adoptium JRE 19.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.