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

feat: implement websockets #1795

Merged
merged 82 commits into from
Dec 16, 2022
Merged

feat: implement websockets #1795

merged 82 commits into from
Dec 16, 2022

Conversation

KhafraDev
Copy link
Member

@KhafraDev KhafraDev commented Dec 1, 2022

Fixes #932
Refs: nodejs/node#19308


To test it out:

import { WebSocket } from './lib/websocket/websocket.js'

const ws = new WebSocket('wss://echo.websocket.events/')

ws.addEventListener('message', (event) => {
  console.log(event.data) // "echo.websocket.events sponsored by Lob.com"

  ws.send('my message')
  
  setTimeout(() => ws.close(), 2000)
})

ws.addEventListener('close', ({ code }) => {
  console.log(code) // 1005
})

@codecov-commenter
Copy link

codecov-commenter commented Dec 2, 2022

Codecov Report

Base: 90.24% // Head: 82.93% // Decreases project coverage by -7.31% ⚠️

Coverage data is based on head (73128fd) compared to base (f38fbdc).
Patch coverage: 15.86% of modified lines in pull request are covered.

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     
Impacted Files Coverage Δ
lib/websocket/receiver.js 5.82% <5.82%> (ø)
lib/websocket/frame.js 10.00% <10.00%> (ø)
lib/websocket/util.js 10.81% <10.81%> (ø)
lib/websocket/websocket.js 11.35% <11.35%> (ø)
lib/websocket/connection.js 17.89% <17.89%> (ø)
lib/websocket/events.js 22.03% <22.03%> (ø)
lib/fetch/index.js 82.12% <47.05%> (-1.39%) ⬇️
lib/fetch/webidl.js 92.92% <60.00%> (-0.92%) ⬇️
index.js 100.00% <100.00%> (ø)
lib/websocket/constants.js 100.00% <100.00%> (ø)
... and 11 more

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.
📢 Do you have feedback about the report comment? Let us know in this issue.

lib/core/request.js Outdated Show resolved Hide resolved

frame.opcode = opcodes.BINARY

data.arrayBuffer().then((ab) => {
Copy link
Member

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.

Copy link
Member Author

@KhafraDev KhafraDev Dec 8, 2022

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.

Copy link
Member

@lpinca lpinca Dec 8, 2022

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

  1. https://github.com/ricea/websocketstream-explainer/tree/832f88d7f8eaecbe51da318d802951a3f8aadb2f#non-goals

Copy link
Member Author

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!

Copy link
Member

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.

Copy link
Contributor

@jimmywarting jimmywarting Dec 8, 2022

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?

Copy link
Contributor

@jimmywarting jimmywarting Dec 8, 2022

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?

Copy link
Member

@lpinca lpinca Dec 8, 2022

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.

lib/websocket/connection.js Outdated Show resolved Hide resolved
@jodevsa

This comment was marked as resolved.

@KhafraDev

This comment was marked as resolved.

lib/websocket/connection.js Outdated Show resolved Hide resolved
lib/websocket/websocket.js Outdated Show resolved Hide resolved
lib/websocket/connection.js Outdated Show resolved Hide resolved
Copy link
Member

@mcollina mcollina left a 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

lib/websocket/receiver.js Show resolved Hide resolved
lib/websocket/connection.js Outdated Show resolved Hide resolved
@KhafraDev
Copy link
Member Author

KhafraDev commented Dec 15, 2022

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):

  • pings and pongs (very easy to add, I want to add tests though)
  • tests (WPTs are "fine", but very lacking -- only 220 tests total)
  • concurrent sends with a blob and string/typed array (we need to resolve the blob asynchronously, causing it to be sent later than other things)
  • permessage-deflate support (likely not for a while)

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)

@lpinca
Copy link
Member

lpinca commented Dec 15, 2022

You have to mask the blob data so support for Blob in Writable.prototype.write() is not sufficient.

@KhafraDev
Copy link
Member Author

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.

@lpinca
Copy link
Member

lpinca commented Dec 15, 2022

There would need to be a way of resolving blobs synchronously, which I doubt has much hope of landing in core.

When permessage-deflate will be implemented, if compression will be done asynchronously (there is currently no support for sync deflate streams in Node.js core) you will have the same problem. You will then need a queue for compressed messages which you can also reuse for blobs.

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 websocket.send().

@KhafraDev
Copy link
Member Author

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())

