oven-sh / bun

Incredibly fast JavaScript runtime, bundler, test runner, and package manager – all in one
https://bun.sh
Other
74.41k stars 2.78k forks source link

TLS version limitation something like Node.js `--tls-max-v1.2` cli arg #13710

Open songinnight opened 2 months ago

songinnight commented 2 months ago

What is the problem this feature would solve?

Currently, my project is experiencing a delay of 8 to 10 seconds in receiving network(client) responses from certain server. (Perhaps the target server is using Akamai and has some additional security settings in place to prevent non-browser http and websocket clients.)

In node.js, the --tls-max-v1.2 option solved the problem, but I haven't found a way to limit the TLS version to 1.2 in Bun yet.

What is the feature you are proposing to solve the problem?

would be nice something like this.

bun --tls-max-v1.2

process.env.BUN_TLS_MAX_VERSION = '1.2'

What alternatives have you considered?

No response

sequencerr commented 2 months ago

https://stackoverflow.com/a/62051684

songinnight commented 2 months ago

https://stackoverflow.com/a/62051684

@sequencerr Thanks for the answer. But, unfortunately, it works in Node.js but not in Bun. I'm having a weird behavior.

I've tried it on Bun 1.1.26 and 1.1.27-canary

Reproducing code.

import tls from 'tls';

tls.DEFAULT_MAX_VERSION = 'TLSv1.2'; // <-- This solves the problem only on Node.js

const wsUrl = `wss://live.fantasysportnetwork.com/public/lobby/socket/v2/r6c3i3wejv3qyqc7?messageFormat=json&device=Desktop&instance=uh2kmg-r6c3i3wejv3qyqc7-&EVOSESSIONID=r6c3i3wejv3qyqc7sg2abheehedelugl97355fc628031ff7877c2d1b4065355cc43ced0edceda0af&client_version=6.20240830.72721.44127-54f6972596`;

const startTime = Date.now();

const ws = new WebSocket(wsUrl, {
    // rejectUnauthorized: false,
    // perMessageDeflate: false,
    // handshakeTimeout: 15000,
    // enableTrace: true,
    tlsOptions: { // <-- It doesn't work on both Node.js and Bun
        maxVersion: "TLSv1.2",
    },
    tls: { // <-- It doesn't work on both Node.js and Bun
        maxVersion: "TLSv1.2",
    }
});
console.log(`▶ --- START WEBSOCKET CONNECTION ---`);

ws.addEventListener('open', async (event) => {
    // Cannot reach here or it takes about 10 seconds with Bun and Node.js(TLSv1.3)
    console.log(`▶ GOOD! WS OPENED  (${Date.now() - startTime}ms)`);
});

ws.addEventListener('message', (event) => {
    console.log(`▶ MESSAGE: ${event.data} (${Date.now() - startTime}ms)`);
    ws.close();
});

ws.addEventListener('close', event => {
    console.log(`▶ WS CLOSED  (${Date.now() - startTime}ms)`);
});

ws.addEventListener('error', event => {
    console.log(`▶ WS ERROR  (${Date.now() - startTime}ms)`, event);
});

Output with bun-debug.exe

