## What does this PR do?
Fixes https://github.com/oven-sh/bun/issues/22650
Fixes https://github.com/oven-sh/bun/issues/22615
Fixes https://github.com/oven-sh/bun/issues/22603
Fixes https://github.com/oven-sh/bun/issues/22602
Fixes a crash that occurred when running shell commands through `bun
run` (package.json scripts) on Windows that use the `&&` operator
followed by an external command.
### The Problem
The minimal reproduction was:
```bash
bun exec 'echo && node --version'
```
This would crash with: `panic(main thread): attempt to use null value`
### Root Causes
Two issues were causing the crash:
1. **Missing top_level_dir**: When `runPackageScriptForeground` creates
a MiniEventLoop for running package scripts, it wasn't setting the
`top_level_dir` field. This caused a null pointer dereference when the
shell tried to access it.
2. **MovableIfWindowsFd handling**: After PR #21800 introduced
`MovableIfWindowsFd` to handle file descriptor ownership on Windows, the
`IOWriter.fd` could be moved to libuv, leaving it null. When the shell
tried to spawn an external command after a `&&` operator, it would crash
trying to access this null fd.
### The Fix
1. Set `mini.top_level_dir = cwd` after initializing the MiniEventLoop
in `run_command.zig`
2. In `IO.zig`, when the fd has been moved to libuv (is null), use
`.inherit` for stdio instead of trying to pass the null fd
### How did you verify your code works?
- Added a regression test that reproduces the issue
- Verified the test fails without the fix and passes with it
- Tested the minimal reproduction command directly
- The fix correctly allows both commands in the `&&` chain to execute
```bash
# Before fix: crashes
> bun exec 'echo test && node --version'
panic(main thread): attempt to use null value
# After fix: works correctly
> bun exec 'echo test && node --version'
test
v22.4.1
```
<sub>
also probably fixes#22615 and fixes#22603 and fixes#22602
</sub>
---------
Co-authored-by: Zack Radisic <zack@theradisic.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
## Summary
Adds support for overriding special WebSocket headers (`Host`,
`Sec-WebSocket-Key`, and `Sec-WebSocket-Protocol`) via the headers
option when creating a WebSocket connection.
## Changes
- Modified `WebSocketUpgradeClient.zig` to check for and use
user-provided special headers
- Added header value validation to prevent CRLF injection attacks
- Updated the NonUTF8Headers struct to automatically filter duplicate
headers
- When a custom `Sec-WebSocket-Protocol` header is provided, it properly
updates the subprotocols list for validation
## Implementation Details
The implementation adds minimal code by:
1. Using the existing `NonUTF8Headers` struct's methods to find valid
header overrides
2. Automatically filtering out WebSocket-specific headers in the format
method to prevent duplication
3. Maintaining a single, clean code path in `buildRequestBody()`
## Testing
Added comprehensive tests in `websocket-custom-headers.test.ts` that
verify:
- Custom Host header support
- Custom Sec-WebSocket-Key header support
- Custom Sec-WebSocket-Protocol header support
- Header override behavior when both protocols array and header are
provided
- CRLF injection prevention
- Protection of system headers (Connection, Upgrade, etc.)
- Support for additional custom headers
All existing WebSocket tests continue to pass, ensuring backward
compatibility.
## Security
The implementation includes validation to:
- Reject header values with control characters (preventing CRLF
injection)
- Prevent users from overriding critical system headers like Connection
and Upgrade
- Validate header values according to RFC 7230 specifications
## Use Cases
This feature enables:
- Testing WebSocket servers with specific header requirements
- Connecting through proxies that require custom Host headers
- Implementing custom WebSocket subprotocol negotiation
- Debugging WebSocket connections with specific keys
Fixes #[issue_number]
---------
Co-authored-by: Claude Bot <claude-bot@bun.sh>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
### What does this PR do?
Fixes a TLS corruption bug in CONNECT proxy tunneling for HTTPS uploads.
When a large request body is sent over a tunneled TLS connection, the
client could interleave direct socket writes with previously buffered
encrypted bytes, causing TLS records to be emitted out-of-order. Some
proxies/upstreams detect this as a MAC mismatch and terminate with
SSLV3_ALERT_BAD_RECORD_MAC, which surfaced to users as ECONNRESET ("The
socket connection was closed unexpectedly").
This change makes `ProxyTunnel.write` preserve strict FIFO ordering of
encrypted bytes: if any bytes are already buffered, we enqueue new bytes
instead of calling `socket.write` directly. Flushing continues
exclusively via `onWritable`, which writes the buffered stream in order.
This eliminates interleaving and restores correctness for large proxied
HTTPS POST requests.
### How did you verify your code works?
- Local reproduction using a minimal script that POSTs ~20MB over HTTPS
via an HTTP proxy (CONNECT):
- Before: frequent ECONNRESET. With detailed SSL logs, upstream sent
`SSLV3_ALERT_BAD_RECORD_MAC`.
- After: requests complete successfully. Upstream responds as expected
- Verified small bodies and non-proxied HTTPS continue to work.
- Verified no linter issues and no unrelated code changes. The edit is
isolated to `src/http/ProxyTunnel.zig` and only affects the write path
to maintain TLS record ordering.
Rationale: TLS record boundaries must be preserved; mixing buffered data
with immediate writes risks fragmenting or reordering records under
backpressure. Enqueuing while buffered guarantees FIFO semantics and
avoids record corruption.
fixes:
#17434#18490 (false fix in corresponding pr)
---------
Co-authored-by: Ciro Spaciari <ciro.spaciari@gmail.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
### What does this PR do?
Allow upgrade to websockets using fetch
This will avoid hanging in http.request and is a step necessary to
implement the upgrade event in the node:http client.
Changes in node:http need to be made in another PR to support 'upgrade'
event (see https://github.com/oven-sh/bun/pull/22412)
### How did you verify your code works?
Test
---------
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
## Summary
Fixes#18413 - Empty chunked gzip responses were causing `Decompression
error: ShortRead`
## The Issue
When a server sends an empty response with `Content-Encoding: gzip` and
`Transfer-Encoding: chunked`, Bun was throwing a `ShortRead` error. This
occurred because the code was checking if `avail_in == 0` (no input
data) and immediately returning an error, without attempting to
decompress what could be a valid empty gzip stream.
## The Fix
Instead of checking `avail_in == 0` before calling `inflate()`, we now:
1. Always call `inflate()` even when `avail_in == 0`
2. Check the return code from `inflate()`
3. If it returns `BufError` with `avail_in == 0`, then we truly need
more data and return `ShortRead`
4. If it returns `StreamEnd`, it was a valid empty gzip stream and we
finish successfully
This approach correctly distinguishes between "no data yet" and "valid
empty gzip stream".
## Why This Works
- A valid empty gzip stream still has headers and trailers (~20 bytes)
- The zlib `inflate()` function can handle empty streams correctly
- `BufError` with `avail_in == 0` specifically means "need more input
data"
## Test Plan
✅ Added regression test in `test/regression/issue/18413.test.ts`
covering:
- Empty chunked gzip response
- Empty non-chunked gzip response
- Empty chunked response without gzip
✅ Verified all existing gzip-related tests still pass
✅ Tested with the original failing case from the issue
🤖 Generated with [Claude Code](https://claude.ai/code)
---------
Co-authored-by: Claude Bot <claude-bot@bun.sh>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Ciro Spaciari <ciro.spaciari@gmail.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
## Summary
- Implements proper WebSocket subprotocol negotiation per RFC 6455 and
WHATWG standards
- Adds HeaderValueIterator utility for parsing comma-separated header
values
- Fixes WebSocket client to correctly validate server subprotocol
responses
- Sets WebSocket.protocol property to negotiated subprotocol per WHATWG
spec
- Includes comprehensive test coverage for all subprotocol scenarios
## Changes
**Core Implementation:**
- Add `HeaderValueIterator` utility for parsing comma-separated HTTP
header values
- Replace hash-based protocol matching with proper string set comparison
- Implement WHATWG compliant protocol property setting on successful
negotiation
**WebSocket Client (`WebSocketUpgradeClient.zig`):**
- Parse client subprotocols into StringSet using HeaderValueIterator
- Validate server response against requested protocols
- Set protocol property when server selects a matching subprotocol
- Allow connections when server omits Sec-WebSocket-Protocol header (per
spec)
- Reject connections when server sends unknown or empty subprotocol
values
**C++ Bindings:**
- Add `setProtocol` method to WebSocket class for updating protocol
property
- Export C binding for Zig integration
## Test Plan
Comprehensive test coverage for all subprotocol scenarios:
- ✅ Server omits Sec-WebSocket-Protocol header (connection allowed,
protocol="")
- ✅ Server sends empty Sec-WebSocket-Protocol header (connection
rejected)
- ✅ Server selects valid subprotocol from multiple client options
(protocol set correctly)
- ✅ Server responds with unknown subprotocol (connection rejected with
code 1002)
- ✅ Validates CloseEvent objects don't trigger [Circular] console bugs
All tests use proper WebSocket handshake implementation and validate
both client and server behavior per RFC 6455 requirements.
## Issues Fixed
Fixes#10459 - WebSocket client does not retrieve the protocol sent by
the server
Fixes#10672 - `obs-websocket-js` is not compatible with Bun
Fixes#17707 - Incompatibility with NodeJS when using obs-websocket-js
library
Fixes#19785 - Mismatch client protocol when connecting with multiple
Sec-WebSocket-Protocol
This enables obs-websocket-js and other libraries that rely on proper
RFC 6455 subprotocol negotiation to work correctly with Bun.
🤖 Generated with [Claude Code](https://claude.ai/code)
---------
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Replace `catch bun.outOfMemory()`, which can accidentally catch
non-OOM-related errors, with either `bun.handleOom` or a manual `catch
|err| switch (err)`.
(For internal tracking: fixes STAB-1070)
---------
Co-authored-by: Dylan Conway <dylan.conway567@gmail.com>
### What does this PR do?
This PR adds builtin YAML parsing with `Bun.YAML.parse`
```js
import { YAML } from "bun";
const items = YAML.parse("- item1");
console.log(items); // [ "item1" ]
```
Also YAML imports work just like JSON and TOML imports
```js
import pkg from "./package.yaml"
console.log({ pkg }); // { pkg: { name: "pkg", version: "1.1.1" } }
```
### How did you verify your code works?
Added some tests for YAML imports and parsed values.
---------
Co-authored-by: Claude Bot <claude-bot@bun.sh>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Jarred Sumner <jarred@jarredsumner.com>
* Move `DebugThreadLock` to `bun.safety`
* Enable in `ci_assert` builds, but store stack traces only in debug
builds
* Reduce size of struct by making optional field non-optional
* Add `initLockedIfNonComptime` as a workaround for not being able to
call `initLocked` in comptime contexts
* Add `lockOrAssert` method to acquire the lock if unlocked, or else
assert that the current thread acquired the lock
* Add stack traces to `CriticalSection` and `AllocPtr` in debug builds
* Make `MimallocArena.init` infallible
* Make `MimallocArena.heap` non-nullable
* Rename `RefCount.active_counts` to `raw_count` and provide read-only
`get` method
* Add `bun.safety.alloc.assertEq` to assert that two allocators are
equal (avoiding comparison of undefined `ptr`s)
(For internal tracking: fixes STAB-917, STAB-918, STAB-962, STAB-963,
STAB-964, STAB-965)
---------
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
### What does this PR do?
### How did you verify your code works?
---------
Co-authored-by: Claude Bot <claude-bot@bun.sh>
Co-authored-by: Claude <noreply@anthropic.com>
### What does this PR do?
Remove some duplicate code
### How did you verify your code works?
Ran the tests
---------
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>