diff --git a/README.md b/README.md index 8faf69be..92dc093f 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,7 @@ const server = new ProxyChain.Server({ // Custom user-defined function to authenticate incoming proxy requests, // and optionally provide the URL to chained upstream proxy. // The function must return an object (or promise resolving to the object) with the following signature: - // { requestAuthentication: Boolean, upstreamProxyUrl: String } + // { requestAuthentication: boolean, upstreamProxyUrl: string, failMsg?: string, customTag?: unknown } // If the function is not defined or is null, the server runs in simple mode. // Note that the function takes a single argument with the following properties: // * request - An instance of http.IncomingMessage class with information about the client request @@ -72,6 +72,12 @@ const server = new ProxyChain.Server({ // If "requestAuthentication" is true, you can use the following property // to define a custom error message to return to the client instead of the default "Proxy credentials required" failMsg: 'Bad username or password, please try again.', + + // Optional custom tag that will be passed back via + // `tunnelConnectResponded` or `tunnelConnectFailed` events + // Can be used to pass information between proxy-chain + // and any external code or application using it + customTag: { userId: '123' }, }; }, }); @@ -326,7 +332,7 @@ the parameter types of the event callback are described in [Node.js's documentat [1]: https://nodejs.org/api/http.html#http_event_connect ```javascript -server.on('tunnelConnectResponded', ({ proxyChainId, response, socket, head }) => { +server.on('tunnelConnectResponded', ({ proxyChainId, response, socket, head, customTag }) => { console.log(`CONNECT response headers received: ${response.headers}`); }); ``` @@ -339,6 +345,14 @@ listenConnectAnonymizedProxy(anonymizedProxyUrl, ({ response, socket, head }) => }); ``` +You can also listen to CONNECT requests that receive response with status code different from 200. +The proxy server would emit a `tunnelConnectFailed` event. + +```javascript +server.on('tunnelConnectFailed', ({ proxyChainId, response, socket, head, customTag }) => { + console.log(`CONNECT response failed with status code: ${response.statusCode}`); +}); +``` ## Helper functions diff --git a/package.json b/package.json index abbb0a60..7417a5f6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "proxy-chain", - "version": "2.2.1", + "version": "2.3.0", "description": "Node.js implementation of a proxy server (think Squid) with support for SSL, authentication, upstream proxy chaining, and protocol tunneling.", "main": "dist/index.js", "keywords": [ diff --git a/src/chain.ts b/src/chain.ts index 9f70cbb6..f9a07545 100644 --- a/src/chain.ts +++ b/src/chain.ts @@ -33,6 +33,7 @@ export interface HandlerOpts { localAddress?: string; ipFamily?: number; dnsLookup?: typeof dns['lookup']; + customTag?: unknown; } interface ChainOpts { @@ -72,7 +73,7 @@ export const chain = ( const { proxyChainId } = sourceSocket; - const { upstreamProxyUrlParsed: proxy } = handlerOpts; + const { upstreamProxyUrlParsed: proxy, customTag } = handlerOpts; const options: Options = { method: 'CONNECT', @@ -127,6 +128,14 @@ export const chain = ( sourceSocket.end(createHttpResponse(status, `UPSTREAM${response.statusCode}`)); } + server.emit('tunnelConnectFailed', { + proxyChainId, + response, + customTag, + socket: targetSocket, + head: clientHead, + }); + return; } @@ -138,6 +147,7 @@ export const chain = ( server.emit('tunnelConnectResponded', { proxyChainId, response, + customTag, socket: targetSocket, head: clientHead, }); diff --git a/src/server.ts b/src/server.ts index b0387651..b4fe8f52 100644 --- a/src/server.ts +++ b/src/server.ts @@ -52,6 +52,7 @@ type HandlerOpts = { localAddress?: string; ipFamily?: number; dnsLookup?: typeof dns['lookup']; + customTag?: unknown; }; export type PrepareRequestFunctionOpts = { @@ -73,6 +74,7 @@ export type PrepareRequestFunctionResult = { localAddress?: string; ipFamily?: number; dnsLookup?: typeof dns['lookup']; + customTag?: unknown; }; type Promisable = T | Promise; @@ -419,6 +421,7 @@ export class Server extends EventEmitter { handlerOpts.ipFamily = funcResult.ipFamily; handlerOpts.dnsLookup = funcResult.dnsLookup; handlerOpts.customConnectServer = funcResult.customConnectServer; + handlerOpts.customTag = funcResult.customTag; // If not authenticated, request client to authenticate if (funcResult.requestAuthentication) {