-
Notifications
You must be signed in to change notification settings - Fork 12
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
The WebSocketStream Interface #48
base: main
Are you sure you want to change the base?
Conversation
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.
Hi Adam,
Thanks for the PR! Looks good in general. However, I left a few comments.
Thanks,
Oleh
The algorithms are complete now. @nidhijaju @domenic PTAL The PR Preview is unhappy for some reason, but it builds locally. |
Temporarily force-link "writable stream writer" until it can be exported from the streams standard.
This feature is not worth the complexity so it is being removed.
…cancel()" Put back mention of passing close code or reason to abort() or cancel(), in preparation for updating it to use WebSocketError.
Some algorithms that are common to WebSocket and WebSocketStream have been moved to a new section to be shared.
f8b22a2
to
fa5ff83
Compare
Quick question: wouldn't it be better to model it this way?
WebIDL: dictionary WebSocketStreamOptions {
sequence<USVString> protocols;
AbortSignal signal;
};
interface WebSocketStream {
constructor(USVString url,
optional WebSocketStreamOptions options);
readonly attribute ReadableStream readable;
readonly attribute WritableStream writable;
readonly attribute sequence<DOMString> extensions;
readonly attribute DOMString protocol;
readonly attribute Promise<undefined> opened;
readonly attribute Promise<undefined> closed;
[Clamp] attribute unsigned short closeCode;
attribute USVString closeReason;
}; This has a few other added benefits as well:
|
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.
Only got a little ways through before I had to wrap up for the day.
Also apply backpressure on connection, and linkify "exists".
I think always throwing an exception on close would lead to poor ergonomics. For example, even a simple function like const wss = new WebSocketStream('/messages');
for await (const message of wss.readable) {
const div = new HTMLDivElement();
div.textContent = message;
body.appendChild(div);
} would have to be wrapped in a
This is what WebTransport does with datagrams, but it adds a lot of complexity to the standard and implementation. In the case of WebSockets, no messages can be sent until the handshake response has been received anyway, so there's no performance benefit in queuing them early. I consciously chose not to give out the streams until they were useful to avoid the need to think about these issues.
This means you have to pass around the writer to any place that needs to be able to close the WebSocket. I have the impression that most users of WebSockets don't bother with the close codes but for those who do it feels awkward: wss.closeCode = 1000;
wss.closeReason = 'Thank you';
writer.close();
I'm not comfortable that they can be changed asynchronously outside of script:
In the case of HTTP, it's necessary to send the request body first, so it's not an exact parallel.
It's a bit easier to implement, yes, but in my opinion it's harder to use. Developers won't have to think about the various dictionaries in my design, they can just copy the calling patterns from examples. Implementing the construction of a
You can actually pass a
I'm not sure of the usefulness of the URL field, but it's very easy to implement, and maybe if it saves someone from needing to use a |
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.
Didn't yet get to the last two sections, but it's all looking quite nice so far; no substantial comments.
Co-authored-by: Domenic Denicola <[email protected]>
Co-authored-by: Domenic Denicola <[email protected]>
Co-authored-by: Domenic Denicola <[email protected]>
Co-authored-by: Domenic Denicola <[email protected]>
Co-authored-by: Domenic Denicola <[email protected]>
Also use <p class=note> everywhere, and other cleanups.
Make those headings be indented under "The WebSocket interface" for a more logical organisation. Also move "Ping and Pong frames" section to the bottom as it applies to both APIs.
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.
Finished!
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.
LGTM with nits, nice work!
Co-authored-by: Domenic Denicola <[email protected]>
Co-authored-by: Domenic Denicola <[email protected]>
Co-authored-by: Domenic Denicola <[email protected]>
Co-authored-by: Domenic Denicola <[email protected]>
Don't nest a link inside another link.
Any thoughts on taking this as an opportunity to facilitate adding custom HTTP UPGRADE request headers? For example, web clients currently must provide any auth token as a query parameter on the URL. This conflicts with security best practice, which recommend using an Authorization header to avoid accidental logging of security related info. |
I'm not going to add it in the initial PR. I've considered adding it initially for WebSocketStream so we can experiment with a smaller and more adaptable user base. But we'd have to add it to the old WebSocket API too eventually. This is getting off-topic for this PR so please take future discussion to #16 (or don't -- that issue has already reached a length where most people won't read the whole thing). |
WebIDL requires explicit defaults for optional arguments. Co-authored-by: Philip Jägenstedt <[email protected]>
This issue was mentioned in WEBRTCWG-2024-05-21 (Page 38) |
Specify the WebSocketStream API. This exposes backpressure to
JavaScript, avoiding pathological behavior when one side of the
connection is not keeping up. It also provides a modern API using
Streams and Promises to interoperate cleanly with the modern web
ecosystem.
See the explainer at
https://github.com/ricea/websocketstream-explainer/blob/master/README.md
for examples and motivation.
(See WHATWG Working Mode: Changes for more details.)
Preview | Diff