@KhafraDev KhafraDev merged commit 7e12397 into nodejs:main Dec 16, 2022
@KhafraDev KhafraDev deleted the ws branch December 16, 2022 17:54
anonrig pushed a commit to anonrig/undici that referenced this pull request Apr 4, 2023
* 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>
kodiakhq bot referenced this pull request in X-oss-byte/Canary-nextjs Sep 18, 2023
[![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 ([@&#8203;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 [@&#8203;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 [@&#8203;climba03003](https://github.com/climba03003) in [https://github.com/nodejs/undici/pull/1911](https://github.com/nodejs/undici/pull/1911)
-   fix: remove test by [@&#8203;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 [@&#8203;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 [@&#8203;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 [@&#8203;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 [@&#8203;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 [@&#8203;mcollina](https://github.com/mcollina) in [https://github.com/nodejs/undici/pull/1927](https://github.com/nodejs/undici/pull/1927)

#### New Contributors

-   [@&#8203;climba03003](https://github.com/climba03003) made their first contribution in [https://github.com/nodejs/undici/pull/1911](https://github.com/nodejs/undici/pull/1911)
-   [@&#8203;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 [@&#8203;xconverge](https://github.com/xconverge) in [https://github.com/nodejs/undici/pull/1904](https://github.com/nodejs/undici/pull/1904)
-   use faster timers by [@&#8203;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 [@&#8203;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 [@&#8203;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 [@&#8203;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 [@&#8203;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 [@&#8203;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 [@&#8203;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 [@&#8203;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 [@&#8203;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 [@&#8203;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 [@&#8203;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 [@&#8203;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 [@&#8203;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 [@&#8203;KhafraDev](https://github.com/KhafraDev) in [https://github.com/nodejs/undici/pull/1906](https://github.com/nodejs/undici/pull/1906)
-   Use FastBuffer by [@&#8203;ronag](https://github.com/ronag) in [https://github.com/nodejs/undici/pull/1907](https://github.com/nodejs/undici/pull/1907)

#### New Contributors

-   [@&#8203;pan93412](https://github.com/pan93412) made their first contribution in [https://github.com/nodejs/undici/pull/1896](https://github.com/nodejs/undici/pull/1896)
-   [@&#8203;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 [@&#8203;Sebmaster](https://github.com/Sebmaster) in [https://github.com/nodejs/undici/pull/1877](https://github.com/nodejs/undici/pull/1877)

#### New Contributors

-   [@&#8203;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 [@&#8203;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 [@&#8203;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 [@&#8203;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 [@&#8203;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 [@&#8203;sosoba](https://github.com/sosoba) in [https://github.com/nodejs/undici/pull/1801](https://github.com/nodejs/undici/pull/1801)
-   feat: implement websockets by [@&#8203;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 [@&#8203;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 [@&#8203;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 [@&#8203;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 [#&#8203;1814](https://github.com/nodejs/undici/issues/1814) by [@&#8203;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 [@&#8203;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 [@&#8203;KhafraDev](https://github.com/KhafraDev) in [https://github.com/nodejs/undici/pull/1817](https://github.com/nodejs/undici/pull/1817)
-   build(deps-dev): bump [@&#8203;sinonjs/fake-timers](https://github.com/sinonjs/fake-timers) from 9.1.2 to 10.0.2 by [@&#8203;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 [@&#8203;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 [@&#8203;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 [@&#8203;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 [@&#8203;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 [@&#8203;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 [@&#8203;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 [@&#8203;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 [@&#8203;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 [@&#8203;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 [@&#8203;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 [@&#8203;KhafraDev](https://github.com/KhafraDev) in [https://github.com/nodejs/undici/pull/1835](https://github.com/nodejs/undici/pull/1835)
-   wpt: add test by [@&#8203;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 [@&#8203;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 [@&#8203;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 [@&#8203;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 [@&#8203;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 [@&#8203;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 [@&#8203;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 [@&#8203;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 [@&#8203;mcollina](https://github.com/mcollina) in [https://github.com/nodejs/undici/pull/1853](https://github.com/nodejs/undici/pull/1853)

#### New Contributors

-   [@&#8203;sosoba](https://github.com/sosoba) made their first contribution in [https://github.com/nodejs/undici/pull/1801](https://github.com/nodejs/undici/pull/1801)
-   [@&#8203;debadree25](https://github.com/debadree25) made their first contribution in [https://github.com/nodejs/undici/pull/1821](https://github.com/nodejs/undici/pull/1821)
-   [@&#8203;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).
crysmags pushed a commit to crysmags/undici that referenced this pull request Feb 27, 2024
* 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>
@lpinca lpinca mentioned this pull request Mar 12, 2024
1 task
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

Successfully merging this pull request may close these issues.

WebSocket
8 participants