You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							224 lines
						
					
					
						
							5.6 KiB
						
					
					
				
			
		
		
	
	
							224 lines
						
					
					
						
							5.6 KiB
						
					
					
				var capability = require('./capability') | 
						|
var inherits = require('inherits') | 
						|
var stream = require('readable-stream') | 
						|
 | 
						|
var rStates = exports.readyStates = { | 
						|
	UNSENT: 0, | 
						|
	OPENED: 1, | 
						|
	HEADERS_RECEIVED: 2, | 
						|
	LOADING: 3, | 
						|
	DONE: 4 | 
						|
} | 
						|
 | 
						|
var IncomingMessage = exports.IncomingMessage = function (xhr, response, mode, fetchTimer) { | 
						|
	var self = this | 
						|
	stream.Readable.call(self) | 
						|
 | 
						|
	self._mode = mode | 
						|
	self.headers = {} | 
						|
	self.rawHeaders = [] | 
						|
	self.trailers = {} | 
						|
	self.rawTrailers = [] | 
						|
 | 
						|
	// Fake the 'close' event, but only once 'end' fires | 
						|
	self.on('end', function () { | 
						|
		// The nextTick is necessary to prevent the 'request' module from causing an infinite loop | 
						|
		process.nextTick(function () { | 
						|
			self.emit('close') | 
						|
		}) | 
						|
	}) | 
						|
 | 
						|
	if (mode === 'fetch') { | 
						|
		self._fetchResponse = response | 
						|
 | 
						|
		self.url = response.url | 
						|
		self.statusCode = response.status | 
						|
		self.statusMessage = response.statusText | 
						|
		 | 
						|
		response.headers.forEach(function (header, key){ | 
						|
			self.headers[key.toLowerCase()] = header | 
						|
			self.rawHeaders.push(key, header) | 
						|
		}) | 
						|
 | 
						|
		if (capability.writableStream) { | 
						|
			var writable = new WritableStream({ | 
						|
				write: function (chunk) { | 
						|
					return new Promise(function (resolve, reject) { | 
						|
						if (self._destroyed) { | 
						|
							reject() | 
						|
						} else if(self.push(new Buffer(chunk))) { | 
						|
							resolve() | 
						|
						} else { | 
						|
							self._resumeFetch = resolve | 
						|
						} | 
						|
					}) | 
						|
				}, | 
						|
				close: function () { | 
						|
					global.clearTimeout(fetchTimer) | 
						|
					if (!self._destroyed) | 
						|
						self.push(null) | 
						|
				}, | 
						|
				abort: function (err) { | 
						|
					if (!self._destroyed) | 
						|
						self.emit('error', err) | 
						|
				} | 
						|
			}) | 
						|
 | 
						|
			try { | 
						|
				response.body.pipeTo(writable).catch(function (err) { | 
						|
					global.clearTimeout(fetchTimer) | 
						|
					if (!self._destroyed) | 
						|
						self.emit('error', err) | 
						|
				}) | 
						|
				return | 
						|
			} catch (e) {} // pipeTo method isn't defined. Can't find a better way to feature test this | 
						|
		} | 
						|
		// fallback for when writableStream or pipeTo aren't available | 
						|
		var reader = response.body.getReader() | 
						|
		function read () { | 
						|
			reader.read().then(function (result) { | 
						|
				if (self._destroyed) | 
						|
					return | 
						|
				if (result.done) { | 
						|
					global.clearTimeout(fetchTimer) | 
						|
					self.push(null) | 
						|
					return | 
						|
				} | 
						|
				self.push(new Buffer(result.value)) | 
						|
				read() | 
						|
			}).catch(function (err) { | 
						|
				global.clearTimeout(fetchTimer) | 
						|
				if (!self._destroyed) | 
						|
					self.emit('error', err) | 
						|
			}) | 
						|
		} | 
						|
		read() | 
						|
	} else { | 
						|
		self._xhr = xhr | 
						|
		self._pos = 0 | 
						|
 | 
						|
		self.url = xhr.responseURL | 
						|
		self.statusCode = xhr.status | 
						|
		self.statusMessage = xhr.statusText | 
						|
		var headers = xhr.getAllResponseHeaders().split(/\r?\n/) | 
						|
		headers.forEach(function (header) { | 
						|
			var matches = header.match(/^([^:]+):\s*(.*)/) | 
						|
			if (matches) { | 
						|
				var key = matches[1].toLowerCase() | 
						|
				if (key === 'set-cookie') { | 
						|
					if (self.headers[key] === undefined) { | 
						|
						self.headers[key] = [] | 
						|
					} | 
						|
					self.headers[key].push(matches[2]) | 
						|
				} else if (self.headers[key] !== undefined) { | 
						|
					self.headers[key] += ', ' + matches[2] | 
						|
				} else { | 
						|
					self.headers[key] = matches[2] | 
						|
				} | 
						|
				self.rawHeaders.push(matches[1], matches[2]) | 
						|
			} | 
						|
		}) | 
						|
 | 
						|
		self._charset = 'x-user-defined' | 
						|
		if (!capability.overrideMimeType) { | 
						|
			var mimeType = self.rawHeaders['mime-type'] | 
						|
			if (mimeType) { | 
						|
				var charsetMatch = mimeType.match(/;\s*charset=([^;])(;|$)/) | 
						|
				if (charsetMatch) { | 
						|
					self._charset = charsetMatch[1].toLowerCase() | 
						|
				} | 
						|
			} | 
						|
			if (!self._charset) | 
						|
				self._charset = 'utf-8' // best guess | 
						|
		} | 
						|
	} | 
						|
} | 
						|
 | 
						|
