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

Websocket RangeError: Invalid WebSocket frame: RSV2 and RSV3 must be clear #1354

Closed
ztnark opened this issue Apr 6, 2018 · 29 comments
Closed

Comments

@ztnark
Copy link

ztnark commented Apr 6, 2018

  • [ x] I've searched for any related issues and avoided creating a duplicate issue.

Description

I'm using ws as a client in my node application, and when I attempt to connect to the host, the connection is closed with the error 'Websocket RangeError: Invalid WebSocket frame: RSV2 and RSV3 must be clear'.

It is worth noting that the connection is successful to a different host, but I believe this is a client issue?

It's also worth noting that faye-websocket functions correctly in the same situation.

Reproducible in:

version: 5.1.1
Node.js version(s): 8.9.4
OS version(s): Windows 7 Professional v6.1 (build 7601)

Steps to reproduce:

  1. Run the following code inside of a Node Application:
const WebSocket = require('ws')

const ws = new WebSocket('ws://site:8765/ws')

ws.on('message', data => {
  console.log(data)
})
ws.on('error', err => { console.log(err) })
ws.on('close', () => { console.log('close') })
@lpinca
Copy link
Member

lpinca commented Apr 6, 2018

It might be a bug that has been recently fixed in nodejs/node#17806 but I think not yet backported to Node.js 8.

Try to use Node.js 9. If the issue persists please post the received data on the socket. You can do that by attaching a listener for the 'data' event on the internal socket.

const ws = new WebSocket('ws://site:8765/ws');

ws.on('open', function () {
  ws._socket.on('data', console.log);
});

@lpinca
Copy link
Member

lpinca commented Apr 10, 2018

@ztnark any update?

@jeroenvollenbrock
Copy link

Should be resolved in the next Node.js 8 version as well, presumably 8.11.2 :)

@ztnark
Copy link
Author

ztnark commented Apr 11, 2018 via email

@ztnark
Copy link
Author

ztnark commented Apr 12, 2018

@lpinca, good news. I can confirm that upgrading to Node.js 9 (11.1) fixes this issue. I can also test on 8.11.2, when released. Thank you.

@lpinca
Copy link
Member

lpinca commented Apr 13, 2018

I'm closing then. Please reopen if the issue persists.
Thank you.

@danielmurray
Copy link

danielmurray commented Jun 6, 2018

Howdy all,

As of today, electron is currently running node@8.9.3 in it's main process. So the solution of upgrading the local node version is not a viable option for electron app developers.

Dan

@Turbo87
Copy link

Turbo87 commented Jul 2, 2018

FWIW I'm hitting the same issue with Node 10 too, but I would expect that this is a server-side issue, with a server sending a frame with those flags that should actually not be set.

@ashbrener
Copy link

ashbrener commented Jul 12, 2018

I am experiencing a similar issue on node v8.11.3 and v10.6.0

node_modules/ws/lib/receiver.js:167
      return error(RangeError, 'RSV1 must be clear', true, 1002);
             ^
RangeError: Invalid WebSocket frame: RSV1 must be clear
    at Receiver.getInfo (/node_modules/ws/lib/receiver.js:167:14)
    at Receiver.startLoop (/node_modules/ws/lib/receiver.js:121:22)
    at Receiver._write (/node_modules/ws/lib/receiver.js:69:10)
    at doWrite (_stream_writable.js:397:12)
    at writeOrBuffer (_stream_writable.js:383:5)
    at Receiver.Writable.write (_stream_writable.js:290:11)
    at TLSSocket.socketOnData (/node_modules/ws/lib/websocket.js:795:35)
    at emitOne (events.js:116:13)
    at TLSSocket.emit (events.js:211:7)
    at addChunk (_stream_readable.js:263:12)
    at readableAddChunk (_stream_readable.js:250:11)
    at TLSSocket.Readable.push (_stream_readable.js:208:10)
    at TLSWrap.onread (net.js:597:20)

Have wrapped ws.send() in a try / catch as well as set perMessageDeflate=false in the client constructor options.

@lpinca
Copy link
Member

lpinca commented Jul 12, 2018

