Http.js

/* globals Wsh: false */
/* globals Buffer: false */

(function () {
  if (Wsh && Wsh.Http) return;

  /**
   * [W.I.P] HTTP handler module for WSH (Windows Script Host).
   *
   * @namespace Http
   * @memberof Wsh
   * @requires ./Buffer.js
   */
  Wsh.Http = {};

  // Shorthands
  var util = Wsh.Util;

  var obtain = util.obtainPropVal;
  var startsWith = util.startsWith;
  var isSolidString = util.isSolidString;
  var isPlainObject = util.isPlainObject;

  var http = Wsh.Http;

  /** @constant {string} */
  var MODULE_TITLE = 'WshModeJs/Http.js';

  var throwErrNonStr = function (functionName, typeErrVal) {
    util.throwTypeError('string', MODULE_TITLE, functionName, typeErrVal);
  };

  // http.request {{{
  /**
   * [W.I.P] Requests the URL. Similar to {@link https://nodejs.org/api/http.html#http_http_request_options_callback|Node.js http.request()}.
   *
   * @example
   * var http = Wsh.Http; // Shorthand
   *
   * // Ex 1. Auto JSON parsing
   * http.request('https://httpbin.org/ip');
   * // Returns: { origin: '111.222.333.444' }
   *
   * // Ex 2. Basic Auth
   * http.request('https://httpbin.org/basic-auth', { auth: 'myUser:myPasswd' });
   *
   * // Ex 3. Using proxy
   * http.request('https://httpbin.org/headers', {
   *   proxy: {
   *     host: '192.168.12.34',
   *     port: 8087,
   *     proxyAuth: 'proxyUser:proxyPassword'
   * });
   * // Returns:
   * // { headers: {
   * //     Accept: "\*\/*",
   * //     Accept-Language: "ja",
   * //     Host: "httpbin.org",
   * //     User-Agent: "Mozilla/4.0 (compatible; Win32; WinHttp.WinHttpRequest.5)",
   * //     X-Amzn-Trace-Id: "Root=1-1234abcd-12345678abcdefg123456789"
   * //   }
   * // }
   *
   * // Ex 4. POST data
   * http.request('https://httpbin.org/post?argA=valA', {
   *   method: 'POST',
   *   dataToSend: 'formB=' + encodeURIComponent('value B')
   * });
   * // Returns:
   * // { args: { argA: "valA" },
   * //   data: "",
   * //   files: {},
   * //   form: { formB: "valB" },
   * //   headers: { ... },
   * //   json: null,
   * //   origin: "111.222.333.444",
   * //   url: "https://httpbin.org/post?%3FargA=valA" }
   * @function request
   * @memberof Wsh.Http
   * @param {string} url - The URL to requrest.
   * @param {object} [options] - Optional parameters.
   * @param {number} [options.port=80] - Port of remote server.
   * @param {string} [options.auth] - Basic authentication. e.g. `user:password` to compute an Authorization header
   * @param {string} [options.method='GET'] - The string specifying the HTTP request method.
   * @param {object} [options.dataToSend]
   * @param {object} [options.headers]
   * @param {number} [options.timeout=0] - The number specifying the socket timeout in milliseconds. This will set the timeout before the socket is connected. Default: 0 is no timeout
   * @param {object} [options.proxy]
   * @param {string} [options.proxy.host] - 'yourproxy',
   * @param {number} [options.proxy.port] - 8087,
   * @param {string} [options.proxy.proxyAuth] - 'proxyUser:proxyPass'
   * @returns {(string|Object)}
   */
  http.request = function (url, options) {
    var functionName = 'http.request';
    if (!isSolidString(url)) throwErrNonStr(functionName, url);

    var dataToSend;
    var method = obtain(options, 'method', 'GET');
    var httpObj = WScript.CreateObject('Msxml2.ServerXMLHTTP');

    /**
     * @function open
     * @memberof Msxml2.ServerXMLHTTP
     * @param {string} Method
     * @param {string} URL
     * @param {boolean} Async
     * @param {string} [User]
     * @param {string} [Password]
     */
    httpObj.open(method, url, false);

    // Protocol
    if (startsWith(url) === 'https') {
      /**
       * @see https://qiita.com/tnakagawa/items/8d9d093b83cab48ae6c7
       * SXH_OPTION_IGNORE_SERVER_SSL_CERT_ERROR_FLAGS: 2
       * SXH_SERVER_CERT_IGNORE_ALL_SERVER_ERRORS: 13056
       */
      httpObj.setOption(2, 13056); // Ignore a SSH error
    }

    var headers = obtain(options, 'headers', null);
    if (isPlainObject(headers)) {
      Object.keys(headers).forEach(function (prop) {
        httpObj.setRequestHeader(prop, headers[prop]);
      });
    }

    // Method
    if (method === 'POST') {
      httpObj.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
      dataToSend = obtain(options, 'dataToSend', null);
    }

    // Basic Auth
    var auth = obtain(options, 'auth', null);
    if (auth) {
      var authBase64 = Buffer.from(auth).toString('base64');
      httpObj.setRequestHeader('Authorization', 'Basic ' + authBase64);
    }

    // Bypass a proxy
    var proxy = obtain(options, 'proxy', null);
    if (proxy) {
      /**
       * SetProxy {@link https://docs.microsoft.com/en-us/windows/win32/winhttp/iwinhttprequest-setproxy?redirectedfrom=MSDN|Microsoft Docs}
       *
       * @function setProxy
       * @memberof Msxml2.ServerXMLHTTP
       * @param {number} ProxySetting - 2: HTTPREQUEST_PROXYSETTING_PROXY
       * @param {string} [ProxyServer]
       * @param {string} [BypassList]
       */
      httpObj.setProxy(2, proxy.host + ':' + proxy.port);

      // Proxy Auth
      if (proxy.proxyAuth) {
        var pAuthStrs = proxy.proxyAuth.split(':');
        var pUserName = pAuthStrs[0];
        var pPassword = pAuthStrs[1];
        httpObj.setProxyCredentials(pUserName, pPassword);
      }
    }

    // Timeout
    /**
     * @function setTimeouts
     * @memberof Msxml2.ServerXMLHTTP
     * @param {number} ResolveTimeout=0
     * @param {number} connectTimeout=60000
     * @param {number} sendTimeout=30000
     * @param {number} receiveTimeout=30000
     */
    var timeout = obtain(options, 'timeout', 60000);
    httpObj.setTimeouts(0, timeout, 30000, 30000);

    // Send the request
    httpObj.send(dataToSend);

    // Get a response
    var res = httpObj.responseText;
    try {
      return JSON.parse(res);
    } catch (e) {
      return res;
    }
  }; // }}}
})();

// vim:set foldmethod=marker commentstring=//%s :