Source: src/client.js

const http = require("http");
const uuidv4 = require("uuid/v4");
const crypto = require("crypto");

const digest = require("./digest");
const logger = require("./logger");

class TimeoutError extends Error {
  constructor(...params) {
    super(...params);
    if (Error.captureStackTrace) {
      Error.captureStackTrace(this, CustomError);
    }
    this.name = "TimeoutError";
  }
}

function createRequest(opt) {
  opt = opt || {};

  if (!opt.url) throw new TypeError("invalid arguments");
  if (!opt.data) throw new TypeError("invalid arguments");
  if (!opt.secret) throw new TypeError("invalid arguments");
  if (!opt.port) throw new TypeError("invalid arguments");

  const options = {
    port: opt.port,
    method: "POST",
    headers: {
      "X-Hub-Signature": digest(opt.data, opt.secret),
      "X-SimpleWebhooks-Delivery": uuidv4()
    }
  };
  const req = http.request(opt.url, options);
  req.write(opt.data);
  return req;
}

function sendRequest(req) {
  return new Promise((resolve, reject) => {
    req.once("response", res => {
      logger.debug("RESPONSE: " + res.statusCode);
      if (res.statusCode === 200) {
        resolve(true);
      } else {
        resolve(false);
      }
    });

    req.on("error", e => {
      reject(e);
    });

    req.on("timeout", () => {
      reject(new TimeoutError());
    });

    req.end();
  });
}

/**
 * Client for Webhook system. Can be used to trigger jobs at remote endpoints.
 */
class WebhookClient {
  /**
   * Creates a Webhook client that can be used to trigger the jobs at the
   * endpoint.
   *
   * @param {Object} opt - Options for the Webhook client.
   * @param {String} opt.secret - Shared secret between server and client for
   *    HMAC signature verification.
   * @param {String} [opt.url=http://localhost] - Hostname or IP of the Webhook
   *    endpoint.
   * @param {Number} [opt.port=8338] - Port of the Webhook server.
   * @param {String} [opt.data] - Default data to send with the request if no
   *    data is given to the `trigger` method.
   * @returns {WebhookClient} - The created client.
   */
  constructor(opt) {
    opt = opt || {};

    if (!opt.secret) throw new TypeError("invalid arguments");

    this.secret = opt.secret;

    this.url = opt.url || "http://localhost";
    this.port = opt.port || 8338;
    this.defaultData =
      opt.defaultData || crypto.randomBytes(64).toString("hex");
  }

  /**
   * Sends the request.
   * @param {string} data - Data to send within the request body.
   */
  async trigger(data) {
    data = data || this.defaultData;

    const request = createRequest({
      url: this.url,
      port: this.port,
      secret: this.secret,
      data: data
    });

    try {
      return sendRequest(request);
    } catch (err) {
      logger.error(err);
    }
  }
}

module.exports = WebhookClient;