-
-
Notifications
You must be signed in to change notification settings - Fork 2.4k
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
Breaking changes #1904
Breaking changes #1904
Conversation
ae5362b
to
fba0658
Compare
I'm not sure if ab7b6d4 makes sense. It is good for performance but adapting existing code to this change might require a little effort. Users have call ws.on('message', function (data, isBinary) {
const message = isBinary ? data : data.toString();
// Continue as before.
});
ws.on('close', function (code, data) {
const reason = data.toString();
// continue as before.
}); If anyone is watching this, please share your opinions. |
I say: emit a ArrayBuffer (or Blob) & follow the spec instead do not emit Buffer or strings, do what is best for cross platform coding, ppl should build there application so they work in Deno or browser the same way if you are planing on making a breaking change anyway, what do you feel about switching to ESM? node "^12.20.0 || ^14.13.0" While we are at it, go for Event and EventTarget that exist in NodeJS now Fyi, Node ships with experimental Blob support now now (v15.7) also, If you switch to ESM then you can use fetch-blob as a polyfill/fallback it only exports ESM (it ditched commonjs) |
@jimmywarting ab7b6d4 is about using
See #1886. I'm fine with that as long as it does not cause issues with bundlers.
This requires Node.js >= 14.5.0 and comes with significant performance regressions. This is currently a no go.
I know and it might make sense to change the default |
b9eb026
to
b60712d
Compare
This is definitely one hell of a breaking change. While i am in favor of removing the "implicit" Have the removal of the |
Yes, especially when using the streaming interface. It prevents the data from being copied twice (from Another similar situation is when sending a text message from a client to another: wss.on('connection', function (ws) {
ws.on('message', function (data, isBinary) {
ws.send(data, { binary: isBinary});
});
});
|
My environment isn't big enough to get a hint about performance (2 ws servers with an average of 100 clients that connect to both node processes), but after testing this branch in the develop environment with no code changes on our base, I've tested it in one of the ws servers without any problem. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm good with the changes!
"browser": "browser.js", | ||
"engines": { | ||
"node": ">=8.3.0" | ||
"node": ">=10.0.0" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would recommend to make the jump to v12.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
v10 is officially no longer supported but only two months have passed since its EOL date. I prefer to wait a little longer before dropping support for it, there is no additional maintenance burden compared to v12.
Changes all make sense to me, definitely required quite a few updates on my end so it is a bit disruptive, but all very small and easy to do, and everything's still working nicely afterwards 👍 |
I did a bit of testing against the 10f27d7 with both string and binary messages (haven't tried a mixing them in a single session). |
Make the `WebSocket` constructor throw a `SyntaxError` if any of the subprotocol names are invalid or duplicated.
Abort the handshake if the `Sec-WebSocket-Protocol` header is invalid.
Make the parser throw an error if the header field value is empty or if it begins or ends with a white space.
Make the `WebSocket` constructor throw a `SyntaxError` if the URL contains a fragment identifier or if the URL's protocol is not one of `'ws:'`, `'wss:'`, or `'ws+unix:'`.
I've just hit something that is obliquely related. I can open a new bug (and if it gets ignored here I will) but currently it seems to be impossible to write to a websocket stream as text. Any idea if you can make the writeableObjectMode useful while you make the readableObjectMode useful too? (This affects me because the code that controls the websocket in the browser side assumes that it gets text and I have no control over that code https://github.com/glyptodon/guacamole-client/blob/754e9649f1fa0ba225ee42b56ded64bc283d17df/guacamole-common-js/src/main/webapp/modules/Tunnel.js#L1009) It seems that a minimal fix is to simply allow the options to override the defaults that you give it https://github.com/websockets/ws/blob/master/lib/stream.js#L68 . I know that leaves the client open to breaking other options but hopefully "you break it, you bought it" applies. |
@jberger this is unrelated to this PR so yes, a new issue would have been better. Anyway you can use the const WebSocket = require('ws');
const ws = new WebSocket(url);
const duplex = WebSocket.createWebSocketStream(ws, { decodeStrings: false });
duplex.write('foo');
duplex.write('bar');
duplex.write('baz'); |
e98f3be
to
09334ff
Compare
doc/ws.md
Outdated
@@ -209,7 +209,8 @@ added when the `clientTracking` is truthy. | |||
|
|||
Close the HTTP server if created internally, terminate all clients and call |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should probably say 'close' all clients now, not 'terminate'.
lib/websocket-server.js
Outdated
if (!this.clients.size) { | ||
process.nextTick(emitClose, this); | ||
} else { | ||
for (const client of this.clients) client.close(1001); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
From a networking POV, this all seems like a good improvement to me. It definitely seems clearer for clients if servers send a 1001 close frame and tidily shutdown connections where possible 👍
That said, it does mean the connections could now unexpectedly stay open whilst waiting for a close response, until the close timeout, if you have any poorly behaving clients. That might be problematic if users are trying to immediately shut everything down, which seems like a reasonable use case, and there's now no easy alternative way to do that when necessary.
Maybe the server should have a server.terminate()
method too, which does terminate everything immediately?
That would make the server API match the individual websockets API, which already has websocket.terminate()
, and it would cover that immediate-shutdown use case.
As a bonus, this could also easily support cases like "try to shut down cleanly, but then always close within X milliseconds" with just:
server.close();
setTimeout(() => server.terminate(), X);
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Users can terminate clients by themself if they want to close immediately:
wss.close();
for (const ws of wss.clients) ws.terminate();
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah ok, fair enough. I'm not sure if that trick will be obvious to people, so a convenience server.terminate()
method to do the same could still be useful, but you're right that that means it's not strictly necessary 👍
It's probably worth mentioning this in the breaking-changes release notes though imo, as the suggested way to migrate for people who do need the previous behaviour.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have two doubts:
- If it is better to just prevent new connections from being established and not close/terminate existing clients. Closing clients is user responsibility so they can use
websocket.close()
orwebsocket.terminate()
at will. This is also consistent with the behavior ofserver.close()
. - Regardless of 1., if/when to emit the
'close'
event when client tracking is disabled. This is currently done in the next tick.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I went ahead and changed WebSocketServer.prototype.close()
behavior in 6a238da. It no longer closes existing connections as per point 1. of my previous comment.
I've also updated the documentation for point 2. I think that emitting the 'close'
event when connections are still active is a bit misleading but this behavior already exists and was not changed here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missed this sorry, I've been busy. That sounds very reasonable to me and the consistency with node is great.
I do think documenting this clearly for those who do want to terminate the server will be useful though, and it's not obvious right now. It might be worth a "how to fully shutdown a server" section in the readme, and/or the release notes.
2da7977
to
c8615c4
Compare
When `WebSocketServer.prototype.close()` is called, stop accepting new connections but do not close the existing ones. If the HTTP/S server was created internally, then close it and emit the `'close'` event when it closes. Otherwise, if client tracking is enabled, then emit the `'close'` event when the number of connections goes down to zero. Otherwise, emit it in the next tick. Refs: #1902
Match the behavior of Node.js core `net.Server` and call the callback of `WebSocketServer.prototype.close()` with an error if the server is already closed.
I'm opening this PR to get a feedback on some breaking changes that I have been delaying for a while and planning to release in a new major version soon.