"use strict";

function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; var ownKeys = Object.keys(source); if (typeof Object.getOwnPropertySymbols === 'function') { ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function (sym) { return Object.getOwnPropertyDescriptor(source, sym).enumerable; })); } ownKeys.forEach(function (key) { _defineProperty(target, key, source[key]); }); } return target; }

function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }

var Stream = require('stream');

var util = require('util');

var net = require('net');

var tls = require('tls'); // eslint-disable-next-line node/no-deprecated-api


var _require = require('url'),
    parse = _require.parse;

var semver = require('semver');

var http2;
if (semver.gte(process.version, 'v10.10.0')) http2 = require('http2');else throw new Error('superagent: this version of Node.js does not support http2');
var _http2$constants = http2.constants,
    HTTP2_HEADER_PATH = _http2$constants.HTTP2_HEADER_PATH,
    HTTP2_HEADER_STATUS = _http2$constants.HTTP2_HEADER_STATUS,
    HTTP2_HEADER_METHOD = _http2$constants.HTTP2_HEADER_METHOD,
    HTTP2_HEADER_AUTHORITY = _http2$constants.HTTP2_HEADER_AUTHORITY,
    HTTP2_HEADER_HOST = _http2$constants.HTTP2_HEADER_HOST,
    HTTP2_HEADER_SET_COOKIE = _http2$constants.HTTP2_HEADER_SET_COOKIE,
    NGHTTP2_CANCEL = _http2$constants.NGHTTP2_CANCEL;

function setProtocol(protocol) {
  return {
    request: function request(options) {
      return new Request(protocol, options);
    }
  };
}

function Request(protocol, options) {
  var _this = this;

  Stream.call(this);
  var defaultPort = protocol === 'https:' ? 443 : 80;
  var defaultHost = 'localhost';
  var port = options.port || defaultPort;
  var host = options.host || defaultHost;
  delete options.port;
  delete options.host;
  this.method = options.method;
  this.path = options.path;
  this.protocol = protocol;
  this.host = host;
  delete options.method;
  delete options.path;

  var sessionOptions = _objectSpread({}, options);

  if (options.socketPath) {
    sessionOptions.socketPath = options.socketPath;
    sessionOptions.createConnection = this.createUnixConnection.bind(this);
  }

  this._headers = {};
  var session = http2.connect("".concat(protocol, "//").concat(host, ":").concat(port), sessionOptions);
  this.setHeader('host', "".concat(host, ":").concat(port));
  session.on('error', function (err) {
    return _this.emit('error', err);
  });
  this.session = session;
}
/**
 * Inherit from `Stream` (which inherits from `EventEmitter`).
 */


util.inherits(Request, Stream);

Request.prototype.createUnixConnection = function (authority, options) {
  switch (this.protocol) {
    case 'http:':
      return net.connect(options.socketPath);

    case 'https:':
      options.ALPNProtocols = ['h2'];
      options.servername = this.host;
      options.allowHalfOpen = true;
      return tls.connect(options.socketPath, options);

    default:
      throw new Error('Unsupported protocol', this.protocol);
  }
}; // eslint-disable-next-line no-unused-vars


Request.prototype.setNoDelay = function (bool) {// We can not use setNoDelay with HTTP/2.
  // Node 10 limits http2session.socket methods to ones safe to use with HTTP/2.
  // See also https://nodejs.org/api/http2.html#http2_http2session_socket
};

Request.prototype.getFrame = function () {
  var _method,
      _this2 = this;

  if (this.frame) {
    return this.frame;
  }

  var method = (_method = {}, _defineProperty(_method, HTTP2_HEADER_PATH, this.path), _defineProperty(_method, HTTP2_HEADER_METHOD, this.method), _method);
  var headers = this.mapToHttp2Header(this._headers);
  headers = Object.assign(headers, method);
  var frame = this.session.request(headers); // eslint-disable-next-line no-unused-vars

  frame.once('response', function (headers, flags) {
    headers = _this2.mapToHttpHeader(headers);
    frame.headers = headers;
    frame.statusCode = headers[HTTP2_HEADER_STATUS];
    frame.status = frame.statusCode;

    _this2.emit('response', frame);
  });
  this._headerSent = true;
  frame.once('drain', function () {
    return _this2.emit('drain');
  });
  frame.on('error', function (err) {
    return _this2.emit('error', err);
  });
  frame.on('close', function () {
    return _this2.session.close();
  });
  this.frame = frame;
  return frame;
};

Request.prototype.mapToHttpHeader = function (headers) {
  var keys = Object.keys(headers);
  var http2Headers = {};

  for (var i = 0; i < keys.length; i++) {
    var key = keys[i];
    var value = headers[key];
    key = key.toLowerCase();

    switch (key) {
      case HTTP2_HEADER_SET_COOKIE:
        value = Array.isArray(value) ? value : [value];
        break;

      default:
        break;
    }

    http2Headers[key] = value;
  }

  return http2Headers;
};

Request.prototype.mapToHttp2Header = function (headers) {
  var keys = Object.keys(headers);
  var http2Headers = {};

  for (var i = 0; i < keys.length; i++) {
    var key = keys[i];
    var value = headers[key];
    key = key.toLowerCase();

    switch (key) {
      case HTTP2_HEADER_HOST:
        key = HTTP2_HEADER_AUTHORITY;
        value = /^http:\/\/|^https:\/\//.test(value) ? parse(value).host : value;
        break;

      default:
        break;
    }

    http2Headers[key] = value;
  }

  return http2Headers;
};

Request.prototype.setHeader = function (name, value) {
  this._headers[name.toLowerCase()] = value;
};

Request.prototype.getHeader = function (name) {
  return this._headers[name.toLowerCase()];
};

Request.prototype.write = function (data, encoding) {
  var frame = this.getFrame();
  return frame.write(data, encoding);
};

Request.prototype.pipe = function (stream, options) {
  var frame = this.getFrame();
  return frame.pipe(stream, options);
};

Request.prototype.end = function (data) {
  var frame = this.getFrame();
  frame.end(data);
}; // eslint-disable-next-line no-unused-vars


Request.prototype.abort = function (data) {
  var frame = this.getFrame();
  frame.close(NGHTTP2_CANCEL);
  this.session.destroy();
};

exports.setProtocol = setProtocol;