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

// This test verifies the guards that reject transferring a net.Socket,
// net.Server or net.BoundSocket that is not in a clean, movable state.
// Serialization is triggered with a MessageChannel, so no worker thread is
// required.

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

const assert = require('assert');
const net = require('net');
const { MessageChannel } = require('worker_threads');
const tmpdir = require('../common/tmpdir');

tmpdir.refresh();

const { port1 } = new MessageChannel();

// A pipe BoundSocket is not transferable; only TCP binds can move between
// event loops.
{
  const bound = new net.BoundSocket({ path: common.PIPE });
  assert.throws(() => port1.postMessage({ bound }, [bound]), {
    code: 'ERR_WORKER_HANDLE_NOT_TRANSFERABLE',
  });
  bound.close();
}

// A Server that is not listening (no handle) cannot be transferred.
{
  const server = net.createServer();
  assert.throws(() => port1.postMessage({ server }, [server]), {
    code: 'ERR_WORKER_HANDLE_NOT_TRANSFERABLE',
  });
}

// A closed BoundSocket no longer owns a handle and cannot be transferred.
{
  const bound = new net.BoundSocket({ host: '127.0.0.1', port: 0 });
  bound.close();
  assert.throws(() => port1.postMessage({ bound }, [bound]), {
    code: 'ERR_WORKER_HANDLE_NOT_TRANSFERABLE',
  });
}

// An adopted BoundSocket no longer owns a handle and cannot be transferred.
{
  const bound = new net.BoundSocket({ host: '127.0.0.1', port: 0 });
  const socket = new net.Socket({ handle: bound });
  assert.throws(() => port1.postMessage({ bound }, [bound]), {
    code: 'ERR_WORKER_HANDLE_NOT_TRANSFERABLE',
  });
  socket.destroy();
}

// A Socket that has already consumed data cannot be transferred, because that
// buffered data would be lost on the sending side.
{
  const server = net.createServer(common.mustCall((socket) => {
    socket.on('data', common.mustCall(() => {
      assert.throws(() => port1.postMessage({ socket }, [socket]), {
        code: 'ERR_WORKER_HANDLE_NOT_TRANSFERABLE',
      });
      // The socket is still usable after a rejected transfer.
      assert.ok(socket._handle != null);
      socket.destroy();
      server.close();
      port1.close();
    }));
  }));

  server.listen(0, common.mustCall(() => {
    const client = net.connect(server.address().port, common.mustCall(() => {
      client.end('data');
    }));
    // The server destroys the socket after the rejected transfer, which may
    // surface as a reset on the client; swallow it either way.
    client.on('error', () => {});
  }));
}