ws: Revisiting Invalid WebSocket frame: RSV1 must be clear

Is there an existing issue for this?

  • I’ve searched for any related issues and avoided creating a duplicate issue.

Description

There are a few other issues for this, but it seems that in all of them, the answer has been, “The client sent bad data, so the error is expected.” However, after doing some testing with several standard web browsers, unless I am missing something very basic, this is difficult to believe.

My server code is dead simple:

const {WebSocketServer} = require("ws");
const port = 8080;

const server = new WebSocketServer({
	port: port
});

server.on('connection', function(sock) {
	sock.on('message', function(data) {
		console.log('--- message ---');
		console.log(data);
	});

	sock.on("error", function(error) {
		console.log("--- error ---");
		console.log(error);
	});

	console.log("--- connection ---");

	sock.send("hello");
});

console.log(`waiting for connections on port ${port}...`);

I’ve opened the JS console in Chrome, Safari, and Firefox and typed these three commands with identical results:

let sock = new WebSocket('ws://localhost:8080');
console.log(sock.extensions);
sock.send("hello");

sock.extensions is empty, but the logging results on the server are:

--- connection ---
--- error ---
RangeError: Invalid WebSocket frame: RSV1 must be clear
	etc.

So I just want to clarify that all three of these browsers on Mac have a bad websocket implementation? Or am I missing something fundamental?

ws version

8.11.0

Node.js Version

v17.1.0

System

System: OS: macOS 13.0.1 CPU: (4) x64 Intel® Core™ i5-8210Y CPU @ 1.60GHz Memory: 50.64 MB / 8.00 GB Shell: 5.8.1 - /bin/zsh

Expected result

A message from a standard browser’s WebSocket implementation would function properly

Actual result

Each browsers caused an error in the ws library

Attachments

No response

About this issue

  • Original URL
  • State: closed
  • Created 2 years ago
  • Reactions: 2
  • Comments: 19 (4 by maintainers)

Most upvoted comments

Thanks @timschwab for pointing us in the right direction. Just as a reference for others you are banging their heads on this issue. We had a similar issue and it turned out to be the Parental Control was enabled on the laptop which hijacked port 8080 (https://discussions.apple.com/thread/250500940). Conclusion: don’t use 8080 on mac osx…

@mattvb91 Hey, I did eventually find a solution - use a port other than 8080.

I still don’t know why that port did not work, while others did. My theory is that Mac Docker was doing something weird with it that caused it to exhibit the behavior I found above, but once I found I could simply switch ports, I stopped investigating.

Okay, finally did that. I used tcpdump to capture this browser code:

let sock = new WebSocket("ws://localhost:8080");
sock.onopen = function() {
	sock.send('"test"');
};

So, simply opening the connection and sending a single message. The results of the tcpdump:

sh-3.2# tcpdump -i lo0 port 8080
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on lo0, link-type NULL (BSD loopback), snapshot length 524288 bytes
16:04:03.064671 IP6 localhost.56285 > localhost.http-alt: Flags [S], seq 177896051, win 65535, options [mss 16324,nop,wscale 6,nop,nop,TS val 3213282305 ecr 0,sackOK,eol], length 0
16:04:03.064821 IP6 localhost.http-alt > localhost.56285: Flags [S.], seq 1374648243, ack 177896052, win 65535, options [mss 16324,nop,wscale 6,nop,nop,TS val 1258666383 ecr 3213282305,sackOK,eol], length 0
16:04:03.064839 IP6 localhost.56285 > localhost.http-alt: Flags [.], ack 1, win 6371, options [nop,nop,TS val 3213282305 ecr 1258666383], length 0
16:04:03.064853 IP6 localhost.http-alt > localhost.56285: Flags [.], ack 1, win 6371, options [nop,nop,TS val 1258666383 ecr 3213282305], length 0
16:04:03.070828 IP6 localhost.56285 > localhost.http-alt: Flags [P.], seq 1:498, ack 1, win 6371, options [nop,nop,TS val 3213282312 ecr 1258666383], length 497: HTTP: GET / HTTP/1.1
16:04:03.070867 IP6 localhost.http-alt > localhost.56285: Flags [.], ack 498, win 6364, options [nop,nop,TS val 1258666390 ecr 3213282312], length 0
16:04:03.075263 IP6 localhost.http-alt > localhost.56285: Flags [P.], seq 1:130, ack 498, win 6364, options [nop,nop,TS val 1258666395 ecr 3213282312], length 129: HTTP: HTTP/1.1 101 Switching Protocols
16:04:03.075293 IP6 localhost.56285 > localhost.http-alt: Flags [.], ack 130, win 6369, options [nop,nop,TS val 3213282317 ecr 1258666395], length 0
16:04:03.114177 IP6 localhost.56285 > localhost.http-alt: Flags [P.], seq 498:995, ack 130, win 6369, options [nop,nop,TS val 3213282356 ecr 1258666395], length 497: HTTP: GET / HTTP/1.1
16:04:03.114210 IP6 localhost.http-alt > localhost.56285: Flags [.], ack 995, win 6356, options [nop,nop,TS val 1258666434 ecr 3213282356], length 0
16:04:03.114227 IP6 localhost.56285 > localhost.http-alt: Flags [P.], seq 995:1007, ack 130, win 6369, options [nop,nop,TS val 3213282356 ecr 1258666434], length 12: HTTP
16:04:03.114231 IP6 localhost.http-alt > localhost.56285: Flags [.], ack 1007, win 6356, options [nop,nop,TS val 1258666434 ecr 3213282356], length 0

So, this seems pretty definitive that the client is somehow sending the GET request again with the first WebSocket message.