@browserless.io/browserless 2.7.1 → 2.9.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/CHANGELOG.md +19 -1
- package/README.md +41 -3
- package/assets/debugger.png +0 -0
- package/bin/browserless.js +8 -4
- package/bin/scaffold/README.md +6 -4
- package/bin/scaffold/src/hello-world.http.ts +7 -1
- package/build/browserless.d.ts +8 -5
- package/build/browserless.js +23 -22
- package/build/browsers/chrome.cdp.d.ts +2 -2
- package/build/browsers/chrome.cdp.js +2 -2
- package/build/browsers/chrome.playwright.d.ts +2 -2
- package/build/browsers/chrome.playwright.js +2 -2
- package/build/browsers/chromium.cdp.d.ts +4 -4
- package/build/browsers/chromium.cdp.js +49 -32
- package/build/browsers/chromium.playwright.d.ts +4 -4
- package/build/browsers/chromium.playwright.js +14 -13
- package/build/browsers/firefox.playwright.d.ts +4 -4
- package/build/browsers/firefox.playwright.js +14 -13
- package/build/browsers/index.d.ts +24 -8
- package/build/browsers/index.js +20 -15
- package/build/browsers/webkit.playwright.d.ts +4 -4
- package/build/browsers/webkit.playwright.js +14 -13
- package/build/config.d.ts +3 -0
- package/build/config.js +3 -0
- package/build/exports.d.ts +1 -0
- package/build/exports.js +1 -0
- package/build/file-system.d.ts +2 -3
- package/build/file-system.js +2 -2
- package/build/index.js +7 -7
- package/build/limiter.d.ts +2 -3
- package/build/limiter.js +11 -11
- package/build/logger.d.ts +19 -0
- package/build/logger.js +43 -0
- package/build/monitoring.d.ts +2 -3
- package/build/monitoring.js +4 -4
- package/build/router.d.ts +4 -5
- package/build/router.js +31 -28
- package/build/routes/chrome/http/content.post.body.json +8 -8
- package/build/routes/chrome/http/pdf.post.body.json +9 -9
- package/build/routes/chrome/http/scrape.post.body.json +8 -8
- package/build/routes/chrome/http/screenshot.post.body.json +8 -8
- package/build/routes/chromium/http/content.post.body.json +8 -8
- package/build/routes/chromium/http/pdf.post.body.json +9 -9
- package/build/routes/chromium/http/scrape.post.body.json +8 -8
- package/build/routes/chromium/http/screenshot.post.body.json +8 -8
- package/build/routes/firefox/ws/playwright.d.ts +2 -2
- package/build/routes/firefox/ws/playwright.js +1 -1
- package/build/routes/management/http/static.get.d.ts +2 -2
- package/build/routes/management/http/static.get.js +8 -10
- package/build/routes/management/tests/management.spec.js +9 -0
- package/build/routes/webkit/ws/playwright.d.ts +2 -2
- package/build/routes/webkit/ws/playwright.js +1 -1
- package/build/sdk-utils.js +23 -10
- package/build/server.d.ts +4 -5
- package/build/server.js +38 -33
- package/build/shared/browser.ws.d.ts +2 -2
- package/build/shared/browser.ws.js +1 -1
- package/build/shared/chromium.playwright.ws.d.ts +2 -2
- package/build/shared/chromium.playwright.ws.js +1 -1
- package/build/shared/chromium.ws.d.ts +2 -2
- package/build/shared/chromium.ws.js +1 -1
- package/build/shared/content.http.d.ts +2 -2
- package/build/shared/content.http.js +4 -1
- package/build/shared/download.http.d.ts +2 -2
- package/build/shared/download.http.js +11 -12
- package/build/shared/function.http.d.ts +2 -2
- package/build/shared/function.http.js +4 -5
- package/build/shared/json-protocol.http.d.ts +3 -3
- package/build/shared/json-protocol.http.js +2 -2
- package/build/shared/json-version.http.d.ts +3 -3
- package/build/shared/json-version.http.js +2 -2
- package/build/shared/page.ws.d.ts +2 -2
- package/build/shared/page.ws.js +1 -1
- package/build/shared/pdf.http.d.ts +2 -2
- package/build/shared/pdf.http.js +6 -4
- package/build/shared/performance.http.d.ts +2 -2
- package/build/shared/performance.http.js +2 -1
- package/build/shared/scrape.http.d.ts +2 -2
- package/build/shared/scrape.http.js +4 -1
- package/build/shared/screenshot.http.d.ts +2 -2
- package/build/shared/screenshot.http.js +4 -1
- package/build/shared/utils/function/handler.d.ts +2 -3
- package/build/shared/utils/function/handler.js +8 -8
- package/build/shared/utils/performance/child.js +4 -4
- package/build/shared/utils/performance/main.d.ts +1 -1
- package/build/shared/utils/performance/main.js +5 -7
- package/build/shared/utils/performance/types.d.ts +2 -1
- package/build/types.d.ts +6 -15
- package/build/types.js +1 -10
- package/build/utils.d.ts +1 -1
- package/build/utils.js +6 -2
- package/package.json +18 -15
- package/scripts/install-debugger.js +55 -15
- package/src/browserless.ts +29 -21
- package/src/browsers/chrome.cdp.ts +2 -5
- package/src/browsers/chrome.playwright.ts +2 -5
- package/src/browsers/chromium.cdp.ts +84 -35
- package/src/browsers/chromium.playwright.ts +26 -13
- package/src/browsers/firefox.playwright.ts +28 -13
- package/src/browsers/index.ts +24 -16
- package/src/browsers/webkit.playwright.ts +28 -13
- package/src/config.ts +4 -0
- package/src/exports.ts +1 -0
- package/src/file-system.ts +2 -7
- package/src/index.ts +7 -7
- package/src/limiter.ts +13 -11
- package/src/logger.ts +52 -0
- package/src/monitoring.ts +6 -8
- package/src/router.ts +29 -27
- package/src/routes/firefox/ws/playwright.ts +2 -0
- package/src/routes/management/http/static.get.ts +13 -10
- package/src/routes/management/tests/management.spec.ts +15 -0
- package/src/routes/webkit/ws/playwright.ts +2 -0
- package/src/sdk-utils.ts +20 -2
- package/src/server.ts +47 -32
- package/src/shared/browser.ws.ts +2 -0
- package/src/shared/chromium.playwright.ws.ts +2 -0
- package/src/shared/chromium.ws.ts +2 -0
- package/src/shared/content.http.ts +6 -0
- package/src/shared/download.http.ts +14 -11
- package/src/shared/function.http.ts +5 -4
- package/src/shared/json-protocol.http.ts +8 -3
- package/src/shared/json-version.http.ts +8 -4
- package/src/shared/page.ws.ts +2 -0
- package/src/shared/pdf.http.ts +7 -3
- package/src/shared/performance.http.ts +3 -0
- package/src/shared/scrape.http.ts +6 -0
- package/src/shared/screenshot.http.ts +5 -0
- package/src/shared/utils/function/handler.ts +9 -13
- package/src/shared/utils/performance/child.ts +4 -4
- package/src/shared/utils/performance/main.ts +5 -6
- package/src/shared/utils/performance/types.ts +2 -1
- package/src/types.ts +5 -9
- package/src/utils.ts +7 -2
- package/static/docs/swagger.json +11 -11
- package/static/docs/swagger.min.json +10 -10
- package/static/function/client.js +1656 -2916
- package/static/function/index.html +1656 -2916
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
BrowserServerOptions,
|
|
3
3
|
Config,
|
|
4
|
+
Logger,
|
|
4
5
|
Request,
|
|
5
6
|
ServerError,
|
|
6
|
-
createLogger,
|
|
7
7
|
} from '@browserless.io/browserless';
|
|
8
8
|
import playwright, { Page } from 'playwright-core';
|
|
9
9
|
import { Duplex } from 'stream';
|
|
@@ -15,24 +15,27 @@ export class FirefoxPlaywright extends EventEmitter {
|
|
|
15
15
|
protected config: Config;
|
|
16
16
|
protected userDataDir: string | null;
|
|
17
17
|
protected running = false;
|
|
18
|
+
protected logger: Logger;
|
|
18
19
|
protected proxy = httpProxy.createProxyServer();
|
|
19
20
|
protected browser: playwright.BrowserServer | null = null;
|
|
20
21
|
protected browserWSEndpoint: string | null = null;
|
|
21
|
-
protected debug = createLogger('browsers:firefox:playwright');
|
|
22
22
|
|
|
23
23
|
constructor({
|
|
24
24
|
config,
|
|
25
25
|
userDataDir,
|
|
26
|
+
logger,
|
|
26
27
|
}: {
|
|
27
28
|
config: Config;
|
|
29
|
+
logger: Logger;
|
|
28
30
|
userDataDir: FirefoxPlaywright['userDataDir'];
|
|
29
31
|
}) {
|
|
30
32
|
super();
|
|
31
33
|
|
|
32
34
|
this.userDataDir = userDataDir;
|
|
33
35
|
this.config = config;
|
|
36
|
+
this.logger = logger;
|
|
34
37
|
|
|
35
|
-
this.
|
|
38
|
+
this.logger.info(`Starting new ${this.constructor.name} instance`);
|
|
36
39
|
}
|
|
37
40
|
|
|
38
41
|
protected cleanListeners() {
|
|
@@ -43,7 +46,9 @@ export class FirefoxPlaywright extends EventEmitter {
|
|
|
43
46
|
|
|
44
47
|
public close = async (): Promise<void> => {
|
|
45
48
|
if (this.browser) {
|
|
46
|
-
this.
|
|
49
|
+
this.logger.trace(
|
|
50
|
+
`Closing ${this.constructor.name} process and all listeners`,
|
|
51
|
+
);
|
|
47
52
|
this.emit('close');
|
|
48
53
|
this.cleanListeners();
|
|
49
54
|
this.browser.close();
|
|
@@ -56,21 +61,27 @@ export class FirefoxPlaywright extends EventEmitter {
|
|
|
56
61
|
public pages = async (): Promise<[]> => [];
|
|
57
62
|
|
|
58
63
|
public getPageId = (): string => {
|
|
59
|
-
throw new ServerError(
|
|
64
|
+
throw new ServerError(
|
|
65
|
+
`#getPageId is not yet supported with ${this.constructor.name}.`,
|
|
66
|
+
);
|
|
60
67
|
};
|
|
61
68
|
|
|
62
69
|
public makeLiveURL = (): void => {
|
|
63
|
-
throw new ServerError(
|
|
70
|
+
throw new ServerError(
|
|
71
|
+
`Live URLs are not yet supported with ${this.constructor.name}.`,
|
|
72
|
+
);
|
|
64
73
|
};
|
|
65
74
|
|
|
66
75
|
public newPage = async (): Promise<Page> => {
|
|
67
|
-
throw new ServerError(
|
|
76
|
+
throw new ServerError(
|
|
77
|
+
`Can't create new page with ${this.constructor.name}`,
|
|
78
|
+
);
|
|
68
79
|
};
|
|
69
80
|
|
|
70
81
|
public launch = async (
|
|
71
82
|
options: BrowserServerOptions = {},
|
|
72
83
|
): Promise<playwright.BrowserServer> => {
|
|
73
|
-
this.
|
|
84
|
+
this.logger.info(`Launching ${this.constructor.name} Handler`);
|
|
74
85
|
|
|
75
86
|
this.browser = await playwright.firefox.launchServer({
|
|
76
87
|
...options,
|
|
@@ -83,7 +94,9 @@ export class FirefoxPlaywright extends EventEmitter {
|
|
|
83
94
|
|
|
84
95
|
const browserWSEndpoint = this.browser.wsEndpoint();
|
|
85
96
|
|
|
86
|
-
this.
|
|
97
|
+
this.logger.info(
|
|
98
|
+
`${this.constructor.name} is running on ${browserWSEndpoint}`,
|
|
99
|
+
);
|
|
87
100
|
this.browserWSEndpoint = browserWSEndpoint;
|
|
88
101
|
this.running = true;
|
|
89
102
|
|
|
@@ -110,7 +123,7 @@ export class FirefoxPlaywright extends EventEmitter {
|
|
|
110
123
|
};
|
|
111
124
|
|
|
112
125
|
public proxyPageWebSocket = async () => {
|
|
113
|
-
|
|
126
|
+
this.logger.warn(`Not yet implemented in ${this.constructor.name}`);
|
|
114
127
|
};
|
|
115
128
|
|
|
116
129
|
public proxyWebSocket = async (
|
|
@@ -126,8 +139,8 @@ export class FirefoxPlaywright extends EventEmitter {
|
|
|
126
139
|
}
|
|
127
140
|
socket.once('close', resolve);
|
|
128
141
|
|
|
129
|
-
this.
|
|
130
|
-
`Proxying ${req.parsed.href} to
|
|
142
|
+
this.logger.info(
|
|
143
|
+
`Proxying ${req.parsed.href} to ${this.constructor.name} ${this.browserWSEndpoint}`,
|
|
131
144
|
);
|
|
132
145
|
|
|
133
146
|
// Delete headers known to cause issues
|
|
@@ -144,7 +157,9 @@ export class FirefoxPlaywright extends EventEmitter {
|
|
|
144
157
|
target: this.browserWSEndpoint,
|
|
145
158
|
},
|
|
146
159
|
(error) => {
|
|
147
|
-
this.
|
|
160
|
+
this.logger.error(
|
|
161
|
+
`Error proxying session to ${this.constructor.name}: ${error}`,
|
|
162
|
+
);
|
|
148
163
|
this.close();
|
|
149
164
|
return reject(error);
|
|
150
165
|
},
|
package/src/browsers/index.ts
CHANGED
|
@@ -17,13 +17,13 @@ import {
|
|
|
17
17
|
FirefoxPlaywright,
|
|
18
18
|
HTTPManagementRoutes,
|
|
19
19
|
Hooks,
|
|
20
|
+
Logger,
|
|
20
21
|
NotFound,
|
|
21
22
|
Request,
|
|
22
23
|
ServerError,
|
|
23
24
|
WebkitPlaywright,
|
|
24
25
|
availableBrowsers,
|
|
25
26
|
convertIfBase64,
|
|
26
|
-
createLogger,
|
|
27
27
|
exists,
|
|
28
28
|
generateDataDir,
|
|
29
29
|
makeExternalURL,
|
|
@@ -38,7 +38,7 @@ export class BrowserManager {
|
|
|
38
38
|
protected browsers: Map<BrowserInstance, BrowserlessSession> = new Map();
|
|
39
39
|
protected launching: Map<string, Promise<unknown>> = new Map();
|
|
40
40
|
protected timers: Map<string, number> = new Map();
|
|
41
|
-
protected
|
|
41
|
+
protected log = new Logger('browser-manager');
|
|
42
42
|
protected chromeBrowsers = [ChromiumCDP, ChromeCDP];
|
|
43
43
|
protected playwrightBrowserNames = [
|
|
44
44
|
ChromiumPlaywright.name,
|
|
@@ -52,14 +52,14 @@ export class BrowserManager {
|
|
|
52
52
|
protected hooks: Hooks,
|
|
53
53
|
) {}
|
|
54
54
|
|
|
55
|
-
|
|
55
|
+
protected browserIsChrome = (b: BrowserInstance) =>
|
|
56
56
|
this.chromeBrowsers.some((chromeBrowser) => b instanceof chromeBrowser);
|
|
57
57
|
|
|
58
58
|
protected removeUserDataDir = async (userDataDir: string | null) => {
|
|
59
59
|
if (userDataDir && (await exists(userDataDir))) {
|
|
60
|
-
this.
|
|
60
|
+
this.log.info(`Deleting data directory "${userDataDir}"`);
|
|
61
61
|
await deleteAsync(userDataDir, { force: true }).catch((err) => {
|
|
62
|
-
this.
|
|
62
|
+
this.log.error(
|
|
63
63
|
`Error cleaning up user-data-dir "${err}" at ${userDataDir}`,
|
|
64
64
|
);
|
|
65
65
|
});
|
|
@@ -75,7 +75,7 @@ export class BrowserManager {
|
|
|
75
75
|
* and modifies URLs to set them to the appropriate addresses configured.
|
|
76
76
|
* When both Chrome and Chromium are installed, defaults to Chromium.
|
|
77
77
|
*/
|
|
78
|
-
public getProtocolJSON = async (): Promise<object> => {
|
|
78
|
+
public getProtocolJSON = async (logger: Logger): Promise<object> => {
|
|
79
79
|
const Browser = (await availableBrowsers).find((InstalledBrowser) =>
|
|
80
80
|
this.chromeBrowsers.some(
|
|
81
81
|
(ChromeBrowser) => InstalledBrowser === ChromeBrowser,
|
|
@@ -87,6 +87,7 @@ export class BrowserManager {
|
|
|
87
87
|
const browser = new Browser({
|
|
88
88
|
blockAds: false,
|
|
89
89
|
config: this.config,
|
|
90
|
+
logger,
|
|
90
91
|
userDataDir: null,
|
|
91
92
|
});
|
|
92
93
|
await browser.launch();
|
|
@@ -110,8 +111,8 @@ export class BrowserManager {
|
|
|
110
111
|
* and modifies URLs to set them to the appropriate addresses configured.
|
|
111
112
|
* When both Chrome and Chromium are installed, defaults to Chromium.
|
|
112
113
|
*/
|
|
113
|
-
public getVersionJSON = async (): Promise<CDPJSONPayload> => {
|
|
114
|
-
this.
|
|
114
|
+
public getVersionJSON = async (logger: Logger): Promise<CDPJSONPayload> => {
|
|
115
|
+
this.log.info(`Launching Chromium to generate /json/version results`);
|
|
115
116
|
const Browser = (await availableBrowsers).find((InstalledBrowser) =>
|
|
116
117
|
this.chromeBrowsers.some(
|
|
117
118
|
(ChromeBrowser) => InstalledBrowser === ChromeBrowser,
|
|
@@ -124,6 +125,7 @@ export class BrowserManager {
|
|
|
124
125
|
const browser = new Browser({
|
|
125
126
|
blockAds: false,
|
|
126
127
|
config: this.config,
|
|
128
|
+
logger,
|
|
127
129
|
userDataDir: null,
|
|
128
130
|
});
|
|
129
131
|
await browser.launch();
|
|
@@ -214,7 +216,7 @@ export class BrowserManager {
|
|
|
214
216
|
.filter((_) => _ !== null) as Array<CDPJSONPayload>;
|
|
215
217
|
};
|
|
216
218
|
|
|
217
|
-
|
|
219
|
+
protected generateSessionJson = async (
|
|
218
220
|
browser: BrowserInstance,
|
|
219
221
|
session: BrowserlessSession,
|
|
220
222
|
) => {
|
|
@@ -267,18 +269,18 @@ export class BrowserManager {
|
|
|
267
269
|
session: BrowserlessSession,
|
|
268
270
|
): Promise<void> => {
|
|
269
271
|
const cleanupACtions: Array<() => Promise<void>> = [];
|
|
270
|
-
this.
|
|
272
|
+
this.log.info(`${session.numbConnected} Client(s) are currently connected`);
|
|
271
273
|
|
|
272
274
|
// Don't close if there's clients still connected
|
|
273
275
|
if (session.numbConnected > 0) {
|
|
274
276
|
return;
|
|
275
277
|
}
|
|
276
278
|
|
|
277
|
-
this.
|
|
279
|
+
this.log.info(`Closing browser session`);
|
|
278
280
|
cleanupACtions.push(() => browser.close());
|
|
279
281
|
|
|
280
282
|
if (session.isTempDataDir) {
|
|
281
|
-
this.
|
|
283
|
+
this.log.info(
|
|
282
284
|
`Deleting "${session.userDataDir}" user-data-dir and session from memory`,
|
|
283
285
|
);
|
|
284
286
|
this.browsers.delete(browser);
|
|
@@ -302,7 +304,9 @@ export class BrowserManager {
|
|
|
302
304
|
public complete = async (browser: BrowserInstance): Promise<void> => {
|
|
303
305
|
const session = this.browsers.get(browser);
|
|
304
306
|
if (!session) {
|
|
305
|
-
this.
|
|
307
|
+
this.log.info(
|
|
308
|
+
`Couldn't locate session for browser, proceeding with close`,
|
|
309
|
+
);
|
|
306
310
|
return browser.close();
|
|
307
311
|
}
|
|
308
312
|
|
|
@@ -321,6 +325,7 @@ export class BrowserManager {
|
|
|
321
325
|
public getBrowserForRequest = async (
|
|
322
326
|
req: Request,
|
|
323
327
|
router: BrowserHTTPRoute | BrowserWebsocketRoute,
|
|
328
|
+
logger: Logger,
|
|
324
329
|
): Promise<BrowserInstance> => {
|
|
325
330
|
const { browser: Browser } = router;
|
|
326
331
|
const blockAds = parseBooleanParam(
|
|
@@ -344,7 +349,7 @@ export class BrowserManager {
|
|
|
344
349
|
if (found) {
|
|
345
350
|
const [browser, session] = found;
|
|
346
351
|
++session.numbConnected;
|
|
347
|
-
this.debug(`Located browser with ID ${id}`);
|
|
352
|
+
this.log.debug(`Located browser with ID ${id}`);
|
|
348
353
|
return browser;
|
|
349
354
|
}
|
|
350
355
|
|
|
@@ -387,6 +392,8 @@ export class BrowserManager {
|
|
|
387
392
|
const found = allPages.flat().find((b) => b.id === id);
|
|
388
393
|
|
|
389
394
|
if (found) {
|
|
395
|
+
const session = this.browsers.get(found.browser)!;
|
|
396
|
+
++session.numbConnected;
|
|
390
397
|
return found.browser;
|
|
391
398
|
}
|
|
392
399
|
|
|
@@ -446,6 +453,7 @@ export class BrowserManager {
|
|
|
446
453
|
const browser = new Browser({
|
|
447
454
|
blockAds,
|
|
448
455
|
config: this.config,
|
|
456
|
+
logger,
|
|
449
457
|
userDataDir,
|
|
450
458
|
});
|
|
451
459
|
|
|
@@ -477,7 +485,7 @@ export class BrowserManager {
|
|
|
477
485
|
};
|
|
478
486
|
|
|
479
487
|
public shutdown = async (): Promise<void> => {
|
|
480
|
-
this.
|
|
488
|
+
this.log.info(`Closing down browser instances`);
|
|
481
489
|
const sessions = Array.from(this.browsers);
|
|
482
490
|
await Promise.all(sessions.map(([b]) => b.close()));
|
|
483
491
|
const timers = Array.from(this.timers);
|
|
@@ -486,7 +494,7 @@ export class BrowserManager {
|
|
|
486
494
|
this.browsers = new Map();
|
|
487
495
|
this.timers = new Map();
|
|
488
496
|
await this.stop();
|
|
489
|
-
this.
|
|
497
|
+
this.log.info(`Shutdown complete`);
|
|
490
498
|
};
|
|
491
499
|
|
|
492
500
|
/**
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
BrowserServerOptions,
|
|
3
3
|
Config,
|
|
4
|
+
Logger,
|
|
4
5
|
Request,
|
|
5
6
|
ServerError,
|
|
6
|
-
createLogger,
|
|
7
7
|
} from '@browserless.io/browserless';
|
|
8
8
|
import playwright, { Page } from 'playwright-core';
|
|
9
9
|
import { Duplex } from 'stream';
|
|
@@ -18,21 +18,24 @@ export class WebkitPlaywright extends EventEmitter {
|
|
|
18
18
|
protected proxy = httpProxy.createProxyServer();
|
|
19
19
|
protected browser: playwright.BrowserServer | null = null;
|
|
20
20
|
protected browserWSEndpoint: string | null = null;
|
|
21
|
-
protected
|
|
21
|
+
protected logger: Logger;
|
|
22
22
|
|
|
23
23
|
constructor({
|
|
24
24
|
config,
|
|
25
25
|
userDataDir,
|
|
26
|
+
logger,
|
|
26
27
|
}: {
|
|
27
28
|
config: Config;
|
|
29
|
+
logger: Logger;
|
|
28
30
|
userDataDir: WebkitPlaywright['userDataDir'];
|
|
29
31
|
}) {
|
|
30
32
|
super();
|
|
31
33
|
|
|
32
34
|
this.userDataDir = userDataDir;
|
|
33
35
|
this.config = config;
|
|
36
|
+
this.logger = logger;
|
|
34
37
|
|
|
35
|
-
this.
|
|
38
|
+
this.logger.info(`Starting new ${this.constructor.name} instance`);
|
|
36
39
|
}
|
|
37
40
|
|
|
38
41
|
protected cleanListeners() {
|
|
@@ -43,7 +46,9 @@ export class WebkitPlaywright extends EventEmitter {
|
|
|
43
46
|
|
|
44
47
|
public close = async (): Promise<void> => {
|
|
45
48
|
if (this.browser) {
|
|
46
|
-
this.
|
|
49
|
+
this.logger.info(
|
|
50
|
+
`Closing ${this.constructor.name} process and all listeners`,
|
|
51
|
+
);
|
|
47
52
|
this.emit('close');
|
|
48
53
|
this.cleanListeners();
|
|
49
54
|
this.browser.close();
|
|
@@ -56,21 +61,27 @@ export class WebkitPlaywright extends EventEmitter {
|
|
|
56
61
|
public pages = async (): Promise<[]> => [];
|
|
57
62
|
|
|
58
63
|
public getPageId = (): string => {
|
|
59
|
-
throw new ServerError(
|
|
64
|
+
throw new ServerError(
|
|
65
|
+
`#getPageId is not yet supported with ${this.constructor.name}.`,
|
|
66
|
+
);
|
|
60
67
|
};
|
|
61
68
|
|
|
62
69
|
public makeLiveURL = (): void => {
|
|
63
|
-
throw new ServerError(
|
|
70
|
+
throw new ServerError(
|
|
71
|
+
`Live URLs are not yet supported with ${this.constructor.name}.`,
|
|
72
|
+
);
|
|
64
73
|
};
|
|
65
74
|
|
|
66
75
|
public newPage = async (): Promise<Page> => {
|
|
67
|
-
throw new ServerError(
|
|
76
|
+
throw new ServerError(
|
|
77
|
+
`Can't create new page with ${this.constructor.name}`,
|
|
78
|
+
);
|
|
68
79
|
};
|
|
69
80
|
|
|
70
81
|
public launch = async (
|
|
71
82
|
options: BrowserServerOptions = {},
|
|
72
83
|
): Promise<playwright.BrowserServer> => {
|
|
73
|
-
this.
|
|
84
|
+
this.logger.info(`Launching ${this.constructor.name} Handler`);
|
|
74
85
|
|
|
75
86
|
this.browser = await playwright.webkit.launchServer({
|
|
76
87
|
...options,
|
|
@@ -83,7 +94,9 @@ export class WebkitPlaywright extends EventEmitter {
|
|
|
83
94
|
|
|
84
95
|
const browserWSEndpoint = this.browser.wsEndpoint();
|
|
85
96
|
|
|
86
|
-
this.
|
|
97
|
+
this.logger.info(
|
|
98
|
+
`${this.constructor.name} is running on ${browserWSEndpoint}`,
|
|
99
|
+
);
|
|
87
100
|
this.browserWSEndpoint = browserWSEndpoint;
|
|
88
101
|
this.running = true;
|
|
89
102
|
|
|
@@ -110,7 +123,7 @@ export class WebkitPlaywright extends EventEmitter {
|
|
|
110
123
|
};
|
|
111
124
|
|
|
112
125
|
public proxyPageWebSocket = async () => {
|
|
113
|
-
|
|
126
|
+
this.logger.warn(`Not yet implemented`);
|
|
114
127
|
};
|
|
115
128
|
|
|
116
129
|
public proxyWebSocket = async (
|
|
@@ -126,8 +139,8 @@ export class WebkitPlaywright extends EventEmitter {
|
|
|
126
139
|
}
|
|
127
140
|
socket.once('close', resolve);
|
|
128
141
|
|
|
129
|
-
this.
|
|
130
|
-
`Proxying ${req.parsed.href} to
|
|
142
|
+
this.logger.info(
|
|
143
|
+
`Proxying ${req.parsed.href} to ${this.constructor.name} ${this.browserWSEndpoint}`,
|
|
131
144
|
);
|
|
132
145
|
|
|
133
146
|
// Delete headers known to cause issues
|
|
@@ -144,7 +157,9 @@ export class WebkitPlaywright extends EventEmitter {
|
|
|
144
157
|
target: this.browserWSEndpoint,
|
|
145
158
|
},
|
|
146
159
|
(error) => {
|
|
147
|
-
this.
|
|
160
|
+
this.logger.error(
|
|
161
|
+
`Error proxying session to ${this.constructor.name}: ${error}`,
|
|
162
|
+
);
|
|
148
163
|
this.close();
|
|
149
164
|
return reject(error);
|
|
150
165
|
},
|
package/src/config.ts
CHANGED
|
@@ -148,6 +148,7 @@ export class Config extends EventEmitter {
|
|
|
148
148
|
'30000'
|
|
149
149
|
);
|
|
150
150
|
protected static = process.env.STATIC ?? path.join(__dirname, '..', 'static');
|
|
151
|
+
protected debuggerDir = path.join(this.static, 'debugger');
|
|
151
152
|
protected retries = +(process.env.RETRIES ?? '5');
|
|
152
153
|
protected allowFileProtocol = !!parseEnvVars(false, 'ALLOW_FILE_PROTOCOL');
|
|
153
154
|
protected allowGet = !!parseEnvVars(false, 'ALLOW_GET', 'ENABLE_API_GET');
|
|
@@ -187,6 +188,7 @@ export class Config extends EventEmitter {
|
|
|
187
188
|
public getQueued = (): number => this.queued;
|
|
188
189
|
public getTimeout = (): number => this.timeout;
|
|
189
190
|
public getStatic = (): string => this.static;
|
|
191
|
+
public getDebuggerDir = (): string => this.debuggerDir;
|
|
190
192
|
public getRetries = (): number => this.retries;
|
|
191
193
|
public getAllowFileProtocol = (): boolean => this.allowFileProtocol;
|
|
192
194
|
public getCPULimit = (): number => this.maxCpu;
|
|
@@ -198,6 +200,8 @@ export class Config extends EventEmitter {
|
|
|
198
200
|
public getTimeoutAlertURL = () => this.timeoutAlertURL;
|
|
199
201
|
public getErrorAlertURL = () => this.errorAlertURL;
|
|
200
202
|
|
|
203
|
+
public hasDebugger = (): Promise<boolean> => exists(this.debuggerDir);
|
|
204
|
+
|
|
201
205
|
/**
|
|
202
206
|
* If true, allows GET style calls on our browser-based APIs, using
|
|
203
207
|
* ?body=JSON format.
|
package/src/exports.ts
CHANGED
package/src/file-system.ts
CHANGED
|
@@ -1,16 +1,11 @@
|
|
|
1
|
-
import {
|
|
2
|
-
Config,
|
|
3
|
-
createLogger,
|
|
4
|
-
decrypt,
|
|
5
|
-
encrypt,
|
|
6
|
-
} from '@browserless.io/browserless';
|
|
1
|
+
import { Config, Logger, decrypt, encrypt } from '@browserless.io/browserless';
|
|
7
2
|
import { readFile, writeFile } from 'fs/promises';
|
|
8
3
|
import { EventEmitter } from 'events';
|
|
9
4
|
|
|
10
5
|
export class FileSystem extends EventEmitter {
|
|
11
6
|
protected fsMap: Map<string, string[]> = new Map();
|
|
12
7
|
protected currentAESKey: Buffer;
|
|
13
|
-
protected
|
|
8
|
+
protected logger = new Logger('file-system');
|
|
14
9
|
|
|
15
10
|
constructor(protected config: Config) {
|
|
16
11
|
super();
|
package/src/index.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { Browserless,
|
|
1
|
+
import { Browserless, Logger } from '@browserless.io/browserless';
|
|
2
2
|
|
|
3
3
|
(async () => {
|
|
4
4
|
const browserless = new Browserless();
|
|
5
|
-
const
|
|
5
|
+
const logger = new Logger('index.js');
|
|
6
6
|
browserless.start();
|
|
7
7
|
|
|
8
8
|
process
|
|
@@ -15,27 +15,27 @@ import { Browserless, createLogger } from '@browserless.io/browserless';
|
|
|
15
15
|
process.exit(1);
|
|
16
16
|
})
|
|
17
17
|
.once('SIGTERM', async () => {
|
|
18
|
-
|
|
18
|
+
logger.info(`SIGTERM received, saving and closing down`);
|
|
19
19
|
await browserless.stop();
|
|
20
20
|
process.exit(0);
|
|
21
21
|
})
|
|
22
22
|
.once('SIGINT', async () => {
|
|
23
|
-
|
|
23
|
+
logger.info(`SIGINT received, saving and closing down`);
|
|
24
24
|
await browserless.stop();
|
|
25
25
|
process.exit(0);
|
|
26
26
|
})
|
|
27
27
|
.once('SIGHUP', async () => {
|
|
28
|
-
|
|
28
|
+
logger.info(`SIGHUP received, saving and closing down`);
|
|
29
29
|
await browserless.stop();
|
|
30
30
|
process.exit(0);
|
|
31
31
|
})
|
|
32
32
|
.once('SIGUSR2', async () => {
|
|
33
|
-
|
|
33
|
+
logger.info(`SIGUSR2 received, saving and closing down`);
|
|
34
34
|
await browserless.stop();
|
|
35
35
|
process.exit(0);
|
|
36
36
|
})
|
|
37
37
|
.once('exit', () => {
|
|
38
|
-
|
|
38
|
+
logger.info(`Process is finished, exiting`);
|
|
39
39
|
process.exit(0);
|
|
40
40
|
});
|
|
41
41
|
})();
|
package/src/limiter.ts
CHANGED
|
@@ -2,11 +2,11 @@ import {
|
|
|
2
2
|
AfterResponse,
|
|
3
3
|
Config,
|
|
4
4
|
Hooks,
|
|
5
|
+
Logger,
|
|
5
6
|
Metrics,
|
|
6
7
|
Monitoring,
|
|
7
8
|
TooManyRequests,
|
|
8
9
|
WebHooks,
|
|
9
|
-
createLogger,
|
|
10
10
|
} from '@browserless.io/browserless';
|
|
11
11
|
import q from 'queue';
|
|
12
12
|
|
|
@@ -26,7 +26,7 @@ interface Job {
|
|
|
26
26
|
|
|
27
27
|
export class Limiter extends q {
|
|
28
28
|
protected queued: number;
|
|
29
|
-
protected
|
|
29
|
+
protected logger = new Logger('limiter');
|
|
30
30
|
|
|
31
31
|
constructor(
|
|
32
32
|
protected config: Config,
|
|
@@ -42,22 +42,22 @@ export class Limiter extends q {
|
|
|
42
42
|
});
|
|
43
43
|
this.queued = config.getQueued();
|
|
44
44
|
|
|
45
|
-
this.
|
|
45
|
+
this.logger.info(
|
|
46
46
|
`Concurrency: ${this.concurrency} queue: ${this.queued} timeout: ${this.timeout}ms`,
|
|
47
47
|
);
|
|
48
48
|
|
|
49
49
|
config.on('concurrent', (concurrency: number) => {
|
|
50
|
-
this.
|
|
50
|
+
this.logger.info(`Concurrency updated to ${concurrency}`);
|
|
51
51
|
this.concurrency = concurrency;
|
|
52
52
|
});
|
|
53
53
|
|
|
54
54
|
config.on('queued', (queued: number) => {
|
|
55
|
-
this.
|
|
55
|
+
this.logger.info(`Queue updated to ${queued}`);
|
|
56
56
|
this.queued = queued;
|
|
57
57
|
});
|
|
58
58
|
|
|
59
59
|
config.on('timeout', (timeout: number) => {
|
|
60
|
-
this.
|
|
60
|
+
this.logger.info(`Timeout updated to ${timeout}ms`);
|
|
61
61
|
this.timeout = timeout <= 0 ? 0 : timeout;
|
|
62
62
|
});
|
|
63
63
|
|
|
@@ -83,7 +83,7 @@ export class Limiter extends q {
|
|
|
83
83
|
|
|
84
84
|
protected handleSuccess({ detail: { job } }: { detail: { job: Job } }) {
|
|
85
85
|
const timeUsed = Date.now() - job.start;
|
|
86
|
-
this.
|
|
86
|
+
this.logger.info(
|
|
87
87
|
`Job has succeeded after ${timeUsed.toLocaleString()}ms of activity.`,
|
|
88
88
|
);
|
|
89
89
|
this.metrics.addSuccessful(Date.now() - job.start);
|
|
@@ -101,12 +101,12 @@ export class Limiter extends q {
|
|
|
101
101
|
detail: { job: Job; next: Job };
|
|
102
102
|
}) {
|
|
103
103
|
const timeUsed = Date.now() - job.start;
|
|
104
|
-
this.
|
|
104
|
+
this.logger.warn(
|
|
105
105
|
`Job has hit timeout after ${timeUsed.toLocaleString()}ms of activity.`,
|
|
106
106
|
);
|
|
107
107
|
this.metrics.addTimedout(Date.now() - job.start);
|
|
108
108
|
this.webhooks.callTimeoutAlertURL();
|
|
109
|
-
this.
|
|
109
|
+
this.logger.info(`Calling timeout handler`);
|
|
110
110
|
job?.onTimeoutFn(job);
|
|
111
111
|
this.jobEnd({
|
|
112
112
|
req: job.args[0],
|
|
@@ -122,7 +122,9 @@ export class Limiter extends q {
|
|
|
122
122
|
}: {
|
|
123
123
|
detail: { error: unknown; job: Job };
|
|
124
124
|
}) {
|
|
125
|
-
this.
|
|
125
|
+
this.logger.info(
|
|
126
|
+
`Recording failed stat, cleaning up: "${error?.toString()}"`,
|
|
127
|
+
);
|
|
126
128
|
this.metrics.addError(Date.now() - job.start);
|
|
127
129
|
this.webhooks.callErrorAlertURL(error?.toString() ?? 'Unknown Error');
|
|
128
130
|
this.jobEnd({
|
|
@@ -133,7 +135,7 @@ export class Limiter extends q {
|
|
|
133
135
|
}
|
|
134
136
|
|
|
135
137
|
protected logQueue(message: string) {
|
|
136
|
-
this.
|
|
138
|
+
this.logger.info(
|
|
137
139
|
`(Running: ${this.executing}, Pending: ${this.waiting}) ${message} `,
|
|
138
140
|
);
|
|
139
141
|
}
|
package/src/logger.ts
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { Request, createLogger } from '@browserless.io/browserless';
|
|
2
|
+
|
|
3
|
+
export class Logger {
|
|
4
|
+
protected _trace: (...args: unknown[]) => void;
|
|
5
|
+
protected _debug: (...args: unknown[]) => void;
|
|
6
|
+
protected _info: (...args: unknown[]) => void;
|
|
7
|
+
protected _warn: (...args: unknown[]) => void;
|
|
8
|
+
protected _error: (...args: unknown[]) => void;
|
|
9
|
+
protected _fatal: (...args: unknown[]) => void;
|
|
10
|
+
|
|
11
|
+
constructor(
|
|
12
|
+
protected prefix: string,
|
|
13
|
+
protected request?: Request,
|
|
14
|
+
) {
|
|
15
|
+
const logger = createLogger(prefix);
|
|
16
|
+
|
|
17
|
+
this._trace = logger.extend('trace');
|
|
18
|
+
this._debug = logger.extend('debug');
|
|
19
|
+
this._info = logger.extend('info');
|
|
20
|
+
this._warn = logger.extend('warn');
|
|
21
|
+
this._error = logger.extend('error');
|
|
22
|
+
this._fatal = logger.extend('fatal');
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
protected get reqInfo() {
|
|
26
|
+
return this.request ? this.request.socket.remoteAddress ?? 'Unknown' : '';
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
public trace = (...messages: unknown[]) => {
|
|
30
|
+
this._trace(this.reqInfo, ...messages);
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
public debug = (...messages: unknown[]) => {
|
|
34
|
+
this._debug(this.reqInfo, ...messages);
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
public info = (...messages: unknown[]) => {
|
|
38
|
+
this._info(this.reqInfo, ...messages);
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
public warn = (...messages: unknown[]) => {
|
|
42
|
+
this._warn(this.reqInfo, ...messages);
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
public error = (...messages: unknown[]) => {
|
|
46
|
+
this._error(this.reqInfo, ...messages);
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
public fatal = (...messages: unknown[]) => {
|
|
50
|
+
this._fatal(this.reqInfo, ...messages);
|
|
51
|
+
};
|
|
52
|
+
}
|
package/src/monitoring.ts
CHANGED
|
@@ -1,13 +1,9 @@
|
|
|
1
|
-
import {
|
|
2
|
-
Config,
|
|
3
|
-
IResourceLoad,
|
|
4
|
-
createLogger,
|
|
5
|
-
} from '@browserless.io/browserless';
|
|
1
|
+
import { Config, IResourceLoad, Logger } from '@browserless.io/browserless';
|
|
6
2
|
import { EventEmitter } from 'events';
|
|
7
3
|
import si from 'systeminformation';
|
|
8
4
|
|
|
9
5
|
export class Monitoring extends EventEmitter {
|
|
10
|
-
protected log =
|
|
6
|
+
protected log = new Logger('hardware');
|
|
11
7
|
constructor(protected config: Config) {
|
|
12
8
|
super();
|
|
13
9
|
}
|
|
@@ -17,7 +13,7 @@ export class Monitoring extends EventEmitter {
|
|
|
17
13
|
si.currentLoad(),
|
|
18
14
|
si.mem(),
|
|
19
15
|
]).catch((err) => {
|
|
20
|
-
this.log(`Error checking machine stats`, err);
|
|
16
|
+
this.log.error(`Error checking machine stats`, err);
|
|
21
17
|
return [null, null];
|
|
22
18
|
});
|
|
23
19
|
|
|
@@ -40,7 +36,9 @@ export class Monitoring extends EventEmitter {
|
|
|
40
36
|
const cpuInt = cpu && Math.ceil(cpu * 100);
|
|
41
37
|
const memoryInt = memory && Math.ceil(memory * 100);
|
|
42
38
|
|
|
43
|
-
this.log(
|
|
39
|
+
this.log.info(
|
|
40
|
+
`Checking overload status: CPU ${cpuInt}% Memory ${memoryInt}%`,
|
|
41
|
+
);
|
|
44
42
|
|
|
45
43
|
const cpuOverloaded = !!(cpuInt && cpuInt >= this.config.getCPULimit());
|
|
46
44
|
const memoryOverloaded = !!(
|