Skip to content

Commit

Permalink
chore: proxy connect refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
zdm committed Sep 2, 2024
1 parent 5c6cbd4 commit 6e71022
Showing 1 changed file with 116 additions and 82 deletions.
198 changes: 116 additions & 82 deletions lib/proxy/clients/static.js
Original file line number Diff line number Diff line change
Expand Up @@ -122,8 +122,7 @@ export default class ProxyClientStatic extends mixins( OptionsCountry, OptionsRe
return this;
}

// XXX timeout
async connect ( url, { hostnameAddress, timeout } = {} ) {
async connect ( url, { hostnameAddress, connectTimeout, checkCertificate = true } = {} ) {
if ( typeof url === "string" ) url = new URL( url );

var hostname;
Expand All @@ -147,20 +146,34 @@ export default class ProxyClientStatic extends mixins( OptionsCountry, OptionsRe

// local proxy
if ( this.isLocal ) {
return this.#connectLocal( hostname, port );
return this.#connect( {
connectTimeout,
"tls": this.isTls,
"servername": hostname,
"rejectUnauthorized": !!checkCertificate,
"host": hostname,
port,
"localAddress": this.hostname,
"family": this.localAddress.isIpV4 ? 4 : 6,
} );
}

// http: protocol
else if ( url.protocol === "http:" ) {

// http
if ( this.isHttp ) {
return this.#connectHttp( url );
return this.#connectHttp( { url, connectTimeout, checkCertificate } );
}

// socks5
else if ( this.isSocks5 ) {
return this.#connectSocks5( hostname, port );
return this.#connectSocks5( {
connectTimeout,
checkCertificate,
hostname,
port,
} );
}
}

Expand All @@ -169,12 +182,22 @@ export default class ProxyClientStatic extends mixins( OptionsCountry, OptionsRe

// https
if ( this.isHttp ) {
return this.#connectHttps( hostname, port );
return this.#connectHttps( {
connectTimeout,
checkCertificate,
hostname,
port,
} );
}

// socks5
else if ( this.isSocks5 ) {
return this.#connectSocks5( hostname, port );
return this.#connectSocks5( {
connectTimeout,
checkCertificate,
hostname,
port,
} );
}
}

Expand All @@ -183,12 +206,22 @@ export default class ProxyClientStatic extends mixins( OptionsCountry, OptionsRe

// socks5
if ( this.isSocks5 ) {
return this.#connectSocks5( hostname, port );
return this.#connectSocks5( {
connectTimeout,
checkCertificate,
hostname,
port,
} );
}

// https
else if ( this.isHttp ) {
return this.#connectHttps( hostname, port );
return this.#connectHttps( {
connectTimeout,
checkCertificate,
hostname,
port,
} );
}
}

Expand All @@ -210,43 +243,36 @@ export default class ProxyClientStatic extends mixins( OptionsCountry, OptionsRe
}

// private
async #connectHttp ( url ) {
return new Promise( ( resolve, reject ) => {
let socket;

if ( this.isTls ) {
socket = tls.connect( {
"host": this.hostname,
"port": this.port,
"servername": this.hostname,
} );

socket.once( "secureConnect", () => {
socket.removeAllListeners();

resolve( this.#patchHttpSocket( socket, url ) );
} );
}
else {
socket = net.connect( {
"host": this.hostname,
"port": this.port,
} );

socket.once( "connect", () => {
socket.removeAllListeners();

resolve( this.#patchHttpSocket( socket, url ) );
} );
}

socket.once( "end", () => reject( "Connection closed" ) );
async #connectHttp ( { url, connectTimeout, checkCertificate } ) {
return this.#connect(
{
connectTimeout,
"tls": this.isTls,
"servername": this.hostname,
"rejectUnauthorized": !!checkCertificate,
"host": this.hostname,
"port": this.port,
},
socket => this.#patchHttpSocket( socket, url )
);
}

socket.once( "error", e => reject( e ) );
} );
async #connectHttps ( { hostname, port, connectTimeout, checkCertificate } ) {
return this.#connect(
{
connectTimeout,
"tls": this.isTls,
"servername": this.hostname,
"rejectUnauthorized": !!checkCertificate,
"host": this.hostname,
"port": this.port,
},
socket => this.#createHttpsTunnel( socket, hostname, port )
);
}

async #connectHttps ( hostname, port ) {
// XXX
async #createHttpsTunnel ( socket, hostname, port ) {
var req,
basicAuth = this.basicAuth;

Expand Down Expand Up @@ -295,30 +321,21 @@ export default class ProxyClientStatic extends mixins( OptionsCountry, OptionsRe
}
}

async #connectSocks5 ( hostname, port ) {
const socket = await new Promise( ( resolve, reject ) => {
if ( this.isTls ) {
const socket = tls.connect( {
"host": this.hostname,
"port": this.port,
"servername": this.hostname,
} );

socket.once( "error", reject );

socket.once( "secureConnect", () => resolve( socket ) );
}
else {
const socket = net.connect( {
"host": this.hostname,
"port": this.port,
} );
async #connectSocks5 ( { hostname, port, connectTimeout, checkCertificate } ) {
return this.#connect(
{
connectTimeout,
"tls": this.isTls,
"servername": this.hostname,
"rejectUnauthorized": !!checkCertificate,
"host": this.hostname,
"port": this.port,
},
socket => this.#createSocks5Tunnel( socket, hostname, port )
);
}

socket.once( "error", reject );

socket.once( "connect", () => resolve( socket ) );
}
} );
async #createSocks5Tunnel ( socket, hostname, port ) {

// authenticate
if ( this.username !== "" ) {
Expand Down Expand Up @@ -465,27 +482,44 @@ export default class ProxyClientStatic extends mixins( OptionsCountry, OptionsRe
return socket;
}

async #connectLocal ( hostname, port ) {
return new Promise( ( resolve, reject ) => {
const socket = new net.Socket();
// XXX handle on end, close
async #connect ( { connectTimeout, "tls": useTls, ...options } = {}, onConnect ) {
var socket, error, timeout;

socket.once( "end", () => reject( "Connection closed" ) );

socket.once( "error", e => reject( e ) );
if ( connectTimeout ) {
timeout = setTimeout( () => socket.destroy( "Connection timeout" ), connectTimeout );
}

socket.once( "connect", () => {
socket.removeAllListeners();
await new Promise( ( resolve, reject ) => {
var callback = () => {
if ( onConnect ) {
onConnect( socket )
.then( resolve )
.catch( e => socket.destroy( e ) );
}
else {
resolve();
}
};

resolve( socket );
} );
if ( useTls ) {
socket = tls.connect( options, callback );
}
else {
socket = net.connect( options, callback );
}

socket.connect( {
hostname,
port,
"localAddress": this.hostname,
"family": this.localAddress.isIpV4 ? 4 : 6,
} );
socket.once( "error", reject );
} ).catch( e => {
error = e;
} );

clearTimeout( timeout );
socket.removeAllListeners();

if ( error ) throw error;

return socket;
}

#patchHttpSocket ( socket, url ) {
Expand Down

0 comments on commit 6e71022

Please sign in to comment.