Are you getting it on the client or server? If it is on the server it's possible that a malicious client is sending faulty frames intentionally.

If it's on client instead, you can try to log the received data

ws.on('open', function () {
  ws._socket.prependListener('data', function (chunk) {
    console.log(chunk.toString('hex'));
  });
});

@ashbrener
Copy link

@lpinca thank you for the fast response. This advice was most helpful, the error is on the client and I added the listener as you suggested.

This led me to find what I think could be the cause. The server (which also uses ws) is aborting the handshake based on a certain condition (a custom override in our app) using a copy of the abortHandshake function.

And so it is writing the following data to the socket:

HTTP/1.1 401 Unauthorized
Connection: close
Content-type: text/html
Content-Length: 12

Unauthorized

... which the client receives and throws the RangeError on. Does this make sense?

@lpinca
Copy link
Member

lpinca commented Jul 12, 2018

No not really, that should not trigger the 'upgrade' event on the client, so you should only get an error with a message like "Unexpected server response...".

@lpinca
Copy link
Member

lpinca commented Jul 12, 2018

Are you able to create a reproducible test case? If so please do it so I can investigate.

@lpinca
Copy link
Member

lpinca commented Jul 12, 2018

Or are you writing that on the socket after the 101 response? In this case, yes, it makes sense.

@ashbrener
Copy link

Its quite possible this is after the 101, my class inherits from WebSocket.Server and overrides the handleUpgrade function to inspect the client IP address and abort the handshake if unrecognized.

@lpinca
Copy link
Member

lpinca commented Jul 12, 2018

You probably have your own reasons to do that (overriding handleUpgrade) but the verifyClient option can be used for this.

@tgraupmann
Copy link

tgraupmann commented Feb 7, 2021

FYI, this issue still reproduces on both WSL2 and Windows 10 for Node v14.15.4.

I have a sample client that can crash a WS server instantly.

I'm using pretty much the minimal sample - Server: https://github.com/tgraupmann/NodeJS_WS/blob/master/app.js

This worked fine sending JSON payloads. I'm testing a C client with binary payloads which crash the server.

C:\Public\NodeJS_WS>node app.js
events.js:292
      throw er; // Unhandled 'error' event
      ^

