transport-udp.js

'use strict';

const merge = require('merge');
const { Duplex: DuplexStream } = require('node:stream');
const dgram = require('node:dgram');
const utils = require('./utils');


/**
 * Implements a UDP transport adapter
 */
class UDPTransport extends DuplexStream {

  static get DEFAULTS() {
    return {
      type: 'udp4',
      reuseAddr: false,
      allowLoopbackAddresses: true
    };
  }

  /**
   * Constructs a datagram socket interface
   * @constructor
   * @param {object} [socketOpts] - Passed to dgram.createSocket(options)
   */
  constructor(options) {
    super({ objectMode: true });
    this._options = merge(UDPTransport.DEFAULTS, options);

    this.socket = dgram.createSocket({
      type: this._options.type,
      reuseAddr: this._options.reuseAddr
    });

    this.socket.on('error', (err) => this.emit('error', err));
  }

  /**
   * Implements the writable interface
   * @private
   */
  _write([, buffer, target], encoding, callback) {
    let [, contact] = target;

    this.socket.send(buffer, 0, buffer.length, contact.port, contact.hostname,
      callback);
  }

  /**
   * Implements the readable interface
   * @private
   */
  _read() {
    this.socket.once('message', (buffer) => {
      this.push(buffer);
    });
  }

  /**
   * @private
   */
  _validate(contact) {
    return utils.isValidContact(contact, this._options.allowLoopbackAddresses);
  }

  /**
   * Binds the socket to the [port] [, address] [, callback]
   * @param {number} [port=0] - Port to bind to
   * @param {string} [address=0.0.0.0] - Address to bind to
   * @param {function} [callback] - called after bind complete
   */
  listen() {
    this.socket.bind(...arguments);
  }

}

module.exports = UDPTransport;