-
Notifications
You must be signed in to change notification settings - Fork 543
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
feat: implement websockets #1795
Conversation
Codecov ReportBase: 90.24% // Head: 82.93% // Decreases project coverage by
Additional details and impacted files@@ Coverage Diff @@
## main #1795 +/- ##
==========================================
- Coverage 90.24% 82.93% -7.32%
==========================================
Files 58 66 +8
Lines 5179 5755 +576
==========================================
+ Hits 4674 4773 +99
- Misses 505 982 +477
Help us with your feedback. Take ten seconds to tell us how you rate us. Have a feature suggestion? Share it here. ☔ View full report at Codecov. |
implement sending a message for WS and add a websocketFrame class
|
||
frame.opcode = opcodes.BINARY | ||
|
||
data.arrayBuffer().then((ab) => { |
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.
If you have concurrent writes this might change the order of messages:
websocket.send(blob);
websocket.send(string);
The string message is sent before the blob.
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 don't know of any good way of fixing this without needing a Promise for every send.. Preferably node would allow writing a blob to a writable/duplex stream.
But then there would be issues masking the body/creating a frame. I guess the only other solution is adding a queue, but that's better for another time.
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 is the reason why there is no Blob
support in ws
. This and because the user can already do
websocket.send(await blob.arrayBuffer());
I agree with this 1
Non-goals
- Support Blob chunks. The old WebSocket API defaults to receiving messages as Blobs; however, creating and reading Blobs is more costly than creating and reading ArrayBuffers. In practice, even though it requires explicitly setting binaryType, 97% of messages are received as ArrayBuffers. On the send side, sending Blobs adds considerable complexity to the implementation because the contents are not available synchronously. Since less than 4% of sent messages are Blobs it is better to avoid this complexity where we can.
Footnotes
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.
Yes, when I was writing out my comment is kinda just clicked - "oh... this is why ws doesn't support blobs", lol. I still think undici should implement it for the sake of complying with the spec.
When I get around to implementing websocketstream this shouldn't be a problem at least!
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.
Well, ws
already uses a queue (only when permessage-deflate
is enabled and negotiated since compression is asynchronous) so support for Blob
could be added, but I think it does not worth the effort. I think that Blob
is of little use in Node.js if not natively supported by streams.
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.
hmm, but send(await blob.arrayBuffer())
would require reading the hole blob into memory before sending...
sure you could do something like:
for await (const chunk of blob.stream()) {
websocket.send(chunk)
}
But then you would get multiple messages and you would have to assemble them yourself on the receiving side as well 😞
if you can accept blob response types then it could as well be piped to a tmp location on the disc and wouldn't hold up any memory when receiving blobs
are there any limit on how large a message can be?
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, just went into the github repo and learned about a new WebSocketStream
forgot that it existed, only head about it when it was in early proposal. Well, then you could instead maybe do: blob.stream().pipeTo(websocket)
?
Should we implement a globalThis.WebSocketStream
also?
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.
But then you would get multiple messages and you would have to assemble them yourself on the receiving side as well 😞
That's up to you. With ws
you could send each chunk as a message fragment and receive the whole thing as a single message on the receiving end.
for await (const chunk of blob.stream()) {
websocket.send(chunk, { fin: false}); // For the sake of simplicity backpressure is not handled.
}
websocket.send(Buffer.alloc(0), { fin: true });
if you can accept blobs then it could as well be piped to a tmp location on the disc and wouldn't hold up any memory when receiving blobs
AFAIK the whole message is buffered in memory before the 'message'
event is emitted. That message is then returned to the user as a Blob
.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
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.
good work here, apart from the outstanding comments this is ready for me
Any last minute issues to fix? I'd like to merge this by tonight and subsequently fix/add the remaining features over the next few days. My next PRs (if this is merged, otherwise they'll be here):
Regarding the concurrent sends with a blob, we'd need core support for writing blobs to a writable stream. Then we could do: const frame = new WebsocketFrameSend(Buffer.allocUnsafe(0))
const buffer = frame.createFrame(opcodes.TEXT) // or binary
socket.cork()
socket.write(buffer)
socket.write(blob)
socket.uncork()
socket.write(string) |
You have to mask the blob data so support for |
Right, completely forgot about masking. There would need to be a way of resolving blobs synchronously, which I doubt has much hope of landing in core. |
When FWIW you can also chose to never compress messages and still be RFC compliant. This is another area where the WHATWG specification falls short. It's all or nothing, there is no way to specify which messages to compress via |
Also found a way to read blobs synchronously, just took a deep dive into node internals. Obviously not going to implement it because it's a terrible hack lol. const blob = new Blob(['hello world'])
const handle = Reflect.ownKeys(blob)[0] // kHandle
console.log(blob[handle].toArrayBuffer()) |
* initial handshake * minor fixes * feat: working initial handshake! * feat(ws): initial WebSocket class implementation * fix: allow http: and ws: urls * fix(ws): use websocket spec * fix(ws): use websocket spec * feat: implement url getter * feat: implement some of `WebSocket.close` and ready state * fix: body is null for websockets & pass socket to response * fix: store the fetch controller & response on ws * fix: remove invalid tests * feat: implement readyState getter * feat: implement `protocol` and `extensions` getters * feat: implement event listeners * feat: implement binaryType attribute * fix: add argument length checks * feat: basic unfragmented message parsing * fix: always remove previous listener * feat: add in idlharness WPT * implement sending a message for WS and add a websocketFrame class * feat: allow sending ArrayBuffer/Views & Blob * fix: remove duplicate `upgrade` and `connection` headers * feat: add in WebSocket.close() and handle closing frames * refactor WebsocketFrame and support receiving frames in multiple chunks * fixes * move WebsocketFrame to its own file * feat: export WebSocket & add types * fix: tsd * feat(wpt): use WebSocketServer & run test * fix: properly set/read close code & close reason * fix: flakiness in websocket test runner * fix: receive message with arraybuffer binary type * feat: split WebsocketFrame into 2 classes (sent & received) * fix: parse fragmented frames more efficiently & close frame * fix: add types for MessageEvent and CloseEvent * fix: subprotocol validation & add wpts * fix: protocol validation & protocol webidl & add wpts * fix: correct bufferedAmount calc. & message event w/ blob * fix: don't truncate typedarrays * feat: add remaining wpts * fix: allow sending payloads > 65k bytes * fix: mask data > 125 bytes properly * revert changes to core * fix: decrement bufferedAmount after write * fix: handle ping and pong frames * fix: simplify receiving frame logic * fix: disable extensions & validate frames * fix: send close frame upon receiving * lint * fix: validate status code & utf-8 * fix: add hooks * fix: check if frame is unfragmented correctly * fix: send ping app data in pong frames * export websocket on node >= 18 & add diagnostic_channels * mark test as flaky * fix: couple bug fixes * fix: fragmented frame end detection * fix: use TextDecoder for utf-8 validation * fix: handle incomplete chunks * revert: handle incomplete chunks * mark WebSockets as experimental * fix: sending 65k bytes is still flaky on linux * fix: apply suggestions * fix: apply some suggestions * add basic docs * feat: use streaming parser for frames * feat: validate some frames & remove WebsocketFrame class * fix: parse close frame & move failWebsocketConnection * fix: read close reason and read entire close body * fix: echo close frame if one hasn't been sent * fix: emit message event on message receive * fix: minor fixes * fix: ci * fix: set was clean exit after server receives close frame * fix: check if received close frame for clean close * fix: set sent close after writing frame * feat: implement error messages * fix: add error event handler to socket * fix: address reviews Co-authored-by: Subhi Al Hasan <jodevsa@gmail.com>
[![Mend Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com) This PR contains the following updates: | Package | Change | Age | Adoption | Passing | Confidence | |---|---|---|---|---|---| | [undici](https://undici.nodejs.org) ([source](https://github.com/nodejs/undici)) | [`5.14.0` -> `5.19.1`](https://renovatebot.com/diffs/npm/undici/5.14.0/5.19.1) | [![age](https://developer.mend.io/api/mc/badges/age/npm/undici/5.19.1?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/undici/5.19.1?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/undici/5.14.0/5.19.1?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/undici/5.14.0/5.19.1?slim=true)](https://docs.renovatebot.com/merge-confidence/) | ### GitHub Vulnerability Alerts #### [CVE-2023-23936](https://github.com/nodejs/undici/security/advisories/GHSA-5r9g-qh6m-jxff) ### Impact undici library does not protect `host` HTTP header from CRLF injection vulnerabilities. ### Patches This issue was patched in Undici v5.19.1. ### Workarounds Sanitize the `headers.host` string before passing to undici. ### References Reported at https://hackerone.com/reports/1820955. ### Credits Thank you to Zhipeng Zhang ([@​timon8](https://hackerone.com/timon8)) for reporting this vulnerability. --- ### Release Notes <details> <summary>nodejs/undici (undici)</summary> ### [`v5.19.1`](https://github.com/nodejs/undici/releases/tag/v5.19.1) [Compare Source](https://github.com/nodejs/undici/compare/v5.19.0...v5.19.1) ####⚠️ Security Release⚠️ - [Regular Expression Denial of Service in Headers](https://github.com/nodejs/undici/security/advisories/GHSA-r6ch-mqf9-qc9w) with CVE-2023-24807 - [CRLF Injection in Nodejs ‘undici’ via host](https://github.com/nodejs/undici/security/advisories/GHSA-5r9g-qh6m-jxff) with CVE-2023-23936 This release is part of the Node.js security release train: https://nodejs.org/en/blog/vulnerability/february-2023-security-releases/ ### [`v5.19.0`](https://github.com/nodejs/undici/releases/tag/v5.19.0) [Compare Source](https://github.com/nodejs/undici/compare/v5.18.0...v5.19.0) #### What's Changed - fix(fetch): raise AbortSignal max event listeners by [@​KhafraDev](https://github.com/KhafraDev) in [https://github.com/nodejs/undici/pull/1910](https://github.com/nodejs/undici/pull/1910) - fix: content-disposition header parsing by [@​climba03003](https://github.com/climba03003) in [https://github.com/nodejs/undici/pull/1911](https://github.com/nodejs/undici/pull/1911) - fix: remove test by [@​KhafraDev](https://github.com/KhafraDev) in [https://github.com/nodejs/undici/pull/1916](https://github.com/nodejs/undici/pull/1916) - feat: add Headers.prototype.getSetCookie by [@​KhafraDev](https://github.com/KhafraDev) in [https://github.com/nodejs/undici/pull/1915](https://github.com/nodejs/undici/pull/1915) - fix(headers): clone getSetCookie list & add getSetCookie type by [@​KhafraDev](https://github.com/KhafraDev) in [https://github.com/nodejs/undici/pull/1917](https://github.com/nodejs/undici/pull/1917) - doc(mock): update out-of-date reply documentation by [@​p9f](https://github.com/p9f) in [https://github.com/nodejs/undici/pull/1913](https://github.com/nodejs/undici/pull/1913) - fix(types): add missing keepAlive params by [@​SkeLLLa](https://github.com/SkeLLLa) in [https://github.com/nodejs/undici/pull/1918](https://github.com/nodejs/undici/pull/1918) - Make the fetch() abort test pass locally, on Linux and Mac, Node 18/19. by [@​mcollina](https://github.com/mcollina) in [https://github.com/nodejs/undici/pull/1927](https://github.com/nodejs/undici/pull/1927) #### New Contributors - [@​climba03003](https://github.com/climba03003) made their first contribution in [https://github.com/nodejs/undici/pull/1911](https://github.com/nodejs/undici/pull/1911) - [@​p9f](https://github.com/p9f) made their first contribution in [https://github.com/nodejs/undici/pull/1913](https://github.com/nodejs/undici/pull/1913) **Full Changelog**: nodejs/undici@v5.18.0...v5.19.0 ### [`v5.18.0`](https://github.com/nodejs/undici/releases/tag/v5.18.0) [Compare Source](https://github.com/nodejs/undici/compare/v5.17.1...v5.18.0) ##### What's Changed - Add ability to set TCP keepalive by [@​xconverge](https://github.com/xconverge) in [https://github.com/nodejs/undici/pull/1904](https://github.com/nodejs/undici/pull/1904) - use faster timers by [@​ronag](https://github.com/ronag) in [https://github.com/nodejs/undici/pull/1908](https://github.com/nodejs/undici/pull/1908) - fix: ensure header value is a string by [@​ronag](https://github.com/ronag) in [https://github.com/nodejs/undici/pull/1899](https://github.com/nodejs/undici/pull/1899) **Full Changelog**: nodejs/undici@v5.17.1...v5.18.0 ### [`v5.17.1`](https://github.com/nodejs/undici/releases/tag/v5.17.1) [Compare Source](https://github.com/nodejs/undici/compare/v5.17.0...v5.17.1) #### What's Changed - fix: bad buffer slice (nodejs/undici@d2be675) **Full Changelog**: nodejs/undici@v5.17.0...v5.17.1 ### [`v5.17.0`](https://github.com/nodejs/undici/releases/tag/v5.17.0) [Compare Source](https://github.com/nodejs/undici/compare/v5.16.0...v5.17.0) #### What's Changed - fix(wpts): Blob is a global getter in >=v19.x.x by [@​KhafraDev](https://github.com/KhafraDev) in [https://github.com/nodejs/undici/pull/1880](https://github.com/nodejs/undici/pull/1880) - doc: fix anchor links dispatcher.stream by [@​RafaelGSS](https://github.com/RafaelGSS) in [https://github.com/nodejs/undici/pull/1881](https://github.com/nodejs/undici/pull/1881) - wpt: make runner more resilient by [@​KhafraDev](https://github.com/KhafraDev) in [https://github.com/nodejs/undici/pull/1884](https://github.com/nodejs/undici/pull/1884) - Make test pass in v19.x by [@​mcollina](https://github.com/mcollina) in [https://github.com/nodejs/undici/pull/1879](https://github.com/nodejs/undici/pull/1879) - Correct the type of DispatchOptions\["headers"] by [@​pan93412](https://github.com/pan93412) in [https://github.com/nodejs/undici/pull/1896](https://github.com/nodejs/undici/pull/1896) - perf(content-type parser): faster string collector by [@​KhafraDev](https://github.com/KhafraDev) in [https://github.com/nodejs/undici/pull/1894](https://github.com/nodejs/undici/pull/1894) - feat: expose content-type parser by [@​KhafraDev](https://github.com/KhafraDev) in [https://github.com/nodejs/undici/pull/1895](https://github.com/nodejs/undici/pull/1895) - fix(types): Update DispatchOptions type for missing "blocking" by [@​xconverge](https://github.com/xconverge) in [https://github.com/nodejs/undici/pull/1889](https://github.com/nodejs/undici/pull/1889) - fix(types): update error type definitions by [@​rafaelcr](https://github.com/rafaelcr) in [https://github.com/nodejs/undici/pull/1888](https://github.com/nodejs/undici/pull/1888) - fix: ensure connection header is a string by [@​ronag](https://github.com/ronag) in [https://github.com/nodejs/undici/pull/1900](https://github.com/nodejs/undici/pull/1900) - fix: throw if invalid content-type header by [@​ronag](https://github.com/ronag) in [https://github.com/nodejs/undici/pull/1901](https://github.com/nodejs/undici/pull/1901) - fix(fetch): use semicolon for Cookie header delimiter by [@​KhafraDev](https://github.com/KhafraDev) in [https://github.com/nodejs/undici/pull/1906](https://github.com/nodejs/undici/pull/1906) - Use FastBuffer by [@​ronag](https://github.com/ronag) in [https://github.com/nodejs/undici/pull/1907](https://github.com/nodejs/undici/pull/1907) #### New Contributors - [@​pan93412](https://github.com/pan93412) made their first contribution in [https://github.com/nodejs/undici/pull/1896](https://github.com/nodejs/undici/pull/1896) - [@​rafaelcr](https://github.com/rafaelcr) made their first contribution in [https://github.com/nodejs/undici/pull/1888](https://github.com/nodejs/undici/pull/1888) **Full Changelog**: nodejs/undici@v5.16.0...v5.17.0 ### [`v5.16.0`](https://github.com/nodejs/undici/releases/tag/v5.16.0) [Compare Source](https://github.com/nodejs/undici/compare/v5.15.2...v5.16.0) #### What's Changed - Add feature to specify custom headers for proxies by [@​Sebmaster](https://github.com/Sebmaster) in [https://github.com/nodejs/undici/pull/1877](https://github.com/nodejs/undici/pull/1877) #### New Contributors - [@​Sebmaster](https://github.com/Sebmaster) made their first contribution in [https://github.com/nodejs/undici/pull/1877](https://github.com/nodejs/undici/pull/1877) **Full Changelog**: nodejs/undici@v5.15.2...v5.16.0 ### [`v5.15.2`](https://github.com/nodejs/undici/compare/9d5f23177408dc16d3d4cbb8cebf463081c54e16...9457c9719029945ef9ff36b71d58557443730942) [Compare Source](https://github.com/nodejs/undici/compare/v5.15.1...v5.15.2) ### [`v5.15.1`](https://github.com/nodejs/undici/releases/tag/v5.15.1) [Compare Source](https://github.com/nodejs/undici/compare/v5.15.0...v5.15.1) #### What's Changed - fix(websocket): simplify typedarray copying by [@​KhafraDev](https://github.com/KhafraDev) in [https://github.com/nodejs/undici/pull/1854](https://github.com/nodejs/undici/pull/1854) - fix: wpts on node v18.13.0+ by [@​KhafraDev](https://github.com/KhafraDev) in [https://github.com/nodejs/undici/pull/1859](https://github.com/nodejs/undici/pull/1859) - perf: allow keep alive for HEAD requests by [@​ronag](https://github.com/ronag) in [https://github.com/nodejs/undici/pull/1858](https://github.com/nodejs/undici/pull/1858) - fix: flaky abort test by [@​KhafraDev](https://github.com/KhafraDev) in [https://github.com/nodejs/undici/pull/1863](https://github.com/nodejs/undici/pull/1863) **Full Changelog**: nodejs/undici@v5.15.0...v5.15.1 ### [`v5.15.0`](https://github.com/nodejs/undici/releases/tag/v5.15.0) [Compare Source](https://github.com/nodejs/undici/compare/v5.14.0...v5.15.0) #### What's Changed - \[types] update ProxyAgent Options (timeout) by [@​sosoba](https://github.com/sosoba) in [https://github.com/nodejs/undici/pull/1801](https://github.com/nodejs/undici/pull/1801) - feat: implement websockets by [@​KhafraDev](https://github.com/KhafraDev) in [https://github.com/nodejs/undici/pull/1795](https://github.com/nodejs/undici/pull/1795) - feat(websocket): handle ping/pong frames & fix fragmented frames by [@​KhafraDev](https://github.com/KhafraDev) in [https://github.com/nodejs/undici/pull/1809](https://github.com/nodejs/undici/pull/1809) - docs: add basic fetch & company docs by [@​KhafraDev](https://github.com/KhafraDev) in [https://github.com/nodejs/undici/pull/1810](https://github.com/nodejs/undici/pull/1810) - make formdata body immutable and encode it only once by [@​jimmywarting](https://github.com/jimmywarting) in [https://github.com/nodejs/undici/pull/1814](https://github.com/nodejs/undici/pull/1814) - test: add regression test for [#​1814](https://github.com/nodejs/undici/issues/1814) by [@​KhafraDev](https://github.com/KhafraDev) in [https://github.com/nodejs/undici/pull/1815](https://github.com/nodejs/undici/pull/1815) - feat(websocket): only consume necessary bytes by [@​KhafraDev](https://github.com/KhafraDev) in [https://github.com/nodejs/undici/pull/1812](https://github.com/nodejs/undici/pull/1812) - websocket: use Buffer.allocUnsafe by [@​KhafraDev](https://github.com/KhafraDev) in [https://github.com/nodejs/undici/pull/1817](https://github.com/nodejs/undici/pull/1817) - build(deps-dev): bump [@​sinonjs/fake-timers](https://github.com/sinonjs/fake-timers) from 9.1.2 to 10.0.2 by [@​dependabot](https://github.com/dependabot) in [https://github.com/nodejs/undici/pull/1819](https://github.com/nodejs/undici/pull/1819) - fix(websocket): deprecation warning & 64-bit unsigned int body length by [@​KhafraDev](https://github.com/KhafraDev) in [https://github.com/nodejs/undici/pull/1818](https://github.com/nodejs/undici/pull/1818) - Use nodejs.stream.destroyed symbol by [@​ronag](https://github.com/ronag) in [https://github.com/nodejs/undici/pull/1816](https://github.com/nodejs/undici/pull/1816) - fetch: removal of redundant condition by [@​debadree25](https://github.com/debadree25) in [https://github.com/nodejs/undici/pull/1821](https://github.com/nodejs/undici/pull/1821) - fix(request): request headers array by [@​jd-carroll](https://github.com/jd-carroll) in [https://github.com/nodejs/undici/pull/1807](https://github.com/nodejs/undici/pull/1807) - fix(websocket): validate payload length received by [@​KhafraDev](https://github.com/KhafraDev) in [https://github.com/nodejs/undici/pull/1822](https://github.com/nodejs/undici/pull/1822) - fix(websocket): run parser in loop, instead of recursively by [@​KhafraDev](https://github.com/KhafraDev) in [https://github.com/nodejs/undici/pull/1828](https://github.com/nodejs/undici/pull/1828) - fix(fetch): weaker refs by [@​ronag](https://github.com/ronag) in [https://github.com/nodejs/undici/pull/1824](https://github.com/nodejs/undici/pull/1824) - websocket: add tests for opening handshake by [@​KhafraDev](https://github.com/KhafraDev) in [https://github.com/nodejs/undici/pull/1831](https://github.com/nodejs/undici/pull/1831) - websocket: add tests for constructor, close, and send by [@​KhafraDev](https://github.com/KhafraDev) in [https://github.com/nodejs/undici/pull/1832](https://github.com/nodejs/undici/pull/1832) - websocket: more test coverage by [@​KhafraDev](https://github.com/KhafraDev) in [https://github.com/nodejs/undici/pull/1833](https://github.com/nodejs/undici/pull/1833) - fix(WPTs): flaky abort test by [@​KhafraDev](https://github.com/KhafraDev) in [https://github.com/nodejs/undici/pull/1835](https://github.com/nodejs/undici/pull/1835) - wpt: add test by [@​KhafraDev](https://github.com/KhafraDev) in [https://github.com/nodejs/undici/pull/1836](https://github.com/nodejs/undici/pull/1836) - fix: don't send keep-alive if we want reset by [@​ronag](https://github.com/ronag) in [https://github.com/nodejs/undici/pull/1846](https://github.com/nodejs/undici/pull/1846) - fetch: update body consume to match spec by [@​KhafraDev](https://github.com/KhafraDev) in [https://github.com/nodejs/undici/pull/1847](https://github.com/nodejs/undici/pull/1847) - feat: allow connection header in request by [@​metcoder95](https://github.com/metcoder95) in [https://github.com/nodejs/undici/pull/1829](https://github.com/nodejs/undici/pull/1829) - feat: add cookie parsing ability by [@​KhafraDev](https://github.com/KhafraDev) in [https://github.com/nodejs/undici/pull/1848](https://github.com/nodejs/undici/pull/1848) - fix(cookie): add docs & expose in node v16 by [@​KhafraDev](https://github.com/KhafraDev) in [https://github.com/nodejs/undici/pull/1849](https://github.com/nodejs/undici/pull/1849) - fix(cookies): work with global Headers by [@​KhafraDev](https://github.com/KhafraDev) in [https://github.com/nodejs/undici/pull/1850](https://github.com/nodejs/undici/pull/1850) - docs(Dispatcher): adjust documentation for reset flag by [@​metcoder95](https://github.com/metcoder95) in [https://github.com/nodejs/undici/pull/1852](https://github.com/nodejs/undici/pull/1852) - Fix broken interceptor test by [@​mcollina](https://github.com/mcollina) in [https://github.com/nodejs/undici/pull/1853](https://github.com/nodejs/undici/pull/1853) #### New Contributors - [@​sosoba](https://github.com/sosoba) made their first contribution in [https://github.com/nodejs/undici/pull/1801](https://github.com/nodejs/undici/pull/1801) - [@​debadree25](https://github.com/debadree25) made their first contribution in [https://github.com/nodejs/undici/pull/1821](https://github.com/nodejs/undici/pull/1821) - [@​jd-carroll](https://github.com/jd-carroll) made their first contribution in [https://github.com/nodejs/undici/pull/1807](https://github.com/nodejs/undici/pull/1807) **Full Changelog**: nodejs/undici@v5.14.0...v5.15.0 </details> --- ### Configuration 📅 **Schedule**: Branch creation - "" (UTC), Automerge - At any time (no schedule defined). 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR has been generated by [Mend Renovate](https://www.mend.io/free-developer-tools/renovate/). View repository job log [here](https://developer.mend.io/github/sammyfilly/Canary-nextjs).
* initial handshake * minor fixes * feat: working initial handshake! * feat(ws): initial WebSocket class implementation * fix: allow http: and ws: urls * fix(ws): use websocket spec * fix(ws): use websocket spec * feat: implement url getter * feat: implement some of `WebSocket.close` and ready state * fix: body is null for websockets & pass socket to response * fix: store the fetch controller & response on ws * fix: remove invalid tests * feat: implement readyState getter * feat: implement `protocol` and `extensions` getters * feat: implement event listeners * feat: implement binaryType attribute * fix: add argument length checks * feat: basic unfragmented message parsing * fix: always remove previous listener * feat: add in idlharness WPT * implement sending a message for WS and add a websocketFrame class * feat: allow sending ArrayBuffer/Views & Blob * fix: remove duplicate `upgrade` and `connection` headers * feat: add in WebSocket.close() and handle closing frames * refactor WebsocketFrame and support receiving frames in multiple chunks * fixes * move WebsocketFrame to its own file * feat: export WebSocket & add types * fix: tsd * feat(wpt): use WebSocketServer & run test * fix: properly set/read close code & close reason * fix: flakiness in websocket test runner * fix: receive message with arraybuffer binary type * feat: split WebsocketFrame into 2 classes (sent & received) * fix: parse fragmented frames more efficiently & close frame * fix: add types for MessageEvent and CloseEvent * fix: subprotocol validation & add wpts * fix: protocol validation & protocol webidl & add wpts * fix: correct bufferedAmount calc. & message event w/ blob * fix: don't truncate typedarrays * feat: add remaining wpts * fix: allow sending payloads > 65k bytes * fix: mask data > 125 bytes properly * revert changes to core * fix: decrement bufferedAmount after write * fix: handle ping and pong frames * fix: simplify receiving frame logic * fix: disable extensions & validate frames * fix: send close frame upon receiving * lint * fix: validate status code & utf-8 * fix: add hooks * fix: check if frame is unfragmented correctly * fix: send ping app data in pong frames * export websocket on node >= 18 & add diagnostic_channels * mark test as flaky * fix: couple bug fixes * fix: fragmented frame end detection * fix: use TextDecoder for utf-8 validation * fix: handle incomplete chunks * revert: handle incomplete chunks * mark WebSockets as experimental * fix: sending 65k bytes is still flaky on linux * fix: apply suggestions * fix: apply some suggestions * add basic docs * feat: use streaming parser for frames * feat: validate some frames & remove WebsocketFrame class * fix: parse close frame & move failWebsocketConnection * fix: read close reason and read entire close body * fix: echo close frame if one hasn't been sent * fix: emit message event on message receive * fix: minor fixes * fix: ci * fix: set was clean exit after server receives close frame * fix: check if received close frame for clean close * fix: set sent close after writing frame * feat: implement error messages * fix: add error event handler to socket * fix: address reviews Co-authored-by: Subhi Al Hasan <jodevsa@gmail.com>
Fixes #932
Refs: nodejs/node#19308
await fetch('wss://whatever')
)& bufferingedit: since the socket is a duplex, buffering should be handled already for usTo test it out: