|                                                                                                                                                                                                                                                                                                                                        |  | 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.IncomingMessagevar 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']
 |