Projekt

Obecné

Profil

Stáhnout (5.58 KB) Statistiky
| Větev: | Revize:
1
var capability = require('./capability')
2
var inherits = require('inherits')
3
var stream = require('readable-stream')
4

    
5
var rStates = exports.readyStates = {
6
	UNSENT: 0,
7
	OPENED: 1,
8
	HEADERS_RECEIVED: 2,
9
	LOADING: 3,
10
	DONE: 4
11
}
12

    
13
var IncomingMessage = exports.IncomingMessage = function (xhr, response, mode, fetchTimer) {
14
	var self = this
15
	stream.Readable.call(self)
16

    
17
	self._mode = mode
18
	self.headers = {}
19
	self.rawHeaders = []
20
	self.trailers = {}
21
	self.rawTrailers = []
22

    
23
	// Fake the 'close' event, but only once 'end' fires
24
	self.on('end', function () {
25
		// The nextTick is necessary to prevent the 'request' module from causing an infinite loop
26
		process.nextTick(function () {
27
			self.emit('close')
28
		})
29
	})
30

    
31
	if (mode === 'fetch') {
32
		self._fetchResponse = response
33

    
34
		self.url = response.url
35
		self.statusCode = response.status
36
		self.statusMessage = response.statusText
37
		
38
		response.headers.forEach(function (header, key){
39
			self.headers[key.toLowerCase()] = header
40
			self.rawHeaders.push(key, header)
41
		})
42

    
43
		if (capability.writableStream) {
44
			var writable = new WritableStream({
45
				write: function (chunk) {
46
					return new Promise(function (resolve, reject) {
47
						if (self._destroyed) {
48
							reject()
49
						} else if(self.push(new Buffer(chunk))) {
50
							resolve()
51
						} else {
52
							self._resumeFetch = resolve
53
						}
54
					})
55
				},
56
				close: function () {
57
					global.clearTimeout(fetchTimer)
58
					if (!self._destroyed)
59
						self.push(null)
60
				},
61
				abort: function (err) {
62
					if (!self._destroyed)
63
						self.emit('error', err)
64
				}
65
			})
66

    
67
			try {
68
				response.body.pipeTo(writable).catch(function (err) {
69
					global.clearTimeout(fetchTimer)
70
					if (!self._destroyed)
71
						self.emit('error', err)
72
				})
73
				return
74
			} catch (e) {} // pipeTo method isn't defined. Can't find a better way to feature test this
75
		}
76
		// fallback for when writableStream or pipeTo aren't available
77
		var reader = response.body.getReader()
78
		function read () {
79
			reader.read().then(function (result) {
80
				if (self._destroyed)
81
					return
82
				if (result.done) {
83
					global.clearTimeout(fetchTimer)
84
					self.push(null)
85
					return
86
				}
87
				self.push(new Buffer(result.value))
88
				read()
89
			}).catch(function (err) {
90
				global.clearTimeout(fetchTimer)
91
				if (!self._destroyed)
92
					self.emit('error', err)
93
			})
94
		}
95
		read()
96
	} else {
97
		self._xhr = xhr
98
		self._pos = 0
99

    
100
		self.url = xhr.responseURL
101
		self.statusCode = xhr.status
102
		self.statusMessage = xhr.statusText
103
		var headers = xhr.getAllResponseHeaders().split(/\r?\n/)
104
		headers.forEach(function (header) {
105
			var matches = header.match(/^([^:]+):\s*(.*)/)
106
			if (matches) {
107
				var key = matches[1].toLowerCase()
108
				if (key === 'set-cookie') {
109
					if (self.headers[key] === undefined) {
110
						self.headers[key] = []
111
					}
112
					self.headers[key].push(matches[2])
113
				} else if (self.headers[key] !== undefined) {
114
					self.headers[key] += ', ' + matches[2]
115
				} else {
116
					self.headers[key] = matches[2]
117
				}
118
				self.rawHeaders.push(matches[1], matches[2])
119
			}
120
		})
121

    
122
		self._charset = 'x-user-defined'
123
		if (!capability.overrideMimeType) {
124
			var mimeType = self.rawHeaders['mime-type']
125
			if (mimeType) {
126
				var charsetMatch = mimeType.match(/;\s*charset=([^;])(;|$)/)
127
				if (charsetMatch) {
128
					self._charset = charsetMatch[1].toLowerCase()
129
				}
130
			}
131
			if (!self._charset)
132
				self._charset = 'utf-8' // best guess
133
		}
134
	}
135
}
136

    
137
inherits(IncomingMessage, stream.Readable)
138

    
139
IncomingMessage.prototype._read = function () {
140
	var self = this
141

    
142
	var resolve = self._resumeFetch
143
	if (resolve) {
144
		self._resumeFetch = null
145
		resolve()
146
	}
147
}
148

    
149
IncomingMessage.prototype._onXHRProgress = function () {
150
	var self = this
151

    
152
	var xhr = self._xhr
153

    
154
	var response = null
155
	switch (self._mode) {
156
		case 'text:vbarray': // For IE9
157
			if (xhr.readyState !== rStates.DONE)
158
				break
159
			try {
160
				// This fails in IE8
161
				response = new global.VBArray(xhr.responseBody).toArray()
162
			} catch (e) {}
163
			if (response !== null) {
164
				self.push(new Buffer(response))
165
				break
166
			}
167
			// Falls through in IE8	
168
		case 'text':
169
			try { // This will fail when readyState = 3 in IE9. Switch mode and wait for readyState = 4
170
				response = xhr.responseText
171
			} catch (e) {
172
				self._mode = 'text:vbarray'
173
				break
174
			}
175
			if (response.length > self._pos) {
176
				var newData = response.substr(self._pos)
177
				if (self._charset === 'x-user-defined') {
178
					var buffer = new Buffer(newData.length)
179
					for (var i = 0; i < newData.length; i++)
180
						buffer[i] = newData.charCodeAt(i) & 0xff
181

    
182
					self.push(buffer)
183
				} else {
184
					self.push(newData, self._charset)
185
				}
186
				self._pos = response.length
187
			}
188
			break
189
		case 'arraybuffer':
190
			if (xhr.readyState !== rStates.DONE || !xhr.response)
191
				break
192
			response = xhr.response
193
			self.push(new Buffer(new Uint8Array(response)))
194
			break
195
		case 'moz-chunked-arraybuffer': // take whole
196
			response = xhr.response
197
			if (xhr.readyState !== rStates.LOADING || !response)
198
				break
199
			self.push(new Buffer(new Uint8Array(response)))
200
			break
201
		case 'ms-stream':
202
			response = xhr.response
203
			if (xhr.readyState !== rStates.LOADING)
204
				break
205
			var reader = new global.MSStreamReader()
206
			reader.onprogress = function () {
207
				if (reader.result.byteLength > self._pos) {
208
					self.push(new Buffer(new Uint8Array(reader.result.slice(self._pos))))
209
					self._pos = reader.result.byteLength
210
				}
211
			}
212
			reader.onload = function () {
213
				self.push(null)
214
			}
215
			// reader.onerror = ??? // TODO: this
216
			reader.readAsArrayBuffer(response)
217
			break
218
	}
219

    
220
	// The ms-stream case handles end separately in reader.onload()
221
	if (self._xhr.readyState === rStates.DONE && self._mode !== 'ms-stream') {
222
		self.push(null)
223
	}
224
}
(3-3/3)