All files / lib/internal socket_list.js

100% Statements 108/108
100% Branches 35/35
100% Functions 9/9
100% Lines 108/108

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109109x 109x 109x 109x 109x 109x 109x 109x 109x 16x 16x 16x 16x 16x 109x 109x 28x 28x 28x 25x 25x 25x 9x 9x 9x 25x 25x 32x 19x 19x 19x 19x 32x 25x 25x 25x 28x 109x 109x 11x 11x 11x 11x 11x 109x 109x 14x 14x 14x 14x 14x 13x 14x 14x 109x 109x 109x 109x 109x 109x 10x 10x 10x 10x 10x 10x 10x 5x 3x 3x 3x 3x 3x 5x 10x 10x 14x 8x 11x 5x 5x 2x 2x 2x 13x 2x 1x 1x 1x 1x 1x 1x 10x 10x 109x 109x 8x 8x 8x 8x 6x 6x 6x 8x 8x 109x 109x 109x  
'use strict';
 
const { ERR_CHILD_CLOSED_BEFORE_REPLY } = require('internal/errors').codes;
 
const EventEmitter = require('events');
 
// This object keeps track of the sockets that are sent
class SocketListSend extends EventEmitter {
  constructor(child, key) {
    super();
    this.key = key;
    this.child = child;
    child.once('exit', () => this.emit('exit', this));
  }
 
  _request(msg, cmd, swallowErrors, callback) {
    const self = this;
 
    if (!this.child.connected) return onclose();
    this.child._send(msg, undefined, swallowErrors);
 
    function onclose() {
      self.child.removeListener('internalMessage', onreply);
      callback(new ERR_CHILD_CLOSED_BEFORE_REPLY());
    }
 
    function onreply(msg) {
      if (!(msg.cmd === cmd && msg.key === self.key)) return;
      self.child.removeListener('disconnect', onclose);
      self.child.removeListener('internalMessage', onreply);
 
      callback(null, msg);
    }
 
    this.child.once('disconnect', onclose);
    this.child.on('internalMessage', onreply);
  }
 
  close(callback) {
    this._request({
      cmd: 'NODE_SOCKET_NOTIFY_CLOSE',
      key: this.key
    }, 'NODE_SOCKET_ALL_CLOSED', true, callback);
  }
 
  getConnections(callback) {
    this._request({
      cmd: 'NODE_SOCKET_GET_COUNT',
      key: this.key
    }, 'NODE_SOCKET_COUNT', false, (err, msg) => {
      if (err) return callback(err);
      callback(null, msg.count);
    });
  }
}
 
 
// This object keeps track of the sockets that are received
class SocketListReceive extends EventEmitter {
  constructor(child, key) {
    super();
 
    this.connections = 0;
    this.key = key;
    this.child = child;
 
    function onempty(self) {
      if (!self.child.connected) return;
 
      self.child._send({
        cmd: 'NODE_SOCKET_ALL_CLOSED',
        key: self.key
      }, undefined, true);
    }
 
    this.child.on('internalMessage', (msg) => {
      if (msg.key !== this.key) return;
 
      if (msg.cmd === 'NODE_SOCKET_NOTIFY_CLOSE') {
        // Already empty
        if (this.connections === 0) return onempty(this);
 
        // Wait for sockets to get closed
        this.once('empty', onempty);
      } else if (msg.cmd === 'NODE_SOCKET_GET_COUNT') {
        if (!this.child.connected) return;
        this.child._send({
          cmd: 'NODE_SOCKET_COUNT',
          key: this.key,
          count: this.connections
        });
      }
    });
  }
 
  add(obj) {
    this.connections++;
 
    // Notify the previous owner of the socket about its state change
    obj.socket.once('close', () => {
      this.connections--;
 
      if (this.connections === 0) this.emit('empty', this);
    });
  }
}
 
module.exports = { SocketListSend, SocketListReceive };