gologin 2.2.9 → 3.0.0

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/utils/http.js CHANGED
@@ -1,64 +1,360 @@
1
- import { get as _get } from 'https';
2
- import requests from 'requestretry';
1
+ import { get as _get, request as httpsRequest } from 'https';
2
+ import { request as httpRequest } from 'http';
3
3
 
4
4
  import packageJson from '../../package.json' with { type: 'json' };
5
+ import { loadHttpsProxyAgent } from './lazy-deps.js';
5
6
 
6
7
  const { version } = packageJson;
7
8
 
8
9
  const TIMEZONE_URL = 'https://geo.myip.link';
9
10
 
10
- const createTimeoutPromise = (timeoutMs) => new Promise((_, reject) => {
11
- setTimeout(() => {
12
- reject(new Error(`Request timeout after ${timeoutMs}ms`));
11
+ const DEFAULT_TIMEOUT_MS = 30000;
12
+ const DEFAULT_MAX_ATTEMPTS = 1;
13
+ const DEFAULT_RETRY_DELAY_MS = 1000;
14
+
15
+ const delay = (timeMs) => new Promise((resolve) => setTimeout(resolve, timeMs));
16
+
17
+ const shouldRetryRequest = ({ error, attempt, maxAttempts }) => {
18
+ if (attempt >= maxAttempts) {
19
+ return false;
20
+ }
21
+
22
+ if (error.statusCode) {
23
+ return error.statusCode >= 500;
24
+ }
25
+
26
+ return true;
27
+ };
28
+
29
+ const createRequestSignal = (timeoutMs) => {
30
+ const controller = new AbortController();
31
+ const timeoutId = setTimeout(() => {
32
+ controller.abort(new Error(`Request timeout after ${timeoutMs}ms`));
13
33
  }, timeoutMs);
14
- });
15
34
 
16
- const attemptRequest = async (requestUrl, options) => {
17
- const requestPromise = requests(requestUrl, options);
35
+ return {
36
+ signal: controller.signal,
37
+ clear: () => clearTimeout(timeoutId),
38
+ };
39
+ };
18
40
 
19
- let req;
20
- if (options.proxy) {
21
- const timeoutPromise = createTimeoutPromise(options.timeout || 30000);
22
- req = await Promise.race([requestPromise, timeoutPromise]);
23
- } else {
24
- req = await requestPromise;
41
+ const parseJsonBody = (text) => {
42
+ if (!text) {
43
+ return null;
25
44
  }
26
45
 
27
- if (req.statusCode >= 400) {
28
- const error = new Error(req.body);
29
- error.statusCode = req.statusCode;
30
- throw error;
46
+ return JSON.parse(text);
47
+ };
48
+
49
+ const readResponseBody = async (response, options) => {
50
+ const shouldParseJson = options.json === true || (options.json && typeof options.json === 'object');
51
+
52
+ if (shouldParseJson) {
53
+ const text = await response.text();
54
+
55
+ return parseJsonBody(text);
31
56
  }
32
57
 
33
- return req.body;
58
+ return response.text();
34
59
  };
35
60
 
36
- export const makeRequest = async (url, options = {}, internalOptions) => {
37
- options.headers = {
61
+ const buildRequestHeaders = ({ options, internalOptions }) => {
62
+ const headers = {
38
63
  ...options.headers,
39
- 'User-Agent': `gologin-nodejs-sdk/${version}`,
64
+ 'User-Agent': options.headers?.['User-Agent'] || `gologin-nodejs-sdk/${version}`,
40
65
  };
41
66
 
42
67
  if (internalOptions?.token) {
43
- options.headers = {
44
- ...options.headers,
45
- Authorization: `Bearer ${internalOptions.token}`,
46
- };
68
+ headers.Authorization = `Bearer ${internalOptions.token}`;
69
+ }
70
+
71
+ return headers;
72
+ };
73
+
74
+ const buildRequestBody = (options) => {
75
+ if (options.json !== undefined && options.json !== true) {
76
+ return JSON.stringify(options.json);
77
+ }
78
+
79
+ return options.body;
80
+ };
81
+
82
+ const buildFetchInit = ({ options, internalOptions }) => {
83
+ const headers = buildRequestHeaders({ options, internalOptions });
84
+ const body = buildRequestBody(options);
85
+
86
+ if (options.json !== undefined && options.json !== true) {
87
+ headers['Content-Type'] = headers['Content-Type'] || 'application/json';
88
+ }
89
+
90
+ return {
91
+ method: options.method || 'GET',
92
+ headers,
93
+ body: body ?? undefined,
94
+ };
95
+ };
96
+
97
+ const parseProxyResponseBody = (text, options) => {
98
+ const shouldParseJson = options.json === true || (options.json && typeof options.json === 'object');
99
+
100
+ if (shouldParseJson) {
101
+ return parseJsonBody(text);
102
+ }
103
+
104
+ return text;
105
+ };
106
+
107
+ const normalizeProxyUrl = (proxyUrl) => {
108
+ const parsed = new URL(proxyUrl);
109
+
110
+ if (parsed.protocol === 'https:') {
111
+ parsed.protocol = 'http:';
112
+ }
113
+
114
+ return parsed.toString();
115
+ };
116
+
117
+ const executeProxyRequest = async (url, options, internalOptions) => {
118
+ const HttpsProxyAgent = await loadHttpsProxyAgent();
119
+ const agent = new HttpsProxyAgent(normalizeProxyUrl(options.proxy));
120
+ const timeoutMs = options.timeout || DEFAULT_TIMEOUT_MS;
121
+ const headers = buildRequestHeaders({ options, internalOptions });
122
+ const body = buildRequestBody(options);
123
+ const method = options.method || 'GET';
124
+ const parsedUrl = new URL(url);
125
+ const requestFn = parsedUrl.protocol === 'http:' ? httpRequest : httpsRequest;
126
+
127
+ if (options.json !== undefined && options.json !== true) {
128
+ headers['Content-Type'] = headers['Content-Type'] || 'application/json';
129
+ }
130
+
131
+ const requestOptions = {
132
+ hostname: parsedUrl.hostname,
133
+ port: parsedUrl.port || (parsedUrl.protocol === 'https:' ? 443 : 80),
134
+ path: `${parsedUrl.pathname}${parsedUrl.search}`,
135
+ method,
136
+ headers,
137
+ agent,
138
+ timeout: timeoutMs,
139
+ };
140
+
141
+ return new Promise((resolve, reject) => {
142
+ const req = requestFn(requestOptions, (response) => {
143
+ let responseText = '';
144
+
145
+ response.on('data', (chunk) => {
146
+ responseText += chunk;
147
+ });
148
+
149
+ response.on('end', () => {
150
+ if (response.statusCode >= 400) {
151
+ const error = new Error(responseText);
152
+ error.statusCode = response.statusCode;
153
+
154
+ reject(error);
155
+
156
+ return;
157
+ }
158
+
159
+ resolve(parseProxyResponseBody(responseText, options));
160
+ });
161
+ });
162
+
163
+ req.on('error', reject);
164
+ req.on('timeout', () => {
165
+ req.destroy(new Error(`Request timeout after ${timeoutMs}ms`));
166
+ });
167
+
168
+ if (body) {
169
+ req.write(body);
170
+ }
171
+
172
+ req.end();
173
+ });
174
+ };
175
+
176
+ const executeFetch = async (url, options = {}, internalOptions) => {
177
+ if (options.proxy) {
178
+ const timeoutMs = options.timeout || DEFAULT_TIMEOUT_MS;
179
+ const maxAttempts = options.maxAttempts || DEFAULT_MAX_ATTEMPTS;
180
+ const retryDelayMs = options.retryDelay ?? DEFAULT_RETRY_DELAY_MS;
181
+ let lastError;
182
+
183
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
184
+ try {
185
+ return await executeProxyRequest(url, options, internalOptions);
186
+ } catch (error) {
187
+ lastError = error;
188
+
189
+ if (!shouldRetryRequest({ error, attempt, maxAttempts })) {
190
+ throw error;
191
+ }
192
+
193
+ await delay(retryDelayMs);
194
+ }
195
+ }
196
+
197
+ throw lastError;
198
+ }
199
+
200
+ const timeoutMs = options.timeout || DEFAULT_TIMEOUT_MS;
201
+ const maxAttempts = options.maxAttempts || DEFAULT_MAX_ATTEMPTS;
202
+ const retryDelayMs = options.retryDelay ?? DEFAULT_RETRY_DELAY_MS;
203
+ let lastError;
204
+
205
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
206
+ const { signal, clear } = createRequestSignal(timeoutMs);
207
+
208
+ try {
209
+ const init = buildFetchInit({ options, internalOptions });
210
+ init.signal = signal;
211
+
212
+ const response = await fetch(url, init);
213
+ clear();
214
+
215
+ const body = await readResponseBody(response, options);
216
+
217
+ if (response.status >= 400) {
218
+ const errorMessage = typeof body === 'string' ? body : JSON.stringify(body);
219
+ const error = new Error(errorMessage);
220
+ error.statusCode = response.status;
221
+
222
+ throw error;
223
+ }
224
+
225
+ return body;
226
+ } catch (error) {
227
+ clear();
228
+ lastError = error;
229
+
230
+ if (!shouldRetryRequest({ error, attempt, maxAttempts })) {
231
+ throw error;
232
+ }
233
+
234
+ await delay(retryDelayMs);
235
+ }
47
236
  }
48
237
 
238
+ throw lastError;
239
+ };
240
+
241
+ export const makeRequest = async (url, options = {}, internalOptions) => {
49
242
  try {
50
- return await attemptRequest(url, options);
243
+ return await executeFetch(url, options, internalOptions);
51
244
  } catch (error) {
52
245
  if (internalOptions?.fallbackUrl && !error.statusCode) {
53
- const fallbackData = await attemptRequest(internalOptions.fallbackUrl, options);
54
-
55
- return fallbackData;
246
+ return executeFetch(internalOptions.fallbackUrl, options, internalOptions);
56
247
  }
57
248
 
58
249
  throw error;
59
250
  }
60
251
  };
61
252
 
253
+ export const fetchHeadWithRetry = async (url, options = {}) => {
254
+ const timeoutMs = options.timeout || DEFAULT_TIMEOUT_MS;
255
+ const maxAttempts = options.maxAttempts || DEFAULT_MAX_ATTEMPTS;
256
+ const retryDelayMs = options.retryDelay ?? DEFAULT_RETRY_DELAY_MS;
257
+ let lastError;
258
+
259
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
260
+ const { signal, clear } = createRequestSignal(timeoutMs);
261
+
262
+ try {
263
+ const response = await fetch(url, {
264
+ method: 'HEAD',
265
+ redirect: 'follow',
266
+ signal,
267
+ });
268
+
269
+ clear();
270
+
271
+ if (response.status >= 400) {
272
+ const error = new Error(`HEAD request failed with status ${response.status}`);
273
+ error.statusCode = response.status;
274
+
275
+ throw error;
276
+ }
277
+
278
+ const responseUrl = new URL(response.url);
279
+
280
+ return {
281
+ req: { path: `${responseUrl.pathname}${responseUrl.search}` },
282
+ statusCode: response.status,
283
+ };
284
+ } catch (error) {
285
+ clear();
286
+ lastError = error;
287
+
288
+ if (!shouldRetryRequest({ error, attempt, maxAttempts })) {
289
+ throw error;
290
+ }
291
+
292
+ await delay(retryDelayMs);
293
+ }
294
+ }
295
+
296
+ throw lastError;
297
+ };
298
+
299
+ export const fetchBufferWithRetry = async (url, options = {}) => {
300
+ const timeoutMs = options.timeout || DEFAULT_TIMEOUT_MS;
301
+ const maxAttempts = options.maxAttempts || DEFAULT_MAX_ATTEMPTS;
302
+ const retryDelayMs = options.retryDelay ?? DEFAULT_RETRY_DELAY_MS;
303
+ let lastError;
304
+
305
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
306
+ const { signal, clear } = createRequestSignal(timeoutMs);
307
+
308
+ try {
309
+ const response = await fetch(url, {
310
+ method: 'GET',
311
+ redirect: 'follow',
312
+ signal,
313
+ });
314
+
315
+ clear();
316
+
317
+ if (response.status >= 400) {
318
+ const error = new Error(`GET request failed with status ${response.status}`);
319
+ error.statusCode = response.status;
320
+
321
+ throw error;
322
+ }
323
+
324
+ const arrayBuffer = await response.arrayBuffer();
325
+
326
+ return Buffer.from(arrayBuffer);
327
+ } catch (error) {
328
+ clear();
329
+ lastError = error;
330
+
331
+ if (!shouldRetryRequest({ error, attempt, maxAttempts })) {
332
+ throw error;
333
+ }
334
+
335
+ await delay(retryDelayMs);
336
+ }
337
+ }
338
+
339
+ throw lastError;
340
+ };
341
+
342
+ export const fetchToWriteStream = async (url, writeStream, options = {}) => {
343
+ const buffer = await fetchBufferWithRetry(url, options);
344
+
345
+ await new Promise((resolve, reject) => {
346
+ writeStream.write(buffer, (error) => {
347
+ if (error) {
348
+ reject(error);
349
+
350
+ return;
351
+ }
352
+
353
+ writeStream.end(resolve);
354
+ });
355
+ });
356
+ };
357
+
62
358
  export const checkSocksProxy = async (agent) => new Promise((resolve, reject) => {
63
359
  _get(TIMEZONE_URL, { agent, timeout: 8000 }, (res) => {
64
360
  let resultResponse = '';
@@ -0,0 +1,206 @@
1
+ const cache = {};
2
+
3
+ const resolveDefault = (module) => module?.default ?? module;
4
+
5
+ const resolveSocksProxyAgent = (module) => {
6
+ const resolved = resolveDefault(module);
7
+
8
+ return resolved.SocksProxyAgent ?? resolved;
9
+ };
10
+
11
+ const resolveHttpsProxyAgent = (module) => {
12
+ const resolved = resolveDefault(module);
13
+
14
+ return resolved.HttpsProxyAgent ?? resolved;
15
+ };
16
+
17
+ const importAndAssign = (key, importPromise) => importPromise.then((value) => {
18
+ cache[key] = value;
19
+
20
+ return value;
21
+ });
22
+
23
+ export const loadRimraf = () => {
24
+ if (cache.rimraf !== undefined) {
25
+ return Promise.resolve(cache.rimraf);
26
+ }
27
+
28
+ cache.rimrafPromise ||= import('rimraf').then(resolveDefault).then((value) => {
29
+ cache.rimraf = value;
30
+
31
+ return value;
32
+ });
33
+
34
+ return cache.rimrafPromise;
35
+ };
36
+
37
+ export const loadDecompress = () => {
38
+ if (cache.decompressReady) {
39
+ return Promise.resolve(cache.decompressReady);
40
+ }
41
+
42
+ cache.decompressPromise ||= Promise.all([
43
+ import('decompress').then(resolveDefault),
44
+ import('decompress-unzip').then(resolveDefault),
45
+ ]).then(([decompress, decompressUnzip]) => {
46
+ cache.decompressReady = { decompress, decompressUnzip };
47
+
48
+ return cache.decompressReady;
49
+ });
50
+
51
+ return cache.decompressPromise;
52
+ };
53
+
54
+ export const loadSocksProxyAgent = () => {
55
+ if (cache.socksProxyAgent !== undefined) {
56
+ return Promise.resolve(cache.socksProxyAgent);
57
+ }
58
+
59
+ cache.socksProxyAgentPromise ||= import('socks-proxy-agent').then(resolveSocksProxyAgent).then((value) => {
60
+ cache.socksProxyAgent = value;
61
+
62
+ return value;
63
+ });
64
+
65
+ return cache.socksProxyAgentPromise;
66
+ };
67
+
68
+ export const loadHttpsProxyAgent = () => {
69
+ if (cache.httpsProxyAgent !== undefined) {
70
+ return Promise.resolve(cache.httpsProxyAgent);
71
+ }
72
+
73
+ cache.httpsProxyAgentPromise ||= import('https-proxy-agent').then(resolveHttpsProxyAgent).then((value) => {
74
+ cache.httpsProxyAgent = value;
75
+
76
+ return value;
77
+ });
78
+
79
+ return cache.httpsProxyAgentPromise;
80
+ };
81
+
82
+ export const loadSentry = () => {
83
+ return Promise.resolve(null);
84
+
85
+ // cache.sentry ||= import('@sentry/node');
86
+ //
87
+ // return cache.sentry;
88
+ };
89
+
90
+ export const loadCookiesManager = () => {
91
+ if (cache.cookiesManager) {
92
+ return Promise.resolve(cache.cookiesManager);
93
+ }
94
+
95
+ cache.cookiesManagerPromise ||= import('../cookies/cookies-manager.js').then((value) => {
96
+ cache.cookiesManager = value;
97
+
98
+ return value;
99
+ });
100
+
101
+ return cache.cookiesManagerPromise;
102
+ };
103
+
104
+ export const loadProfileArchiver = () => {
105
+ cache.profileArchiver ||= import('../profile/profile-archiver.js');
106
+
107
+ return cache.profileArchiver;
108
+ };
109
+
110
+ export const loadExtensionsManager = () => {
111
+ if (cache.extensionsManagerClass !== undefined) {
112
+ return Promise.resolve(cache.extensionsManagerClass);
113
+ }
114
+
115
+ cache.extensionsManagerPromise ||= import('../extensions/extensions-manager.js').then(resolveDefault).then((value) => {
116
+ cache.extensionsManagerClass = value;
117
+
118
+ return value;
119
+ });
120
+
121
+ return cache.extensionsManagerPromise;
122
+ };
123
+
124
+ export const loadBrowserChecker = () => {
125
+ if (cache.browserCheckerClass !== undefined) {
126
+ return Promise.resolve(cache.browserCheckerClass);
127
+ }
128
+
129
+ cache.browserCheckerPromise ||= import('../browser/browser-checker.js').then(resolveDefault).then((value) => {
130
+ cache.browserCheckerClass = value;
131
+
132
+ return value;
133
+ });
134
+
135
+ return cache.browserCheckerPromise;
136
+ };
137
+
138
+ export const preloadStartupDeps = () => {
139
+ if (cache.startupPreload) {
140
+ return cache.startupPreload;
141
+ }
142
+
143
+ cache.startupPreload = Promise.all([
144
+ importAndAssign('rimraf', import('rimraf').then(resolveDefault)),
145
+ importAndAssign('decompressLib', import('decompress').then(resolveDefault)),
146
+ importAndAssign('decompressUnzipLib', import('decompress-unzip').then(resolveDefault)),
147
+ importAndAssign('socksProxyAgent', import('socks-proxy-agent').then(resolveSocksProxyAgent)),
148
+ importAndAssign('cookiesManager', import('../cookies/cookies-manager.js')),
149
+ importAndAssign('extensionsManagerClass', import('../extensions/extensions-manager.js').then(resolveDefault)),
150
+ ]).then(() => {
151
+ cache.decompressReady = {
152
+ decompress: cache.decompressLib,
153
+ decompressUnzip: cache.decompressUnzipLib,
154
+ };
155
+ });
156
+
157
+ return cache.startupPreload;
158
+ };
159
+
160
+ export const ensureSentryInitialized = async (_options = {}) => {
161
+ return null;
162
+
163
+ // if (process.env.DISABLE_TELEMETRY === 'true') {
164
+ // return null;
165
+ // }
166
+ //
167
+ // if (cache.sentryInitialized) {
168
+ // return cache.sentryModule;
169
+ // }
170
+ //
171
+ // const Sentry = await loadSentry();
172
+ // Sentry.init({
173
+ // dsn: 'https://a13d5939a60ae4f6583e228597f1f2a0@sentry-new.amzn.pro/24',
174
+ // tracesSampleRate: 1.0,
175
+ // defaultIntegrations: false,
176
+ // release: release || '2.1.34',
177
+ // });
178
+ // cache.sentryInitialized = true;
179
+ // cache.sentryModule = Sentry;
180
+ //
181
+ // return Sentry;
182
+ };
183
+
184
+ export const removePath = async (targetPath, options) => {
185
+ const rimrafFn = await loadRimraf();
186
+
187
+ return new Promise((resolve, reject) => {
188
+ const onComplete = (error) => {
189
+ if (error) {
190
+ reject(error);
191
+
192
+ return;
193
+ }
194
+
195
+ resolve();
196
+ };
197
+
198
+ if (options && typeof options === 'object') {
199
+ rimrafFn(targetPath, options, onComplete);
200
+
201
+ return;
202
+ }
203
+
204
+ rimrafFn(targetPath, onComplete);
205
+ });
206
+ };
@@ -1,6 +1,11 @@
1
- import * as Sentry from '@sentry/node';
1
+ // import { loadSentry } from './lazy-deps.js';
2
2
 
3
- export const captureGroupedSentryError = (error, context = {}) => {
3
+ export const captureGroupedSentryError = async (_error, _context = {}) => {
4
+ return;
5
+ };
6
+
7
+ /*
8
+ export const captureGroupedSentryError = async (error, context = {}) => {
4
9
  if (process.env.DISABLE_TELEMETRY === 'true') {
5
10
  return;
6
11
  }
@@ -61,6 +66,7 @@ export const captureGroupedSentryError = (error, context = {}) => {
61
66
  break;
62
67
  }
63
68
 
69
+ const Sentry = await loadSentry();
64
70
  Sentry.captureException(error, scope => {
65
71
  scope.setFingerprint(fingerprint);
66
72
  scope.setTransactionName(fingerprint);
@@ -71,3 +77,4 @@ export const captureGroupedSentryError = (error, context = {}) => {
71
77
  });
72
78
  });
73
79
  };
80
+ */