@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/src/proxy.ts CHANGED
@@ -1,10 +1,15 @@
1
1
  import JSONStream from 'JSONStream';
2
2
  import buildDebug from 'debug';
3
- import got, { RequiredRetryOptions, Headers as gotHeaders } from 'got';
4
- import type { Agents, Options } from 'got';
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: number;
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(name: string, options: ISyncUplinksOptions): Promise<[Manifest, string]>;
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: number;
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<RequiredRetryOptions> | number;
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 = LoggerApi.logger.child({ sub: 'out' });
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 = parseInterval(setConfig(this.config, 'timeout', '30s'));
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 getHeadersNext(headers = {}): gotHeaders {
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('get metadata for %s', name);
307
- let headers = this.getHeadersNext(options?.headers);
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('request uri for %s retry %s', uri);
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 times %s for %s', retry, uri);
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
- // @ts-ignore
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('code response %s', code);
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
- // FUTURE: got 12.0.0, the option arg should be removed
353
- (_options, error: any, count) => {
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 fail', uri);
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.getHeadersNext(options?.headers);
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
- response = await undiciFetch(uri, {
495
- method: 'GET',
496
- // FUTURE: whitelist domains what we are sending not need it headers, security check
497
- // headers: new Headers({
498
- // ...headers,
499
- // connection: 'keep-alive',
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 { setup } from '@verdaccio/logger';
3
+ import { Logger } from '@verdaccio/types';
4
4
  import { buildToken } from '@verdaccio/utils';
5
5
 
6
6
  import { ProxyStorage } from '../src';
7
7
 
8
- setup();
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.getHeadersNext({ ...headers });
33
+ return uplink.getHeaders({ ...headers });
22
34
  }
23
35
 
24
36
  describe('setHeadersNext', () => {
@@ -1,11 +1,13 @@
1
+ import { logger, setup } from '@verdaccio/logger';
2
+
1
3
  import { ProxyStorage } from '../src';
2
4
 
3
- require('@verdaccio/logger').setup([]);
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
  });