Skip to content

perf: relay untouched net.fetch responses from protocol.handle without pumping them through JS - #53378

Merged
MarshallOfSound merged 1 commit into
44-x-yfrom
trop/44-x-y-bp-perf-relay-untouched-net-fetch-responses-from-protocol-handle-without-pumping-them-through-js-1788291925207
Sep 3, 2026
Merged

MarshallOfSound merged 1 commit into
44-x-yfrom
trop/44-x-y-bp-perf-relay-untouched-net-fetch-responses-from-protocol-handle-without-pumping-them-through-js-1788291925207

Conversation

@trop

@trop trop Bot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Backport of #52953

See that PR for details.

Notes: Improved throughput of responses that protocol.handle handlers return straight from net.fetch.

…t pumping them through JS (#52953)

* perf: relay untouched net.fetch responses from protocol.handle without pumping them through JS

protocol.handle(scheme, req => net.fetch(...)) is the documented way to
serve an app's own files from a custom scheme and to proxy a remote origin
through one, and every byte of such a response crossed the browser main
thread twice: SimpleURLLoader's chunks were copied into ArrayBuffers for
an IncomingMessage, wrapped into a web ReadableStream for the Response,
unwrapped again with Readable.fromWeb and written back into a mojo pipe by
NodeStreamLoader in 64 KB slices.

When a handler returns the Response net.fetch produced with its body still
untouched (the exact stream net.fetch created, not read, locked, cloned or
replaced), protocol.handle now tells the underlying SimpleURLLoaderWrapper
to stop handing chunks to JS, collects whatever JS had already pulled as a
prefix, and passes the client end to the wrapper, which writes prefix and
remainder straight into the response pipe and completes the client with
the request's outcome. Status and headers of the returned Response
(including ones the handler set) are used as before; anything else - a
re-wrapped or re-created Response, a body the handler started reading,
Response.error() - keeps the existing path. The response pipe grows to the
upstream's declared length (up to the network service default) but never
below mojo's default. Works for any scheme net.fetch can load.

Renderer fetch() through such a handler, release build, medians of
interleaved fresh-process runs: forwarding to file:// 16 MB 40.0 -> 24.1
ms, 1 MB 4.5 -> 3.8 ms, 64 KB 2.0 -> 1.9 ms; forwarding to a local http
server (cache: 'no-store') 16 MB 48.2 -> 24.2 ms, 1 MB 4.0 -> 3.3 ms,
64 KB unchanged. With the HTTP cache writing the proxied body through, the
cache write dominates either way (16 MB ~480 ms on this disk).

* refactor: move response body holding/relaying out of SimpleURLLoaderWrapper

Review follow-up: SimpleURLLoaderWrapper no longer tracks relay state itself.
A ResponseBody object is the SimpleURLLoader stream consumer; it hands chunks
to its delegate (the wrapper, which emits them to JS) until held, and once
RelayTo() gives it a destination it writes the held and all further chunks
into that client's data pipe and completes the client when the request
completes. The wrapper keeps only the JS-facing events and request lifetime.

* fix: complete relayed responses whose fetch finished before hold()

ResponseBody::OnComplete() returned before recording the result when the
body had not been held yet, so a handler that awaited between net.fetch()
and returning the response handed RelayTo() a finished request with no
result to complete the client with, and the load hung. Record the result
unconditionally, and when JS has already drained the whole body before
the relay decision, answer with that data instead of a native relay.

Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
@trop
trop Bot requested a review from codebytere September 1, 2026 19:45
@trop trop Bot added 44-x-y backport This is a backport PR semver/patch backwards-compatible bug fixes labels Sep 1, 2026
@MarshallOfSound
MarshallOfSound merged commit 81fe24e into 44-x-y Sep 3, 2026
89 of 91 checks passed
@MarshallOfSound
MarshallOfSound deleted the trop/44-x-y-bp-perf-relay-untouched-net-fetch-responses-from-protocol-handle-without-pumping-them-through-js-1788291925207 branch September 3, 2026 17:16
@release-clerk

release-clerk Bot commented Sep 3, 2026

Copy link
Copy Markdown

Release Notes Persisted

Improved throughput of responses that protocol.handle handlers return straight from net.fetch.

renovate Bot added a commit to cigaleapp/cigale that referenced this pull request Sep 13, 2026
##### [v44.2.0](https://github.com/electron/electron/releases/tag/v44.2.0)

### Release Notes for v44.2.0

#### Fixes

- Fixed a pending read on a `net.request` chunked upload stream inside a protocol handler never settling when the request failed or was aborted. [#53373](electron/electron#53373) <sup>(Also in [43](electron/electron#53374), [45](https://github.com/electron/electron/pull/53368))</sup>
- Fixed a potential crash when using `menu.popup` with a frame from an offscreen rendered window. [#53362](electron/electron#53362) <sup>(Also in [43](https://github.com/electron/electron/pull/53432))</sup>
- Fixed an intermittent crash (access violation) on Windows when an ASAR integrity violation is detected, so the process now exits with code 1 as intended. [#53455](electron/electron#53455) <sup>(Also in [43](electron/electron#53456), [45](https://github.com/electron/electron/pull/53438))</sup>
- Fixed application crash after a large number of IPC messages from renderers. [#53417](electron/electron#53417) <sup>(Also in [42](electron/electron#53419), [43](electron/electron#53420), [45](https://github.com/electron/electron/pull/53418))</sup>
- Fixed native addons deriving from `node::ObjectWrap` aborting during garbage collection on Node.js 24.19.0 and later. [#53392](electron/electron#53392) <sup>(Also in [42](electron/electron#53394), [43](electron/electron#53393), [45](https://github.com/electron/electron/pull/53391))</sup>

#### Other Changes

- Backported fixes from upstream ANGLE, Chromium, Skia and V8. [#53479](electron/electron#53479)
- Improved throughput of responses that `protocol.handle` handlers return straight from `net.fetch`. [#53378](electron/electron#53378)
- Updated Chromium to 152.0.7977.76. [#53382](electron/electron#53382)
- Updated Node.js to v24.20.0. [#53250](electron/electron#53250)
##### [v44.1.1](https://github.com/electron/electron/releases/tag/v44.1.1)

### Release Notes for v44.1.1

#### Fixes

- Fixed `chrome.tabs.query()` returning tab `url` and `title` to extensions without the `tabs` permission or host access, aligning with `tabs.get`. [#53354](electron/electron#53354) <sup>(Also in [42](electron/electron#53355), [43](electron/electron#53356), [45](https://github.com/electron/electron/pull/53353))</sup>
- Fixed a renderer crash when an array with a throwing property getter is passed across `contextBridge`, and several main/utility-process crashes when option objects passed to Electron APIs contain throwing accessors or Proxy traps. [#53331](electron/electron#53331) <sup>(Also in [42](electron/electron#53329), [43](https://github.com/electron/electron/pull/53330))</sup>
- Fixed an intermittent crash at startup on Linux caused by a race between Pango and the main thread initializing fontconfig. [#53340](electron/electron#53340) <sup>(Also in [43](electron/electron#53339), [45](https://github.com/electron/electron/pull/53338))</sup>
- Fixed an issue where an exception thrown by a property getter on an object passed through `contextBridge` was swallowed instead of being thrown back to the caller. [#53335](electron/electron#53335) <sup>(Also in [43](electron/electron#53334), [45](https://github.com/electron/electron/pull/53297))</sup>
- Fixed main process crashes when navigating to `http://accessibility/` or `http://devtools/`, when an extension background page used `navigator.mediaDevices`, and when calling `SerialPort.forget()` for a disconnected device. [#53333](electron/electron#53333) <sup>(Also in [43](electron/electron#53332), [45](https://github.com/electron/electron/pull/53325))</sup>
- Fixed the PDF viewer failing to save an edited PDF with `NotAllowedError: Third party iframes are not allowed to show a file picker`. [#53328](electron/electron#53328) <sup>(Also in [43](electron/electron#53327), [45](https://github.com/electron/electron/pull/53326))</sup>
gwennlbh pushed a commit to cigaleapp/cigale that referenced this pull request Sep 14, 2026
##### [v44.2.0](https://github.com/electron/electron/releases/tag/v44.2.0)

### Release Notes for v44.2.0

#### Fixes

- Fixed a pending read on a `net.request` chunked upload stream inside a protocol handler never settling when the request failed or was aborted. [#53373](electron/electron#53373) <sup>(Also in [43](electron/electron#53374), [45](https://github.com/electron/electron/pull/53368))</sup>
- Fixed a potential crash when using `menu.popup` with a frame from an offscreen rendered window. [#53362](electron/electron#53362) <sup>(Also in [43](https://github.com/electron/electron/pull/53432))</sup>
- Fixed an intermittent crash (access violation) on Windows when an ASAR integrity violation is detected, so the process now exits with code 1 as intended. [#53455](electron/electron#53455) <sup>(Also in [43](electron/electron#53456), [45](https://github.com/electron/electron/pull/53438))</sup>
- Fixed application crash after a large number of IPC messages from renderers. [#53417](electron/electron#53417) <sup>(Also in [42](electron/electron#53419), [43](electron/electron#53420), [45](https://github.com/electron/electron/pull/53418))</sup>
- Fixed native addons deriving from `node::ObjectWrap` aborting during garbage collection on Node.js 24.19.0 and later. [#53392](electron/electron#53392) <sup>(Also in [42](electron/electron#53394), [43](electron/electron#53393), [45](https://github.com/electron/electron/pull/53391))</sup>

#### Other Changes

- Backported fixes from upstream ANGLE, Chromium, Skia and V8. [#53479](electron/electron#53479)
- Improved throughput of responses that `protocol.handle` handlers return straight from `net.fetch`. [#53378](electron/electron#53378)
- Updated Chromium to 152.0.7977.76. [#53382](electron/electron#53382)
- Updated Node.js to v24.20.0. [#53250](electron/electron#53250)
##### [v44.1.1](https://github.com/electron/electron/releases/tag/v44.1.1)

### Release Notes for v44.1.1

#### Fixes

- Fixed `chrome.tabs.query()` returning tab `url` and `title` to extensions without the `tabs` permission or host access, aligning with `tabs.get`. [#53354](electron/electron#53354) <sup>(Also in [42](electron/electron#53355), [43](electron/electron#53356), [45](https://github.com/electron/electron/pull/53353))</sup>
- Fixed a renderer crash when an array with a throwing property getter is passed across `contextBridge`, and several main/utility-process crashes when option objects passed to Electron APIs contain throwing accessors or Proxy traps. [#53331](electron/electron#53331) <sup>(Also in [42](electron/electron#53329), [43](https://github.com/electron/electron/pull/53330))</sup>
- Fixed an intermittent crash at startup on Linux caused by a race between Pango and the main thread initializing fontconfig. [#53340](electron/electron#53340) <sup>(Also in [43](electron/electron#53339), [45](https://github.com/electron/electron/pull/53338))</sup>
- Fixed an issue where an exception thrown by a property getter on an object passed through `contextBridge` was swallowed instead of being thrown back to the caller. [#53335](electron/electron#53335) <sup>(Also in [43](electron/electron#53334), [45](https://github.com/electron/electron/pull/53297))</sup>
- Fixed main process crashes when navigating to `http://accessibility/` or `http://devtools/`, when an extension background page used `navigator.mediaDevices`, and when calling `SerialPort.forget()` for a disconnected device. [#53333](electron/electron#53333) <sup>(Also in [43](electron/electron#53332), [45](https://github.com/electron/electron/pull/53325))</sup>
- Fixed the PDF viewer failing to save an edited PDF with `NotAllowedError: Third party iframes are not allowed to show a file picker`. [#53328](electron/electron#53328) <sup>(Also in [43](electron/electron#53327), [45](https://github.com/electron/electron/pull/53326))</sup>

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

Labels

44-x-y backport This is a backport PR semver/patch backwards-compatible bug fixes

Projects

No open projects
Status: No status

Development

Successfully merging this pull request may close these issues.

1 participant