follow-ups
This commit is contained in:
parent
c3fdaf9907
commit
3571538e58
|
|
@ -30,20 +30,18 @@ import { debugLogger } from '../utils/debugLogger';
|
||||||
class ALPNCache {
|
class ALPNCache {
|
||||||
private _cache = new Map<string, ManualPromise<string>>();
|
private _cache = new Map<string, ManualPromise<string>>();
|
||||||
|
|
||||||
get(host: string, port: number, success: (protocol: string) => void, error: (error: Error) => void): void {
|
get(host: string, port: number, success: (protocol: string) => void) {
|
||||||
const cacheKey = `${host}:${port}`;
|
const cacheKey = `${host}:${port}`;
|
||||||
{
|
{
|
||||||
const result = this._cache.get(cacheKey);
|
const result = this._cache.get(cacheKey);
|
||||||
if (result) {
|
if (result) {
|
||||||
result.then(success);
|
result.then(success);
|
||||||
result.catch(error);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const result = new ManualPromise<string>();
|
const result = new ManualPromise<string>();
|
||||||
this._cache.set(cacheKey, result);
|
this._cache.set(cacheKey, result);
|
||||||
result.then(success);
|
result.then(success);
|
||||||
result.catch(error);
|
|
||||||
const socket = tls.connect({
|
const socket = tls.connect({
|
||||||
host,
|
host,
|
||||||
port,
|
port,
|
||||||
|
|
@ -57,14 +55,13 @@ class ALPNCache {
|
||||||
socket.end();
|
socket.end();
|
||||||
});
|
});
|
||||||
socket.on('error', error => {
|
socket.on('error', error => {
|
||||||
result.reject(error);
|
debugLogger.log('client-certificates', `ALPN error: ${error.message}`);
|
||||||
|
result.resolve('http/1.1');
|
||||||
socket.end();
|
socket.end();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const alpnCache = new ALPNCache();
|
|
||||||
|
|
||||||
class SocksProxyConnection {
|
class SocksProxyConnection {
|
||||||
private readonly socksProxy: ClientCertificatesProxy;
|
private readonly socksProxy: ClientCertificatesProxy;
|
||||||
private readonly uid: string;
|
private readonly uid: string;
|
||||||
|
|
@ -123,16 +120,17 @@ class SocksProxyConnection {
|
||||||
callback();
|
callback();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
alpnCache.get(rewriteToLocalhostIfNeeded(this.host), this.port, serverRequestedAlpnProtocol => {
|
this.socksProxy.alpnCache.get(rewriteToLocalhostIfNeeded(this.host), this.port, alpnProtocolChosenByServer => {
|
||||||
debugLogger.log('socks', `Proxy->Target ${this.host}:${this.port} chooses ALPN ${serverRequestedAlpnProtocol}`);
|
debugLogger.log('client-certificates', `Proxy->Target ${this.host}:${this.port} chooses ALPN ${alpnProtocolChosenByServer}`);
|
||||||
const dummyServer = tls.createServer({
|
const dummyServer = tls.createServer({
|
||||||
key: fs.readFileSync(path.join(__dirname, '../../bin/socks-certs/key.pem')),
|
key: fs.readFileSync(path.join(__dirname, '../../bin/socks-certs/key.pem')),
|
||||||
cert: fs.readFileSync(path.join(__dirname, '../../bin/socks-certs/cert.pem')),
|
cert: fs.readFileSync(path.join(__dirname, '../../bin/socks-certs/cert.pem')),
|
||||||
ALPNProtocols: serverRequestedAlpnProtocol === 'h2' ? ['h2', 'http/1.1'] : ['http/1.1'],
|
ALPNProtocols: alpnProtocolChosenByServer === 'h2' ? ['h2', 'http/1.1'] : ['http/1.1'],
|
||||||
});
|
});
|
||||||
|
this.internal?.on('close', () => dummyServer.close());
|
||||||
dummyServer.emit('connection', this.internal);
|
dummyServer.emit('connection', this.internal);
|
||||||
dummyServer.on('secureConnection', internalTLS => {
|
dummyServer.on('secureConnection', internalTLS => {
|
||||||
debugLogger.log('socks', `Browser->Proxy ${this.host}:${this.port} chooses ALPN ${internalTLS.alpnProtocol}`);
|
debugLogger.log('client-certificates', `Browser->Proxy ${this.host}:${this.port} chooses ALPN ${internalTLS.alpnProtocol}`);
|
||||||
internalTLS.on('close', () => this.socksProxy._socksProxy.sendSocketEnd({ uid: this.uid }));
|
internalTLS.on('close', () => this.socksProxy._socksProxy.sendSocketEnd({ uid: this.uid }));
|
||||||
const tlsOptions: tls.ConnectionOptions = {
|
const tlsOptions: tls.ConnectionOptions = {
|
||||||
socket: this.target,
|
socket: this.target,
|
||||||
|
|
@ -162,7 +160,7 @@ class SocksProxyConnection {
|
||||||
|
|
||||||
internalTLS.on('error', () => closeBothSockets());
|
internalTLS.on('error', () => closeBothSockets());
|
||||||
targetTLS.on('error', error => {
|
targetTLS.on('error', error => {
|
||||||
debugLogger.log('socks', `error when connecting to target: ${error.message}`);
|
debugLogger.log('client-certificates', `error when connecting to target: ${error.message}`);
|
||||||
if (internalTLS?.alpnProtocol === 'h2') {
|
if (internalTLS?.alpnProtocol === 'h2') {
|
||||||
// https://github.com/nodejs/node/issues/46152
|
// https://github.com/nodejs/node/issues/46152
|
||||||
// TODO: http2.performServerHandshake does not work here for some reason.
|
// TODO: http2.performServerHandshake does not work here for some reason.
|
||||||
|
|
@ -179,9 +177,6 @@ class SocksProxyConnection {
|
||||||
closeBothSockets();
|
closeBothSockets();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
}, error => {
|
|
||||||
this.socksProxy._socksProxy.sendSocketError({ uid: this.uid, error: error.message });
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -191,10 +186,12 @@ export class ClientCertificatesProxy {
|
||||||
private _connections: Map<string, SocksProxyConnection> = new Map();
|
private _connections: Map<string, SocksProxyConnection> = new Map();
|
||||||
ignoreHTTPSErrors: boolean | undefined;
|
ignoreHTTPSErrors: boolean | undefined;
|
||||||
clientCertificates: channels.BrowserNewContextOptions['clientCertificates'];
|
clientCertificates: channels.BrowserNewContextOptions['clientCertificates'];
|
||||||
|
alpnCache: ALPNCache;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
contextOptions: Pick<channels.BrowserNewContextOptions, 'clientCertificates' | 'ignoreHTTPSErrors'>
|
contextOptions: Pick<channels.BrowserNewContextOptions, 'clientCertificates' | 'ignoreHTTPSErrors'>
|
||||||
) {
|
) {
|
||||||
|
this.alpnCache = new ALPNCache();
|
||||||
this.ignoreHTTPSErrors = contextOptions.ignoreHTTPSErrors;
|
this.ignoreHTTPSErrors = contextOptions.ignoreHTTPSErrors;
|
||||||
this.clientCertificates = contextOptions.clientCertificates;
|
this.clientCertificates = contextOptions.clientCertificates;
|
||||||
this._socksProxy = new SocksProxy();
|
this._socksProxy = new SocksProxy();
|
||||||
|
|
@ -263,10 +260,3 @@ export function clientCertificatesToTLSOptions(
|
||||||
function rewriteToLocalhostIfNeeded(host: string): string {
|
function rewriteToLocalhostIfNeeded(host: string): string {
|
||||||
return host === 'local.playwright' ? 'localhost' : host;
|
return host === 'local.playwright' ? 'localhost' : host;
|
||||||
}
|
}
|
||||||
|
|
||||||
// For testing.
|
|
||||||
if (require.main === module) {
|
|
||||||
const proxy = new ClientCertificatesProxy({ clientCertificates: [], ignoreHTTPSErrors: false });
|
|
||||||
// eslint-disable-next-line no-console
|
|
||||||
proxy.listen().then(url => console.log('Listening on', url));
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ const debugLoggerColorMap = {
|
||||||
'download': 34, // green
|
'download': 34, // green
|
||||||
'browser': 0, // reset
|
'browser': 0, // reset
|
||||||
'socks': 92, // purple
|
'socks': 92, // purple
|
||||||
|
'client-certificates': 92, // purple
|
||||||
'error': 160, // red,
|
'error': 160, // red,
|
||||||
'channel': 33, // blue
|
'channel': 33, // blue
|
||||||
'server': 45, // cyan
|
'server': 45, // cyan
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue