var capability = require('./capability');
							 | 
						|
								var inherits = require('inherits');
							 | 
						|
								var response = require('./response');
							 | 
						|
								var stream = require('readable-stream');
							 | 
						|
								var toArrayBuffer = require('to-arraybuffer');
							 | 
						|
								
							 | 
						|
								var IncomingMessage = response.IncomingMessage;
							 | 
						|
								var rStates = response.readyStates;
							 | 
						|
								
							 | 
						|
								function decideMode(preferBinary, useFetch) {
							 | 
						|
								  if (capability.fetch && useFetch) {
							 | 
						|
								    return 'fetch';
							 | 
						|
								  } else if (capability.mozchunkedarraybuffer) {
							 | 
						|
								    return 'moz-chunked-arraybuffer';
							 | 
						|
								  } else if (capability.msstream) {
							 | 
						|
								    return 'ms-stream';
							 | 
						|
								  } else if (capability.arraybuffer && preferBinary) {
							 | 
						|
								    return 'arraybuffer';
							 | 
						|
								  } else if (capability.vbArray && preferBinary) {
							 | 
						|
								    return 'text:vbarray';
							 | 
						|
								  } else {
							 | 
						|
								    return 'text';
							 | 
						|
								  }
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								var ClientRequest = (module.exports = function (opts) {
							 | 
						|
								  var self = this;
							 | 
						|
								  stream.Writable.call(self);
							 | 
						|
								
							 | 
						|
								  self._opts = opts;
							 | 
						|
								  self._body = [];
							 | 
						|
								  self._headers = {};
							 | 
						|
								  if (opts.auth) self.setHeader('Authorization', 'Basic ' + new Buffer(opts.auth).toString('base64'));
							 | 
						|
								  Object.keys(opts.headers).forEach(function (name) {
							 | 
						|
								    self.setHeader(name, opts.headers[name]);
							 | 
						|
								  });
							 | 
						|
								
							 | 
						|
								  var preferBinary;
							 | 
						|
								  var useFetch = true;
							 | 
						|
								  if (opts.mode === 'disable-fetch' || ('requestTimeout' in opts && !capability.abortController)) {
							 | 
						|
								    // If the use of XHR should be preferred. Not typically needed.
							 | 
						|
								    useFetch = false;
							 | 
						|
								    preferBinary = true;
							 | 
						|
								  } else if (opts.mode === 'prefer-streaming') {
							 | 
						|
								    // If streaming is a high priority but binary compatibility and
							 | 
						|
								    // the accuracy of the 'content-type' header aren't
							 | 
						|
								    preferBinary = false;
							 | 
						|
								  } else if (opts.mode === 'allow-wrong-content-type') {
							 | 
						|
								    // If streaming is more important than preserving the 'content-type' header
							 | 
						|
								    preferBinary = !capability.overrideMimeType;
							 | 
						|
								  } else if (!opts.mode || opts.mode === 'default' || opts.mode === 'prefer-fast') {
							 | 
						|
								    // Use binary if text streaming may corrupt data or the content-type header, or for speed
							 | 
						|
								    preferBinary = true;
							 | 
						|
								  } else {
							 | 
						|
								    throw new Error('Invalid value for opts.mode');
							 | 
						|
								  }
							 | 
						|
								  self._mode = decideMode(preferBinary, useFetch);
							 | 
						|
								  self._fetchTimer = null;
							 | 
						|
								
							 | 
						|
								  self.on('finish', function () {
							 | 
						|
								    self._onFinish();
							 | 
						|
								  });
							 | 
						|
								});
							 | 
						|
								
							 | 
						|
								inherits(ClientRequest, stream.Writable);
							 | 
						|
								
							 | 
						|
								ClientRequest.prototype.setHeader = function (name, value) {
							 | 
						|
								  var self = this;
							 | 
						|
								  var lowerName = name.toLowerCase();
							 | 
						|
								  // This check is not necessary, but it prevents warnings from browsers about setting unsafe
							 | 
						|
								  // headers. To be honest I'm not entirely sure hiding these warnings is a good thing, but
							 | 
						|
								  // http-browserify did it, so I will too.
							 | 
						|
								  if (unsafeHeaders.indexOf(lowerName) !== -1) return;
							 | 
						|
								
							 | 
						|
								  self._headers[lowerName] = {
							 | 
						|
								    name: name,
							 | 
						|
								    value: value
							 | 
						|
								  };
							 | 
						|
								};
							 | 
						|
								
							 | 
						|
								ClientRequest.prototype.getHeader = function (name) {
							 | 
						|
								  var header = this._headers[name.toLowerCase()];
							 | 
						|
								  if (header) return header.value;
							 | 
						|
								  return null;
							 | 
						|
								};
							 | 
						|
								
							 | 
						|
								ClientRequest.prototype.removeHeader = function (name) {
							 | 
						|
								  var self = this;
							 | 
						|
								  delete self._headers[name.toLowerCase()];
							 | 
						|
								};
							 | 
						|
								
							 | 
						|
								ClientRequest.prototype._onFinish = function () {
							 | 
						|
								  var self = this;
							 | 
						|
								
							 | 
						|
								  if (self._destroyed) return;
							 | 
						|
								  var opts = self._opts;
							 | 
						|
								
							 | 
						|
								  var headersObj = self._headers;
							 | 
						|
								  var body = null;
							 | 
						|
								  if (opts.method !== 'GET' && opts.method !== 'HEAD') {
							 | 
						|
								    if (capability.arraybuffer) {
							 | 
						|
								      body = toArrayBuffer(Buffer.concat(self._body));
							 | 
						|
								    } else if (capability.blobConstructor) {
							 | 
						|
								      body = new global.Blob(
							 | 
						|
								        self._body.map(function (buffer) {
							 | 
						|
								          return toArrayBuffer(buffer);
							 | 
						|
								        }),
							 | 
						|
								        {
							 | 
						|
								          type: (headersObj['content-type'] || {}).value || ''
							 | 
						|
								        }
							 | 
						|
								      );
							 | 
						|
								    } else {
							 | 
						|
								      // get utf8 string
							 | 
						|
								      body = Buffer.concat(self._body).toString();
							 | 
						|
								    }
							 | 
						|
								  }
							 | 
						|
								
							 | 
						|
								  // create flattened list of headers
							 | 
						|
								  var headersList = [];
							 | 
						|
								  Object.keys(headersObj).forEach(function (keyName) {
							 | 
						|
								    var name = headersObj[keyName].name;
							 | 
						|
								    var value = headersObj[keyName].value;
							 | 
						|
								    if (Array.isArray(value)) {
							 | 
						|
								      value.forEach(function (v) {
							 | 
						|
								        headersList.push([name, v]);
							 | 
						|
								      });
							 | 
						|
								    } else {
							 | 
						|
								      headersList.push([name, value]);
							 | 
						|
								    }
							 | 
						|
								  });
							 | 
						|
								
							 | 
						|
								  if (self._mode === 'fetch') {
							 | 
						|
								    var signal = null;
							 | 
						|
								    var fetchTimer = null;
							 | 
						|
								    if (capability.abortController) {
							 | 
						|
								      var controller = new AbortController();
							 | 
						|
								      signal = controller.signal;
							 | 
						|
								      self._fetchAbortController = controller;
							 | 
						|
								
							 | 
						|
								      if ('requestTimeout' in opts && opts.requestTimeout !== 0) {
							 | 
						|
								        self._fetchTimer = global.setTimeout(function () {
							 | 
						|
								          self.emit('requestTimeout');
							 | 
						|
								          if (self._fetchAbortController) self._fetchAbortController.abort();
							 | 
						|
								        }, opts.requestTimeout);
							 | 
						|
								      }
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    global
							 | 
						|
								      .fetch(self._opts.url, {
							 | 
						|
								        method: self._opts.method,
							 | 
						|
								        headers: headersList,
							 | 
						|
								        body: body || undefined,
							 | 
						|
								        mode: 'cors',
							 | 
						|
								        credentials: opts.withCredentials ? 'include' : 'same-origin',
							 | 
						|
								        signal: signal
							 | 
						|
								      })
							 | 
						|
								      .then(
							 | 
						|
								        function (response) {
							 | 
						|
								          self._fetchResponse = response;
							 | 
						|
								          self._connect();
							 | 
						|
								        },
							 | 
						|
								        function (reason) {
							 | 
						|
								          global.clearTimeout(self._fetchTimer);
							 | 
						|
								          if (!self._destroyed) self.emit('error', reason);
							 | 
						|
								        }
							 | 
						|
								      );
							 | 
						|
								  } else {
							 | 
						|
								    var xhr = (self._xhr = new global.XMLHttpRequest());
							 | 
						|
								    try {
							 | 
						|
								      xhr.open(self._opts.method, self._opts.url, true);
							 | 
						|
								    } catch (err) {
							 | 
						|
								      process.nextTick(function () {
							 | 
						|
								        self.emit('error', err);
							 | 
						|
								      });
							 | 
						|
								      return;
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    // Can't set responseType on really old browsers
							 | 
						|
								    if ('responseType' in xhr) xhr.responseType = self._mode.split(':')[0];
							 | 
						|
								
							 | 
						|
								    if ('withCredentials' in xhr) xhr.withCredentials = !!opts.withCredentials;
							 | 
						|
								
							 | 
						|
								    if (self._mode === 'text' && 'overrideMimeType' in xhr) xhr.overrideMimeType('text/plain; charset=x-user-defined');
							 | 
						|
								
							 | 
						|
								    if ('requestTimeout' in opts) {
							 | 
						|
								      xhr.timeout = opts.requestTimeout;
							 | 
						|
								      xhr.ontimeout = function () {
							 | 
						|
								        self.emit('requestTimeout');
							 | 
						|
								      };
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    headersList.forEach(function (header) {
							 | 
						|
								      xhr.setRequestHeader(header[0], header[1]);
							 | 
						|
								    });
							 | 
						|
								
							 | 
						|
								    self._response = null;
							 | 
						|
								    xhr.onreadystatechange = function () {
							 | 
						|
								      switch (xhr.readyState) {
							 | 
						|
								        case rStates.LOADING:
							 | 
						|
								        case rStates.DONE:
							 | 
						|
								          self._onXHRProgress();
							 | 
						|
								          break;
							 | 
						|
								      }
							 | 
						|
								    };
							 | 
						|
								    // Necessary for streaming in Firefox, since xhr.response is ONLY defined
							 | 
						|
								    // in onprogress, not in onreadystatechange with xhr.readyState = 3
							 | 
						|
								    if (self._mode === 'moz-chunked-arraybuffer') {
							 | 
						|
								      xhr.onprogress = function () {
							 | 
						|
								        self._onXHRProgress();
							 | 
						|
								      };
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    xhr.onerror = function () {
							 | 
						|
								      if (self._destroyed) return;
							 | 
						|
								      self.emit('error', new Error('XHR error'));
							 | 
						|
								    };
							 | 
						|
								
							 | 
						|
								    try {
							 | 
						|
								      xhr.send(body);
							 | 
						|
								    } catch (err) {
							 | 
						|
								      process.nextTick(function () {
							 | 
						|
								        self.emit('error', err);
							 | 
						|
								      });
							 | 
						|
								      return;
							 | 
						|
								    }
							 | 
						|
								  }
							 | 
						|
								};
							 | 
						|
								
							 | 
						|
								/**
							 | 
						|
								 * Checks if xhr.status is readable and non-zero, indicating no error.
							 | 
						|
								 * Even though the spec says it should be available in readyState 3,
							 | 
						|
								 * accessing it throws an exception in IE8
							 | 
						|
								 */
							 | 
						|
								function statusValid(xhr) {
							 | 
						|
								  try {
							 | 
						|
								    var status = xhr.status;
							 | 
						|
								    return status !== null && status !== 0;
							 | 
						|
								  } catch (e) {
							 | 
						|
								    return false;
							 | 
						|
								  }
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								ClientRequest.prototype._onXHRProgress = function () {
							 | 
						|
								  var self = this;
							 | 
						|
								
							 | 
						|
								  if (!statusValid(self._xhr) || self._destroyed) return;
							 | 
						|
								
							 | 
						|
								  if (!self._response) self._connect();
							 | 
						|
								
							 | 
						|
								  self._response._onXHRProgress();
							 | 
						|
								};
							 | 
						|
								
							 | 
						|
								ClientRequest.prototype._connect = function () {
							 | 
						|
								  var self = this;
							 | 
						|
								
							 | 
						|
								  if (self._destroyed) return;
							 | 
						|
								
							 | 
						|
								  self._response = new IncomingMessage(self._xhr, self._fetchResponse, self._mode, self._fetchTimer);
							 | 
						|
								  self._response.on('error', function (err) {
							 | 
						|
								    self.emit('error', err);
							 | 
						|
								  });
							 | 
						|
								
							 | 
						|
								  self.emit('response', self._response);
							 | 
						|
								};
							 | 
						|
								
							 | 
						|
								ClientRequest.prototype._write = function (chunk, encoding, cb) {
							 | 
						|
								  var self = this;
							 | 
						|
								
							 | 
						|
								  self._body.push(chunk);
							 | 
						|
								  cb();
							 | 
						|
								};
							 | 
						|
								
							 | 
						|
								ClientRequest.prototype.abort = ClientRequest.prototype.destroy = function () {
							 | 
						|
								  var self = this;
							 | 
						|
								  self._destroyed = true;
							 | 
						|
								  global.clearTimeout(self._fetchTimer);
							 | 
						|
								  if (self._response) self._response._destroyed = true;
							 | 
						|
								  if (self._xhr) self._xhr.abort();
							 | 
						|
								  else if (self._fetchAbortController) self._fetchAbortController.abort();
							 | 
						|
								};
							 | 
						|
								
							 | 
						|
								ClientRequest.prototype.end = function (data, encoding, cb) {
							 | 
						|
								  var self = this;
							 | 
						|
								  if (typeof data === 'function') {
							 | 
						|
								    cb = data;
							 | 
						|
								    data = undefined;
							 | 
						|
								  }
							 | 
						|
								
							 | 
						|
								  stream.Writable.prototype.end.call(self, data, encoding, cb);
							 | 
						|
								};
							 | 
						|
								
							 | 
						|
								ClientRequest.prototype.flushHeaders = function () {};
							 | 
						|
								ClientRequest.prototype.setTimeout = function () {};
							 | 
						|
								ClientRequest.prototype.setNoDelay = function () {};
							 | 
						|
								ClientRequest.prototype.setSocketKeepAlive = function () {};
							 | 
						|
								
							 | 
						|
								// Taken from http://www.w3.org/TR/XMLHttpRequest/#the-setrequestheader%28%29-method
							 | 
						|
								var unsafeHeaders = [
							 | 
						|
								  'accept-charset',
							 | 
						|
								  'accept-encoding',
							 | 
						|
								  'access-control-request-headers',
							 | 
						|
								  'access-control-request-method',
							 | 
						|
								  'connection',
							 | 
						|
								  'content-length',
							 | 
						|
								  'cookie',
							 | 
						|
								  'cookie2',
							 | 
						|
								  'date',
							 | 
						|
								  'dnt',
							 | 
						|
								  'expect',
							 | 
						|
								  'host',
							 | 
						|
								  'keep-alive',
							 | 
						|
								  'origin',
							 | 
						|
								  'referer',
							 | 
						|
								  'te',
							 | 
						|
								  'trailer',
							 | 
						|
								  'transfer-encoding',
							 | 
						|
								  'upgrade',
							 | 
						|
								  'user-agent',
							 | 
						|
								  'via'
							 | 
						|
								];
							 |