@verdaccio/proxy 6.0.0-6-next.52 → 6.0.0-6-next.53
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/CHANGELOG.md +14 -0
- package/build/agent.d.ts +1 -1
- package/build/agent.js.map +1 -1
- package/build/proxy.d.ts +12 -13
- package/build/proxy.js +49 -41
- package/build/proxy.js.map +1 -1
- package/package.json +9 -12
- package/src/agent.ts +1 -1
- package/src/proxy.ts +60 -47
- package/test/headers.auth.spec.ts +16 -4
- package/test/noProxy.spec.ts +4 -2
- package/test/proxy.metadata.spec.ts +147 -31
- package/test/proxy.search.spec.ts +37 -53
- package/test/proxy.tarball.spec.ts +2 -2
package/src/proxy.ts
CHANGED
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
import JSONStream from 'JSONStream';
|
|
2
2
|
import buildDebug from 'debug';
|
|
3
|
-
import got, {
|
|
4
|
-
|
|
3
|
+
import got, {
|
|
4
|
+
Agents,
|
|
5
|
+
Delays,
|
|
6
|
+
Options,
|
|
7
|
+
RequestError,
|
|
8
|
+
RetryOptions,
|
|
9
|
+
Headers as gotHeaders,
|
|
10
|
+
} from 'got-cjs';
|
|
5
11
|
import _ from 'lodash';
|
|
6
12
|
import Stream, { PassThrough, Readable } from 'stream';
|
|
7
|
-
import { Headers, fetch as undiciFetch } from 'undici';
|
|
8
13
|
import { URL } from 'url';
|
|
9
14
|
|
|
10
15
|
import {
|
|
@@ -24,8 +29,6 @@ import { buildToken } from '@verdaccio/utils';
|
|
|
24
29
|
import CustomAgents, { AgentOptionsConf } from './agent';
|
|
25
30
|
import { parseInterval } from './proxy-utils';
|
|
26
31
|
|
|
27
|
-
const LoggerApi = require('@verdaccio/logger');
|
|
28
|
-
|
|
29
32
|
const debug = buildDebug('verdaccio:proxy');
|
|
30
33
|
|
|
31
34
|
const encode = function (thing): string {
|
|
@@ -51,10 +54,11 @@ export interface ProxyList {
|
|
|
51
54
|
}
|
|
52
55
|
|
|
53
56
|
export type ProxySearchParams = {
|
|
54
|
-
headers?: Headers;
|
|
55
57
|
url: string;
|
|
56
|
-
query?: searchUtils.SearchQuery;
|
|
57
58
|
abort: AbortController;
|
|
59
|
+
query?: searchUtils.SearchQuery;
|
|
60
|
+
headers?: Headers;
|
|
61
|
+
retry?: Partial<RetryOptions>;
|
|
58
62
|
};
|
|
59
63
|
export interface IProxy {
|
|
60
64
|
config: UpLinkConfLocal;
|
|
@@ -65,15 +69,18 @@ export interface IProxy {
|
|
|
65
69
|
server_id: string;
|
|
66
70
|
url: URL;
|
|
67
71
|
maxage: number;
|
|
68
|
-
timeout:
|
|
72
|
+
timeout: Delays;
|
|
69
73
|
max_fails: number;
|
|
70
74
|
fail_timeout: number;
|
|
71
75
|
upname: string;
|
|
72
76
|
search(options: ProxySearchParams): Promise<Stream.Readable>;
|
|
73
|
-
getRemoteMetadata(
|
|
77
|
+
getRemoteMetadata(
|
|
78
|
+
name: string,
|
|
79
|
+
options: Partial<ISyncUplinksOptions>
|
|
80
|
+
): Promise<[Manifest, string]>;
|
|
74
81
|
fetchTarball(
|
|
75
82
|
url: string,
|
|
76
|
-
options: Pick<ISyncUplinksOptions, 'remoteAddress' | 'etag' | 'retry'
|
|
83
|
+
options: Partial<Pick<ISyncUplinksOptions, 'remoteAddress' | 'etag' | 'retry'>>
|
|
77
84
|
): PassThrough;
|
|
78
85
|
}
|
|
79
86
|
|
|
@@ -99,7 +106,7 @@ class ProxyStorage implements IProxy {
|
|
|
99
106
|
public server_id: string;
|
|
100
107
|
public url: URL;
|
|
101
108
|
public maxage: number;
|
|
102
|
-
public timeout:
|
|
109
|
+
public timeout: Delays;
|
|
103
110
|
public max_fails: number;
|
|
104
111
|
public fail_timeout: number;
|
|
105
112
|
public agent_options: AgentOptionsConf;
|
|
@@ -111,14 +118,14 @@ class ProxyStorage implements IProxy {
|
|
|
111
118
|
// @ts-ignore
|
|
112
119
|
public last_request_time: number | null;
|
|
113
120
|
public strict_ssl: boolean;
|
|
114
|
-
private retry: Partial<
|
|
121
|
+
private retry: Partial<RetryOptions>;
|
|
115
122
|
|
|
116
|
-
public constructor(config: UpLinkConfLocal, mainConfig: Config, agent?: Agents) {
|
|
123
|
+
public constructor(config: UpLinkConfLocal, mainConfig: Config, logger: Logger, agent?: Agents) {
|
|
117
124
|
this.config = config;
|
|
118
125
|
this.failed_requests = 0;
|
|
119
126
|
this.userAgent = mainConfig.user_agent ?? 'hidden';
|
|
120
127
|
this.ca = config.ca;
|
|
121
|
-
this.logger =
|
|
128
|
+
this.logger = logger;
|
|
122
129
|
this.server_id = mainConfig.server_id;
|
|
123
130
|
this.agent_options = setConfig(this.config, 'agent_options', {
|
|
124
131
|
keepAlive: true,
|
|
@@ -145,7 +152,10 @@ class ProxyStorage implements IProxy {
|
|
|
145
152
|
// a bunch of different configurable timers
|
|
146
153
|
this.maxage = parseInterval(setConfig(this.config, 'maxage', '2m'));
|
|
147
154
|
// https://github.com/sindresorhus/got/blob/main/documentation/6-timeout.md
|
|
148
|
-
this.timeout =
|
|
155
|
+
this.timeout = {
|
|
156
|
+
request: parseInterval(setConfig(this.config, 'timeout', '30s')),
|
|
157
|
+
};
|
|
158
|
+
debug('set timeout %s', this.timeout);
|
|
149
159
|
this.max_fails = Number(setConfig(this.config, 'max_fails', this.config.max_fails ?? 2));
|
|
150
160
|
this.fail_timeout = parseInterval(setConfig(this.config, 'fail_timeout', '5m'));
|
|
151
161
|
this.strict_ssl = Boolean(setConfig(this.config, 'strict_ssl', true));
|
|
@@ -162,7 +172,7 @@ class ProxyStorage implements IProxy {
|
|
|
162
172
|
}
|
|
163
173
|
}
|
|
164
174
|
|
|
165
|
-
public
|
|
175
|
+
public getHeaders(headers = {}): gotHeaders {
|
|
166
176
|
const accept = HEADERS.ACCEPT;
|
|
167
177
|
const acceptEncoding = HEADERS.ACCEPT_ENCODING;
|
|
168
178
|
const userAgent = HEADERS.USER_AGENT;
|
|
@@ -296,15 +306,15 @@ class ProxyStorage implements IProxy {
|
|
|
296
306
|
|
|
297
307
|
public async getRemoteMetadata(
|
|
298
308
|
name: string,
|
|
299
|
-
options: ISyncUplinksOptions
|
|
309
|
+
options: Partial<ISyncUplinksOptions>
|
|
300
310
|
): Promise<[Manifest, string]> {
|
|
301
311
|
if (this._ifRequestFailure()) {
|
|
302
312
|
throw errorUtils.getInternalError(API_ERROR.UPLINK_OFFLINE);
|
|
303
313
|
}
|
|
304
314
|
|
|
305
315
|
// FUTURE: allow mix headers that comes from the client
|
|
306
|
-
debug('
|
|
307
|
-
let headers = this.
|
|
316
|
+
debug('getting metadata for package %s', name);
|
|
317
|
+
let headers = this.getHeaders(options?.headers);
|
|
308
318
|
headers = this.addProxyHeaders(headers, options.remoteAddress);
|
|
309
319
|
headers = this.applyUplinkHeaders(headers);
|
|
310
320
|
// the following headers cannot be overwritten
|
|
@@ -314,25 +324,24 @@ class ProxyStorage implements IProxy {
|
|
|
314
324
|
}
|
|
315
325
|
const method = options.method || 'GET';
|
|
316
326
|
const uri = this.config.url + `/${encode(name)}`;
|
|
317
|
-
debug('
|
|
327
|
+
debug('set retry limit is %s', this.retry.limit);
|
|
318
328
|
let response;
|
|
319
329
|
let responseLength = 0;
|
|
320
330
|
try {
|
|
321
331
|
const retry = options?.retry ?? this.retry;
|
|
322
|
-
debug('retry
|
|
332
|
+
debug('retry initial count %s', retry);
|
|
323
333
|
response = await got(uri, {
|
|
324
334
|
headers,
|
|
325
335
|
responseType: 'json',
|
|
326
336
|
method,
|
|
327
337
|
agent: this.agent,
|
|
328
338
|
retry,
|
|
329
|
-
|
|
330
|
-
timeout: { request: options?.timeout ?? this.timeout },
|
|
339
|
+
timeout: this.timeout,
|
|
331
340
|
hooks: {
|
|
332
341
|
afterResponse: [
|
|
333
342
|
(afterResponse) => {
|
|
334
343
|
const code = afterResponse.statusCode;
|
|
335
|
-
debug('
|
|
344
|
+
debug('after response code is %s', code);
|
|
336
345
|
if (code >= HTTP_STATUS.OK && code < HTTP_STATUS.MULTIPLE_CHOICES) {
|
|
337
346
|
if (this.failed_requests >= this.max_fails) {
|
|
338
347
|
this.failed_requests = 0;
|
|
@@ -349,8 +358,8 @@ class ProxyStorage implements IProxy {
|
|
|
349
358
|
},
|
|
350
359
|
],
|
|
351
360
|
beforeRetry: [
|
|
352
|
-
|
|
353
|
-
|
|
361
|
+
(error: RequestError, count: number) => {
|
|
362
|
+
debug('retry %s count: %s', uri, count);
|
|
354
363
|
this.failed_requests = count ?? 0;
|
|
355
364
|
this.logger.info(
|
|
356
365
|
{
|
|
@@ -378,7 +387,7 @@ class ProxyStorage implements IProxy {
|
|
|
378
387
|
.on('request', () => {
|
|
379
388
|
this.last_request_time = Date.now();
|
|
380
389
|
})
|
|
381
|
-
.on('response', (eventResponse) => {
|
|
390
|
+
.on<any>('response', (eventResponse) => {
|
|
382
391
|
const message = "@{!status}, req: '@{request.method} @{request.url}' (streaming)";
|
|
383
392
|
this.logger.http(
|
|
384
393
|
{
|
|
@@ -422,9 +431,10 @@ class ProxyStorage implements IProxy {
|
|
|
422
431
|
);
|
|
423
432
|
return [data, etag];
|
|
424
433
|
} catch (err: any) {
|
|
425
|
-
debug('uri %s
|
|
434
|
+
debug('error %s on uri %s', err.code, uri);
|
|
426
435
|
if (err.code === 'ERR_NON_2XX_3XX_RESPONSE') {
|
|
427
436
|
const code = err.response.statusCode;
|
|
437
|
+
debug('error code %s', code);
|
|
428
438
|
if (code === HTTP_STATUS.NOT_FOUND) {
|
|
429
439
|
throw errorUtils.getNotFound(errorUtils.API_ERROR.NOT_PACKAGE_UPLINK);
|
|
430
440
|
}
|
|
@@ -437,6 +447,15 @@ class ProxyStorage implements IProxy {
|
|
|
437
447
|
error.remoteStatus = code;
|
|
438
448
|
throw error;
|
|
439
449
|
}
|
|
450
|
+
} else if (err.code === 'ETIMEDOUT') {
|
|
451
|
+
debug('error code timeout');
|
|
452
|
+
const code = err.code;
|
|
453
|
+
const error = errorUtils.getInternalError(
|
|
454
|
+
`${errorUtils.API_ERROR.SERVER_TIME_OUT}: ${code}`
|
|
455
|
+
);
|
|
456
|
+
// we need this code to identify outside which status code triggered the error
|
|
457
|
+
error.remoteStatus = code;
|
|
458
|
+
throw error;
|
|
440
459
|
}
|
|
441
460
|
throw err;
|
|
442
461
|
}
|
|
@@ -449,7 +468,7 @@ class ProxyStorage implements IProxy {
|
|
|
449
468
|
): any {
|
|
450
469
|
debug('fetching url for %s', url);
|
|
451
470
|
const options = { ...this.config, ...overrideOptions };
|
|
452
|
-
let headers = this.
|
|
471
|
+
let headers = this.getHeaders(options?.headers);
|
|
453
472
|
headers = this.addProxyHeaders(headers, options.remoteAddress);
|
|
454
473
|
headers = this.applyUplinkHeaders(headers);
|
|
455
474
|
// the following headers cannot be overwritten
|
|
@@ -482,37 +501,31 @@ class ProxyStorage implements IProxy {
|
|
|
482
501
|
* @param {*} options request options
|
|
483
502
|
* @return {Stream}
|
|
484
503
|
*/
|
|
485
|
-
public async search({ url, abort }: ProxySearchParams): Promise<Stream.Readable> {
|
|
486
|
-
debug('search url %o', url);
|
|
487
|
-
|
|
488
|
-
let response;
|
|
504
|
+
public async search({ url, abort, retry }: ProxySearchParams): Promise<Stream.Readable> {
|
|
489
505
|
try {
|
|
490
506
|
const fullURL = new URL(`${this.url}${url}`);
|
|
491
507
|
// FIXME: a better way to remove duplicate slashes?
|
|
492
508
|
const uri = fullURL.href.replace(/([^:]\/)\/+/g, '$1');
|
|
493
509
|
this.logger.http({ uri, uplink: this.upname }, 'search request to uplink @{uplink} - @{uri}');
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
// }),
|
|
501
|
-
signal: abort?.signal,
|
|
510
|
+
debug('searching on %s', uri);
|
|
511
|
+
const response = got(uri, {
|
|
512
|
+
signal: abort ? abort.signal : {},
|
|
513
|
+
agent: this.agent,
|
|
514
|
+
timeout: this.timeout,
|
|
515
|
+
retry: retry ?? this.retry,
|
|
502
516
|
});
|
|
503
|
-
debug('response.status %o', response.status);
|
|
504
|
-
|
|
505
|
-
if (response.status >= HTTP_STATUS.BAD_REQUEST) {
|
|
506
|
-
throw errorUtils.getInternalError(`bad status code ${response.status} from uplink`);
|
|
507
|
-
}
|
|
508
517
|
|
|
509
|
-
const streamSearch = new PassThrough({ objectMode: true });
|
|
510
518
|
const res = await response.text();
|
|
519
|
+
const streamSearch = new PassThrough({ objectMode: true });
|
|
511
520
|
const streamResponse = Readable.from(res);
|
|
512
521
|
// objects is one of the properties on the body, it ignores date and total
|
|
513
522
|
streamResponse.pipe(JSONStream.parse('objects')).pipe(streamSearch, { end: true });
|
|
514
523
|
return streamSearch;
|
|
515
524
|
} catch (err: any) {
|
|
525
|
+
debug('search error %s', err);
|
|
526
|
+
if (err.response.statusCode === 409) {
|
|
527
|
+
throw errorUtils.getInternalError(`bad status code ${err.response.statusCode} from uplink`);
|
|
528
|
+
}
|
|
516
529
|
this.logger.error(
|
|
517
530
|
{ errorMessage: err?.message, name: this.upname },
|
|
518
531
|
'proxy uplink @{name} search error: @{errorMessage}'
|
|
@@ -1,11 +1,23 @@
|
|
|
1
1
|
import { DEFAULT_REGISTRY } from '@verdaccio/config';
|
|
2
2
|
import { HEADERS, TOKEN_BASIC, TOKEN_BEARER, constants } from '@verdaccio/core';
|
|
3
|
-
import {
|
|
3
|
+
import { Logger } from '@verdaccio/types';
|
|
4
4
|
import { buildToken } from '@verdaccio/utils';
|
|
5
5
|
|
|
6
6
|
import { ProxyStorage } from '../src';
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
const mockDebug = jest.fn();
|
|
9
|
+
const mockInfo = jest.fn();
|
|
10
|
+
const mockHttp = jest.fn();
|
|
11
|
+
const mockError = jest.fn();
|
|
12
|
+
const mockWarn = jest.fn();
|
|
13
|
+
|
|
14
|
+
const logger = {
|
|
15
|
+
debug: mockDebug,
|
|
16
|
+
info: mockInfo,
|
|
17
|
+
http: mockHttp,
|
|
18
|
+
error: mockError,
|
|
19
|
+
warn: mockWarn,
|
|
20
|
+
} as unknown as Logger;
|
|
9
21
|
|
|
10
22
|
function createUplink(config) {
|
|
11
23
|
const defaultConfig = {
|
|
@@ -13,12 +25,12 @@ function createUplink(config) {
|
|
|
13
25
|
};
|
|
14
26
|
const mergeConfig = Object.assign({}, defaultConfig, config);
|
|
15
27
|
// @ts-ignore
|
|
16
|
-
return new ProxyStorage(mergeConfig, {});
|
|
28
|
+
return new ProxyStorage(mergeConfig, {}, logger);
|
|
17
29
|
}
|
|
18
30
|
|
|
19
31
|
function setHeadersNext(config: unknown = {}, headers: any = {}) {
|
|
20
32
|
const uplink = createUplink(config);
|
|
21
|
-
return uplink.
|
|
33
|
+
return uplink.getHeaders({ ...headers });
|
|
22
34
|
}
|
|
23
35
|
|
|
24
36
|
describe('setHeadersNext', () => {
|
package/test/noProxy.spec.ts
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
|
+
import { logger, setup } from '@verdaccio/logger';
|
|
2
|
+
|
|
1
3
|
import { ProxyStorage } from '../src';
|
|
2
4
|
|
|
3
|
-
|
|
5
|
+
setup({});
|
|
4
6
|
|
|
5
7
|
function getProxyInstance(host, uplinkConf, appConfig) {
|
|
6
8
|
uplinkConf.url = host;
|
|
7
9
|
|
|
8
|
-
return new ProxyStorage(uplinkConf, appConfig);
|
|
10
|
+
return new ProxyStorage(uplinkConf, appConfig, logger);
|
|
9
11
|
}
|
|
10
12
|
|
|
11
13
|
describe('Use proxy', () => {
|
|
@@ -4,6 +4,7 @@ import { setTimeout } from 'timers/promises';
|
|
|
4
4
|
|
|
5
5
|
import { Config, parseConfigFile } from '@verdaccio/config';
|
|
6
6
|
import { API_ERROR, errorUtils } from '@verdaccio/core';
|
|
7
|
+
import { Logger } from '@verdaccio/types';
|
|
7
8
|
|
|
8
9
|
import { ProxyStorage } from '../src';
|
|
9
10
|
|
|
@@ -15,6 +16,14 @@ const mockHttp = jest.fn();
|
|
|
15
16
|
const mockError = jest.fn();
|
|
16
17
|
const mockWarn = jest.fn();
|
|
17
18
|
|
|
19
|
+
const logger = {
|
|
20
|
+
debug: mockDebug,
|
|
21
|
+
info: mockInfo,
|
|
22
|
+
http: mockHttp,
|
|
23
|
+
error: mockError,
|
|
24
|
+
warn: mockWarn,
|
|
25
|
+
} as unknown as Logger;
|
|
26
|
+
|
|
18
27
|
// mock to get the headers fixed value
|
|
19
28
|
jest.mock('crypto', () => {
|
|
20
29
|
return {
|
|
@@ -31,22 +40,6 @@ jest.mock('crypto', () => {
|
|
|
31
40
|
};
|
|
32
41
|
});
|
|
33
42
|
|
|
34
|
-
jest.mock('@verdaccio/logger', () => {
|
|
35
|
-
const originalLogger = jest.requireActual('@verdaccio/logger');
|
|
36
|
-
return {
|
|
37
|
-
...originalLogger,
|
|
38
|
-
logger: {
|
|
39
|
-
child: () => ({
|
|
40
|
-
debug: (arg, msg) => mockDebug(arg, msg),
|
|
41
|
-
info: (arg, msg) => mockInfo(arg, msg),
|
|
42
|
-
http: (arg, msg) => mockHttp(arg, msg),
|
|
43
|
-
error: (arg, msg) => mockError(arg, msg),
|
|
44
|
-
warn: (arg, msg) => mockWarn(arg, msg),
|
|
45
|
-
}),
|
|
46
|
-
},
|
|
47
|
-
};
|
|
48
|
-
});
|
|
49
|
-
|
|
50
43
|
const domain = 'https://registry.npmjs.org';
|
|
51
44
|
|
|
52
45
|
describe('proxy', () => {
|
|
@@ -77,7 +70,7 @@ describe('proxy', () => {
|
|
|
77
70
|
})
|
|
78
71
|
.get('/jquery')
|
|
79
72
|
.reply(200, { body: 'test' });
|
|
80
|
-
const prox1 = new ProxyStorage(defaultRequestOptions, conf);
|
|
73
|
+
const prox1 = new ProxyStorage(defaultRequestOptions, conf, logger);
|
|
81
74
|
const [manifest] = await prox1.getRemoteMetadata('jquery', {
|
|
82
75
|
remoteAddress: '127.0.0.1',
|
|
83
76
|
});
|
|
@@ -103,7 +96,7 @@ describe('proxy', () => {
|
|
|
103
96
|
etag: () => `_ref_4444`,
|
|
104
97
|
}
|
|
105
98
|
);
|
|
106
|
-
const prox1 = new ProxyStorage(defaultRequestOptions, conf);
|
|
99
|
+
const prox1 = new ProxyStorage(defaultRequestOptions, conf, logger);
|
|
107
100
|
const [manifest, etag] = await prox1.getRemoteMetadata('jquery', {
|
|
108
101
|
remoteAddress: '127.0.0.1',
|
|
109
102
|
});
|
|
@@ -130,7 +123,7 @@ describe('proxy', () => {
|
|
|
130
123
|
etag: () => `_ref_4444`,
|
|
131
124
|
}
|
|
132
125
|
);
|
|
133
|
-
const prox1 = new ProxyStorage(defaultRequestOptions, conf);
|
|
126
|
+
const prox1 = new ProxyStorage(defaultRequestOptions, conf, logger);
|
|
134
127
|
const [manifest, etag] = await prox1.getRemoteMetadata('jquery', {
|
|
135
128
|
etag: 'foo',
|
|
136
129
|
remoteAddress: '127.0.0.1',
|
|
@@ -145,7 +138,7 @@ describe('proxy', () => {
|
|
|
145
138
|
nock(domain)
|
|
146
139
|
.get('/jquery')
|
|
147
140
|
.reply(200, { body: { name: 'foo', version: '1.0.0' } }, {});
|
|
148
|
-
const prox1 = new ProxyStorage(defaultRequestOptions, conf);
|
|
141
|
+
const prox1 = new ProxyStorage(defaultRequestOptions, conf, logger);
|
|
149
142
|
await prox1.getRemoteMetadata('jquery', {
|
|
150
143
|
remoteAddress: '127.0.0.1',
|
|
151
144
|
});
|
|
@@ -174,7 +167,7 @@ describe('proxy', () => {
|
|
|
174
167
|
describe('error handling', () => {
|
|
175
168
|
test('proxy call with 304', async () => {
|
|
176
169
|
nock(domain).get('/jquery').reply(304);
|
|
177
|
-
const prox1 = new ProxyStorage(defaultRequestOptions, conf);
|
|
170
|
+
const prox1 = new ProxyStorage(defaultRequestOptions, conf, logger);
|
|
178
171
|
await expect(prox1.getRemoteMetadata('jquery', { etag: 'rev_3333' })).rejects.toThrow(
|
|
179
172
|
'no data'
|
|
180
173
|
);
|
|
@@ -182,7 +175,7 @@ describe('proxy', () => {
|
|
|
182
175
|
|
|
183
176
|
test('reply with error', async () => {
|
|
184
177
|
nock(domain).get('/jquery').replyWithError('something awful happened');
|
|
185
|
-
const prox1 = new ProxyStorage(defaultRequestOptions, conf);
|
|
178
|
+
const prox1 = new ProxyStorage(defaultRequestOptions, conf, logger);
|
|
186
179
|
await expect(
|
|
187
180
|
prox1.getRemoteMetadata('jquery', {
|
|
188
181
|
remoteAddress: '127.0.0.1',
|
|
@@ -192,15 +185,15 @@ describe('proxy', () => {
|
|
|
192
185
|
|
|
193
186
|
test('reply with 409 error', async () => {
|
|
194
187
|
nock(domain).get('/jquery').reply(409);
|
|
195
|
-
const prox1 = new ProxyStorage(defaultRequestOptions, conf);
|
|
196
|
-
await expect(prox1.getRemoteMetadata('jquery', { retry: 0 })).rejects.toThrow(
|
|
188
|
+
const prox1 = new ProxyStorage(defaultRequestOptions, conf, logger);
|
|
189
|
+
await expect(prox1.getRemoteMetadata('jquery', { retry: { limit: 0 } })).rejects.toThrow(
|
|
197
190
|
new Error('bad status code: 409')
|
|
198
191
|
);
|
|
199
192
|
});
|
|
200
193
|
|
|
201
194
|
test('reply with bad body json format', async () => {
|
|
202
195
|
nock(domain).get('/jquery').reply(200, 'some-text');
|
|
203
|
-
const prox1 = new ProxyStorage(defaultRequestOptions, conf);
|
|
196
|
+
const prox1 = new ProxyStorage(defaultRequestOptions, conf, logger);
|
|
204
197
|
await expect(
|
|
205
198
|
prox1.getRemoteMetadata('jquery', {
|
|
206
199
|
remoteAddress: '127.0.0.1',
|
|
@@ -214,7 +207,7 @@ describe('proxy', () => {
|
|
|
214
207
|
|
|
215
208
|
test('400 error proxy call', async () => {
|
|
216
209
|
nock(domain).get('/jquery').reply(409);
|
|
217
|
-
const prox1 = new ProxyStorage(defaultRequestOptions, conf);
|
|
210
|
+
const prox1 = new ProxyStorage(defaultRequestOptions, conf, logger);
|
|
218
211
|
await expect(
|
|
219
212
|
prox1.getRemoteMetadata('jquery', {
|
|
220
213
|
remoteAddress: '127.0.0.1',
|
|
@@ -226,7 +219,7 @@ describe('proxy', () => {
|
|
|
226
219
|
|
|
227
220
|
test('proxy not found', async () => {
|
|
228
221
|
nock(domain).get('/jquery').reply(404);
|
|
229
|
-
const prox1 = new ProxyStorage(defaultRequestOptions, conf);
|
|
222
|
+
const prox1 = new ProxyStorage(defaultRequestOptions, conf, logger);
|
|
230
223
|
await expect(
|
|
231
224
|
prox1.getRemoteMetadata('jquery', {
|
|
232
225
|
remoteAddress: '127.0.0.1',
|
|
@@ -253,7 +246,7 @@ describe('proxy', () => {
|
|
|
253
246
|
.once()
|
|
254
247
|
.reply(200, { body: { name: 'foo', version: '1.0.0' } });
|
|
255
248
|
|
|
256
|
-
const prox1 = new ProxyStorage(defaultRequestOptions, conf);
|
|
249
|
+
const prox1 = new ProxyStorage(defaultRequestOptions, conf, logger);
|
|
257
250
|
const [manifest] = await prox1.getRemoteMetadata('jquery', {
|
|
258
251
|
retry: { limit: 2 },
|
|
259
252
|
});
|
|
@@ -269,10 +262,10 @@ describe('proxy', () => {
|
|
|
269
262
|
);
|
|
270
263
|
});
|
|
271
264
|
|
|
272
|
-
test('retry is exceded and uplink goes offline with logging activity', async () => {
|
|
265
|
+
test('retry count is exceded and uplink goes offline with logging activity', async () => {
|
|
273
266
|
nock(domain).get('/jquery').times(10).reply(500);
|
|
274
267
|
|
|
275
|
-
const prox1 = new ProxyStorage(defaultRequestOptions, conf);
|
|
268
|
+
const prox1 = new ProxyStorage(defaultRequestOptions, conf, logger);
|
|
276
269
|
await expect(
|
|
277
270
|
prox1.getRemoteMetadata('jquery', {
|
|
278
271
|
remoteAddress: '127.0.0.1',
|
|
@@ -307,7 +300,8 @@ describe('proxy', () => {
|
|
|
307
300
|
|
|
308
301
|
const prox1 = new ProxyStorage(
|
|
309
302
|
{ ...defaultRequestOptions, fail_timeout: '1s', max_fails: 1 },
|
|
310
|
-
conf
|
|
303
|
+
conf,
|
|
304
|
+
logger
|
|
311
305
|
);
|
|
312
306
|
// force retry
|
|
313
307
|
await expect(
|
|
@@ -350,5 +344,127 @@ describe('proxy', () => {
|
|
|
350
344
|
);
|
|
351
345
|
}, 10000);
|
|
352
346
|
});
|
|
347
|
+
|
|
348
|
+
describe('timeout', () => {
|
|
349
|
+
test('fail for timeout (2 seconds)', async () => {
|
|
350
|
+
nock(domain)
|
|
351
|
+
.get('/jquery')
|
|
352
|
+
.times(10)
|
|
353
|
+
.delayConnection(6000)
|
|
354
|
+
.reply(200, { body: { name: 'foo', version: '1.0.0' } });
|
|
355
|
+
|
|
356
|
+
const confTimeout = { ...defaultRequestOptions };
|
|
357
|
+
// @ts-expect-error
|
|
358
|
+
confTimeout.timeout = '2s';
|
|
359
|
+
const prox1 = new ProxyStorage(confTimeout, conf, logger);
|
|
360
|
+
await expect(
|
|
361
|
+
prox1.getRemoteMetadata('jquery', {
|
|
362
|
+
retry: { limit: 0 },
|
|
363
|
+
})
|
|
364
|
+
).rejects.toThrow('ETIMEDOUT');
|
|
365
|
+
}, 10000);
|
|
366
|
+
|
|
367
|
+
test('fail for one failure and timeout (2 seconds)', async () => {
|
|
368
|
+
nock(domain)
|
|
369
|
+
.get('/jquery')
|
|
370
|
+
.times(1)
|
|
371
|
+
.reply(500)
|
|
372
|
+
.get('/jquery')
|
|
373
|
+
.delayConnection(4000)
|
|
374
|
+
.reply(200, { body: { name: 'foo', version: '1.0.0' } });
|
|
375
|
+
|
|
376
|
+
const confTimeout = { ...defaultRequestOptions };
|
|
377
|
+
// @ts-expect-error
|
|
378
|
+
confTimeout.timeout = '2s';
|
|
379
|
+
const prox1 = new ProxyStorage(confTimeout, conf, logger);
|
|
380
|
+
await expect(
|
|
381
|
+
prox1.getRemoteMetadata('jquery', {
|
|
382
|
+
retry: { limit: 1 },
|
|
383
|
+
})
|
|
384
|
+
).rejects.toThrow('ETIMEDOUT');
|
|
385
|
+
}, 10000);
|
|
386
|
+
|
|
387
|
+
// test('retry count is exceded and uplink goes offline with logging activity', async () => {
|
|
388
|
+
// nock(domain).get('/jquery').times(10).reply(500);
|
|
389
|
+
|
|
390
|
+
// const prox1 = new ProxyStorage(defaultRequestOptions, conf, logger);
|
|
391
|
+
// await expect(
|
|
392
|
+
// prox1.getRemoteMetadata('jquery', {
|
|
393
|
+
// remoteAddress: '127.0.0.1',
|
|
394
|
+
// retry: { limit: 2 },
|
|
395
|
+
// })
|
|
396
|
+
// ).rejects.toThrow();
|
|
397
|
+
// await expect(
|
|
398
|
+
// prox1.getRemoteMetadata('jquery', {
|
|
399
|
+
// remoteAddress: '127.0.0.1',
|
|
400
|
+
// retry: { limit: 2 },
|
|
401
|
+
// })
|
|
402
|
+
// ).rejects.toThrow(errorUtils.getInternalError(errorUtils.API_ERROR.UPLINK_OFFLINE));
|
|
403
|
+
// expect(mockWarn).toHaveBeenCalledTimes(1);
|
|
404
|
+
// expect(mockWarn).toHaveBeenLastCalledWith(
|
|
405
|
+
// {
|
|
406
|
+
// host: 'registry.npmjs.org',
|
|
407
|
+
// },
|
|
408
|
+
// 'host @{host} is now offline'
|
|
409
|
+
// );
|
|
410
|
+
// });
|
|
411
|
+
|
|
412
|
+
// test('fails calls and recover with 200 with log online activity', async () => {
|
|
413
|
+
// // This unit test is designed to verify if the uplink goes to offline
|
|
414
|
+
// // and recover after the fail_timeout has expired.
|
|
415
|
+
// nock(domain)
|
|
416
|
+
// .get('/jquery')
|
|
417
|
+
// .thrice()
|
|
418
|
+
// .reply(500, 'some-text')
|
|
419
|
+
// .get('/jquery')
|
|
420
|
+
// .once()
|
|
421
|
+
// .reply(200, { body: { name: 'foo', version: '1.0.0' } });
|
|
422
|
+
|
|
423
|
+
// const prox1 = new ProxyStorage(
|
|
424
|
+
// { ...defaultRequestOptions, fail_timeout: '1s', max_fails: 1 },
|
|
425
|
+
// conf,
|
|
426
|
+
// logger
|
|
427
|
+
// );
|
|
428
|
+
// // force retry
|
|
429
|
+
// await expect(
|
|
430
|
+
// prox1.getRemoteMetadata('jquery', {
|
|
431
|
+
// remoteAddress: '127.0.0.1',
|
|
432
|
+
// retry: { limit: 2 },
|
|
433
|
+
// })
|
|
434
|
+
// ).rejects.toThrow();
|
|
435
|
+
// // display offline error on exausted retry
|
|
436
|
+
// await expect(
|
|
437
|
+
// prox1.getRemoteMetadata('jquery', {
|
|
438
|
+
// remoteAddress: '127.0.0.1',
|
|
439
|
+
// retry: { limit: 2 },
|
|
440
|
+
// })
|
|
441
|
+
// ).rejects.toThrow(errorUtils.getInternalError(errorUtils.API_ERROR.UPLINK_OFFLINE));
|
|
442
|
+
// expect(mockWarn).toHaveBeenCalledTimes(2);
|
|
443
|
+
// expect(mockWarn).toHaveBeenLastCalledWith(
|
|
444
|
+
// {
|
|
445
|
+
// host: 'registry.npmjs.org',
|
|
446
|
+
// },
|
|
447
|
+
// 'host @{host} is now offline'
|
|
448
|
+
// );
|
|
449
|
+
// expect(mockWarn).toHaveBeenLastCalledWith(
|
|
450
|
+
// {
|
|
451
|
+
// host: 'registry.npmjs.org',
|
|
452
|
+
// },
|
|
453
|
+
// 'host @{host} is now offline'
|
|
454
|
+
// );
|
|
455
|
+
// // this is based on max_fails, if change that also change here acordingly
|
|
456
|
+
// await setTimeout(3000);
|
|
457
|
+
// const [manifest] = await prox1.getRemoteMetadata('jquery', {
|
|
458
|
+
// retry: { limit: 2 },
|
|
459
|
+
// });
|
|
460
|
+
// expect(manifest).toEqual({ body: { name: 'foo', version: '1.0.0' } });
|
|
461
|
+
// expect(mockWarn).toHaveBeenLastCalledWith(
|
|
462
|
+
// {
|
|
463
|
+
// host: 'registry.npmjs.org',
|
|
464
|
+
// },
|
|
465
|
+
// 'host @{host} is now online'
|
|
466
|
+
// );
|
|
467
|
+
// }, 10000);
|
|
468
|
+
});
|
|
353
469
|
});
|
|
354
470
|
});
|