Skip to content

Implement node:dgram over inbound UDP listeners - #7506

Draft
guybedford wants to merge 2 commits into
gbedford/udp-remote-addressfrom
gbedford/node-dgram
Draft

guybedford wants to merge 2 commits into
gbedford/udp-remote-addressfrom
gbedford/node-dgram

Conversation

@guybedford

Copy link
Copy Markdown
Contributor

This makes node:dgram functional over the inbound UDP connect() handler, the same way node:net's Server is virtualized over TCP listeners. Previously the module was a silent no-op stub.

  • cloudflare-internal:http gains a udpPorts table seeded from the worker's declared UDP listeners, alongside tcpPorts.
  • connectHandler() in cloudflare:node routes socket.protocol === 'udp' flows to the dgram.Socket bound to the port they arrived on.
  • dgram.Socket.bind() claims a declared UDP port with the same rules as net.Server.listen() (EADDRINUSE/EADDRNOTAVAIL, port 0 takes the first unclaimed declared port). Each inbound flow becomes a peer whose datagrams are emitted as 'message' events with rinfo; the flow's request lives until the flow ends or the socket closes.
  • send() (all Node argument forms, plus sendto and connected mode via connect()/disconnect()/remoteAddress()) delivers one datagram per call to any peer with a live flow. A v4-mapped peer on the dual-stack listener is reported as IPv4 to a udp4 socket and matched by either form.
  • The platform has no unconnected UDP egress, so a datagram to a peer without a flow fails with EHOSTUNREACH in the callback. An implied bind from send()/connect() takes an ephemeral port rather than a declared listener.
  • Multicast membership throws ERR_FEATURE_UNAVAILABLE_ON_PLATFORM; TTL/broadcast/buffer-size setters validate and record.

Tests: dgram-nodejs-test is rewritten to drive fake platform flows through handleAsNodeConnection (declared-port rules, message/rinfo, every send form, mapped addresses, connected mode, implicit bind, close ending flows, option validation); a new udp-dgram-node server test runs a real Node UDP client against a node:dgram echo server in workerd across flow re-creation and concurrent peers.

Compatibility: the stub accepted any call sequence silently, so nothing built on it functioned. Validation and the already-bound/not-running/is-connected errors now match Node and only affect sequences Node itself rejects, so no compat flag is added.

@codecov-commenter

codecov-commenter commented Sep 24, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 0.24938% with 400 lines in your changes missing coverage. Please review.
✅ Project coverage is 38.11%. Comparing base (1a8abde) to head (f8d6e48).

Files with missing lines Patch % Lines
src/workerd/api/node/tests/dgram-nodejs-test.js 0.00% 379 Missing ⚠️
src/workerd/server/tests/udp-dgram-node/index.mjs 0.00% 21 Missing ⚠️
Additional details and impacted files
@@                       Coverage Diff                       @@
##           gbedford/udp-remote-address    #7506      +/-   ##
===============================================================
- Coverage                        38.15%   38.11%   -0.05%     
===============================================================
  Files                              858      859       +1     
  Lines                           262910   262561     -349     
  Branches                         24388    24287     -101     
===============================================================
- Hits                            100313   100073     -240     
+ Misses                          148928   148856      -72     
+ Partials                         13669    13632      -37     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@ask-bonk

ask-bonk Bot commented Sep 24, 2026

Copy link
Copy Markdown
Contributor

@guybedford Bonk workflow was cancelled.

View workflow run · To retry, trigger Bonk again.

node:dgram was a non-functional stub. A dgram.Socket now works the way
net.Server does over TCP listeners: bind() claims a port in the UDP port
table (seeded from the worker's declared UDP listeners) and installs a
connect handler there; connectHandler() in cloudflare:node routes each
inbound UDP flow to the socket bound to the port it arrived on, and every
datagram on the flow is emitted as a 'message' event with the peer's
rinfo. send() delivers to any peer with a live flow, keyed by address
and port, with connected mode fixing the destination; a peer without a
flow is unreachable, since the platform has no unconnected UDP egress,
and fails with EHOSTUNREACH. An implied bind from send() or connect()
takes an ephemeral port rather than a declared listener port.

Compatibility: the stub previously accepted anything silently, so code
that appeared to work under it did nothing. Argument validation and the
already-bound / not-running / is-connected errors now match Node, which
only affects call sequences Node itself rejects. No compat flag is added
on that basis.
@ThomasRubini

Copy link
Copy Markdown
Contributor

I had a quick look at the code and everything seems good, although I don't think I'm the best person to review this one

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