Skip to content

node: make the isolate the port namespace for http and net servers - #7357

Merged
guybedford merged 1 commit into
mainfrom
gbedford/isolate-port-scope
Sep 14, 2026
Merged

guybedford merged 1 commit into
mainfrom
gbedford/isolate-port-scope

Conversation

@guybedford

Copy link
Copy Markdown
Contributor

Follow-on to #7306, which made the virtual port table per-DO, when instead it should be per-isolate.

#7306 gave each Durable Object instance its own virtual port table, with the isolate's table underneath it. That made the scope of a listen() depend on where it was called: at module top-level it bound in the isolate table, in a Durable Object constructor or handler it bound in that instance's table, in the same worker. It also broke the Vite and Vitest plugins, which evaluate user modules inside a runner Durable Object: a top-level server.listen(8080) landed in the runner instance's table and httpServerHandler from the stateless entrypoint could not find it (cloudflare/workers-sdk CI).

This fixes the regressed DO behavior, with the simpler per-isolate scoping model, which is also the correct model since the isolate is the VM abstraction not the DO:

  • one tcpPorts table per isolate; IoContext::getPortScopeKey and SocketsModule::getPortScopeKey are removed
  • one host address per isolate (240.1.0.1) rather than one per Durable Object instance; the gateway address is unchanged
  • owners no longer capture the table they bound in: bindPort() drops its table parameter, BoundSocket, net.Server, net.Socket, and http.Server use tcpPorts directly, and kBoundTable becomes a kBoundReserved flag

Tests: testDurableObjectScoping becomes testDurableObjectsShareIsolatePorts, covering a net.Server listened on in one instance holding the port against another instance, the entrypoint, and a BoundSocket, being reached through stub.connect() on either instance with the isolate host address and gateway peer, reservations shared and released across instances and the entrypoint, and an http.Server listened on inside an instance served through httpServerHandler from the entrypoint and another instance, with the reverse direction and the not-found error after close.

The virtual port table is shared state, and the isolate is the one unit that
shares state: module scope and top-level listen() run once per isolate, and the
platform routes between isolates before a port is consulted. #7306 gave each
Durable Object instance its own table, which made a listen() at module
top-level and one in a Durable Object handler bind in different tables in the
same worker, and left a server registered during module evaluation inside a
Durable Object invisible to httpServerHandler from a stateless request.

One table per isolate, for http and net alike, as in Node. Removes the
IoContext scope key, the per-scope host address, and the table threading
through socket owners.
@guybedford
guybedford requested review from a team as code owners September 14, 2026 16:01
Comment thread src/workerd/api/node/tests/net-server-nodejs-test.js
@ask-bonk

ask-bonk Bot commented Sep 14, 2026

Copy link
Copy Markdown
Contributor

I'm Bonk, and I've done a quick review of your PR.

Makes Node HTTP/net ports isolate-scoped across Durable Objects.

  1. P1: Posted one inline suggestion covering cross-IoContext Durable Object dispatch.

Time for a pun! This port scope needs an actor to keep it in context.

github run

@petebacondarwin petebacondarwin 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.

This should resolve the Vite/Vitest regression, thanks!
Are there any concerns about ports being broken when a DO gets moved from one metal to another? I assume that it doesn't matter since the DO must have died (or is killed) and so any sockets will already get closed and need to be re-opened.

@guybedford
guybedford enabled auto-merge (squash) September 14, 2026 16:45
@guybedford
guybedford merged commit 98c7856 into main Sep 14, 2026
45 of 48 checks passed
@guybedford
guybedford deleted the gbedford/isolate-port-scope branch September 14, 2026 17:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants