@actions/http-client 1.0.7 → 1.0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/auth.js +5 -2
- package/index.d.ts +1 -1
- package/index.js +55 -25
- package/interfaces.d.ts +2 -2
- package/interfaces.js +0 -1
- package/package.json +6 -2
- package/proxy.js +7 -6
package/auth.js
CHANGED
|
@@ -6,7 +6,9 @@ class BasicCredentialHandler {
|
|
|
6
6
|
this.password = password;
|
|
7
7
|
}
|
|
8
8
|
prepareRequest(options) {
|
|
9
|
-
options.headers['Authorization'] =
|
|
9
|
+
options.headers['Authorization'] =
|
|
10
|
+
'Basic ' +
|
|
11
|
+
Buffer.from(this.username + ':' + this.password).toString('base64');
|
|
10
12
|
}
|
|
11
13
|
// This handler cannot handle 401
|
|
12
14
|
canHandleAuthentication(response) {
|
|
@@ -42,7 +44,8 @@ class PersonalAccessTokenCredentialHandler {
|
|
|
42
44
|
// currently implements pre-authorization
|
|
43
45
|
// TODO: support preAuth = false where it hooks on 401
|
|
44
46
|
prepareRequest(options) {
|
|
45
|
-
options.headers['Authorization'] =
|
|
47
|
+
options.headers['Authorization'] =
|
|
48
|
+
'Basic ' + Buffer.from('PAT:' + this.token).toString('base64');
|
|
46
49
|
}
|
|
47
50
|
// This handler cannot handle 401
|
|
48
51
|
canHandleAuthentication(response) {
|
package/index.d.ts
CHANGED
package/index.js
CHANGED
|
@@ -53,8 +53,18 @@ function getProxyUrl(serverUrl) {
|
|
|
53
53
|
return proxyUrl ? proxyUrl.href : '';
|
|
54
54
|
}
|
|
55
55
|
exports.getProxyUrl = getProxyUrl;
|
|
56
|
-
const HttpRedirectCodes = [
|
|
57
|
-
|
|
56
|
+
const HttpRedirectCodes = [
|
|
57
|
+
HttpCodes.MovedPermanently,
|
|
58
|
+
HttpCodes.ResourceMoved,
|
|
59
|
+
HttpCodes.SeeOther,
|
|
60
|
+
HttpCodes.TemporaryRedirect,
|
|
61
|
+
HttpCodes.PermanentRedirect
|
|
62
|
+
];
|
|
63
|
+
const HttpResponseRetryCodes = [
|
|
64
|
+
HttpCodes.BadGateway,
|
|
65
|
+
HttpCodes.ServiceUnavailable,
|
|
66
|
+
HttpCodes.GatewayTimeout
|
|
67
|
+
];
|
|
58
68
|
const RetryableHttpVerbs = ['OPTIONS', 'GET', 'DELETE', 'HEAD'];
|
|
59
69
|
const ExponentialBackoffCeiling = 10;
|
|
60
70
|
const ExponentialBackoffTimeSlice = 5;
|
|
@@ -179,18 +189,22 @@ class HttpClient {
|
|
|
179
189
|
*/
|
|
180
190
|
async request(verb, requestUrl, data, headers) {
|
|
181
191
|
if (this._disposed) {
|
|
182
|
-
throw new Error(
|
|
192
|
+
throw new Error('Client has already been disposed.');
|
|
183
193
|
}
|
|
184
194
|
let parsedUrl = url.parse(requestUrl);
|
|
185
195
|
let info = this._prepareRequest(verb, parsedUrl, headers);
|
|
186
196
|
// Only perform retries on reads since writes may not be idempotent.
|
|
187
|
-
let maxTries =
|
|
197
|
+
let maxTries = this._allowRetries && RetryableHttpVerbs.indexOf(verb) != -1
|
|
198
|
+
? this._maxRetries + 1
|
|
199
|
+
: 1;
|
|
188
200
|
let numTries = 0;
|
|
189
201
|
let response;
|
|
190
202
|
while (numTries < maxTries) {
|
|
191
203
|
response = await this.requestRaw(info, data);
|
|
192
204
|
// Check if it's an authentication challenge
|
|
193
|
-
if (response &&
|
|
205
|
+
if (response &&
|
|
206
|
+
response.message &&
|
|
207
|
+
response.message.statusCode === HttpCodes.Unauthorized) {
|
|
194
208
|
let authenticationHandler;
|
|
195
209
|
for (let i = 0; i < this.handlers.length; i++) {
|
|
196
210
|
if (this.handlers[i].canHandleAuthentication(response)) {
|
|
@@ -208,21 +222,32 @@ class HttpClient {
|
|
|
208
222
|
}
|
|
209
223
|
}
|
|
210
224
|
let redirectsRemaining = this._maxRedirects;
|
|
211
|
-
while (HttpRedirectCodes.indexOf(response.message.statusCode) != -1
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
const redirectUrl = response.message.headers[
|
|
225
|
+
while (HttpRedirectCodes.indexOf(response.message.statusCode) != -1 &&
|
|
226
|
+
this._allowRedirects &&
|
|
227
|
+
redirectsRemaining > 0) {
|
|
228
|
+
const redirectUrl = response.message.headers['location'];
|
|
215
229
|
if (!redirectUrl) {
|
|
216
230
|
// if there's no location to redirect to, we won't
|
|
217
231
|
break;
|
|
218
232
|
}
|
|
219
233
|
let parsedRedirectUrl = url.parse(redirectUrl);
|
|
220
|
-
if (parsedUrl.protocol == 'https:' &&
|
|
221
|
-
|
|
234
|
+
if (parsedUrl.protocol == 'https:' &&
|
|
235
|
+
parsedUrl.protocol != parsedRedirectUrl.protocol &&
|
|
236
|
+
!this._allowRedirectDowngrade) {
|
|
237
|
+
throw new Error('Redirect from HTTPS to HTTP protocol. This downgrade is not allowed for security reasons. If you want to allow this behavior, set the allowRedirectDowngrade option to true.');
|
|
222
238
|
}
|
|
223
239
|
// we need to finish reading the response before reassigning response
|
|
224
240
|
// which will leak the open socket.
|
|
225
241
|
await response.readBody();
|
|
242
|
+
// strip authorization header if redirected to a different hostname
|
|
243
|
+
if (parsedRedirectUrl.hostname !== parsedUrl.hostname) {
|
|
244
|
+
for (let header in headers) {
|
|
245
|
+
// header names are case insensitive
|
|
246
|
+
if (header.toLowerCase() === 'authorization') {
|
|
247
|
+
delete headers[header];
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
226
251
|
// let's make the request with the new redirectUrl
|
|
227
252
|
info = this._prepareRequest(verb, parsedRedirectUrl, headers);
|
|
228
253
|
response = await this.requestRaw(info, data);
|
|
@@ -273,8 +298,8 @@ class HttpClient {
|
|
|
273
298
|
*/
|
|
274
299
|
requestRawWithCallback(info, data, onResult) {
|
|
275
300
|
let socket;
|
|
276
|
-
if (typeof
|
|
277
|
-
info.options.headers[
|
|
301
|
+
if (typeof data === 'string') {
|
|
302
|
+
info.options.headers['Content-Length'] = Buffer.byteLength(data, 'utf8');
|
|
278
303
|
}
|
|
279
304
|
let callbackCalled = false;
|
|
280
305
|
let handleResult = (err, res) => {
|
|
@@ -287,7 +312,7 @@ class HttpClient {
|
|
|
287
312
|
let res = new HttpClientResponse(msg);
|
|
288
313
|
handleResult(null, res);
|
|
289
314
|
});
|
|
290
|
-
req.on('socket',
|
|
315
|
+
req.on('socket', sock => {
|
|
291
316
|
socket = sock;
|
|
292
317
|
});
|
|
293
318
|
// If we ever get disconnected, we want the socket to timeout eventually
|
|
@@ -302,10 +327,10 @@ class HttpClient {
|
|
|
302
327
|
// res should have headers
|
|
303
328
|
handleResult(err, null);
|
|
304
329
|
});
|
|
305
|
-
if (data && typeof
|
|
330
|
+
if (data && typeof data === 'string') {
|
|
306
331
|
req.write(data, 'utf8');
|
|
307
332
|
}
|
|
308
|
-
if (data && typeof
|
|
333
|
+
if (data && typeof data !== 'string') {
|
|
309
334
|
data.on('close', function () {
|
|
310
335
|
req.end();
|
|
311
336
|
});
|
|
@@ -332,31 +357,34 @@ class HttpClient {
|
|
|
332
357
|
const defaultPort = usingSsl ? 443 : 80;
|
|
333
358
|
info.options = {};
|
|
334
359
|
info.options.host = info.parsedUrl.hostname;
|
|
335
|
-
info.options.port = info.parsedUrl.port
|
|
336
|
-
|
|
360
|
+
info.options.port = info.parsedUrl.port
|
|
361
|
+
? parseInt(info.parsedUrl.port)
|
|
362
|
+
: defaultPort;
|
|
363
|
+
info.options.path =
|
|
364
|
+
(info.parsedUrl.pathname || '') + (info.parsedUrl.search || '');
|
|
337
365
|
info.options.method = method;
|
|
338
366
|
info.options.headers = this._mergeHeaders(headers);
|
|
339
367
|
if (this.userAgent != null) {
|
|
340
|
-
info.options.headers[
|
|
368
|
+
info.options.headers['user-agent'] = this.userAgent;
|
|
341
369
|
}
|
|
342
370
|
info.options.agent = this._getAgent(info.parsedUrl);
|
|
343
371
|
// gives handlers an opportunity to participate
|
|
344
372
|
if (this.handlers) {
|
|
345
|
-
this.handlers.forEach(
|
|
373
|
+
this.handlers.forEach(handler => {
|
|
346
374
|
handler.prepareRequest(info.options);
|
|
347
375
|
});
|
|
348
376
|
}
|
|
349
377
|
return info;
|
|
350
378
|
}
|
|
351
379
|
_mergeHeaders(headers) {
|
|
352
|
-
const lowercaseKeys = obj => Object.keys(obj).reduce((c, k) => (c[k.toLowerCase()] = obj[k], c), {});
|
|
380
|
+
const lowercaseKeys = obj => Object.keys(obj).reduce((c, k) => ((c[k.toLowerCase()] = obj[k]), c), {});
|
|
353
381
|
if (this.requestOptions && this.requestOptions.headers) {
|
|
354
382
|
return Object.assign({}, lowercaseKeys(this.requestOptions.headers), lowercaseKeys(headers));
|
|
355
383
|
}
|
|
356
384
|
return lowercaseKeys(headers || {});
|
|
357
385
|
}
|
|
358
386
|
_getExistingOrDefaultHeader(additionalHeaders, header, _default) {
|
|
359
|
-
const lowercaseKeys = obj => Object.keys(obj).reduce((c, k) => (c[k.toLowerCase()] = obj[k], c), {});
|
|
387
|
+
const lowercaseKeys = obj => Object.keys(obj).reduce((c, k) => ((c[k.toLowerCase()] = obj[k]), c), {});
|
|
360
388
|
let clientHeader;
|
|
361
389
|
if (this.requestOptions && this.requestOptions.headers) {
|
|
362
390
|
clientHeader = lowercaseKeys(this.requestOptions.headers)[header];
|
|
@@ -394,7 +422,7 @@ class HttpClient {
|
|
|
394
422
|
proxyAuth: proxyUrl.auth,
|
|
395
423
|
host: proxyUrl.hostname,
|
|
396
424
|
port: proxyUrl.port
|
|
397
|
-
}
|
|
425
|
+
}
|
|
398
426
|
};
|
|
399
427
|
let tunnelAgent;
|
|
400
428
|
const overHttps = proxyUrl.protocol === 'https:';
|
|
@@ -421,7 +449,9 @@ class HttpClient {
|
|
|
421
449
|
// we don't want to set NODE_TLS_REJECT_UNAUTHORIZED=0 since that will affect request for entire process
|
|
422
450
|
// http.RequestOptions doesn't expose a way to modify RequestOptions.agent.options
|
|
423
451
|
// we have to cast it to any and change it directly
|
|
424
|
-
agent.options = Object.assign(agent.options || {}, {
|
|
452
|
+
agent.options = Object.assign(agent.options || {}, {
|
|
453
|
+
rejectUnauthorized: false
|
|
454
|
+
});
|
|
425
455
|
}
|
|
426
456
|
return agent;
|
|
427
457
|
}
|
|
@@ -482,7 +512,7 @@ class HttpClient {
|
|
|
482
512
|
msg = contents;
|
|
483
513
|
}
|
|
484
514
|
else {
|
|
485
|
-
msg =
|
|
515
|
+
msg = 'Failed request: (' + statusCode + ')';
|
|
486
516
|
}
|
|
487
517
|
let err = new Error(msg);
|
|
488
518
|
// attach statusCode and body obj (if available) to the error object
|
package/interfaces.d.ts
CHANGED
package/interfaces.js
CHANGED
package/package.json
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@actions/http-client",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.8",
|
|
4
4
|
"description": "Actions Http Client",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"build": "rm -Rf ./_out && tsc && cp package*.json ./_out && cp *.md ./_out && cp LICENSE ./_out && cp actions.png ./_out",
|
|
8
|
-
"test": "jest"
|
|
8
|
+
"test": "jest",
|
|
9
|
+
"format": "prettier --write *.ts && prettier --write **/*.ts",
|
|
10
|
+
"format-check": "prettier --check *.ts && prettier --check **/*.ts",
|
|
11
|
+
"audit-check": "npm audit --audit-level=moderate"
|
|
9
12
|
},
|
|
10
13
|
"repository": {
|
|
11
14
|
"type": "git",
|
|
@@ -25,6 +28,7 @@
|
|
|
25
28
|
"@types/jest": "^25.1.4",
|
|
26
29
|
"@types/node": "^12.12.31",
|
|
27
30
|
"jest": "^25.1.0",
|
|
31
|
+
"prettier": "^2.0.4",
|
|
28
32
|
"proxy": "^1.0.1",
|
|
29
33
|
"ts-jest": "^25.2.1",
|
|
30
34
|
"typescript": "^3.8.3"
|
package/proxy.js
CHANGED
|
@@ -9,12 +9,10 @@ function getProxyUrl(reqUrl) {
|
|
|
9
9
|
}
|
|
10
10
|
let proxyVar;
|
|
11
11
|
if (usingSsl) {
|
|
12
|
-
proxyVar = process.env[
|
|
13
|
-
process.env["HTTPS_PROXY"];
|
|
12
|
+
proxyVar = process.env['https_proxy'] || process.env['HTTPS_PROXY'];
|
|
14
13
|
}
|
|
15
14
|
else {
|
|
16
|
-
proxyVar = process.env[
|
|
17
|
-
process.env["HTTP_PROXY"];
|
|
15
|
+
proxyVar = process.env['http_proxy'] || process.env['HTTP_PROXY'];
|
|
18
16
|
}
|
|
19
17
|
if (proxyVar) {
|
|
20
18
|
proxyUrl = url.parse(proxyVar);
|
|
@@ -26,7 +24,7 @@ function checkBypass(reqUrl) {
|
|
|
26
24
|
if (!reqUrl.hostname) {
|
|
27
25
|
return false;
|
|
28
26
|
}
|
|
29
|
-
let noProxy = process.env[
|
|
27
|
+
let noProxy = process.env['no_proxy'] || process.env['NO_PROXY'] || '';
|
|
30
28
|
if (!noProxy) {
|
|
31
29
|
return false;
|
|
32
30
|
}
|
|
@@ -47,7 +45,10 @@ function checkBypass(reqUrl) {
|
|
|
47
45
|
upperReqHosts.push(`${upperReqHosts[0]}:${reqPort}`);
|
|
48
46
|
}
|
|
49
47
|
// Compare request host against noproxy
|
|
50
|
-
for (let upperNoProxyItem of noProxy
|
|
48
|
+
for (let upperNoProxyItem of noProxy
|
|
49
|
+
.split(',')
|
|
50
|
+
.map(x => x.trim().toUpperCase())
|
|
51
|
+
.filter(x => x)) {
|
|
51
52
|
if (upperReqHosts.some(x => x === upperNoProxyItem)) {
|
|
52
53
|
return true;
|
|
53
54
|
}
|