https://github.com/vexcited/tcp-websocket
A WebSocket client-only class made with TCP streams.
https://github.com/vexcited/tcp-websocket
bun tcp websocket
Last synced: about 1 year ago
JSON representation
A WebSocket client-only class made with TCP streams.
- Host: GitHub
- URL: https://github.com/vexcited/tcp-websocket
- Owner: Vexcited
- License: mit
- Created: 2023-09-10T21:21:37.000Z (almost 3 years ago)
- Default Branch: main
- Last Pushed: 2023-09-18T19:10:24.000Z (almost 3 years ago)
- Last Synced: 2024-04-13T16:13:51.085Z (about 2 years ago)
- Topics: bun, tcp, websocket
- Language: TypeScript
- Homepage: https://www.npmjs.com/package/tcp-websocket
- Size: 35.2 KB
- Stars: 7
- Watchers: 1
- Forks: 1
- Open Issues: 1
-
Metadata Files:
- Readme: README.md
- Funding: .github/FUNDING.yml
- License: LICENSE
Awesome Lists containing this project
README
# `tcp-websocket`
> Was originally made to resolve this [Bun](https://bun.sh/) issue: .
Instead of using built-in `WebSocket`, we re-use the `WebSocket` from [`undici`](https://github.com/nodejs/undici) and export it correctly in this package with the typings needed.
## Why not directly use `undici` ?
Bun patches `undici` imports under the hood, resulting in it being broken and useless to resolve the issue, see
[`undici.js`](https://github.com/oven-sh/bun/blob/b124ba056cfdafad7828f86a852a83722f17f8a5/src/js/thirdparty/undici.js) and [`Undici.cpp`](https://github.com/oven-sh/bun/blob/b124ba056cfdafad7828f86a852a83722f17f8a5/src/bun.js/bindings/Undici.cpp).
For example, if we write the following code with Bun (so, using the native `WebSocket` implementation) :
```typescript
const ws = new WebSocket("ws://localhost:8080", {
headers: {
"User-Agent": "hello",
"X-My-HeADeR": "world",
authorization: "Bearer Hello!",
origin: "http://localhost:8080"
}
});
```
Server code (uses Node.js and ws package)
```typescript
import { createServer } from 'http';
import { WebSocketServer } from 'ws';
const server = createServer();
const wss = new WebSocketServer({ server });
wss.on('connection', (_, req) => {
const headers = {};
// We use `req.rawHeaders` to see the case-sensitive headers.
for (let i = 0; i < req.rawHeaders.length; i += 2) {
headers[req.rawHeaders[i]] = req.rawHeaders[i + 1];
}
console.log(headers);
});
server.listen(8080);
```
We get the following headers on the server :
```typescript
{
Host: 'localhost:8080',
Connection: 'Upgrade',
Upgrade: 'websocket',
'Sec-WebSocket-Version': '13',
'Sec-WebSocket-Key': 'RzRIg/gRTDCqu0rhOXe6OQ==',
Authorization: 'Bearer Hello!',
Origin: 'http://localhost:8080',
'User-Agent': 'hello',
'X-My-HeADeR': 'world'
}
```
As you can see, the headers are not the same as the ones we provided. `authorization` and `origin` got uppercased.
Let's now try to use `WebSocket` from `undici` directly :
```typescript
import { WebSocket } from "undici";
const ws = new WebSocket("ws://localhost:8080", {
headers: {
"User-Agent": "hello",
"X-My-HeADeR": "world",
authorization: "Bearer Hello!",
origin: "http://localhost:8080"
}
});
```
We get the following headers on the server :
```typescript
{
Host: 'localhost:8080',
Connection: 'Upgrade',
Upgrade: 'websocket',
'Sec-WebSocket-Version': '13',
'Sec-WebSocket-Key': 'ND4bTHtlQ/e/CwzhJp6mFA==',
Authorization: 'Bearer Hello!',
Origin: 'http://localhost:8080',
'User-Agent': 'hello',
'X-My-HeADeR': 'world'
}
```
We get exactly the same output !
This is because [Bun internally redirects the `WebSocket` import from `undici` to the native `WebSocket` implementation](https://github.com/oven-sh/bun/blob/b124ba056cfdafad7828f86a852a83722f17f8a5/src/bun.js/bindings/Undici.cpp#L59-L61).
Let's prevent this by tweaking the imports :
```typescript
import { WebSocket } from "undici/lib/web/websocket/websocket.js";
const ws = new WebSocket("ws://localhost:8080", {
headers: {
"User-Agent": "hello",
"X-My-HeADeR": "world",
authorization: "Bearer Hello!",
origin: "http://localhost:8080"
}
});
```
Now, we get the following headers on the server :
```typescript
{
host: 'localhost:8080',
connection: 'upgrade',
upgrade: 'websocket',
'User-Agent': 'hello',
'X-My-HeADeR': 'world',
authorization: 'Bearer Hello!',
origin: 'http://localhost:8080',
'sec-websocket-key': 'K8JDPp71F1TYDKXujpqoxw==',
'sec-websocket-version': '13',
'sec-websocket-extensions': 'permessage-deflate; client_max_window_bits',
accept: '*/*',
'accept-language': '*',
'sec-fetch-mode': 'websocket',
pragma: 'no-cache',
'cache-control': 'no-cache',
'accept-encoding': 'gzip, deflate'
}
```
It works as expected !
Now the issue is that we're missing typings, this is what this package is for.
## Usage
```bash
npm add tcp-websocket
yarn add tcp-websocket
pnpm add tcp-websocket
bun add tcp-websocket
```
```typescript
import WebSocket from "tcp-websocket";
const ws = new WebSocket("wss://ws.postman-echo.com/raw");
console.info("connecting...")
ws.onopen = () => {
console.info("[open]: connected");
ws.send("hello world!");
console.info("[open]: will close in 5 seconds...");
setTimeout(() => ws.close(), 5_000);
};
ws.onmessage = (event) => {
console.info("[message]:", event.data);
};
ws.onclose = (event) => {
console.info(`[close(${event.code})]: ${event.reason || "[no reason provided]"}`);
};
```
## Why not use those libraries instead ?
Packages using `node:http` or `node:https` to make the request handshake
will fail in Bun since their implementation also tweaks the headers.
| Package name on NPM | Issues with Bun |
| ------------------- | --------------- |
| [`ws`](https://www.npmjs.com/package/ws) | Uses the `node:http` and `node:https` to make the request handshake. [Source](https://github.com/websockets/ws/blob/7460049ff0a61bef8d5eda4b1d5c8170bc7d6b6f/lib/websocket.js#L715) |
| [`websocket`](https://www.npmjs.com/package/websocket) | Uses the `node:http` and `node:https` to make the request handshake. [Source](https://github.com/theturtle32/WebSocket-Node/blob/cce6d468986dd356a52af5630fd8ed5726ba5b7a/lib/WebSocketClient.js#L254) |
| [`websocket-stream`](https://www.npmjs.com/package/websocket-stream) | Uses the `ws` package internally, see `ws`. [Source](https://github.com/maxogden/websocket-stream/blob/feeb372ff530621d6df85cb85d4bee03b879c54d/stream.js#L5) |
| [`websocket-driver`](https://www.npmjs.com/package/websocket-driver) | Works as expected with Bun and others, but last update was 3 years ago with no TS declarations and ES5 syntax. |