Skip to content

Commit 02eec66

Browse files
authored
feat: add ignoreProxyCertificate option for the internal proxy-chain instance (#3418)
Adds `ignoreProxyCertificate` option to `launchContext`. When set to `true`, this ignores TLS issues with the upstream HTTPS proxy. Closes #3369
1 parent 2658610 commit 02eec66

11 files changed

Lines changed: 91 additions & 10 deletions

File tree

packages/browser-crawler/src/internals/browser-launcher.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,12 @@ export interface BrowserLaunchContext<TOptions, Launcher> extends BrowserPluginO
6767
*/
6868
userAgent?: string;
6969

70+
/**
71+
* If set to `true`, TLS certificate errors from the upstream proxy will be ignored.
72+
* This is useful when using HTTPS proxies with self-signed certificates.
73+
*/
74+
ignoreProxyCertificate?: boolean;
75+
7076
/**
7177
* The type of browser to be launched.
7278
* By default, `chromium` is used. Other browsers like `webkit` or `firefox` can be used.
@@ -108,6 +114,7 @@ export abstract class BrowserLauncher<
108114
useIncognitoPages: ow.optional.boolean,
109115
browserPerProxy: ow.optional.boolean,
110116
experimentalContainers: ow.optional.boolean,
117+
ignoreProxyCertificate: ow.optional.boolean,
111118
userDataDir: ow.optional.string,
112119
launchOptions: ow.optional.object,
113120
userAgent: ow.optional.string,

packages/browser-pool/src/abstract-classes/browser-plugin.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,11 @@ export interface BrowserPluginOptions<LibraryOptions> {
8282
* Might cause performance issues, as Crawlee might launch too many browser instances.
8383
*/
8484
browserPerProxy?: boolean;
85+
/**
86+
* If set to `true`, TLS certificate errors from the upstream proxy will be ignored.
87+
* This is useful when using HTTPS proxies with self-signed certificates.
88+
*/
89+
ignoreProxyCertificate?: boolean;
8590
}
8691

8792
export interface CreateLaunchContextOptions<
@@ -126,6 +131,8 @@ export abstract class BrowserPlugin<
126131

127132
browserPerProxy?: boolean;
128133

134+
ignoreProxyCertificate?: boolean;
135+
129136
constructor(library: Library, options: BrowserPluginOptions<LibraryOptions> = {}) {
130137
const {
131138
launchOptions = {} as LibraryOptions,
@@ -134,6 +141,7 @@ export abstract class BrowserPlugin<
134141
useIncognitoPages = false,
135142
experimentalContainers = false,
136143
browserPerProxy = false,
144+
ignoreProxyCertificate = false,
137145
} = options;
138146

139147
this.library = library;
@@ -143,6 +151,7 @@ export abstract class BrowserPlugin<
143151
this.useIncognitoPages = useIncognitoPages;
144152
this.experimentalContainers = experimentalContainers;
145153
this.browserPerProxy = browserPerProxy;
154+
this.ignoreProxyCertificate = ignoreProxyCertificate;
146155
}
147156

