Skip to content

Latest commit

 

History

History
209 lines (177 loc) · 5.16 KB

File metadata and controls

209 lines (177 loc) · 5.16 KB
 
Jun 24, 2015
Jun 24, 2015
1
// var Base64 = require('Base64')
2
var capability = require('./capability')
3
var response = require('./response')
4
var stream = require('stream')
5
var util = require('util')
6
7
var IncomingMessage = response.IncomingMessage
8
var rStates = response.readyStates
9
10
// function copy (to, from) {
11
// if (!Array.isArray(from))
12
// from = [from]
13
// from.forEach(function (obj) {
14
// Object.keys(function (key), {
15
// to[key] = from[key]
16
// })
17
// })
18
// return to
19
// }
20
Jul 2, 2015
Jul 2, 2015
21
function decideMode (preferBinary) {
22
if (capability.fetch) {
23
return 'fetch'
24
} else if (capability.mozchunkedarraybuffer) {
25
return 'moz-chunked-arraybuffer'
26
} else if (capability.msstream) {
27
return 'ms-stream'
28
} else if (capability.arraybuffer && preferBinary) {
29
return 'arraybuffer'
30
} else {
31
return 'text'
32
}
33
}
34
Jun 24, 2015
Jun 24, 2015
35
var ClientRequest = module.exports = function (opts) {
36
var self = this
37
stream.Writable.call(self)
38
39
self._opts = opts
40
self._body = []
41
self._fullHeaders = {}
42
Object.keys(opts.headers).forEach(function (name) {
43
self.setHeader(name, opts.headers[name])
44
})
45
Jul 2, 2015
Jul 2, 2015
46
var preferBinary
47
if (opts.mode === 'prefer-stream') {
48
// If streaming is a high priority but binary compatibility isn't
49
preferBinary = false
50
} else if (opts.mode === 'prefer-binary') {
51
// If binary compatibility is the highest priority
52
preferBinary = true
53
} else if (!opts.mode || opts.mode === 'default') {
54
// By default, use binary if text streaming may corrupt data
55
preferBinary = !capability.overrideMimeType
56
} else {
57
throw new Error('Invalid value for opts.mode')
58
}
59
self._mode = decideMode(preferBinary)
Jun 24, 2015
Jun 24, 2015
60
Jul 2, 2015
Jul 2, 2015
61
self.on('finish', function () {
62
self._onFinish()
63
})
Jun 24, 2015
Jun 24, 2015
64
}
65
66
util.inherits(ClientRequest, stream.Writable)
67
68
ClientRequest.prototype.setHeader = function (name, value) {
69
var self = this
70
self._fullHeaders[name.toLowerCase()] = value
71
}
72
73
ClientRequest.prototype.getHeader = function (name) {
74
var self = this
75
return self._fullHeaders[name.toLowerCase()]
76
}
77
78
ClientRequest.prototype.removeHeader = function (name) {
79
var self = this
80
delete self._fullHeaders[name.toLowerCase()]
81
}
82
83
ClientRequest.prototype._onFinish = function () {
84
var self = this
85
86
var opts = self._opts
Jul 1, 2015
Jul 1, 2015
87
var url = opts.protocol + '//' + opts.hostname +
88
(opts.port ? ':' + opts.port : '') + opts.path
Jun 24, 2015
Jun 24, 2015
89
90
var user, pass
91
if (opts.auth) {
Jul 1, 2015
Jul 1, 2015
92
var authMatch = opts.auth.match(/^([^:]*):(.*)$/)
Jun 24, 2015
Jun 24, 2015
93
user = authMatch[0]
94
pass = authMatch[1]
95
}
96
97
// process and send data
98
var fullHeaders = self._fullHeaders
99
var body
Jul 1, 2015
Jul 1, 2015
100
if (opts.method in ['PUT', 'POST']) {
101
if (typeof window.Blob === 'function') {
102
body = new window.Blob(self._body.map(function (buffer) {
103
return buffer.toArrayBuffer()
104
}), {
105
type: fullHeaders['content-type'] || ''
106
})
107
} else {
108
// get utf8 string
109
body = Buffer.concat(self._body).toString()
110
}
Jun 24, 2015
Jun 24, 2015
111
}
112
113
if (self._mode === 'fetch') {
114
var headers = Object.keys(fullHeaders).map(function (name) {
115
return [name, fullHeaders[name]]
116
})
117
Jul 1, 2015
Jul 1, 2015
118
window.fetch(url, {
Jun 24, 2015
Jun 24, 2015
119
method: self._opts.method,
120
headers: headers,
121
body: body,
122
mode: 'cors',
123
credentials: opts.credentials ? 'include' : 'omit'
124
}).then(function (response) {
125
self._fetchResponse = response
126
self._connect()
127
})
128
} else {
Jul 1, 2015
Jul 1, 2015
129
var xhr = self._xhr = new window.XMLHttpRequest() // TODO: old IE
Jun 24, 2015
Jun 24, 2015
130
xhr.open(self._opts.method, url, true, user, pass)
131
132
// Can't set responseType on really old browsers
133
if ('responseType' in xhr)
134
xhr.responseType = self._mode
135
136
if ('withCredentials' in xhr)
137
xhr.withCredentials = !!opts.credentials
138
139
if (self._mode === 'text' && 'overrideMimeType' in xhr)
140
xhr.overrideMimeType('text/plain; charset=x-user-defined')
141
142
Object.keys(fullHeaders, function (name) {
143
xhr.setRequestHeader(name, headers[name])
144
})
145
146
xhr.onreadystatechange = function () {
147
switch (xhr.readyState) {
148
case rStates.HEADERS_RECEIVED:
149
self._connect()
150
break
151
case rStates.LOADING:
152
case rStates.DONE:
153
self._response._onXHRReadyStateChange()
154
break
155
}
156
}
Jul 2, 2015
Jul 2, 2015
157
// Necessary for streaming in Firefox, since xhr.response is ONLY defined
158
// in onprogress, not in onreadystatechange with xhr.readyState = 3
159
if (self._mode === 'moz-chunked-arraybuffer') {
160
xhr.onprogress = function () {
161
self._response._onXHRReadyStateChange()
162
}
163
}
Jun 24, 2015
Jun 24, 2015
164
165
xhr.send(body)
166
// This is the best approximation to where 'socket' should be fired
167
process.nextTick(function () {
168
self.emit('socket')
169
})
170
}
171
}
172
173
ClientRequest.prototype._connect = function () {
174
var self = this
175
176
self._response = new IncomingMessage(self._xhr, self._fetchResponse, self._mode)
177
self.emit('response', self._response)
178
}
179
180
ClientRequest.prototype._write = function (chunk, encoding, cb) {
181
var self = this
182
183
self._body.push(chunk)
184
cb()
185
}
186
187
ClientRequest.prototype.abort = function () {
188
var self = this
189
if (self._xhr)
190
self._xhr.abort()
191
}
192
193
ClientRequest.prototype.end = function (data, encoding, cb) {
194
var self = this
195
if (typeof data === 'function') {
196
cb = data
197
data = undefined
198
}
199
200
if (data)
201
stream.Writable.push.call(self, data, encoding)
202
203
stream.Writable.prototype.end.call(self, cb)
204
}
205
206
ClientRequest.prototype.flushHeaders = function () {}
207
ClientRequest.prototype.setTimeout = function () {}
208
ClientRequest.prototype.setNoDelay = function () {}
209
ClientRequest.prototype.setSocketKeepAlive = function () {}