11cb0ef41Sopenharmony_ci'use strict';
21cb0ef41Sopenharmony_ci
31cb0ef41Sopenharmony_ciconst {
41cb0ef41Sopenharmony_ci  ArrayIsArray,
51cb0ef41Sopenharmony_ci  Boolean,
61cb0ef41Sopenharmony_ci  ObjectCreate,
71cb0ef41Sopenharmony_ci  SafeMap,
81cb0ef41Sopenharmony_ci} = primordials;
91cb0ef41Sopenharmony_ci
101cb0ef41Sopenharmony_ciconst assert = require('internal/assert');
111cb0ef41Sopenharmony_ciconst net = require('net');
121cb0ef41Sopenharmony_ciconst { sendHelper } = require('internal/cluster/utils');
131cb0ef41Sopenharmony_ciconst { append, init, isEmpty, peek, remove } = require('internal/linkedlist');
141cb0ef41Sopenharmony_ciconst { constants } = internalBinding('tcp_wrap');
151cb0ef41Sopenharmony_ci
161cb0ef41Sopenharmony_cimodule.exports = RoundRobinHandle;
171cb0ef41Sopenharmony_ci
181cb0ef41Sopenharmony_cifunction RoundRobinHandle(key, address, { port, fd, flags, backlog, readableAll, writableAll }) {
191cb0ef41Sopenharmony_ci  this.key = key;
201cb0ef41Sopenharmony_ci  this.all = new SafeMap();
211cb0ef41Sopenharmony_ci  this.free = new SafeMap();
221cb0ef41Sopenharmony_ci  this.handles = init(ObjectCreate(null));
231cb0ef41Sopenharmony_ci  this.handle = null;
241cb0ef41Sopenharmony_ci  this.server = net.createServer(assert.fail);
251cb0ef41Sopenharmony_ci
261cb0ef41Sopenharmony_ci  if (fd >= 0)
271cb0ef41Sopenharmony_ci    this.server.listen({ fd, backlog });
281cb0ef41Sopenharmony_ci  else if (port >= 0) {
291cb0ef41Sopenharmony_ci    this.server.listen({
301cb0ef41Sopenharmony_ci      port,
311cb0ef41Sopenharmony_ci      host: address,
321cb0ef41Sopenharmony_ci      // Currently, net module only supports `ipv6Only` option in `flags`.
331cb0ef41Sopenharmony_ci      ipv6Only: Boolean(flags & constants.UV_TCP_IPV6ONLY),
341cb0ef41Sopenharmony_ci      backlog,
351cb0ef41Sopenharmony_ci    });
361cb0ef41Sopenharmony_ci  } else
371cb0ef41Sopenharmony_ci    this.server.listen({
381cb0ef41Sopenharmony_ci      path: address,
391cb0ef41Sopenharmony_ci      backlog,
401cb0ef41Sopenharmony_ci      readableAll,
411cb0ef41Sopenharmony_ci      writableAll,
421cb0ef41Sopenharmony_ci    });  // UNIX socket path.
431cb0ef41Sopenharmony_ci  this.server.once('listening', () => {
441cb0ef41Sopenharmony_ci    this.handle = this.server._handle;
451cb0ef41Sopenharmony_ci    this.handle.onconnection = (err, handle) => this.distribute(err, handle);
461cb0ef41Sopenharmony_ci    this.server._handle = null;
471cb0ef41Sopenharmony_ci    this.server = null;
481cb0ef41Sopenharmony_ci  });
491cb0ef41Sopenharmony_ci}
501cb0ef41Sopenharmony_ci
511cb0ef41Sopenharmony_ciRoundRobinHandle.prototype.add = function(worker, send) {
521cb0ef41Sopenharmony_ci  assert(this.all.has(worker.id) === false);
531cb0ef41Sopenharmony_ci  this.all.set(worker.id, worker);
541cb0ef41Sopenharmony_ci
551cb0ef41Sopenharmony_ci  const done = () => {
561cb0ef41Sopenharmony_ci    if (this.handle.getsockname) {
571cb0ef41Sopenharmony_ci      const out = {};
581cb0ef41Sopenharmony_ci      this.handle.getsockname(out);
591cb0ef41Sopenharmony_ci      // TODO(bnoordhuis) Check err.
601cb0ef41Sopenharmony_ci      send(null, { sockname: out }, null);
611cb0ef41Sopenharmony_ci    } else {
621cb0ef41Sopenharmony_ci      send(null, null, null);  // UNIX socket.
631cb0ef41Sopenharmony_ci    }
641cb0ef41Sopenharmony_ci
651cb0ef41Sopenharmony_ci    this.handoff(worker);  // In case there are connections pending.
661cb0ef41Sopenharmony_ci  };
671cb0ef41Sopenharmony_ci
681cb0ef41Sopenharmony_ci  if (this.server === null)
691cb0ef41Sopenharmony_ci    return done();
701cb0ef41Sopenharmony_ci
711cb0ef41Sopenharmony_ci  // Still busy binding.
721cb0ef41Sopenharmony_ci  this.server.once('listening', done);
731cb0ef41Sopenharmony_ci  this.server.once('error', (err) => {
741cb0ef41Sopenharmony_ci    send(err.errno, null);
751cb0ef41Sopenharmony_ci  });
761cb0ef41Sopenharmony_ci};
771cb0ef41Sopenharmony_ci
781cb0ef41Sopenharmony_ciRoundRobinHandle.prototype.remove = function(worker) {
791cb0ef41Sopenharmony_ci  const existed = this.all.delete(worker.id);
801cb0ef41Sopenharmony_ci
811cb0ef41Sopenharmony_ci  if (!existed)
821cb0ef41Sopenharmony_ci    return false;
831cb0ef41Sopenharmony_ci
841cb0ef41Sopenharmony_ci  this.free.delete(worker.id);
851cb0ef41Sopenharmony_ci
861cb0ef41Sopenharmony_ci  if (this.all.size !== 0)
871cb0ef41Sopenharmony_ci    return false;
881cb0ef41Sopenharmony_ci
891cb0ef41Sopenharmony_ci  while (!isEmpty(this.handles)) {
901cb0ef41Sopenharmony_ci    const handle = peek(this.handles);
911cb0ef41Sopenharmony_ci    handle.close();
921cb0ef41Sopenharmony_ci    remove(handle);
931cb0ef41Sopenharmony_ci  }
941cb0ef41Sopenharmony_ci
951cb0ef41Sopenharmony_ci  this.handle.close();
961cb0ef41Sopenharmony_ci  this.handle = null;
971cb0ef41Sopenharmony_ci  return true;
981cb0ef41Sopenharmony_ci};
991cb0ef41Sopenharmony_ci
1001cb0ef41Sopenharmony_ciRoundRobinHandle.prototype.distribute = function(err, handle) {
1011cb0ef41Sopenharmony_ci  // If `accept` fails just skip it (handle is undefined)
1021cb0ef41Sopenharmony_ci  if (err) {
1031cb0ef41Sopenharmony_ci    return;
1041cb0ef41Sopenharmony_ci  }
1051cb0ef41Sopenharmony_ci  append(this.handles, handle);
1061cb0ef41Sopenharmony_ci  // eslint-disable-next-line node-core/no-array-destructuring
1071cb0ef41Sopenharmony_ci  const [ workerEntry ] = this.free; // this.free is a SafeMap
1081cb0ef41Sopenharmony_ci
1091cb0ef41Sopenharmony_ci  if (ArrayIsArray(workerEntry)) {
1101cb0ef41Sopenharmony_ci    const { 0: workerId, 1: worker } = workerEntry;
1111cb0ef41Sopenharmony_ci    this.free.delete(workerId);
1121cb0ef41Sopenharmony_ci    this.handoff(worker);
1131cb0ef41Sopenharmony_ci  }
1141cb0ef41Sopenharmony_ci};
1151cb0ef41Sopenharmony_ci
1161cb0ef41Sopenharmony_ciRoundRobinHandle.prototype.handoff = function(worker) {
1171cb0ef41Sopenharmony_ci  if (!this.all.has(worker.id)) {
1181cb0ef41Sopenharmony_ci    return;  // Worker is closing (or has closed) the server.
1191cb0ef41Sopenharmony_ci  }
1201cb0ef41Sopenharmony_ci
1211cb0ef41Sopenharmony_ci  const handle = peek(this.handles);
1221cb0ef41Sopenharmony_ci
1231cb0ef41Sopenharmony_ci  if (handle === null) {
1241cb0ef41Sopenharmony_ci    this.free.set(worker.id, worker);  // Add to ready queue again.
1251cb0ef41Sopenharmony_ci    return;
1261cb0ef41Sopenharmony_ci  }
1271cb0ef41Sopenharmony_ci
1281cb0ef41Sopenharmony_ci  remove(handle);
1291cb0ef41Sopenharmony_ci
1301cb0ef41Sopenharmony_ci  const message = { act: 'newconn', key: this.key };
1311cb0ef41Sopenharmony_ci
1321cb0ef41Sopenharmony_ci  sendHelper(worker.process, message, handle, (reply) => {
1331cb0ef41Sopenharmony_ci    if (reply.accepted)
1341cb0ef41Sopenharmony_ci      handle.close();
1351cb0ef41Sopenharmony_ci    else
1361cb0ef41Sopenharmony_ci      this.distribute(0, handle);  // Worker is shutting down. Send to another.
1371cb0ef41Sopenharmony_ci
1381cb0ef41Sopenharmony_ci    this.handoff(worker);
1391cb0ef41Sopenharmony_ci  });
1401cb0ef41Sopenharmony_ci};
141