Skip to content

tls: report a fatal post-handshake SSL error instead of a clean close - #41463

Closed
robobun wants to merge 6 commits into
mainfrom
robobun/30172bf2/tls-post-handshake-ssl-error
Closed

robobun wants to merge 6 commits into
mainfrom
robobun/30172bf2/tls-post-handshake-ssl-error

Conversation

@robobun

@robobun robobun commented Sep 6, 2026 •

Copy link
Copy Markdown
Collaborator

Problem

  • A TLS record that fails after the handshake (a bad MAC, a wrong version, a peer's fatal alert) makes SSL_read fail with SSL_ERROR_SSL. uSockets dropped the OpenSSL reason and closed the socket with code 0. node:tls emitted 'end' then 'close' with hadError false. Bun.listen / Bun.connect got a close with no error. Node reports the socket's ERR_SSL_<REASON> 'error' (for example ERR_SSL_DECRYPTION_FAILED_OR_BAD_RECORD_MAC), then 'close' with hadError true.
  • The cause is the fatal branch of us_internal_ssl_on_data (packages/bun-usockets/src/crypto/openssl.c:2656). ssl_park_fatal_reason only parks a reason while the handshake is unfinished, because the handshake-failure dispatch is its only consumer. After the handshake the reason went nowhere and the close looked clean.

Fix

  • us_internal_ssl_on_data closes with the reason string (ssl_close(s, 0, reason)) once the handshake has completed. The alert BoringSSL sealed on the failure still goes out first, so the peer gets its own ERR_SSL_*ALERT_* error.
  • NewSocket::on_close turns a non-null close reason into the same EPROTO SystemError the handshake-failure path already uses (code: "EPROTO", syscall: "read", message is the error:...:SSL routines:... string). The raw half of an upgradeTLS pair keeps its plain close.
  • node:net decomposes that error with the existing tlsHandshakeError into library / function / reason and ERR_SSL_<REASON>, and destroys the socket with it, listener or not, like node's TLSWrap onerror.
  • The root-cause picker now takes the oldest SSL-library entry from the error queue. BoringSSL queues Cipher functions:BAD_DECRYPT ahead of SSL routines:DECRYPTION_FAILED_OR_BAD_RECORD_MAC, where OpenSSL queues only the SSL entry. This applies to the handshake path too.
  • Verified: test/js/node/tls/node-tls-server.test.ts ("a bad record after the handshake surfaces as ERR_SSL_* on both peers") and test/js/bun/net/socket.test.ts ("a TLS record that fails to decrypt after the handshake closes both peers with an EPROTO error"). Both fail on 1.4.3-canary (the server sees a clean end). Also test/js/node/tls/, test/js/node/http2/, test/js/node/https/, test/js/bun/net/, and the node test-tls-* / test-https-* parallel files.

Background

  • uSockets TLS: loop.c routes readable events for a socket with s->ssl to us_internal_ssl_on_data, which runs SSL_read and dispatches plaintext. A fatal SSL_read sends the alert through the write BIO and the function closes the socket.
  • Close reason: us_socket_close(s, code, reason) carries an opaque reason pointer to the group's on_close. Nothing in bun passed a non-null one before. It is now the OpenSSL reason string (a stack buffer that lives for the close dispatch).
  • Parked reason: ssl_park_fatal_reason stores the OpenSSL error string per loop for a failure before the handshake is reported. ssl_dispatch_parked_reason turns it into the EPROTO handshake verdict that node:tls already maps to ERR_SSL_*.
  • Error queue: OpenSSL keeps a per-thread error queue. Node reports the oldest entry (crypto_tls.cc). BoringSSL's layering can put a lower-layer entry first.
Notes

A TLS record that fails after the handshake (a bad MAC, a wrong version,
a peer alert) made SSL_read fail with SSL_ERROR_SSL. uSockets dropped
the OpenSSL reason and closed the socket with code 0, so node:tls
emitted 'end' and 'close' with hadError false, and Bun.listen/connect
saw a close with no error. Node reports the reason as the socket's
ERR_SSL_<REASON> 'error' and then 'close' with hadError true.

us_internal_ssl_on_data now closes with the reason string once the
handshake has completed. NewSocket::on_close turns it into the same
EPROTO SystemError the handshake failure path uses, and node:net
decomposes it into library/function/reason and the ERR_SSL_* code.

The root-cause picker prefers the oldest SSL-library entry in the
error queue: BoringSSL queues Cipher functions:BAD_DECRYPT ahead of
SSL routines:DECRYPTION_FAILED_OR_BAD_RECORD_MAC, where OpenSSL queues
only the SSL entry.
@coderabbitai

coderabbitai Bot commented Sep 6, 2026 •

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

  • Run on-demand review

On-demand reviews are free for the next 15 days. After that, they cost $0.25 per reviewed file.

Or wait 44 seconds for your next included review.

Check out review usage here.

View limit details

Limit details: You’ve used all 10 included reviews currently available.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Essentials

Run ID: 0024ac08-8c61-450a-8aa4-c52bf0548154

📥 Commits

Reviewing files that changed from the base of the PR and between f42e980 and 1be57a8.

📒 Files selected for processing (5)
  • packages/bun-usockets/src/crypto/openssl.c
  • src/js/node/net.ts
  • src/runtime/socket/socket_body.rs
  • test/js/bun/net/socket.test.ts
  • test/js/node/tls/node-tls-server.test.ts

Comment @coderabbitai help to get the list of available commands.

@robobun

robobun commented Sep 6, 2026 •

Copy link
Copy Markdown
Collaborator Author

Status: reproduced with a TCP proxy that injects a bad application_data record after the server's handshake. node v26.3.0 reports ERR_SSL_DECRYPTION_FAILED_OR_BAD_RECORD_MAC on the server and the alert on the client. bun reported a clean end on both.

Closed in favour of #41272, which is the older fix for the same branch and matches Node's event order. The unique pieces of this branch (the bad-record proxy test, the SSL-library error picker on the handshake path) are noted there.

@github-actions github-actions Bot added the claude label Sep 6, 2026
@robobun

robobun commented Sep 6, 2026 •

Copy link
Copy Markdown
Collaborator Author
Updated 11:50 PM PT - Sep 5th, 2026

❌ @robobun, your commit 1be57a8 has 1 failures in Build #110719 (All Failures):


🧪   To try this PR locally:

bunx bun-pr 41463

That installs a local version of the PR into your bun-41463 executable, so you can run:

bun-41463 --bun

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nothing blocking. The comments below are optional suggestions. There is no need to push a fix for them before merging.

@robobun

robobun commented Sep 6, 2026

Copy link
Copy Markdown
Collaborator Author

Applied the review suggestion: both tests now release the proxy and the server in a finally block (node-tls-server.test.ts) or through using (socket.test.ts) before the assertions run. The push follows with the next commit.

Comment thread src/js/node/net.ts Resolved
Comment thread src/js/node/net.ts Resolved
Comment thread src/runtime/socket/socket_body.rs Resolved
Comment thread src/runtime/socket/socket_body.rs Resolved
Comment thread src/runtime/socket/socket_body.rs Resolved
Comment thread src/js/node/net.ts Resolved
Comment thread src/runtime/socket/socket_body.rs Resolved
Comment thread src/runtime/socket/socket_body.rs Resolved

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nothing blocking. The comments below are optional suggestions. There is no need to push a fix for them before merging.

Beyond the inline finding, I also checked whether the new err.code === "EPROTO" gate in SocketHandlers2.close / SocketEmitEndNT could misfire on plain TCP sockets — the Rust side only builds the EPROTO SystemError when const SSL = true (tls_close_reason::<SSL> returns None otherwise), and tlsHandshakeError only decomposes into ERR_SSL_* when the message matches the OpenSSL format, so a stray non-TLS EPROTO would not get the wrong library/reason fields. The earlier note about test cleanup ordering has been addressed by the try/finally and using _proxy disposable in the follow-up commit.

Extended reasoning...

The confirmed inline finding covers the sibling SSLWrapper::handle_reading path in src/uws/lib.rs that this PR does not touch, so a human should look at scope. Separately, this run examined whether the JS-side EPROTO gate is over-broad (it is the default handler for plain TCP too) and ruled it out: the Rust tls_close_reason::<SSL> helper is const-gated on the SSL variant so a non-TLS close never carries the OpenSSL reason string, and tlsHandshakeError's regex only assigns ERR_SSL_* when the message is an OpenSSL error string. The stack-buffer reason[US_SSL_FATAL_ERROR_REASON_MAX] lifetime was also checked — ssl_close dispatches on_close synchronously within the same frame, so the pointer does not outlive its backing memory.

Additional findings (outside the current diff — GitHub can't attach inline comments there):

  • 🟣 src/uws/lib.rs — The fix only covers TLS backed by a real us_socket_t; the sibling SSLWrapper::handle_reading path (used by upgradeDuplexToTLS and Windows named-pipe TLS) still ERR_clear_error()s at line 992 and calls trigger_close_callback() with no reason on SSL_ERROR_SSL/SYSCALL, so tls.connect({ socket: <Duplex or named pipe> }) keeps emitting a clean end/close on a bad record instead of ERR_SSL_* (pre-existing on this path). Fix: capture the root-cause reason here the same way and thread it through trigger_close_callback → DuplexUpgradeContext::on_close (socket_body.rs:4408) / WindowsNamedPipeContext::on_close into TLSSocket::on_close so both SSL_read implementations report the failure.

    Extended reasoning...

    tls.connect({ socket }) with socket a generic Duplex, a Windows named-pipe net.Socket, or a net.Socket with unflushed writes (net.ts:1993, 2027, 2078) takes upgradeDuplexToTLS, which drives BoringSSL through SSLWrapper in src/uws/lib.rs, not through us_internal_ssl_on_data. When a post-handshake record fails to decrypt or a peer alert arrives, handle_reading at src/uws/lib.rs:989-1044 gets SSL_ERROR_SSL, immediately does ERR_clear_error() (line 992), sets fatal_error, and calls trigger_close_callback() (line 1063) with no reason payload. That reaches DuplexUpgradeContext::on_close (socket_body.rs:4395) which forwards TLSSocket::on_close(p, socket, 0, None) — code 0, reason None — so the new tls_close_reason::<SSL> branch never fires and node:tls still emits end then close(hadError=false). Node reports ERR_SSL_<REASON> regardless of transport. REVIEW.md requires same-class sibling sites ("fast/slow paths, POSIX/Windows branches") to be fixed together; the openssl.c change is one of two SSL_read drivers in the tree.

    Verification: pre-existing — The sibling path is real and unfixed. src/uws/lib.rs is untouched by this diff (0 lines changed). SSLWrapper::handle_reading at lines 989-1063: on just_read <= 0 it calls SSL_get_error then immediately boring_sys::ERR_clear_error() at line 992 (discarding the OpenSSL reason queue), and for SSL_ERROR_SSL/SSL_ERROR_SYSCALL only does self.flags.set_fatal_error(true)…

@robobun

robobun commented Sep 6, 2026

Copy link
Copy Markdown
Collaborator Author

On the SSLWrapper sibling (src/uws/lib.rs, the Duplex and named-pipe TLS path): it is a separate SSL_read driver with its own handler table, and #32929 (open) already adds the handshake-time reason there. The post-handshake counterpart for that path is tracked as a follow-up on top of #32929, so this PR stays on the us_socket_t path. Noted in the PR body.

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Findings marked 🟡 are optional suggestions and need no follow-up push.

Comment thread src/js/node/net.ts Resolved
The EPROTO close branch returned before the pending-write settlement, so
a write(buf, cb) still waiting on the native drain never got its
callback. Fail it with the same ERR_SSL_* error first, like the native
error dispatch does. The test now fails on the close when no error is
emitted instead of waiting for the timeout.
Comment thread src/js/node/net.ts Resolved
Comment thread src/js/node/net.ts Resolved

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code review found no issues

No high-confidence issues detected in this change.

@robobun

robobun commented Sep 6, 2026

Copy link
Copy Markdown
Collaborator Author

Closing: #41272 is the older PR for the same branch of us_internal_ssl_on_data and follows Node's post-handshake order (error, then end, then close with hadError false). The bad-record proxy test and the handshake-path use of the SSL-library error picker from this branch are noted on #41272.

@robobun

robobun commented Sep 6, 2026

Copy link
Copy Markdown
Collaborator Author

The SSLWrapper counterpart (node:tls over a Duplex or a Windows named pipe) is #41507, stacked on this branch. It exports the error-queue picker added here as us_ssl_take_fatal_reason so both engines select the same entry.

cirospaciari pushed a commit that referenced this pull request Sep 11, 2026
… a clean close (#41507)

Stacked on #41463. Review the last commit only.

### Problem
- `tls.connect({ socket: <Duplex> })` (and node:tls over a Windows named
pipe) emits a clean `'end'` and `'close'` with `hadError` false when a
TLS record fails after the handshake: a bad MAC, a wrong version, a
peer's fatal alert. Node emits the socket's `ERR_SSL_<REASON>` `'error'`
(for example `ERR_SSL_DECRYPTION_FAILED_OR_BAD_RECORD_MAC`).
- The cause is `SSLWrapper::handle_reading` (`src/uws/lib.rs:1030`). On
`SSL_ERROR_SSL` it called `ERR_clear_error()` right after
`SSL_get_error()` and closed through `Handlers::on_close`, which carried
no reason. The alert BoringSSL sealed into the write BIO was dropped
too. #41463 fixes the same bug for the uSockets engine. The SSLWrapper
engine is a separate `SSL_read` driver, so that fix does not reach it.

### Fix
- `handle_reading` takes the root-cause reason before the queue is
cleared, through `us_ssl_take_fatal_reason`, the picker openssl.c uses
for its own fatal close. It then flushes the sealed alert to the peer
and closes with the reason.
- `Handlers::on_close` carries `Option<&CStr>`. `UpgradedDuplex` and
`WindowsNamedPipe` forward it into `TLSSocket::on_close` through the
close reason slot that #41463 added. A named pipe close deferred by a
busy writer keeps an owned copy. The fetch and WebSocket proxy tunnels
ignore it. `write_data` no longer closes the wrapper while a fatal read
is mid-dispatch, so a `'data'` listener that writes back cannot hide the
reason.
- Correct because the reason then reaches JS through the same `EPROTO`
shape and the same `node:net` decomposition as the uSockets path. Both
engines report one result for one failure.
- Verified: `test/js/node/tls/node-tls-connect.test.ts` ("tls.connect
over a Duplex reports a fatal post-handshake SSL error", three cases).
All three fail on the #41463 base and pass with this branch. The same
cases also pass on Node.js v26.3.0 (see the comment below). Also
`test/js/node/tls/`, `test/js/bun/net/socket.test.ts`, and the fetch and
WebSocket proxy suites.

### Background
- `SSLWrapper` (`src/uws/lib.rs`) runs BoringSSL over memory BIOs for
transports that are not a `us_socket_t`: a generic `Duplex`, a Windows
named pipe, the fetch and WebSocket proxy tunnels. It calls its owner
back through `Handlers` (`on_data`, `write`, `on_close`).
- Error queue: OpenSSL keeps a per-thread error queue. A fatal
`SSL_read` leaves the reason there. Node reports the oldest entry.
BoringSSL can queue a cipher-layer entry first, so the picker takes the
oldest SSL-library entry.
- Fatal alert: on a failed read BoringSSL writes an alert record into
the write BIO. Nothing pumps that BIO after a close, so `handle_reading`
now flushes it before the close. The peer then gets its own
`ERR_SSL_*ALERT_*` error.

<details><summary>Notes</summary>

- Repro: a TCP proxy between a `Duplex`-wrapped client and
`tls.createServer`. After `secureConnect`, write an application_data
record (`17 03 03 00 20` plus 32 bytes of `0x42`) toward the client.
Node v26.3.0: `error ERR_SSL_DECRYPTION_FAILED_OR_BAD_RECORD_MAC`. Bun
1.4.3-canary: `end`, `close false`. With the fix: `error
ERR_SSL_DECRYPTION_FAILED_OR_BAD_RECORD_MAC`, `close true`.
- Written toward the server instead, the server's `bad_record_mac` alert
reaches the client as `ERR_SSL_SSLV3_ALERT_BAD_RECORD_MAC` (BoringSSL's
name for the alert).
- Node leaves the TLSSocket open after the `'error'`. Bun destroys it
with the error, because the engine's only exit on a fatal read is a
close. #41463 made the same choice for the uSockets path.
- The alert flush was checked on the wire: the client sends a 19-byte
application_data record (a TLS 1.3 encrypted alert) before the FIN.
- The tests assert only what holds on both runtimes: the first event is
`'error'` with the `ERR_SSL_*` code and `library: "SSL routines"`. The
alert's code name comes from `process.features.openssl_is_boringssl`
(BoringSSL: `SSLV3_ALERT_BAD_RECORD_MAC`, OpenSSL 3:
`SSL/TLS_ALERT_BAD_RECORD_MAC`).
- Self-reviewed: 9 concerns raised, 7 addressed (stacked on #41463
instead of main, the shared `net.ts` and `NewSocket::on_close` hunks
dropped, the picker shared with openssl.c instead of a Rust copy, the
node `'close'` wording fixed). Not addressed: the Http2SecureServer
upgrade path (`_http2_upgrade.ts` `socketClose`) still drops the reason
on this engine, and #32929 (the handshake-time reason on this engine) is
not stacked under this PR. Both are separate changes.
- #41272 is an alternative shape for the uSockets path (an `error`
dispatch before the close). It states that it does not cover the
SSLWrapper engine. If that shape lands instead of #41463, the reason
capture and the alert flush here stay, and only the final dispatch in
`DuplexUpgradeContext::on_close` changes.
- Pre-existing failures in this container, not from this change:
`SNICallback runs even when the requested servername matches the bind
hostname` (localhost dual-stack) and `socket > should not call drain
before handshake` (needs www.example.com).
- Cross-checked with `cargo check -p bun_runtime --target
x86_64-pc-windows-msvc`.

</details>

<!-- robobun:evidence:begin -->

---

**[human-review]** gate passed · iteration 1 · 9 files touched

<details><summary>fails on main (without fix)</summary>

```console
ASAN without fix: 3 failed, 18 skipped
$ BUN_DEBUG_QUIET_LOGS=1 bun scripts/build.ts --profile=debug --quiet test "--reporter=junit" "--reporter-outfile=/tmp/pr_gate.xml" test/js/node/tls/node-tls-connect.test.ts
bun test v1.4.3 (4ff9193)

test/js/node/tls/node-tls-connect.test.ts:
(pass) should have checkServerIdentity [2.59ms]
(pass) should thow ECONNRESET if FIN is received before handshake [323.96ms]
(pass) initializes authorizationError to null in the TLSSocket constructor [7.81ms]
(pass) setMaxSendFragment mirrors OpenSSL's [512, 16384] acceptance without throwing [113.40ms]
(pass) should be able to grab the JSStreamSocket constructor [13.14ms]
(skip) tls.connect > should work with alpnProtocols
(pass) tls.connect > Bun.serve() should work with tls and Bun.file() [80.19ms]
(pass) tls.connect > should have peer certificate when using self asign certificate [83.18ms]
(skip) tls.connect > should have peer certificate
(skip) tls.connect > getCipher, getProtocol, getEphemeralKeyInfo, getSharedSigalgs, getSession, exportKeyingMaterial and isSessionReused should work
(skip) tls.connect > should process options correctly when connect is called with only options
(skip) tls.connect > should process port an
... (truncated)

release without fix: 3 failed, 18 skipped
bun test v1.4.3-canary.1 (4ff9193)

test/js/node/tls/node-tls-connect.test.ts:
(pass) should have checkServerIdentity [0.06ms]
(pass) should thow ECONNRESET if FIN is received before handshake [6.51ms]
(pass) initializes authorizationError to null in the TLSSocket constructor [0.16ms]
(pass) setMaxSendFragment mirrors OpenSSL's [512, 16384] acceptance without throwing [3.49ms]
(pass) should be able to grab the JSStreamSocket constructor [0.21ms]
(skip) tls.connect > should work with alpnProtocols
(pass) tls.connect > Bun.serve() should work with tls and Bun.file() [7.36ms]
(pass) tls.connect > should have peer certificate when using self asign certificate [4.22ms]
(skip) tls.connect > should have peer certificate
(skip) tls.connect > getCipher, getProtocol, getEphemeralKeyInfo, getSharedSigalgs, getSession, exportKeyingMaterial and isSessionReused should work
(skip) tls.connect > should process options correctly when connect is called with only options
(skip) tls.connect > should process port and host correctly
(skip) tls.connect > should process port, host, and callback correctly
(skip) tls.connect > should handle the absence of a callback gracefully
(skip) tls.c
... (truncated)
```

</details>

<details><summary>passes on PR (with fix)</summary>

```console
ASAN with fix: 18 skipped
$ BUN_DEBUG_QUIET_LOGS=1 bun scripts/build.ts --profile=debug --quiet test "--reporter=junit" "--reporter-outfile=/tmp/pr_gate.xml" test/js/node/tls/node-tls-connect.test.ts
bun test v1.4.3 (4ff9193)

test/js/node/tls/node-tls-connect.test.ts:
(pass) should have checkServerIdentity [2.58ms]
(pass) should thow ECONNRESET if FIN is received before handshake [316.01ms]
(pass) initializes authorizationError to null in the TLSSocket constructor [7.39ms]
(pass) setMaxSendFragment mirrors OpenSSL's [512, 16384] acceptance without throwing [113.24ms]
(pass) should be able to grab the JSStreamSocket constructor [12.50ms]
(skip) tls.connect > should work with alpnProtocols
(pass) tls.connect > Bun.serve() should work with tls and Bun.file() [75.11ms]
(pass) tls.connect > should have peer certificate when using self asign certificate [80.40ms]
(skip) tls.connect > should have peer certificate
(skip) tls.connect > getCipher, getProtocol, getEphemeralKeyInfo, getSharedSigalgs, getSession, exportKeyingMaterial and isSessionReused should work
(skip) tls.connect > should process options correctly when connect is called with only options
(skip) tls.connect > should process port an
... (truncated)

release with fix: 18 skipped
$ bun scripts/build.ts --profile=release
[configured] bun-profile → bun (stripped) in 602ms (unchanged)
ninja: Entering directory `/workspace/bun/build/release'
[1/7] cc obj/packages/bun-usockets/src/crypto/openssl.c.o
[2/7] gen generated_host_exports.rs
generated_host_exports.rs: 122 exports (host=5, lazy=10, generic=107, rust=0); 242 extern-C blocks audited
[2/7] cargo bun_runtime → libbun_runtime.a
�[1m�[92m   Compiling�[0m bun_core v0.0.0 (/workspace/bun/src/bun_core)
�[1m�[92m   Compiling�[0m bun_errno v0.0.0 (/workspace/bun/src/errno)
�[1m�[92m   Compiling�[0m bun_ptr v0.0.0 (/workspace/bun/src/ptr)
�[1m�[92m   Compiling�[0m bun_boringssl_sys v0.0.0 (/workspace/bun/src/boringssl_sys)
�[1m�[92m   Compiling�[0m bun_safety v0.0.0 (/workspace/bun/src/safety)
�[1m�[92m   Compiling�[0m bun_base64 v0.0.0 (/workspace/bun/src/base64)
�[1m�[92m   Compiling�[0m bun_cares_sys v0.0.0 (/workspace/bun/src/cares_sys)
�[1m�[92m   Compiling�[0m bun_zlib_sys v0.0.0 (/workspace/bun/src/zlib_sys)
�[1m�[92m   Compiling�[0m bun_zstd v0.0.0 (/workspace/bun/src/zstd)
�[1m�[92m   Compiling�[0m bun_picohttp v0.0.0 (/workspace/bun/src/picohttp)
�[1m�[92m   Compiling�[0m bun_brotli v0
... (truncated)
```

</details>

<details><summary>diff hotspot</summary>

```
packages/bun-usockets/src/crypto/openssl.c         |  20 +++-
 src/http/ProxyTunnel.rs                            |   2 +-
 .../websocket_client/WebSocketProxyTunnel.rs       |   2 +-
 src/runtime/socket/UpgradedDuplex.rs               |   7 +-
 src/runtime/socket/WindowsNamedPipe.rs             |  22 +++-
 src/runtime/socket/WindowsNamedPipeContext.rs      |   8 +-
 src/runtime/socket/socket_body.rs                  |  18 +--
 src/uws/lib.rs                                     |  73 +++++++++---
 test/js/node/tls/node-tls-connect.test.ts          | 124 +++++++++++++++++++++
 9 files changed, 234 insertions(+), 42 deletions(-)
```

</details>

**gate history** · 3 passed · 0 rejected · iteration 1

<details><summary>evidence per changed file</summary>

```
file                                                   reads  edits  tests
packages/bun-usockets/src/crypto/openssl.c                 0      0     25
src/http/ProxyTunnel.rs                                    0      0     25
src/http_jsc/websocket_client/WebSocketProxyTunnel.rs      0      0     25
src/runtime/socket/UpgradedDuplex.rs                       1      2     25
src/runtime/socket/WindowsNamedPipe.rs                     1      0     25
src/runtime/socket/WindowsNamedPipeContext.rs              1      0     25
src/runtime/socket/socket_body.rs                          3      5     25
src/uws/lib.rs                                             6      7     25
test/js/node/tls/node-tls-connect.test.ts                  8      4     25
```

</details>

<!-- robobun:evidence:end -->

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants