Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download
80537 views
1
var Stream = require('stream');
2
var Response = require('./response');
3
var Base64 = require('Base64');
4
var inherits = require('inherits');
5
6
var Request = module.exports = function (xhr, params) {
7
var self = this;
8
self.writable = true;
9
self.xhr = xhr;
10
self.body = [];
11
12
self.uri = (params.protocol || 'http:') + '//'
13
+ params.host
14
+ (params.port ? ':' + params.port : '')
15
+ (params.path || '/')
16
;
17
18
if (typeof params.withCredentials === 'undefined') {
19
params.withCredentials = true;
20
}
21
22
try { xhr.withCredentials = params.withCredentials }
23
catch (e) {}
24
25
if (params.responseType) try { xhr.responseType = params.responseType }
26
catch (e) {}
27
28
xhr.open(
29
params.method || 'GET',
30
self.uri,
31
true
32
);
33
34
xhr.onerror = function(event) {
35
self.emit('error', new Error('Network error'));
36
};
37
38
self._headers = {};
39
40
if (params.headers) {
41
var keys = objectKeys(params.headers);
42
for (var i = 0; i < keys.length; i++) {
43
var key = keys[i];
44
if (!self.isSafeRequestHeader(key)) continue;
45
var value = params.headers[key];
46
self.setHeader(key, value);
47
}
48
}
49
50
if (params.auth) {
51
//basic auth
52
this.setHeader('Authorization', 'Basic ' + Base64.btoa(params.auth));
53
}
54
55
var res = new Response;
56
res.on('close', function () {
57
self.emit('close');
58
});
59
60
res.on('ready', function () {
61
self.emit('response', res);
62
});
63
64
res.on('error', function (err) {
65
self.emit('error', err);
66
});
67
68
xhr.onreadystatechange = function () {
69
// Fix for IE9 bug
70
// SCRIPT575: Could not complete the operation due to error c00c023f
71
// It happens when a request is aborted, calling the success callback anyway with readyState === 4
72
if (xhr.__aborted) return;
73
res.handle(xhr);
74
};
75
};
76
77
inherits(Request, Stream);
78
79
Request.prototype.setHeader = function (key, value) {
80
this._headers[key.toLowerCase()] = value
81
};
82
83
Request.prototype.getHeader = function (key) {
84
return this._headers[key.toLowerCase()]
85
};
86
87
Request.prototype.removeHeader = function (key) {
88
delete this._headers[key.toLowerCase()]
89
};
90
91
Request.prototype.write = function (s) {
92
this.body.push(s);
93
};
94
95
Request.prototype.destroy = function (s) {
96
this.xhr.__aborted = true;
97
this.xhr.abort();
98
this.emit('close');
99
};
100
101
Request.prototype.end = function (s) {
102
if (s !== undefined) this.body.push(s);
103
104
var keys = objectKeys(this._headers);
105
for (var i = 0; i < keys.length; i++) {
106
var key = keys[i];
107
var value = this._headers[key];
108
if (isArray(value)) {
109
for (var j = 0; j < value.length; j++) {
110
this.xhr.setRequestHeader(key, value[j]);
111
}
112
}
113
else this.xhr.setRequestHeader(key, value)
114
}
115
116
if (this.body.length === 0) {
117
this.xhr.send('');
118
}
119
else if (typeof this.body[0] === 'string') {
120
this.xhr.send(this.body.join(''));
121
}
122
else if (isArray(this.body[0])) {
123
var body = [];
124
for (var i = 0; i < this.body.length; i++) {
125
body.push.apply(body, this.body[i]);
126
}
127
this.xhr.send(body);
128
}
129
else if (/Array/.test(Object.prototype.toString.call(this.body[0]))) {
130
var len = 0;
131
for (var i = 0; i < this.body.length; i++) {
132
len += this.body[i].length;
133
}
134
var body = new(this.body[0].constructor)(len);
135
var k = 0;
136
137
for (var i = 0; i < this.body.length; i++) {
138
var b = this.body[i];
139
for (var j = 0; j < b.length; j++) {
140
body[k++] = b[j];
141
}
142
}
143
this.xhr.send(body);
144
}
145
else if (isXHR2Compatible(this.body[0])) {
146
this.xhr.send(this.body[0]);
147
}
148
else {
149
var body = '';
150
for (var i = 0; i < this.body.length; i++) {
151
body += this.body[i].toString();
152
}
153
this.xhr.send(body);
154
}
155
};
156
157
// Taken from http://dxr.mozilla.org/mozilla/mozilla-central/content/base/src/nsXMLHttpRequest.cpp.html
158
Request.unsafeHeaders = [
159
"accept-charset",
160
"accept-encoding",
161
"access-control-request-headers",
162
"access-control-request-method",
163
"connection",
164
"content-length",
165
"cookie",
166
"cookie2",
167
"content-transfer-encoding",
168
"date",
169
"expect",
170
"host",
171
"keep-alive",
172
"origin",
173
"referer",
174
"te",
175
"trailer",
176
"transfer-encoding",
177
"upgrade",
178
"user-agent",
179
"via"
180
];
181
182
Request.prototype.isSafeRequestHeader = function (headerName) {
183
if (!headerName) return false;
184
return indexOf(Request.unsafeHeaders, headerName.toLowerCase()) === -1;
185
};
186
187
var objectKeys = Object.keys || function (obj) {
188
var keys = [];
189
for (var key in obj) keys.push(key);
190
return keys;
191
};
192
193
var isArray = Array.isArray || function (xs) {
194
return Object.prototype.toString.call(xs) === '[object Array]';
195
};
196
197
var indexOf = function (xs, x) {
198
if (xs.indexOf) return xs.indexOf(x);
199
for (var i = 0; i < xs.length; i++) {
200
if (xs[i] === x) return i;
201
}
202
return -1;
203
};
204
205
var isXHR2Compatible = function (obj) {
206
if (typeof Blob !== 'undefined' && obj instanceof Blob) return true;
207
if (typeof ArrayBuffer !== 'undefined' && obj instanceof ArrayBuffer) return true;
208
if (typeof FormData !== 'undefined' && obj instanceof FormData) return true;
209
};
210
211