148157
/**
@@ -162,6 +171,7 @@ export abstract class BrowserPlugin<
162171
userDataDir = this.userDataDir,
163172
experimentalContainers = this.experimentalContainers,
164173
browserPerProxy = this.browserPerProxy,
174+
ignoreProxyCertificate = this.ignoreProxyCertificate,
165175
proxyTier,
166176
} = options;
167177

@@ -174,6 +184,7 @@ export abstract class BrowserPlugin<
174184
experimentalContainers,
175185
userDataDir,
176186
browserPerProxy,
187+
ignoreProxyCertificate,
177188
proxyTier,
178189
});
179190
}

packages/browser-pool/src/anonymize-proxy.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,16 @@ import { anonymizeProxy, closeAnonymizedProxy } from 'proxy-chain';
22

33
type PromiseVoid = () => Promise<void>;
44

5+
export interface AnonymizeProxySugarOptions {
6+
ignoreProxyCertificate?: boolean;
7+
}
8+
59
/** @internal */
610
export const anonymizeProxySugar = async (
711
proxyUrl?: string,
812
username?: string,
913
password?: string,
14+
options?: AnonymizeProxySugarOptions,
1015
): Promise<[string | undefined, PromiseVoid]> => {
1116
if (proxyUrl) {
1217
const url = new URL(proxyUrl);
@@ -16,10 +21,14 @@ export const anonymizeProxySugar = async (
1621
url.password = password ?? '';
1722
}
1823

19-
if (url.username || url.password) {
24+
if (url.username || url.password || options?.ignoreProxyCertificate) {
2025
// trim off trailing slash if it's present
2126
const proxyUrlString = url.href.endsWith('/') ? url.href.slice(0, -1) : url.href;
22-
const anonymized = await anonymizeProxy(proxyUrlString);
27+
const anonymized = await anonymizeProxy({
28+
url: proxyUrlString,
29+
port: 0,
30+
ignoreProxyCertificate: options?.ignoreProxyCertificate ?? false,
31+
});
2332

2433
return [
2534
anonymized,

packages/browser-pool/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,4 +52,4 @@ export {
5252
GetFingerprintReturn,
5353
} from './fingerprinting/types';
5454
export { InferBrowserPluginArray, UnwrapPromise } from './utils';
55-
export { anonymizeProxySugar } from './anonymize-proxy';
55+
export { anonymizeProxySugar, type AnonymizeProxySugarOptions } from './anonymize-proxy';

packages/browser-pool/src/launch-context.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,11 @@ export interface LaunchContextOptions<
5858
userDataDir?: string;
5959
proxyUrl?: string;
6060
proxyTier?: number;
61+
/**
62+
* If set to `true`, TLS certificate errors from the upstream proxy will be ignored.
63+
* This is useful when using HTTPS proxies with self-signed certificates.
64+
*/
65+
ignoreProxyCertificate?: boolean;
6166
}
6267

6368
export class LaunchContext<
@@ -75,6 +80,7 @@ export class LaunchContext<
7580
experimentalContainers: boolean;
7681
userDataDir: string;
7782
proxyTier?: number;
83+
ignoreProxyCertificate?: boolean;
7884

7985
private _proxyUrl?: string;
8086
private readonly _reservedFieldNames = [...Reflect.ownKeys(this), 'extend'];
@@ -93,6 +99,7 @@ export class LaunchContext<
9399
experimentalContainers,
94100
userDataDir = '',
95101
proxyTier,
102+
ignoreProxyCertificate,
96103
} = options;
97104

98105
this.id = id;
@@ -103,6 +110,7 @@ export class LaunchContext<
103110
this.experimentalContainers = experimentalContainers ?? false;
104111
this.userDataDir = userDataDir;
105112
this.proxyTier = proxyTier;
113+
this.ignoreProxyCertificate = ignoreProxyCertificate ?? false;
106114

107115
this._proxyUrl = proxyUrl;
108116
}

packages/browser-pool/src/playwright/playwright-controller.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ export class PlaywrightController extends BrowserController<
6060
contextOptions.proxy.server,
6161
contextOptions.proxy.username,
6262
contextOptions.proxy.password,
63+
{ ignoreProxyCertificate: this.launchContext.ignoreProxyCertificate },
6364
);
6465

6566
if (anonymizedProxyUrl) {

packages/browser-pool/src/playwright/playwright-plugin.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,9 @@ export class PlaywrightPlugin extends BrowserPlugin<
5959
launchOptions!.args = launchOptions!.args?.filter((arg) => arg !== '--no-sandbox');
6060
}
6161

62-
const [anonymizedProxyUrl, close] = await anonymizeProxySugar(proxyUrl);
62+
const [anonymizedProxyUrl, close] = await anonymizeProxySugar(proxyUrl, undefined, undefined, {
63+
ignoreProxyCertificate: launchContext.ignoreProxyCertificate,
64+
});
6365
if (anonymizedProxyUrl) {
6466
launchOptions!.proxy = {
6567
server: anonymizedProxyUrl,

packages/browser-pool/src/puppeteer/puppeteer-controller.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ export class PuppeteerController extends BrowserController<
5252
contextOptions.proxyServer,
5353
contextOptions.proxyUsername,
5454
contextOptions.proxyPassword,
55+
{ ignoreProxyCertificate: this.launchContext.ignoreProxyCertificate },
5556
);
5657

5758
if (anonymizedProxyUrl) {

packages/browser-pool/src/puppeteer/puppeteer-plugin.ts

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,14 @@ export class PuppeteerPlugin extends BrowserPlugin<
3939
} catch {
4040
// ignore
4141
}
42-
const { launchOptions, userDataDir, useIncognitoPages, experimentalContainers, proxyUrl } = launchContext;
42+
const {
43+
launchOptions,
44+
userDataDir,
45+
useIncognitoPages,
46+
experimentalContainers,
47+
proxyUrl,
48+
ignoreProxyCertificate,
49+
} = launchContext;
4350

4451
if (experimentalContainers) {
4552
throw new Error('Experimental containers are only available with Playwright');
@@ -62,7 +69,9 @@ export class PuppeteerPlugin extends BrowserPlugin<
6269
let browser: PuppeteerTypes.Browser;
6370

6471
{
65-
const [anonymizedProxyUrl, close] = await anonymizeProxySugar(proxyUrl);
72+
const [anonymizedProxyUrl, close] = await anonymizeProxySugar(proxyUrl, undefined, undefined, {
73+
ignoreProxyCertificate: launchContext.ignoreProxyCertificate,
74+
});
6675

6776
if (proxyUrl) {
6877
const proxyArg = `${PROXY_SERVER_ARG}${anonymizedProxyUrl ?? proxyUrl}`;
@@ -133,7 +142,12 @@ export class PuppeteerPlugin extends BrowserPlugin<
133142
let page: PuppeteerTypes.Page;
134143

135144
if (useIncognitoPages) {
136-
const [anonymizedProxyUrl, close] = await anonymizeProxySugar(proxyUrl);
145+
const [anonymizedProxyUrl, close] = await anonymizeProxySugar(
146+
proxyUrl,
147+
undefined,
148+
undefined,
149+
{ ignoreProxyCertificate },
150+
);
137151

138152
try {
139153
const context = (await (browser as any)[method]({

packages/stagehand-crawler/src/internals/stagehand-plugin.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,9 @@ export class StagehandPlugin extends BrowserPlugin<BrowserType, LaunchOptions, P
5757
const isLocal = this.stagehandOptions.env === 'LOCAL' || !this.stagehandOptions.env;
5858

5959
// Use anonymizeProxy to handle proxy authentication transparently
60-
const [anonymizedProxyUrl, closeAnonymizedProxy] = await anonymizeProxySugar(proxyUrl);
60+
const [anonymizedProxyUrl, closeAnonymizedProxy] = await anonymizeProxySugar(proxyUrl, undefined, undefined, {
61+
ignoreProxyCertificate: launchContext.ignoreProxyCertificate,
62+
});
6163

6264
// Build model configuration
6365
// For LOCAL env, we merge apiKey into the model config since Stagehand expects it there

0 commit comments

Comments
 (0)