@@ -24,12 +24,17 @@ import { mkdtempSync, rmSync } from "fs";
2424import { join } from "path" ;
2525import { tmpdir } from "os" ;
2626import pLimit from "p-limit" ;
27- import { anonymizeProxy , closeAnonymizedProxy } from "proxy-chain" ;
27+ import type { Server as ProxyChainServer } from "proxy-chain" ;
2828import { FingerprintGenerator , type BrowserFingerprintWithHeaders } from "fingerprint-generator" ;
2929import { FingerprintInjector } from "fingerprint-injector" ;
3030import { createLogger , type Logger } from "../utils/logger.js" ;
3131import type { ProxyHealthTracker } from "../proxy/health-tracker.js" ;
32- import { findChromePath , buildChromeArgs , CHROME_LAUNCH_TIMEOUT_MS } from "./shared.js" ;
32+ import {
33+ findChromePath ,
34+ buildChromeArgs ,
35+ createProxyTunnel ,
36+ CHROME_LAUNCH_TIMEOUT_MS ,
37+ } from "./shared.js" ;
3338
3439// Lazy-loaded Playwright types — we import dynamically to avoid hard dep at parse time.
3540type PlaywrightBrowser = import ( "playwright-core" ) . Browser ;
@@ -99,7 +104,7 @@ export class ChromeInstance {
99104 private chromeProcess : ChildProcess | null = null ;
100105 private pwBrowser : PlaywrightBrowser | null = null ;
101106 private pwContext : PlaywrightBrowserContext | null = null ;
102- private anonymizedProxyUrl : string | null = null ;
107+ private proxyChainServer : ProxyChainServer | null = null ;
103108 private userDataDir : string | null = null ;
104109 private wsEndpoint : string | null = null ;
105110 private fingerprint : BrowserFingerprintWithHeaders [ "fingerprint" ] | null = null ;
@@ -159,7 +164,7 @@ export class ChromeInstance {
159164 * at most maxTabs calls run concurrently. A hard timeout guarantees the
160165 * slot is always released, even if fn hangs (proxy stall, DNS hang, etc.).
161166 */
162- async withPage < T > ( fn : ( page : PlaywrightPage ) => Promise < T > , timeoutMs = 90_000 ) : Promise < T > {
167+ async withPage < T > ( fn : ( page : PlaywrightPage ) => Promise < T > , timeoutMs = 60_000 ) : Promise < T > {
163168 if ( this . state === "closed" || this . state === "retired" ) {
164169 throw new Error ( `ChromeInstance: cannot withPage on ${ this . state } browser` ) ;
165170 }
@@ -285,12 +290,13 @@ export class ChromeInstance {
285290 try {
286291 this . logger . debug ( { proxy : redactProxy ( this . proxyUrl ) } , "launching Chrome" ) ;
287292
288- // Set up proxy via proxy-chain (handles auth transparently, preserves TLS)
293+ // Set up proxy tunnel with keepAlive disabled (see createProxyTunnel).
289294 let chromeProxyArg : string | undefined ;
290295
291296 if ( this . proxyUrl ) {
292- this . anonymizedProxyUrl = await anonymizeProxy ( this . proxyUrl ) ;
293- chromeProxyArg = this . anonymizedProxyUrl ;
297+ const tunnel = await createProxyTunnel ( this . proxyUrl ) ;
298+ this . proxyChainServer = tunnel . server ;
299+ chromeProxyArg = tunnel . url ;
294300 }
295301
296302 // Generate fingerprint once per ChromeInstance (cached across relaunches)
@@ -461,10 +467,10 @@ export class ChromeInstance {
461467 this . chromeProcess = null ;
462468 }
463469
464- // Stop proxy-chain anonymized proxy
465- if ( this . anonymizedProxyUrl ) {
466- await closeAnonymizedProxy ( this . anonymizedProxyUrl , true ) . catch ( ( ) => { } ) ;
467- this . anonymizedProxyUrl = null ;
470+ // Stop proxy-chain server
471+ if ( this . proxyChainServer ) {
472+ await this . proxyChainServer . close ( true ) . catch ( ( ) => { } ) ;
473+ this . proxyChainServer = null ;
468474 }
469475
470476 // Remove temp profile
0 commit comments