inherits(IncomingMessage, stream.Readable) | 
						|
 | 
						|
IncomingMessage.prototype._read = function () { | 
						|
	var self = this | 
						|
 | 
						|
	var resolve = self._resumeFetch | 
						|
	if (resolve) { | 
						|
		self._resumeFetch = null | 
						|
		resolve() | 
						|
	} | 
						|
} | 
						|
 | 
						|
IncomingMessage.prototype._onXHRProgress = function () { | 
						|
	var self = this | 
						|
 | 
						|
	var xhr = self._xhr | 
						|
 | 
						|
	var response = null | 
						|
	switch (self._mode) { | 
						|
		case 'text:vbarray': // For IE9 | 
						|
			if (xhr.readyState !== rStates.DONE) | 
						|
				break | 
						|
			try { | 
						|
				// This fails in IE8 | 
						|
				response = new global.VBArray(xhr.responseBody).toArray() | 
						|
			} catch (e) {} | 
						|
			if (response !== null) { | 
						|
				self.push(new Buffer(response)) | 
						|
				break | 
						|
			} | 
						|
			// Falls through in IE8	 | 
						|
		case 'text': | 
						|
			try { // This will fail when readyState = 3 in IE9. Switch mode and wait for readyState = 4 | 
						|
				response = xhr.responseText | 
						|
			} catch (e) { | 
						|
				self._mode = 'text:vbarray' | 
						|
				break | 
						|
			} | 
						|
			if (response.length > self._pos) { | 
						|
				var newData = response.substr(self._pos) | 
						|
				if (self._charset === 'x-user-defined') { | 
						|
					var buffer = new Buffer(newData.length) | 
						|
					for (var i = 0; i < newData.length; i++) | 
						|
						buffer[i] = newData.charCodeAt(i) & 0xff | 
						|
 | 
						|
					self.push(buffer) | 
						|
				} else { | 
						|
					self.push(newData, self._charset) | 
						|
				} | 
						|
				self._pos = response.length | 
						|
			} | 
						|
			break | 
						|
		case 'arraybuffer': | 
						|
			if (xhr.readyState !== rStates.DONE || !xhr.response) | 
						|
				break | 
						|
			response = xhr.response | 
						|
			self.push(new Buffer(new Uint8Array(response))) | 
						|
			break | 
						|
		case 'moz-chunked-arraybuffer': // take whole | 
						|
			response = xhr.response | 
						|
			if (xhr.readyState !== rStates.LOADING || !response) | 
						|
				break | 
						|
			self.push(new Buffer(new Uint8Array(response))) | 
						|
			break | 
						|
		case 'ms-stream': | 
						|
			response = xhr.response | 
						|
			if (xhr.readyState !== rStates.LOADING) | 
						|
				break | 
						|
			var reader = new global.MSStreamReader() | 
						|
			reader.onprogress = function () { | 
						|
				if (reader.result.byteLength > self._pos) { | 
						|
					self.push(new Buffer(new Uint8Array(reader.result.slice(self._pos)))) | 
						|
					self._pos = reader.result.byteLength | 
						|
				} | 
						|
			} | 
						|
			reader.onload = function () { | 
						|
				self.push(null) | 
						|
			} | 
						|
			// reader.onerror = ??? // TODO: this | 
						|
			reader.readAsArrayBuffer(response) | 
						|
			break | 
						|
	} | 
						|
 | 
						|
	// The ms-stream case handles end separately in reader.onload() | 
						|
	if (self._xhr.readyState === rStates.DONE && self._mode !== 'ms-stream') { | 
						|
		self.push(null) | 
						|
	} | 
						|
}
 | 
						|
 |