Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

## Unreleased

- Retain corked HTTP response writes in the socket Writable queue, preserving raw-write ordering, backpressure, and callbacks through uncork and end. Adapts dispatcher-cork cleanup from [oven-sh/bun#35664](https://github.com/oven-sh/bun/pull/35664); thanks robobun.

- Match Node.js `SourceMap.findOrigin()` coordinates and field names, and return the closest preceding mapping from `findEntry()`. Adapts [oven-sh/bun#35123](https://github.com/oven-sh/bun/pull/35123); thanks robobun.

- Bind `FileHandle.close` to its handle like Node.js, preventing spurious finalizer errors and accidental descriptor closes after closing through a Proxy.
Expand Down
2 changes: 2 additions & 0 deletions docs/runtime/nodejs-compat.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ On macOS, `fs.copyFile` and `fs.cp` accept regular-file `/dev/fd` sources withou

### [`node:http`](https://nodejs.org/api/http.html)

`ServerResponse.cork()` retains writes until uncork or end. Response and raw socket writes share the socket Writable queue while corked or draining, preserving their order and backpressure. Response and socket cork counts match Node.js.

`ServerResponse.writableFinished` stays false while a standalone response has output queued on its assigned socket.

`net`, `http`, and `https` servers validate listen ports synchronously with `ERR_SOCKET_BAD_PORT`. Bind failures such as `EADDRINUSE` emit an asynchronous `error` event.
Expand Down
2 changes: 1 addition & 1 deletion packages/bun-uws/src/HttpContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -918,7 +918,7 @@ struct HttpContext {
auto *httpContextData = getSocketContextDataS(s);


if (httpResponseData->isConnectRequest && httpResponseData->socketData && httpContextData->onSocketDrain) {
if ((IsNodeHttp || httpResponseData->isConnectRequest) && httpResponseData->socketData && httpContextData->onSocketDrain) {
httpContextData->onSocketDrain(httpResponseData->socketData, SSL, (struct us_socket_t *) s);
}
/* Ask the developer to write data and return success (true) or failure (false), OR skip sending anything and return success (true). */
Expand Down
2 changes: 2 additions & 0 deletions src/js/internal/streams/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ const kIsWritable = SymbolFor("nodejs.stream.writable");
const kIsDisturbed = SymbolFor("nodejs.stream.disturbed");

const kOnConstructed = Symbol("kOnConstructed");
const kWriteOperation = Symbol("kWriteOperation");

const kIsClosedPromise = SymbolFor("nodejs.webstream.isClosedPromise");
const kControllerErrorFunction = SymbolFor("nodejs.webstream.controllerErrorFunction");
Expand Down Expand Up @@ -269,6 +270,7 @@ function isErrored(stream) {

export default {
kOnConstructed,
kWriteOperation,
isDestroyed,
kIsDestroyed,
isDisturbed,
Expand Down
10 changes: 10 additions & 0 deletions src/js/internal/streams/writable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ const { addAbortSignal } = require("internal/streams/add-abort-signal");
const { getHighWaterMark, getDefaultHighWaterMark } = require("internal/streams/state");
const {
kOnConstructed,
kWriteOperation,
kState,
kObjectMode,
kErrorEmitted,
Expand Down Expand Up @@ -437,6 +438,15 @@ function _write(stream, chunk, encoding, cb?) {
}
}

return writeOrError(stream, state, chunk, encoding, cb);
}

// Internal byte-counted operations share the byte stream's queue and lifecycle.
Writable.prototype[kWriteOperation] = function (operation, callback) {
return writeOrError(this, this._writableState, operation, kWriteOperation, callback ?? nop) === true;
Comment on lines +445 to +446

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Normalize optional informational-response callbacks

For a corked writeEarlyHints() or writeProcessing() call, a non-function callback such as 123 reaches this path. The direct _writeRaw path and Node ignore non-function optional callbacks, but callback ?? nop stores the value as Writable's completion callback, so afterWrite later attempts to invoke it and raises an uncaught TypeError. Normalize this argument to nop unless it is callable before passing it into writeOrError.

AGENTS.md reference: AGENTS.md:L187-L191

Useful? React with 👍 / 👎.

};

function writeOrError(stream, state, chunk, encoding, cb) {
let err;
if ((state[kState] & kEnding) !== 0) {
err = $ERR_STREAM_WRITE_AFTER_END();
Expand Down
1 change: 1 addition & 0 deletions src/js/node/_http_outgoing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1292,6 +1292,7 @@ ObjectDefineProperty(OutgoingMessage.prototype, "_headerNames", {
});

export default {
kCorked,
kHighWaterMark,
kUniqueHeaders,
kOutHeaders,
Expand Down
Loading
Loading