| 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) | 
|     } | 
| } |