current commit
260908.16 · net: support sending net.BoundSocket to threads and child processes
'use strict';

// This test verifies that an un-adopted net.BoundSocket can be transferred to
// a worker thread via worker_threads postMessage()'s transferList. The parent
// thread binds synchronously to reserve the port, then hands the bound socket
// off; the worker adopts it via server.listen() and accepts connections.

const common = require('../common');

const assert = require('assert');
const net = require('net');
const {
  Worker,
  parentPort,
  threadId,
  workerData,
} = require('worker_threads');

if (workerData?.role === 'server') {
  parentPort.on('message', common.mustCall(({ bound, port }) => {
    assert.ok(bound instanceof net.BoundSocket);
    // The bound address survives the transfer.
    assert.strictEqual(bound.address().port, port);

    const server = net.createServer((socket) => {
      socket.end(`served-by:${threadId}`);
    });
    server.listen(bound, common.mustCall(() => {
      assert.strictEqual(server.address().port, port);
      // Adoption consumed the bound socket.
      assert.throws(() => bound.address(), {
        code: 'ERR_SOCKET_HANDLE_ADOPTED',
      });
      parentPort.postMessage('listening');
    }));
  }));
  return;
}

const worker = new Worker(__filename, { workerData: { role: 'server' } });

const bound = new net.BoundSocket({ host: '127.0.0.1', port: 0 });
const { port } = bound.address();

// Move the bound socket to the worker thread.
worker.postMessage({ bound, port }, [bound]);

// The source is left in the adopted state; further use throws.
assert.throws(() => bound.address(), { code: 'ERR_SOCKET_HANDLE_ADOPTED' });
assert.throws(() => bound.fd(), { code: 'ERR_SOCKET_HANDLE_ADOPTED' });
assert.throws(() => bound.close(), { code: 'ERR_SOCKET_HANDLE_ADOPTED' });

worker.on('message', common.mustCall((msg) => {
  assert.strictEqual(msg, 'listening');
  const client = net.connect(port, '127.0.0.1');
  client.setEncoding('utf8');
  let response = '';
  client.on('data', (chunk) => { response += chunk; });
  client.on('end', common.mustCall(() => {
    assert.match(response, /^served-by:\d+$/);
    assert.notStrictEqual(response, `served-by:${threadId}`);
    worker.terminate();
  }));
}));