RangeError: Invalid WebSocket frame: RSV2 and RSV3 must be clear
    at Receiver.getInfo (C:\Public\NodeJS_WS\node_modules\←[4mws←[24m\lib\receiver.js:171:14)
    at Receiver.startLoop (C:\Public\NodeJS_WS\node_modules\←[4mws←[24m\lib\receiver.js:131:22)
    at Receiver._write (C:\Public\NodeJS_WS\node_modules\←[4mws←[24m\lib\receiver.js:78:10)
←[90m    at writeOrBuffer (internal/streams/writable.js:358:12)←[39m
←[90m    at Receiver.Writable.write (internal/streams/writable.js:303:10)←[39m
    at Socket.socketOnData (C:\Public\NodeJS_WS\node_modules\←[4mws←[24m\lib\websocket.js:900:35)
←[90m    at Socket.emit (events.js:315:20)←[39m
←[90m    at addChunk (internal/streams/readable.js:309:12)←[39m
←[90m    at readableAddChunk (internal/streams/readable.js:284:9)←[39m
←[90m    at Socket.Readable.push (internal/streams/readable.js:223:10)←[39m
Emitted 'error' event on WebSocket instance at:
    at Receiver.receiverOnError (C:\Public\NodeJS_WS\node_modules\←[4mws←[24m\lib\websocket.js:805:13)
←[90m    at Receiver.emit (events.js:315:20)←[39m
←[90m    at emitErrorNT (internal/streams/destroy.js:106:8)←[39m
←[90m    at emitErrorCloseNT (internal/streams/destroy.js:74:3)←[39m
←[90m    at processTicksAndRejections (internal/process/task_queues.js:80:21)←[39m {
  [←[32mSymbol(status-code)←[39m]: ←[33m1002←[39m
}

C:\Public\NodeJS_WS>node --version
v14.15.4

Also reproduced initially on WSL2.

RangeError: Invalid WebSocket frame: RSV2 and RSV3 must be clear
    at Receiver.getInfo (/home/tgraupmann/NodeJS_WS/node_modules/ws/lib/receiver.js:171:14)
    at Receiver.startLoop (/home/tgraupmann/NodeJS_WS/node_modules/ws/lib/receiver.js:131:22)
    at Receiver._write (/home/tgraupmann/NodeJS_WS/node_modules/ws/lib/receiver.js:78:10)
    at doWrite (_stream_writable.js:415:12)
    at writeOrBuffer (_stream_writable.js:399:5)
    at Receiver.Writable.write (_stream_writable.js:299:11)
    at Socket.socketOnData (/home/tgraupmann/NodeJS_WS/node_modules/ws/lib/websocket.js:900:35)
    at Socket.emit (events.js:198:13)
    at addChunk (_stream_readable.js:288:12)
    at readableAddChunk (_stream_readable.js:269:11)
Emitted 'error' event at:
    at Receiver.receiverOnError (/home/tgraupmann/NodeJS_WS/node_modules/ws/lib/websocket.js:805:13)
    at Receiver.emit (events.js:198:13)
    at errorOrDestroy (internal/streams/destroy.js:107:12)
    at onwriteError (_stream_writable.js:430:5)
    at onwrite (_stream_writable.js:461:5)
    at Receiver.startLoop (/home/tgraupmann/NodeJS_WS/node_modules/ws/lib/receiver.js:152:5)
    at Receiver._write (/home/tgraupmann/NodeJS_WS/node_modules/ws/lib/receiver.js:78:10)
    [... lines matching original stack trace ...]
    at Receiver.Writable.write (_stream_writable.js:299:11)
tgraupmann@polaris:~/NodeJS_WS$ node --version
v10.19.0

The offending C client code looks like this:

_libwsclient_write(_sWebSocketClient, refFrame._mData, FChromaStreamRefFrame_DATA_SIZE);

The buffer is a simple unsigned char* and I'm passing the size. 

This is the client library that I'm using - https://github.com/tgraupmann/libwsclient

It's entirely that I'm using the websocket API incorrectly, and will take any recommendations for a better client that handles binary payloads in pure "C".

That said, it's still able to crash the server though its incorrect use.

I also have a direct repro for any that are interested in following up.

@lpinca
Copy link
Member

lpinca commented Feb 7, 2021

'error' events must be handled.

wss.on('connection', function(ws) {
  ws.on('error', console.error);
});

@tgraupmann
Copy link

tgraupmann commented Feb 7, 2021

'error' events must be handled.

wss.on('connection', function(ws) {
  ws.on('error', console.error);
});

That's all it was. The server just needed an error handler to avoid crashing. And now it logs errors without exiting. Thanks!

@o0101
Copy link

o0101 commented Oct 23, 2022

I'm seeing this in node v18.8.0 when connecting from a Safari client (latest Safari, latest iOS 16.3). Hard to reproduce: it does not happen on first connect, but after reloading the page. Yet after a couple of reloads it works, then breaks again.

Sorry could not provide more details right now. Weird bug...don't even know if it comes from the ws library, just posting here because I found this issue while searching for answers

More details

It's probably been covered in this issue or elsewhere in ws already but I went looking around and it seems:

According to RFC6455:

RSV1, RSV2, RSV3: 1 bit each

MUST be 0 unless an extension is negotiated that defines meanings for non-zero values. If a nonzero value is received and none of the negotiated extensions defines the meaning of such a nonzero value, the receiving endpoint MUST Fail the WebSocket Connection.

So, at least in my case, the Safari client seems to be incorrectly (?) setting these bits.

It may the wrong fit for ws (and there may be valid security concerns around not doing this), but a possible guide here might be the robustness principle: "be conservative in what you send, be liberal in what you accept"

If that could be possible, then adding a ws API (maybe in the form of an optional strictProtocol flag), like:

 const wss = new WebSocketServer({
      server,
      perMessageDeflate: false, 
      strictProtocol: false,
    });

to permit these "non-conforming" messages could be a way to still serve clients that make these errors.

@o0101
Copy link

o0101 commented Oct 23, 2022

'error' events must be handled.

wss.on('connection', function(ws) {
  ws.on('error', console.error);
});

That's all it was. The server just needed an error handler to avoid crashing. And now it logs errors without exiting. Thanks!

@tgraupmann I have an error handler, but my websocket closes after showing this RangeError.

@lpinca
Copy link
Member

lpinca commented Oct 23, 2022

@crisdosyago the spec specifically says that

the receiving endpoint MUST Fail the WebSocket Connection.

I'm strongly against adding a strictProtocol option or anything like that. A lot of WebSocket implementation do not even inform the user of the error. They just close the connection silently.

Also, it is not the first time that Safari introduces bugs like this. See #1922.

@o0101
Copy link

o0101 commented Oct 23, 2022

Understood @lpinca I know you don't want to dirty this library with irregularities or tolerances of those. Thank you for your consideration of this idea, it sounds like you've given this some serious thought and like you believe it's the right choice to not include here because doing so would mean your library would no longer meet the standard of quality you're trying to keep. I trust you. Thank you for your information--including on the other bug (how interesting! that Safari!)--and thanks for the amazing work on this great library!

@misi
Copy link

misi commented Dec 8, 2022

I have set proper error handler on the websocket
wss.on('connection', function(ws) { ws.on('error', console.error); });
I experience that in some cases I see the log, but in rear cases
(Probably ws error handler not working properly or removed?) it does not catch the error and node process exits and it kills my app.
running ws version: 8.11.0 & node16

@lpinca can you help me with few hints.
Where should I start to search the root cause of the issue?
Thanks..

node:events:491
      throw er; // Unhandled 'error' event
      ^
RangeError: Invalid WebSocket frame: RSV2 and RSV3 must be clear
    at Receiver.getInfo (/opt/my/src/node_modules/ws/lib/receiver.js:176:14)
    at Receiver.startLoop (/opt/my/src/node_modules/ws/lib/receiver.js:136:22)
    at Receiver._write (/opt/my/node_modules/ws/lib/receiver.js:83:10)
    at writeOrBuffer (node:internal/streams/writable:391:12)
    at _write (node:internal/streams/writable:332:10)
    at Receiver.Writable.write (node:internal/streams/writable:336:10)
    at TLSSocket.socketOnData (/opt/my/src/node_modules/ws/lib/websocket.js:1272:35)
    at TLSSocket.emit (node:events:513:28)
    at TLSSocket.emit (node:domain:489:12)
    at addChunk (node:internal/streams/readable:315:12)
Emitted 'error' event on WebSocket instance at:
    at Receiver.receiverOnError (/opt/my/src/node_modules/ws/lib/websocket.js:1158:13)
    at Receiver.emit (node:events:513:28)
    at Receiver.emit (node:domain:489:12)
    at emitErrorNT (node:internal/streams/destroy:157:8)
    at emitErrorCloseNT (node:internal/streams/destroy:122:3)
    at processTicksAndRejections (node:internal/process/task_queues:83:21) {
  code: 'WS_ERR_UNEXPECTED_RSV_2_3',
  [Symbol(status-code)]: 1002
}

@lpinca
Copy link
Member

lpinca commented Dec 8, 2022

Probably ws error handler not working properly or removed?

Yes, make sure that the 'error' event listener is not removed before the 'close' event is emitted.

@misi
Copy link

misi commented Dec 12, 2022

Yes removed error handler in tear down, and it caused it. Thanks!

@marcolanaro
Copy link

@misi Can you please provide a code example or reference for this use case?

@atanasyordanov21
Copy link

I was also struggling with such an issue for some time. Tried several different web socket clients, and always the same. Then I started breaking down my code, and it turned out that the following line was causing the issue in my case:
request.socket.setEncoding("utf8");
request comes from here:

wss.on('connection', (ws, request) => {
...
});

@smallCF
Copy link

smallCF commented Jul 13, 2023

Hello author;

Using node as a program to use ws, will inconsistent time between the user and server in the same time zone result in the issue of RSV2 and RSV3 must be clear?

The node service on my end is installed using the Docker method

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