...
[alloc] new(src.http.websocket_http_client.NewHTTPUpgradeClient(true)) = src.http.websocket_http_client.NewHTTPUpgradeClient(true)@5e784541c00
[uws] connect(live.fantasysportnetwork.com, 443)
[alloc] new(Request) = src.bun.js.api.bun.dns_resolver.InternalDNS.Request@5e784340120
▶ --- START WEBSOCKET CONNECTION ---
[WebSocketClient] onOpen
[uws] us_socket_write(*src.deps.boringssl.translated.SSL@23ccc7253d8, 417) = 0
[WebSocketClient] onHandshake A (1)
[EventLoop] enter() = 0
[EventLoop] exit() = 0
[uws] us_socket_write(*src.deps.boringssl.translated.SSL@23ccc7253d8, 417) = 417
  (It's stuck here for about 10 seconds.)
sequencerr commented 2 months ago

nodejs doesn't have builtin WebSocket. WebSocket package does not have tlsOptions, tls options entries.

you want provide same https options to server options entry https://github.com/websockets/ws/blob/master/examples/ssl.js

songinnight commented 2 months ago

Node.js >= v21 has a built-in websocket client.

https://nodejs.org/en/blog/announcements/v21-release-announce

I want to develop a project with Bun. If this not solved, I have to change the entire project code back to Node.js 😱

I've also tried the ws package, but it uses Bun's WebSockets internally, so I don't think that will solve the problem. I've lost almost a week of sleep over this issue.

sequencerr commented 2 months ago

then use bun's websocket api

songinnight commented 2 months ago

As I mentioned above Bun does not have TLS v1.2 limitation option like Node.js

https://github.com/oven-sh/bun/issues/13710#issuecomment-2327045191

Please, refer to my reproducing code.

And there's no guarantee that using TLS v1.2 in Bun will solve the underlying problem. 😢

Electroid commented 2 months ago

There seems to be some confusion. This is a feature in Node.js (both the CLI flag and API option) It does not exist (yet) in Bun.

sequencerr commented 2 months ago

for ws client with tls options you want to use ws package with custom agent

you didn't mention about underlying problem

sequencerr commented 2 months ago

yeah and issue should be called something like

support for tls nodejs cli flags in bun

sequencerr commented 2 months ago

for ws client with tls options you want to use ws package with custom agent

did you figure it out? try this:

import { Agent } from 'https';
import WebSocket from 'ws';

const ws = new WebSocket('wss://yourserver.example.com', {
  agent: new Agent({
    minVersion: 'TLSv1.2',
    maxVersion: 'TLSv1.2'
  }),
});
songinnight commented 2 months ago

I tried many ways before including ws agent handler. However, it doesn't seem to be involved in uws TLS handshaking.

a.mjs

import { Agent } from 'node:https';
import WebSocket from 'ws';

const wsUrl = `wss://live.fantasysportnetwork.com/public/lobby/socket/v2/r6c3i3wejv3qyqc7?messageFormat=json&device=Desktop&instance=uh2kmg-r6c3i3wejv3qyqc7-&EVOSESSIONID=r6c3i3wejv3qyqc7sg2abheehedelugl97355fc628031ff7877c2d1b4065355cc43ced0edceda0af&client_version=6.20240830.72721.44127-54f6972596`;
const startTime = Date.now();
const ws = new WebSocket(wsUrl, {
    agent: new Agent({ // <-- Not working on Bun. It's hang on uWS level
        maxVersion: 'TLSv1.2',
    }),
});
console.log(`▶ --- START WEBSOCKET CONNECTION (using agent) ---`);

ws.addEventListener('open', async (event) => {
    // Cannot reach here or it takes about 10 seconds on Bun
    console.log(`▶ WS OPENED  (${Date.now() - startTime}ms)`);
});

ws.addEventListener('message', (event) => {
    console.log(`▶ MESSAGE: ${event.data} (${Date.now() - startTime}ms)`);
    ws.close();
});

ws.addEventListener('close', event => {
    console.log(`▶ WS CLOSED  (${Date.now() - startTime}ms)`);
});

ws.addEventListener('error', event => {
    console.log(`\n▶▶▶ WS ERROR ${Date.now() - startTime}ms`, event);
});

bun-debug.exe a.mjs Output Log

...
[alloc] new(src.http.websocket_http_client.NewHTTPUpgradeClient(true)) = src.http.websocket_http_client.NewHTTPUpgradeClient(true)@3d8b4431800
[uws] connect(live.fantasysportnetwork.com, 443)
[alloc] new(Request) = src.bun.js.api.bun.dns_resolver.InternalDNS.Request@3d8b4340080
▶ --- START WEBSOCKET CONNECTION (using agent) ---
[WebSocketClient] onOpen
[uws] us_socket_write(*src.deps.boringssl.translated.SSL@1fe667d5788, 417) = 0
[WebSocketClient] onHandshake A (1)
[EventLoop] enter() = 0
[EventLoop] exit() = 0
[uws] us_socket_write(*src.deps.boringssl.translated.SSL@1fe667d5788, 417) = 417

   (...no websocket upgrade response from the server for about 10 seconds)

[WebSocketClient] onData
[WebSocketClient] onDidConnect
[EventLoop] enter() = 0
[alloc] new(src.http.websocket_http_client.NewWebSocketClient(true)) = src.http.websocket_http_client.NewWebSocketClient(true)@3d8b4390180
▶ WS OPENED  (9941ms)
[EventLoop] exit() = 0
[WebSocketClient] onData (need_header)
[WebSocketClient] onData (need_body)
[EventLoop] enter() = 0
▶ MESSAGE: {"type":"connection.kickout","args":{"reason":"inactivity"}} (9942ms)
[WebSocketClient] Sending close with code 1000
[uws] us_socket_write(*src.deps.boringssl.translated.SSL@1fe667d5788, 8) = 8
[EventLoop] enter() = 1
▶ WS CLOSED  (9942ms)
[EventLoop] exit() = 1
[WebSocketClient] clearData
[EventLoop] exit() = 0
[WebSocketClient] onClose
[WebSocketClient] clearData

node a.mjs Output Log

▶ --- START WEBSOCKET CONNECTION (using agent) ---
▶ WS OPENED  (1159ms) <---- in node.js agent works as expected
▶ MESSAGE: {"type":"connection.kickout","args":{"reason":"inactivity"}} (1160ms)
▶ WS CLOSED  (1169ms)
songinnight commented 2 months ago

The weird thing is that the browser has no issues with TLS 1.3. It's responding without a long delay. I really don't know why.

Screen Capture 2024-09-04 045228

Screen Capture 2024-09-04 045645

cirospaciari commented 2 months ago

Currently in Bun we did not implement the options of minVersion and maxVersion on net, tls, http or https modules yet, but we plan to add these options since we wanna to increase node.js compatibility.

sequencerr commented 2 months ago

Well sure compatibility is less prioritized then native bun implementations. But I guess we should figure out what broking stability there and causes delays in tls v1.3 first.

p.s. because I also had problems with tls and fetch api in nodejs but in bun it just automatically puts right version without configs.

songinnight commented 2 months ago

I've found out the underlying problem. It was related to cipher. (It seems to only happen on certain server)

In node.js with ws package I resolved the 10sec delay and no upgrade response problem.

// Node.js
import { WebSocket as WS } from 'ws';

const CIPHERS = [
  // for TLS v1.3
  'TLS_AES_256_GCM_SHA384',
  'TLS_CHACHA20_POLY1305_SHA256 ',
  'TLS_AES_128_GCM_SHA256',
  'TLS_AES_128_CCM_8_SHA256',
];

const ws = new WS(url, {
  // enableTrace: true,
  ciphers: CIPHERS.join(':'),
});

ws.on('upgrade', res => {
  // OK !
  console.log(res?.headers?.['set-cookie']);
});

...

Unfortunately, Bun doesn't support http's request.on('upgrade', (res) => {...}) event. So we can't use the ws. In addition, I need the response header data on upgrade to get set-cookie. (Bun's builtin WebSocket doesn't support the ciphers parameter yet)

These are critical for us. so, I have to go back to node.js. 😂