From 99d0daf608cb4d425d566226ce5e648aaba02f94 Mon Sep 17 00:00:00 2001 From: Sagrera Date: Tue, 22 Jul 2025 00:10:49 +0900 Subject: [PATCH 1/2] feat: added statistics --- package.json | 4 +-- src/anonymize_proxy.ts | 57 +++++++++++++++++++++++++++++------------- src/server.ts | 16 ++++++++++-- 3 files changed, 56 insertions(+), 21 deletions(-) diff --git a/package.json b/package.json index 53963e2d..5b5590e7 100644 --- a/package.json +++ b/package.json @@ -45,7 +45,7 @@ }, "devDependencies": { "@apify/eslint-config": "^1.0.0", - "@apify/tsconfig": "^0.1.0", + "@apify/tsconfig": "^0.1.1", "@types/jest": "^28.1.2", "@types/node": "^18.8.3", "basic-auth": "^2.0.1", @@ -70,7 +70,7 @@ "socksv5": "^0.0.6", "through": "^2.3.8", "ts-node": "^10.2.1", - "typescript": "^4.4.3", + "typescript": "^4.9.5", "typescript-eslint": "^8.20.0", "underscore": "^1.13.1", "ws": "^8.2.2" diff --git a/src/anonymize_proxy.ts b/src/anonymize_proxy.ts index 9b7cbd8e..75bcf254 100644 --- a/src/anonymize_proxy.ts +++ b/src/anonymize_proxy.ts @@ -1,10 +1,10 @@ -import type { Buffer } from 'node:buffer'; -import type http from 'node:http'; -import type net from 'node:net'; -import { URL } from 'node:url'; +import type { Buffer } from "node:buffer"; +import type http from "node:http"; +import type net from "node:net"; +import { URL } from "node:url"; -import { Server, SOCKS_PROTOCOLS } from './server'; -import { nodeify } from './utils/nodeify'; +import { Server, SOCKS_PROTOCOLS } from "./server"; +import { nodeify } from "./utils/nodeify"; // Dictionary, key is value returned from anonymizeProxy(), value is Server instance. const anonymizedProxyUrlToServer: Record = {}; @@ -22,13 +22,13 @@ export interface AnonymizeProxyOptions { */ export const anonymizeProxy = async ( options: string | AnonymizeProxyOptions, - callback?: (error: Error | null) => void, + callback?: (error: Error | null) => void ): Promise => { let proxyUrl: string; let port = 0; let ignoreProxyCertificate = false; - if (typeof options === 'string') { + if (typeof options === "string") { proxyUrl = options; } else { proxyUrl = options.url; @@ -36,7 +36,7 @@ export const anonymizeProxy = async ( if (port < 0 || port > 65535) { throw new Error( - 'Invalid "port" option: only values equals or between 0-65535 are valid', + 'Invalid "port" option: only values equals or between 0-65535 are valid' ); } @@ -46,12 +46,24 @@ export const anonymizeProxy = async ( } const parsedProxyUrl = new URL(proxyUrl); - if (!['http:', 'https:', ...SOCKS_PROTOCOLS].includes(parsedProxyUrl.protocol)) { - throw new Error(`Invalid "proxyUrl" provided: URL must have one of the following protocols: "http", "https", ${SOCKS_PROTOCOLS.map((p) => `"${p.replace(':', '')}"`).join(', ')} (was "${parsedProxyUrl}")`); + if ( + !["http:", "https:", ...SOCKS_PROTOCOLS].includes( + parsedProxyUrl.protocol + ) + ) { + throw new Error( + `Invalid "proxyUrl" provided: URL must have one of the following protocols: "http", "https", ${SOCKS_PROTOCOLS.map( + (p) => `"${p.replace(":", "")}"` + ).join(", ")} (was "${parsedProxyUrl}")` + ); } // If upstream proxy requires no password or if there is no need to ignore HTTPS proxy cert errors, return it directly - if (!parsedProxyUrl.username && !parsedProxyUrl.password && (!ignoreProxyCertificate || parsedProxyUrl.protocol !== 'https:')) { + if ( + !parsedProxyUrl.username && + !parsedProxyUrl.password && + (!ignoreProxyCertificate || parsedProxyUrl.protocol !== "https:") + ) { return nodeify(Promise.resolve(proxyUrl), callback); } @@ -62,7 +74,7 @@ export const anonymizeProxy = async ( server = new Server({ // verbose: true, port, - host: '127.0.0.1', + host: "127.0.0.1", prepareRequestFunction: () => { return { requestAuthentication: false, @@ -94,9 +106,9 @@ export const anonymizeProxy = async ( export const closeAnonymizedProxy = async ( anonymizedProxyUrl: string, closeConnections: boolean, - callback?: (error: Error | null, result?: boolean) => void, + callback?: (error: Error | null, result?: boolean) => void ): Promise => { - if (typeof anonymizedProxyUrl !== 'string') { + if (typeof anonymizedProxyUrl !== "string") { throw new Error('The "anonymizedProxyUrl" parameter must be a string'); } @@ -131,14 +143,25 @@ type Callback = ({ */ export const listenConnectAnonymizedProxy = ( anonymizedProxyUrl: string, - tunnelConnectRespondedCallback: Callback, + tunnelConnectRespondedCallback: Callback ): boolean => { const server = anonymizedProxyUrlToServer[anonymizedProxyUrl]; if (!server) { return false; } - server.on('tunnelConnectResponded', ({ response, socket, head }) => { + server.on("tunnelConnectResponded", ({ response, socket, head }) => { tunnelConnectRespondedCallback({ response, socket, head }); }); return true; }; + +export const statisticsAnonymizedProxy = ( + anonymizedProxyUrl: string +): number => { + const server = anonymizedProxyUrlToServer[anonymizedProxyUrl]; + if (!server) { + return 0; + } + + return server.stats.trafficUsedInBytes; +}; diff --git a/src/server.ts b/src/server.ts index 41fba80d..0fe44075 100644 --- a/src/server.ts +++ b/src/server.ts @@ -111,7 +111,7 @@ export class Server extends EventEmitter { lastHandlerId: number; - stats: { httpRequestCount: number; connectRequestCount: number; }; + stats: { httpRequestCount: number; connectRequestCount: number; trafficUsedInBytes : number; }; connections: Map; @@ -179,6 +179,7 @@ export class Server extends EventEmitter { this.stats = { httpRequestCount: 0, connectRequestCount: 0, + trafficUsedInBytes: 0 }; this.connections = new Map(); @@ -214,11 +215,17 @@ export class Server extends EventEmitter { this.connections.set(unique, socket); socket.on('close', () => { + const socketStats = this.getConnectionStats(unique); this.emit('connectionClosed', { connectionId: unique, - stats: this.getConnectionStats(unique), + stats: socketStats, }); + + if (socketStats) { + this.stats.trafficUsedInBytes += socketStats.srcRxBytes + socketStats.srcTxBytes + (socketStats.trgRxBytes || 0) + (socketStats.trgTxBytes || 0) + } + this.connections.delete(unique); }); // We have to manually destroy the socket if it timeouts. @@ -228,6 +235,11 @@ export class Server extends EventEmitter { }); } + /** + * Registering total stats each server + */ + + /** * Handles incoming sockets, useful for error handling */ From 1590131ed0fee0ee01253567bebc38315e81d0bd Mon Sep 17 00:00:00 2001 From: Sagrera Date: Tue, 22 Jul 2025 00:12:34 +0900 Subject: [PATCH 2/2] build: include compiled output --- dist/anonymize_proxy.d.ts | 40 ++ dist/anonymize_proxy.d.ts.map | 1 + dist/anonymize_proxy.js | 113 ++++ dist/anonymize_proxy.js.map | 1 + dist/chain.d.ts | 38 ++ dist/chain.d.ts.map | 1 + dist/chain.js | 146 +++++ dist/chain.js.map | 1 + dist/chain_socks.d.ts | 30 + dist/chain_socks.d.ts.map | 1 + dist/chain_socks.js | 91 +++ dist/chain_socks.js.map | 1 + dist/custom_connect.d.ts | 4 + dist/custom_connect.d.ts.map | 1 + dist/custom_connect.js | 25 + dist/custom_connect.js.map | 1 + dist/custom_response.d.ts | 15 + dist/custom_response.d.ts.map | 1 + dist/custom_response.js | 22 + dist/custom_response.js.map | 1 + dist/direct.d.ts | 32 + dist/direct.d.ts.map | 1 + dist/direct.js | 73 +++ dist/direct.js.map | 1 + dist/forward.d.ts | 31 + dist/forward.d.ts.map | 1 + dist/forward.js | 106 ++++ dist/forward.js.map | 1 + dist/forward_socks.d.ts | 15 + dist/forward_socks.d.ts.map | 1 + dist/forward_socks.js | 70 +++ dist/forward_socks.js.map | 1 + dist/index.d.ts | 7 + dist/index.d.ts.map | 1 + dist/index.js | 9 + dist/index.js.map | 1 + dist/request_error.d.ts | 14 + dist/request_error.d.ts.map | 1 + dist/request_error.js | 32 + dist/request_error.js.map | 1 + dist/server.d.ts | 212 +++++++ dist/server.d.ts.map | 1 + dist/server.js | 579 ++++++++++++++++++ dist/server.js.map | 1 + dist/socket.d.ts | 11 + dist/socket.d.ts.map | 1 + dist/socket.js | 3 + dist/socket.js.map | 1 + dist/statuses.d.ts | 46 ++ dist/statuses.d.ts.map | 1 + dist/statuses.js | 81 +++ dist/statuses.js.map | 1 + dist/tcp_tunnel_tools.d.ts | 6 + dist/tcp_tunnel_tools.d.ts.map | 1 + dist/tcp_tunnel_tools.js | 104 ++++ dist/tcp_tunnel_tools.js.map | 1 + dist/tsconfig.tsbuildinfo | 1 + dist/utils/count_target_bytes.d.ts | 18 + dist/utils/count_target_bytes.d.ts.map | 1 + dist/utils/count_target_bytes.js | 53 ++ dist/utils/count_target_bytes.js.map | 1 + dist/utils/decode_uri_component_safe.d.ts | 2 + dist/utils/decode_uri_component_safe.d.ts.map | 1 + dist/utils/decode_uri_component_safe.js | 13 + dist/utils/decode_uri_component_safe.js.map | 1 + dist/utils/get_basic.d.ts | 3 + dist/utils/get_basic.d.ts.map | 1 + dist/utils/get_basic.js | 15 + dist/utils/get_basic.js.map | 1 + dist/utils/is_hop_by_hop_header.d.ts | 2 + dist/utils/is_hop_by_hop_header.d.ts.map | 1 + dist/utils/is_hop_by_hop_header.js | 17 + dist/utils/is_hop_by_hop_header.js.map | 1 + dist/utils/nodeify.d.ts | 2 + dist/utils/nodeify.d.ts.map | 1 + dist/utils/nodeify.js | 17 + dist/utils/nodeify.js.map | 1 + dist/utils/normalize_url_port.d.ts | 3 + dist/utils/normalize_url_port.d.ts.map | 1 + dist/utils/normalize_url_port.js | 22 + dist/utils/normalize_url_port.js.map | 1 + dist/utils/parse_authorization_header.d.ts | 9 + .../utils/parse_authorization_header.d.ts.map | 1 + dist/utils/parse_authorization_header.js | 53 ++ dist/utils/parse_authorization_header.js.map | 1 + dist/utils/redact_url.d.ts | 3 + dist/utils/redact_url.d.ts.map | 1 + dist/utils/redact_url.js | 15 + dist/utils/redact_url.js.map | 1 + dist/utils/valid_headers_only.d.ts | 5 + dist/utils/valid_headers_only.d.ts.map | 1 + dist/utils/valid_headers_only.js | 36 ++ dist/utils/valid_headers_only.js.map | 1 + 93 files changed, 2290 insertions(+) create mode 100644 dist/anonymize_proxy.d.ts create mode 100644 dist/anonymize_proxy.d.ts.map create mode 100644 dist/anonymize_proxy.js create mode 100644 dist/anonymize_proxy.js.map create mode 100644 dist/chain.d.ts create mode 100644 dist/chain.d.ts.map create mode 100644 dist/chain.js create mode 100644 dist/chain.js.map create mode 100644 dist/chain_socks.d.ts create mode 100644 dist/chain_socks.d.ts.map create mode 100644 dist/chain_socks.js create mode 100644 dist/chain_socks.js.map create mode 100644 dist/custom_connect.d.ts create mode 100644 dist/custom_connect.d.ts.map create mode 100644 dist/custom_connect.js create mode 100644 dist/custom_connect.js.map create mode 100644 dist/custom_response.d.ts create mode 100644 dist/custom_response.d.ts.map create mode 100644 dist/custom_response.js create mode 100644 dist/custom_response.js.map create mode 100644 dist/direct.d.ts create mode 100644 dist/direct.d.ts.map create mode 100644 dist/direct.js create mode 100644 dist/direct.js.map create mode 100644 dist/forward.d.ts create mode 100644 dist/forward.d.ts.map create mode 100644 dist/forward.js create mode 100644 dist/forward.js.map create mode 100644 dist/forward_socks.d.ts create mode 100644 dist/forward_socks.d.ts.map create mode 100644 dist/forward_socks.js create mode 100644 dist/forward_socks.js.map create mode 100644 dist/index.d.ts create mode 100644 dist/index.d.ts.map create mode 100644 dist/index.js create mode 100644 dist/index.js.map create mode 100644 dist/request_error.d.ts create mode 100644 dist/request_error.d.ts.map create mode 100644 dist/request_error.js create mode 100644 dist/request_error.js.map create mode 100644 dist/server.d.ts create mode 100644 dist/server.d.ts.map create mode 100644 dist/server.js create mode 100644 dist/server.js.map create mode 100644 dist/socket.d.ts create mode 100644 dist/socket.d.ts.map create mode 100644 dist/socket.js create mode 100644 dist/socket.js.map create mode 100644 dist/statuses.d.ts create mode 100644 dist/statuses.d.ts.map create mode 100644 dist/statuses.js create mode 100644 dist/statuses.js.map create mode 100644 dist/tcp_tunnel_tools.d.ts create mode 100644 dist/tcp_tunnel_tools.d.ts.map create mode 100644 dist/tcp_tunnel_tools.js create mode 100644 dist/tcp_tunnel_tools.js.map create mode 100644 dist/tsconfig.tsbuildinfo create mode 100644 dist/utils/count_target_bytes.d.ts create mode 100644 dist/utils/count_target_bytes.d.ts.map create mode 100644 dist/utils/count_target_bytes.js create mode 100644 dist/utils/count_target_bytes.js.map create mode 100644 dist/utils/decode_uri_component_safe.d.ts create mode 100644 dist/utils/decode_uri_component_safe.d.ts.map create mode 100644 dist/utils/decode_uri_component_safe.js create mode 100644 dist/utils/decode_uri_component_safe.js.map create mode 100644 dist/utils/get_basic.d.ts create mode 100644 dist/utils/get_basic.d.ts.map create mode 100644 dist/utils/get_basic.js create mode 100644 dist/utils/get_basic.js.map create mode 100644 dist/utils/is_hop_by_hop_header.d.ts create mode 100644 dist/utils/is_hop_by_hop_header.d.ts.map create mode 100644 dist/utils/is_hop_by_hop_header.js create mode 100644 dist/utils/is_hop_by_hop_header.js.map create mode 100644 dist/utils/nodeify.d.ts create mode 100644 dist/utils/nodeify.d.ts.map create mode 100644 dist/utils/nodeify.js create mode 100644 dist/utils/nodeify.js.map create mode 100644 dist/utils/normalize_url_port.d.ts create mode 100644 dist/utils/normalize_url_port.d.ts.map create mode 100644 dist/utils/normalize_url_port.js create mode 100644 dist/utils/normalize_url_port.js.map create mode 100644 dist/utils/parse_authorization_header.d.ts create mode 100644 dist/utils/parse_authorization_header.d.ts.map create mode 100644 dist/utils/parse_authorization_header.js create mode 100644 dist/utils/parse_authorization_header.js.map create mode 100644 dist/utils/redact_url.d.ts create mode 100644 dist/utils/redact_url.d.ts.map create mode 100644 dist/utils/redact_url.js create mode 100644 dist/utils/redact_url.js.map create mode 100644 dist/utils/valid_headers_only.d.ts create mode 100644 dist/utils/valid_headers_only.d.ts.map create mode 100644 dist/utils/valid_headers_only.js create mode 100644 dist/utils/valid_headers_only.js.map diff --git a/dist/anonymize_proxy.d.ts b/dist/anonymize_proxy.d.ts new file mode 100644 index 00000000..32af82d0 --- /dev/null +++ b/dist/anonymize_proxy.d.ts @@ -0,0 +1,40 @@ +/// +/// +/// +/// +import type { Buffer } from "node:buffer"; +import type http from "node:http"; +import type net from "node:net"; +export interface AnonymizeProxyOptions { + url: string; + port: number; + ignoreProxyCertificate?: boolean; +} +/** + * Parses and validates a HTTP proxy URL. If the proxy requires authentication, + * or if it is an HTTPS proxy and `ignoreProxyCertificate` is `true`, then the function + * starts an open local proxy server that forwards to the upstream proxy. + */ +export declare const anonymizeProxy: (options: string | AnonymizeProxyOptions, callback?: ((error: Error | null) => void) | undefined) => Promise; +/** + * Closes anonymous proxy previously started by `anonymizeProxy()`. + * If proxy was not found or was already closed, the function has no effect + * and its result if `false`. Otherwise the result is `true`. + * @param closeConnections If true, pending proxy connections are forcibly closed. + */ +export declare const closeAnonymizedProxy: (anonymizedProxyUrl: string, closeConnections: boolean, callback?: ((error: Error | null, result?: boolean) => void) | undefined) => Promise; +type Callback = ({ response, socket, head, }: { + response: http.IncomingMessage; + socket: net.Socket; + head: Buffer; +}) => void; +/** + * Add a callback on 'tunnelConnectResponded' Event in order to get headers from CONNECT tunnel to proxy + * Useful for some proxies that are using headers to send information like ProxyMesh + * @returns `true` if the callback is successfully configured, otherwise `false` (e.g. when an + * invalid proxy URL is given). + */ +export declare const listenConnectAnonymizedProxy: (anonymizedProxyUrl: string, tunnelConnectRespondedCallback: Callback) => boolean; +export declare const statisticsAnonymizedProxy: (anonymizedProxyUrl: string) => number; +export {}; +//# sourceMappingURL=anonymize_proxy.d.ts.map \ No newline at end of file diff --git a/dist/anonymize_proxy.d.ts.map b/dist/anonymize_proxy.d.ts.map new file mode 100644 index 00000000..63924a5d --- /dev/null +++ b/dist/anonymize_proxy.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"anonymize_proxy.d.ts","sourceRoot":"","sources":["../src/anonymize_proxy.ts"],"names":[],"mappings":";;;;AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,KAAK,GAAG,MAAM,UAAU,CAAC;AAShC,MAAM,WAAW,qBAAqB;IAClC,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,sBAAsB,CAAC,EAAE,OAAO,CAAC;CACpC;AAED;;;;GAIG;AACH,eAAO,MAAM,cAAc,YACd,MAAM,GAAG,qBAAqB,sBACpB,KAAK,GAAG,IAAI,KAAK,IAAI,kBACzC,QAAQ,MAAM,CAwEhB,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,oBAAoB,uBACT,MAAM,oBACR,OAAO,sBACN,KAAK,GAAG,IAAI,WAAW,OAAO,KAAK,IAAI,kBAC3D,QAAQ,OAAO,CAgBjB,CAAC;AAEF,KAAK,QAAQ,GAAG,CAAC,EACb,QAAQ,EACR,MAAM,EACN,IAAI,GACP,EAAE;IACC,QAAQ,EAAE,IAAI,CAAC,eAAe,CAAC;IAC/B,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;CAChB,KAAK,IAAI,CAAC;AAEX;;;;;GAKG;AACH,eAAO,MAAM,4BAA4B,uBACjB,MAAM,kCACM,QAAQ,KACzC,OASF,CAAC;AAEF,eAAO,MAAM,yBAAyB,uBACd,MAAM,KAC3B,MAOF,CAAC"} \ No newline at end of file diff --git a/dist/anonymize_proxy.js b/dist/anonymize_proxy.js new file mode 100644 index 00000000..01e4f0d2 --- /dev/null +++ b/dist/anonymize_proxy.js @@ -0,0 +1,113 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.statisticsAnonymizedProxy = exports.listenConnectAnonymizedProxy = exports.closeAnonymizedProxy = exports.anonymizeProxy = void 0; +const node_url_1 = require("node:url"); +const server_1 = require("./server"); +const nodeify_1 = require("./utils/nodeify"); +// Dictionary, key is value returned from anonymizeProxy(), value is Server instance. +const anonymizedProxyUrlToServer = {}; +/** + * Parses and validates a HTTP proxy URL. If the proxy requires authentication, + * or if it is an HTTPS proxy and `ignoreProxyCertificate` is `true`, then the function + * starts an open local proxy server that forwards to the upstream proxy. + */ +const anonymizeProxy = async (options, callback) => { + let proxyUrl; + let port = 0; + let ignoreProxyCertificate = false; + if (typeof options === "string") { + proxyUrl = options; + } + else { + proxyUrl = options.url; + port = options.port; + if (port < 0 || port > 65535) { + throw new Error('Invalid "port" option: only values equals or between 0-65535 are valid'); + } + if (options.ignoreProxyCertificate !== undefined) { + ignoreProxyCertificate = options.ignoreProxyCertificate; + } + } + const parsedProxyUrl = new node_url_1.URL(proxyUrl); + if (!["http:", "https:", ...server_1.SOCKS_PROTOCOLS].includes(parsedProxyUrl.protocol)) { + throw new Error(`Invalid "proxyUrl" provided: URL must have one of the following protocols: "http", "https", ${server_1.SOCKS_PROTOCOLS.map((p) => `"${p.replace(":", "")}"`).join(", ")} (was "${parsedProxyUrl}")`); + } + // If upstream proxy requires no password or if there is no need to ignore HTTPS proxy cert errors, return it directly + if (!parsedProxyUrl.username && + !parsedProxyUrl.password && + (!ignoreProxyCertificate || parsedProxyUrl.protocol !== "https:")) { + return (0, nodeify_1.nodeify)(Promise.resolve(proxyUrl), callback); + } + let server; + const startServer = async () => { + return Promise.resolve().then(async () => { + server = new server_1.Server({ + // verbose: true, + port, + host: "127.0.0.1", + prepareRequestFunction: () => { + return { + requestAuthentication: false, + upstreamProxyUrl: proxyUrl, + ignoreUpstreamProxyCertificate: ignoreProxyCertificate, + }; + }, + }); + return server.listen(); + }); + }; + const promise = startServer().then(() => { + const url = `http://127.0.0.1:${server.port}`; + anonymizedProxyUrlToServer[url] = server; + return url; + }); + return (0, nodeify_1.nodeify)(promise, callback); +}; +exports.anonymizeProxy = anonymizeProxy; +/** + * Closes anonymous proxy previously started by `anonymizeProxy()`. + * If proxy was not found or was already closed, the function has no effect + * and its result if `false`. Otherwise the result is `true`. + * @param closeConnections If true, pending proxy connections are forcibly closed. + */ +const closeAnonymizedProxy = async (anonymizedProxyUrl, closeConnections, callback) => { + if (typeof anonymizedProxyUrl !== "string") { + throw new Error('The "anonymizedProxyUrl" parameter must be a string'); + } + const server = anonymizedProxyUrlToServer[anonymizedProxyUrl]; + if (!server) { + return (0, nodeify_1.nodeify)(Promise.resolve(false), callback); + } + delete anonymizedProxyUrlToServer[anonymizedProxyUrl]; + const promise = server.close(closeConnections).then(() => { + return true; + }); + return (0, nodeify_1.nodeify)(promise, callback); +}; +exports.closeAnonymizedProxy = closeAnonymizedProxy; +/** + * Add a callback on 'tunnelConnectResponded' Event in order to get headers from CONNECT tunnel to proxy + * Useful for some proxies that are using headers to send information like ProxyMesh + * @returns `true` if the callback is successfully configured, otherwise `false` (e.g. when an + * invalid proxy URL is given). + */ +const listenConnectAnonymizedProxy = (anonymizedProxyUrl, tunnelConnectRespondedCallback) => { + const server = anonymizedProxyUrlToServer[anonymizedProxyUrl]; + if (!server) { + return false; + } + server.on("tunnelConnectResponded", ({ response, socket, head }) => { + tunnelConnectRespondedCallback({ response, socket, head }); + }); + return true; +}; +exports.listenConnectAnonymizedProxy = listenConnectAnonymizedProxy; +const statisticsAnonymizedProxy = (anonymizedProxyUrl) => { + const server = anonymizedProxyUrlToServer[anonymizedProxyUrl]; + if (!server) { + return 0; + } + return server.stats.trafficUsedInBytes; +}; +exports.statisticsAnonymizedProxy = statisticsAnonymizedProxy; +//# sourceMappingURL=anonymize_proxy.js.map \ No newline at end of file diff --git a/dist/anonymize_proxy.js.map b/dist/anonymize_proxy.js.map new file mode 100644 index 00000000..757a516c --- /dev/null +++ b/dist/anonymize_proxy.js.map @@ -0,0 +1 @@ +{"version":3,"file":"anonymize_proxy.js","sourceRoot":"","sources":["../src/anonymize_proxy.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/chain.d.ts b/dist/chain.d.ts new file mode 100644 index 00000000..8dbeda7b --- /dev/null +++ b/dist/chain.d.ts @@ -0,0 +1,38 @@ +/// +/// +/// +/// +/// +import type { Buffer } from 'node:buffer'; +import type dns from 'node:dns'; +import type { EventEmitter } from 'node:events'; +import type { URL } from 'node:url'; +import type { Socket } from './socket'; +export interface HandlerOpts { + upstreamProxyUrlParsed: URL; + ignoreUpstreamProxyCertificate: boolean; + localAddress?: string; + ipFamily?: number; + dnsLookup?: typeof dns['lookup']; + customTag?: unknown; +} +interface ChainOpts { + request: { + url?: string; + }; + sourceSocket: Socket; + head?: Buffer; + handlerOpts: HandlerOpts; + server: EventEmitter & { + log: (connectionId: unknown, str: string) => void; + }; + isPlain: boolean; +} +/** + * Passes the traffic to upstream HTTP proxy server. + * Client -> Apify -> Upstream -> Web + * Client <- Apify <- Upstream <- Web + */ +export declare const chain: ({ request, sourceSocket, head, handlerOpts, server, isPlain, }: ChainOpts) => void; +export {}; +//# sourceMappingURL=chain.d.ts.map \ No newline at end of file diff --git a/dist/chain.d.ts.map b/dist/chain.d.ts.map new file mode 100644 index 00000000..7e2a1c26 --- /dev/null +++ b/dist/chain.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"chain.d.ts","sourceRoot":"","sources":["../src/chain.ts"],"names":[],"mappings":";;;;;AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,KAAK,GAAG,MAAM,UAAU,CAAC;AAChC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAGhD,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAEpC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAevC,MAAM,WAAW,WAAW;IACxB,sBAAsB,EAAE,GAAG,CAAC;IAC5B,8BAA8B,EAAE,OAAO,CAAC;IACxC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,OAAO,GAAG,CAAC,QAAQ,CAAC,CAAC;IACjC,SAAS,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,UAAU,SAAS;IACf,OAAO,EAAE;QAAE,GAAG,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC1B,YAAY,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,WAAW,CAAC;IACzB,MAAM,EAAE,YAAY,GAAG;QAAE,GAAG,EAAE,CAAC,YAAY,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,IAAI,CAAA;KAAE,CAAC;IAC7E,OAAO,EAAE,OAAO,CAAC;CACpB;AAED;;;;GAIG;AACH,eAAO,MAAM,KAAK,mEAQX,SAAS,KACb,IA4JF,CAAC"} \ No newline at end of file diff --git a/dist/chain.js b/dist/chain.js new file mode 100644 index 00000000..fdb4c231 --- /dev/null +++ b/dist/chain.js @@ -0,0 +1,146 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.chain = void 0; +const tslib_1 = require("tslib"); +const node_http_1 = tslib_1.__importDefault(require("node:http")); +const node_https_1 = tslib_1.__importDefault(require("node:https")); +const statuses_1 = require("./statuses"); +const count_target_bytes_1 = require("./utils/count_target_bytes"); +const get_basic_1 = require("./utils/get_basic"); +/** + * Passes the traffic to upstream HTTP proxy server. + * Client -> Apify -> Upstream -> Web + * Client <- Apify <- Upstream <- Web + */ +const chain = ({ request, sourceSocket, head, handlerOpts, server, isPlain, }) => { + if (head && head.length > 0) { + // HTTP/1.1 has no defined semantics when sending payload along with CONNECT and servers can reject the request. + // HTTP/2 only says that subsequent DATA frames must be transferred after HEADERS has been sent. + // HTTP/3 says that all DATA frames should be transferred (implies pre-HEADERS data). + // + // Let's go with the HTTP/3 behavior. + // There are also clients that send payload along with CONNECT to save milliseconds apparently. + // Beware of upstream proxy servers that send out valid CONNECT responses with diagnostic data such as IPs! + sourceSocket.unshift(head); + } + const { proxyChainId } = sourceSocket; + const { upstreamProxyUrlParsed: proxy, customTag } = handlerOpts; + const options = { + method: 'CONNECT', + path: request.url, + headers: { + host: request.url, + }, + localAddress: handlerOpts.localAddress, + family: handlerOpts.ipFamily, + lookup: handlerOpts.dnsLookup, + }; + if (proxy.username || proxy.password) { + options.headers['proxy-authorization'] = (0, get_basic_1.getBasicAuthorizationHeader)(proxy); + } + const client = proxy.protocol === 'https:' + ? node_https_1.default.request(proxy.origin, { + ...options, + rejectUnauthorized: !handlerOpts.ignoreUpstreamProxyCertificate, + }) + : node_http_1.default.request(proxy.origin, options); + client.once('socket', (targetSocket) => { + // Socket can be re-used by multiple requests. + // That's why we need to track the previous stats. + targetSocket.previousBytesRead = targetSocket.bytesRead; + targetSocket.previousBytesWritten = targetSocket.bytesWritten; + (0, count_target_bytes_1.countTargetBytes)(sourceSocket, targetSocket); + }); + client.on('connect', (response, targetSocket, clientHead) => { + if (sourceSocket.readyState !== 'open') { + // Sanity check, should never reach. + targetSocket.destroy(); + return; + } + targetSocket.on('error', (error) => { + server.log(proxyChainId, `Chain Destination Socket Error: ${error.stack}`); + sourceSocket.destroy(); + }); + sourceSocket.on('error', (error) => { + server.log(proxyChainId, `Chain Source Socket Error: ${error.stack}`); + targetSocket.destroy(); + }); + if (response.statusCode !== 200) { + server.log(proxyChainId, `Failed to authenticate upstream proxy: ${response.statusCode}`); + if (isPlain) { + sourceSocket.end(); + } + else { + const { statusCode } = response; + const status = statusCode === 401 || statusCode === 407 + ? statuses_1.badGatewayStatusCodes.AUTH_FAILED + : statuses_1.badGatewayStatusCodes.NON_200; + sourceSocket.end((0, statuses_1.createCustomStatusHttpResponse)(status, `UPSTREAM${statusCode}`)); + } + targetSocket.end(); + server.emit('tunnelConnectFailed', { + proxyChainId, + response, + customTag, + socket: targetSocket, + head: clientHead, + }); + return; + } + if (clientHead.length > 0) { + // See comment above + targetSocket.unshift(clientHead); + } + server.emit('tunnelConnectResponded', { + proxyChainId, + response, + customTag, + socket: targetSocket, + head: clientHead, + }); + sourceSocket.write(isPlain ? '' : `HTTP/1.1 200 Connection Established\r\n\r\n`); + sourceSocket.pipe(targetSocket); + targetSocket.pipe(sourceSocket); + // Once target socket closes forcibly, the source socket gets paused. + // We need to enable flowing, otherwise the socket would remain open indefinitely. + // Nothing would consume the data, we just want to close the socket. + targetSocket.on('close', () => { + sourceSocket.resume(); + if (sourceSocket.writable) { + sourceSocket.end(); + } + }); + // Same here. + sourceSocket.on('close', () => { + targetSocket.resume(); + if (targetSocket.writable) { + targetSocket.end(); + } + }); + }); + client.on('error', (error) => { + var _a, _b; + server.log(proxyChainId, `Failed to connect to upstream proxy: ${error.stack}`); + // The end socket may get connected after the client to proxy one gets disconnected. + if (sourceSocket.readyState === 'open') { + if (isPlain) { + sourceSocket.end(); + } + else { + const statusCode = (_a = statuses_1.errorCodeToStatusCode[error.code]) !== null && _a !== void 0 ? _a : statuses_1.badGatewayStatusCodes.GENERIC_ERROR; + const response = (0, statuses_1.createCustomStatusHttpResponse)(statusCode, (_b = error.code) !== null && _b !== void 0 ? _b : 'Upstream Closed Early'); + sourceSocket.end(response); + } + } + }); + sourceSocket.on('error', () => { + client.destroy(); + }); + // In case the client ends the socket too early + sourceSocket.on('close', () => { + client.destroy(); + }); + client.end(); +}; +exports.chain = chain; +//# sourceMappingURL=chain.js.map \ No newline at end of file diff --git a/dist/chain.js.map b/dist/chain.js.map new file mode 100644 index 00000000..8b9a749d --- /dev/null +++ b/dist/chain.js.map @@ -0,0 +1 @@ +{"version":3,"file":"chain.js","sourceRoot":"","sources":["../src/chain.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/chain_socks.d.ts b/dist/chain_socks.d.ts new file mode 100644 index 00000000..ed9179f8 --- /dev/null +++ b/dist/chain_socks.d.ts @@ -0,0 +1,30 @@ +/// +/// +/// +/// +/// +import type { Buffer } from 'node:buffer'; +import type { EventEmitter } from 'node:events'; +import type http from 'node:http'; +import { URL } from 'node:url'; +import type { Socket } from './socket'; +export interface HandlerOpts { + upstreamProxyUrlParsed: URL; + customTag?: unknown; +} +interface ChainSocksOpts { + request: http.IncomingMessage; + sourceSocket: Socket; + head: Buffer; + server: EventEmitter & { + log: (connectionId: unknown, str: string) => void; + }; + handlerOpts: HandlerOpts; +} +/** + * Client -> Apify (CONNECT) -> Upstream (SOCKS) -> Web + * Client <- Apify (CONNECT) <- Upstream (SOCKS) <- Web + */ +export declare const chainSocks: ({ request, sourceSocket, head, server, handlerOpts, }: ChainSocksOpts) => Promise; +export {}; +//# sourceMappingURL=chain_socks.d.ts.map \ No newline at end of file diff --git a/dist/chain_socks.d.ts.map b/dist/chain_socks.d.ts.map new file mode 100644 index 00000000..47890f3b --- /dev/null +++ b/dist/chain_socks.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"chain_socks.d.ts","sourceRoot":"","sources":["../src/chain_socks.ts"],"names":[],"mappings":";;;;;AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAElC,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAI/B,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAIvC,MAAM,WAAW,WAAW;IACxB,sBAAsB,EAAE,GAAG,CAAC;IAC5B,SAAS,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,UAAU,cAAc;IACpB,OAAO,EAAE,IAAI,CAAC,eAAe,CAAC;IAC9B,YAAY,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,YAAY,GAAG;QAAE,GAAG,EAAE,CAAC,YAAY,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,IAAI,CAAA;KAAE,CAAC;IAC7E,WAAW,EAAE,WAAW,CAAC;CAC5B;AAYD;;;GAGG;AACH,eAAO,MAAM,UAAU,0DAMpB,cAAc,KAAG,QAAQ,IAAI,CAoF/B,CAAC"} \ No newline at end of file diff --git a/dist/chain_socks.js b/dist/chain_socks.js new file mode 100644 index 00000000..f5707a53 --- /dev/null +++ b/dist/chain_socks.js @@ -0,0 +1,91 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.chainSocks = void 0; +const node_url_1 = require("node:url"); +const socks_1 = require("socks"); +const statuses_1 = require("./statuses"); +const count_target_bytes_1 = require("./utils/count_target_bytes"); +const socksProtocolToVersionNumber = (protocol) => { + switch (protocol) { + case 'socks4:': + case 'socks4a:': + return 4; + default: + return 5; + } +}; +/** + * Client -> Apify (CONNECT) -> Upstream (SOCKS) -> Web + * Client <- Apify (CONNECT) <- Upstream (SOCKS) <- Web + */ +const chainSocks = async ({ request, sourceSocket, head, server, handlerOpts, }) => { + const { proxyChainId } = sourceSocket; + const { hostname, port, username, password } = handlerOpts.upstreamProxyUrlParsed; + const proxy = { + host: hostname, + port: Number(port), + type: socksProtocolToVersionNumber(handlerOpts.upstreamProxyUrlParsed.protocol), + userId: decodeURIComponent(username), + password: decodeURIComponent(password), + }; + if (head && head.length > 0) { + // HTTP/1.1 has no defined semantics when sending payload along with CONNECT and servers can reject the request. + // HTTP/2 only says that subsequent DATA frames must be transferred after HEADERS has been sent. + // HTTP/3 says that all DATA frames should be transferred (implies pre-HEADERS data). + // + // Let's go with the HTTP/3 behavior. + // There are also clients that send payload along with CONNECT to save milliseconds apparently. + // Beware of upstream proxy servers that send out valid CONNECT responses with diagnostic data such as IPs! + sourceSocket.unshift(head); + } + const url = new node_url_1.URL(`connect://${request.url}`); + const destination = { + port: Number(url.port), + host: url.hostname, + }; + let targetSocket; + try { + const client = await socks_1.SocksClient.createConnection({ + proxy, + command: 'connect', + destination, + }); + targetSocket = client.socket; + sourceSocket.write(`HTTP/1.1 200 Connection Established\r\n\r\n`); + } + catch (error) { + const socksError = error; + server.log(proxyChainId, `Failed to connect to upstream SOCKS proxy ${socksError.stack}`); + sourceSocket.end((0, statuses_1.createCustomStatusHttpResponse)((0, statuses_1.socksErrorMessageToStatusCode)(socksError.message), socksError.message)); + return; + } + (0, count_target_bytes_1.countTargetBytes)(sourceSocket, targetSocket); + sourceSocket.pipe(targetSocket); + targetSocket.pipe(sourceSocket); + // Once target socket closes forcibly, the source socket gets paused. + // We need to enable flowing, otherwise the socket would remain open indefinitely. + // Nothing would consume the data, we just want to close the socket. + targetSocket.on('close', () => { + sourceSocket.resume(); + if (sourceSocket.writable) { + sourceSocket.end(); + } + }); + // Same here. + sourceSocket.on('close', () => { + targetSocket.resume(); + if (targetSocket.writable) { + targetSocket.end(); + } + }); + targetSocket.on('error', (error) => { + server.log(proxyChainId, `Chain SOCKS Destination Socket Error: ${error.stack}`); + sourceSocket.destroy(); + }); + sourceSocket.on('error', (error) => { + server.log(proxyChainId, `Chain SOCKS Source Socket Error: ${error.stack}`); + targetSocket.destroy(); + }); +}; +exports.chainSocks = chainSocks; +//# sourceMappingURL=chain_socks.js.map \ No newline at end of file diff --git a/dist/chain_socks.js.map b/dist/chain_socks.js.map new file mode 100644 index 00000000..c877f60b --- /dev/null +++ b/dist/chain_socks.js.map @@ -0,0 +1 @@ +{"version":3,"file":"chain_socks.js","sourceRoot":"","sources":["../src/chain_socks.ts"],"names":[],"mappings":";;;AAIA,uCAA+B;AAE/B,iCAA4E;AAG5E,yCAA2F;AAC3F,mEAA8D;AAe9D,MAAM,4BAA4B,GAAG,CAAC,QAAgB,EAAS,EAAE;IAC7D,QAAQ,QAAQ,EAAE;QACd,KAAK,SAAS,CAAC;QACf,KAAK,UAAU;YACX,OAAO,CAAC,CAAC;QACb;YACI,OAAO,CAAC,CAAC;KAChB;AACL,CAAC,CAAC;AAEF;;;GAGG;AACI,MAAM,UAAU,GAAG,KAAK,EAAE,EAC7B,OAAO,EACP,YAAY,EACZ,IAAI,EACJ,MAAM,EACN,WAAW,GACE,EAAiB,EAAE;IAChC,MAAM,EAAE,YAAY,EAAE,GAAG,YAAY,CAAC;IAEtC,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,GAAG,WAAW,CAAC,sBAAsB,CAAC;IAElF,MAAM,KAAK,GAAe;QACtB,IAAI,EAAE,QAAQ;QACd,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC;QAClB,IAAI,EAAE,4BAA4B,CAAC,WAAW,CAAC,sBAAsB,CAAC,QAAQ,CAAC;QAC/E,MAAM,EAAE,kBAAkB,CAAC,QAAQ,CAAC;QACpC,QAAQ,EAAE,kBAAkB,CAAC,QAAQ,CAAC;KACzC,CAAC;IAEF,IAAI,IAAI,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE;QACzB,gHAAgH;QAChH,gGAAgG;QAChG,qFAAqF;QACrF,EAAE;QACF,qCAAqC;QACrC,+FAA+F;QAC/F,2GAA2G;QAC3G,YAAY,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;KAC9B;IAED,MAAM,GAAG,GAAG,IAAI,cAAG,CAAC,aAAa,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;IAChD,MAAM,WAAW,GAAG;QAChB,IAAI,EAAE,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC;QACtB,IAAI,EAAE,GAAG,CAAC,QAAQ;KACrB,CAAC;IAEF,IAAI,YAAwB,CAAC;IAE7B,IAAI;QACA,MAAM,MAAM,GAAG,MAAM,mBAAW,CAAC,gBAAgB,CAAC;YAC9C,KAAK;YACL,OAAO,EAAE,SAAS;YAClB,WAAW;SACd,CAAC,CAAC;QACH,YAAY,GAAG,MAAM,CAAC,MAAM,CAAC;QAE7B,YAAY,CAAC,KAAK,CAAC,6CAA6C,CAAC,CAAC;KACrE;IAAC,OAAO,KAAK,EAAE;QACZ,MAAM,UAAU,GAAG,KAAyB,CAAC;QAC7C,MAAM,CAAC,GAAG,CAAC,YAAY,EAAE,6CAA6C,UAAU,CAAC,KAAK,EAAE,CAAC,CAAC;QAC1F,YAAY,CAAC,GAAG,CAAC,IAAA,yCAA8B,EAAC,IAAA,wCAA6B,EAAC,UAAU,CAAC,OAAO,CAAC,EAAE,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC;QACxH,OAAO;KACV;IAED,IAAA,qCAAgB,EAAC,YAAY,EAAE,YAAY,CAAC,CAAC;IAE7C,YAAY,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;IAChC,YAAY,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;IAEhC,qEAAqE;IACrE,kFAAkF;IAClF,oEAAoE;IACpE,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE;QAC1B,YAAY,CAAC,MAAM,EAAE,CAAC;QAEtB,IAAI,YAAY,CAAC,QAAQ,EAAE;YACvB,YAAY,CAAC,GAAG,EAAE,CAAC;SACtB;IACL,CAAC,CAAC,CAAC;IAEH,aAAa;IACb,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE;QAC1B,YAAY,CAAC,MAAM,EAAE,CAAC;QAEtB,IAAI,YAAY,CAAC,QAAQ,EAAE;YACvB,YAAY,CAAC,GAAG,EAAE,CAAC;SACtB;IACL,CAAC,CAAC,CAAC;IAEH,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE;QAC/B,MAAM,CAAC,GAAG,CAAC,YAAY,EAAE,yCAAyC,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC;QAEjF,YAAY,CAAC,OAAO,EAAE,CAAC;IAC3B,CAAC,CAAC,CAAC;IAEH,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE;QAC/B,MAAM,CAAC,GAAG,CAAC,YAAY,EAAE,oCAAoC,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC;QAE5E,YAAY,CAAC,OAAO,EAAE,CAAC;IAC3B,CAAC,CAAC,CAAC;AACP,CAAC,CAAC;AA1FW,QAAA,UAAU,cA0FrB"} \ No newline at end of file diff --git a/dist/custom_connect.d.ts b/dist/custom_connect.d.ts new file mode 100644 index 00000000..6ddfaaf0 --- /dev/null +++ b/dist/custom_connect.d.ts @@ -0,0 +1,4 @@ +import type http from 'node:http'; +import type net from 'node:net'; +export declare const customConnect: (socket: net.Socket, server: http.Server) => Promise; +//# sourceMappingURL=custom_connect.d.ts.map \ No newline at end of file diff --git a/dist/custom_connect.d.ts.map b/dist/custom_connect.d.ts.map new file mode 100644 index 00000000..eabb7431 --- /dev/null +++ b/dist/custom_connect.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"custom_connect.d.ts","sourceRoot":"","sources":["../src/custom_connect.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,KAAK,GAAG,MAAM,UAAU,CAAC;AAGhC,eAAO,MAAM,aAAa,WAAkB,IAAI,MAAM,UAAU,KAAK,MAAM,KAAG,QAAQ,IAAI,CAqBzF,CAAC"} \ No newline at end of file diff --git a/dist/custom_connect.js b/dist/custom_connect.js new file mode 100644 index 00000000..0f71d3d4 --- /dev/null +++ b/dist/custom_connect.js @@ -0,0 +1,25 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.customConnect = void 0; +const node_util_1 = require("node:util"); +const customConnect = async (socket, server) => { + // `countTargetBytes(socket, socket)` is incorrect here since `socket` is not a target. + // We would have to create a new stream and pipe traffic through that, + // however this would also increase CPU usage. + // Also, counting bytes here is not correct since we don't know how the response is generated + // (whether any additional sockets are used). + const asyncWrite = (0, node_util_1.promisify)(socket.write).bind(socket); + await asyncWrite('HTTP/1.1 200 Connection Established\r\n\r\n'); + server.emit('connection', socket); + return new Promise((resolve) => { + if (socket.destroyed) { + resolve(); + return; + } + socket.once('close', () => { + resolve(); + }); + }); +}; +exports.customConnect = customConnect; +//# sourceMappingURL=custom_connect.js.map \ No newline at end of file diff --git a/dist/custom_connect.js.map b/dist/custom_connect.js.map new file mode 100644 index 00000000..6300472a --- /dev/null +++ b/dist/custom_connect.js.map @@ -0,0 +1 @@ +{"version":3,"file":"custom_connect.js","sourceRoot":"","sources":["../src/custom_connect.ts"],"names":[],"mappings":";;;AAEA,yCAAsC;AAE/B,MAAM,aAAa,GAAG,KAAK,EAAE,MAAkB,EAAE,MAAmB,EAAiB,EAAE;IAC1F,uFAAuF;IACvF,sEAAsE;IACtE,8CAA8C;IAC9C,6FAA6F;IAC7F,6CAA6C;IAE7C,MAAM,UAAU,GAAG,IAAA,qBAAS,EAAC,MAAM,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACxD,MAAM,UAAU,CAAC,6CAA6C,CAAC,CAAC;IAChE,MAAM,CAAC,IAAI,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC;IAElC,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC3B,IAAI,MAAM,CAAC,SAAS,EAAE;YAClB,OAAO,EAAE,CAAC;YACV,OAAO;SACV;QAED,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,EAAE;YACtB,OAAO,EAAE,CAAC;QACd,CAAC,CAAC,CAAC;IACP,CAAC,CAAC,CAAC;AACP,CAAC,CAAC;AArBW,QAAA,aAAa,iBAqBxB"} \ No newline at end of file diff --git a/dist/custom_response.d.ts b/dist/custom_response.d.ts new file mode 100644 index 00000000..0574e261 --- /dev/null +++ b/dist/custom_response.d.ts @@ -0,0 +1,15 @@ +/// +/// +import type { Buffer } from 'node:buffer'; +import type http from 'node:http'; +export interface CustomResponse { + statusCode?: number; + headers?: Record; + body?: string | Buffer; + encoding?: BufferEncoding; +} +export interface HandlerOpts { + customResponseFunction: () => CustomResponse | Promise; +} +export declare const handleCustomResponse: (_request: http.IncomingMessage, response: http.ServerResponse, handlerOpts: HandlerOpts) => Promise; +//# sourceMappingURL=custom_response.d.ts.map \ No newline at end of file diff --git a/dist/custom_response.d.ts.map b/dist/custom_response.d.ts.map new file mode 100644 index 00000000..6ac920af --- /dev/null +++ b/dist/custom_response.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"custom_response.d.ts","sourceRoot":"","sources":["../src/custom_response.ts"],"names":[],"mappings":";;AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAElC,MAAM,WAAW,cAAc;IAC3B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACvB,QAAQ,CAAC,EAAE,cAAc,CAAC;CAC7B;AAED,MAAM,WAAW,WAAW;IACxB,sBAAsB,EAAE,MAAM,cAAc,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC;CAC1E;AAED,eAAO,MAAM,oBAAoB,aACnB,KAAK,eAAe,YACpB,KAAK,cAAc,eAChB,WAAW,KACzB,QAAQ,IAAI,CAqBd,CAAC"} \ No newline at end of file diff --git a/dist/custom_response.js b/dist/custom_response.js new file mode 100644 index 00000000..17a6d823 --- /dev/null +++ b/dist/custom_response.js @@ -0,0 +1,22 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.handleCustomResponse = void 0; +const handleCustomResponse = async (_request, response, handlerOpts) => { + const { customResponseFunction } = handlerOpts; + if (!customResponseFunction) { + throw new Error('The "customResponseFunction" option is required'); + } + const customResponse = await customResponseFunction(); + if (typeof customResponse !== 'object' || customResponse === null) { + throw new Error('The user-provided "customResponseFunction" must return an object.'); + } + response.statusCode = customResponse.statusCode || 200; + if (customResponse.headers) { + for (const [key, value] of Object.entries(customResponse.headers)) { + response.setHeader(key, value); + } + } + response.end(customResponse.body, customResponse.encoding); +}; +exports.handleCustomResponse = handleCustomResponse; +//# sourceMappingURL=custom_response.js.map \ No newline at end of file diff --git a/dist/custom_response.js.map b/dist/custom_response.js.map new file mode 100644 index 00000000..e59c4ace --- /dev/null +++ b/dist/custom_response.js.map @@ -0,0 +1 @@ +{"version":3,"file":"custom_response.js","sourceRoot":"","sources":["../src/custom_response.ts"],"names":[],"mappings":";;;AAcO,MAAM,oBAAoB,GAAG,KAAK,EACrC,QAA8B,EAC9B,QAA6B,EAC7B,WAAwB,EACX,EAAE;IACf,MAAM,EAAE,sBAAsB,EAAE,GAAG,WAAW,CAAC;IAC/C,IAAI,CAAC,sBAAsB,EAAE;QACzB,MAAM,IAAI,KAAK,CAAC,iDAAiD,CAAC,CAAC;KACtE;IAED,MAAM,cAAc,GAAG,MAAM,sBAAsB,EAAE,CAAC;IAEtD,IAAI,OAAO,cAAc,KAAK,QAAQ,IAAI,cAAc,KAAK,IAAI,EAAE;QAC/D,MAAM,IAAI,KAAK,CAAC,mEAAmE,CAAC,CAAC;KACxF;IAED,QAAQ,CAAC,UAAU,GAAG,cAAc,CAAC,UAAU,IAAI,GAAG,CAAC;IAEvD,IAAI,cAAc,CAAC,OAAO,EAAE;QACxB,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,cAAc,CAAC,OAAO,CAAC,EAAE;YAC/D,QAAQ,CAAC,SAAS,CAAC,GAAG,EAAE,KAAe,CAAC,CAAC;SAC5C;KACJ;IAED,QAAQ,CAAC,GAAG,CAAC,cAAc,CAAC,IAAI,EAAE,cAAc,CAAC,QAAS,CAAC,CAAC;AAChE,CAAC,CAAC;AAzBW,QAAA,oBAAoB,wBAyB/B"} \ No newline at end of file diff --git a/dist/direct.d.ts b/dist/direct.d.ts new file mode 100644 index 00000000..4eea4bfc --- /dev/null +++ b/dist/direct.d.ts @@ -0,0 +1,32 @@ +/// +/// +/// +/// +import type { Buffer } from 'node:buffer'; +import type dns from 'node:dns'; +import type { EventEmitter } from 'node:events'; +import type { Socket } from './socket'; +export interface HandlerOpts { + localAddress?: string; + ipFamily?: number; + dnsLookup?: typeof dns['lookup']; +} +interface DirectOpts { + request: { + url?: string; + }; + sourceSocket: Socket; + head: Buffer; + server: EventEmitter & { + log: (connectionId: unknown, str: string) => void; + }; + handlerOpts: HandlerOpts; +} +/** + * Directly connects to the target. + * Client -> Apify (CONNECT) -> Web + * Client <- Apify (CONNECT) <- Web + */ +export declare const direct: ({ request, sourceSocket, head, server, handlerOpts, }: DirectOpts) => void; +export {}; +//# sourceMappingURL=direct.d.ts.map \ No newline at end of file diff --git a/dist/direct.d.ts.map b/dist/direct.d.ts.map new file mode 100644 index 00000000..b6aa71d2 --- /dev/null +++ b/dist/direct.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"direct.d.ts","sourceRoot":"","sources":["../src/direct.ts"],"names":[],"mappings":";;;;AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,KAAK,GAAG,MAAM,UAAU,CAAC;AAChC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAIhD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAGvC,MAAM,WAAW,WAAW;IACxB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,OAAO,GAAG,CAAC,QAAQ,CAAC,CAAC;CACpC;AAED,UAAU,UAAU;IAChB,OAAO,EAAE;QAAE,GAAG,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC1B,YAAY,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,YAAY,GAAG;QAAE,GAAG,EAAE,CAAC,YAAY,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,IAAI,CAAA;KAAE,CAAC;IAC7E,WAAW,EAAE,WAAW,CAAC;CAC5B;AAED;;;;GAIG;AACH,eAAO,MAAM,MAAM,0DAOZ,UAAU,KACd,IA0EF,CAAC"} \ No newline at end of file diff --git a/dist/direct.js b/dist/direct.js new file mode 100644 index 00000000..83441ce8 --- /dev/null +++ b/dist/direct.js @@ -0,0 +1,73 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.direct = void 0; +const tslib_1 = require("tslib"); +const node_net_1 = tslib_1.__importDefault(require("node:net")); +const node_url_1 = require("node:url"); +const count_target_bytes_1 = require("./utils/count_target_bytes"); +/** + * Directly connects to the target. + * Client -> Apify (CONNECT) -> Web + * Client <- Apify (CONNECT) <- Web + */ +const direct = ({ request, sourceSocket, head, server, handlerOpts, }) => { + const url = new node_url_1.URL(`connect://${request.url}`); + if (!url.hostname) { + throw new Error('Missing CONNECT hostname'); + } + if (!url.port) { + throw new Error('Missing CONNECT port'); + } + if (head.length > 0) { + // See comment in chain.ts + sourceSocket.unshift(head); + } + const options = { + port: Number(url.port), + host: url.hostname, + localAddress: handlerOpts.localAddress, + family: handlerOpts.ipFamily, + lookup: handlerOpts.dnsLookup, + }; + if (options.host[0] === '[') { + options.host = options.host.slice(1, -1); + } + const targetSocket = node_net_1.default.createConnection(options, () => { + try { + sourceSocket.write(`HTTP/1.1 200 Connection Established\r\n\r\n`); + } + catch (error) { + sourceSocket.destroy(error); + } + }); + (0, count_target_bytes_1.countTargetBytes)(sourceSocket, targetSocket); + sourceSocket.pipe(targetSocket); + targetSocket.pipe(sourceSocket); + // Once target socket closes forcibly, the source socket gets paused. + // We need to enable flowing, otherwise the socket would remain open indefinitely. + // Nothing would consume the data, we just want to close the socket. + targetSocket.on('close', () => { + sourceSocket.resume(); + if (sourceSocket.writable) { + sourceSocket.end(); + } + }); + // Same here. + sourceSocket.on('close', () => { + targetSocket.resume(); + if (targetSocket.writable) { + targetSocket.end(); + } + }); + const { proxyChainId } = sourceSocket; + targetSocket.on('error', (error) => { + server.log(proxyChainId, `Direct Destination Socket Error: ${error.stack}`); + sourceSocket.destroy(); + }); + sourceSocket.on('error', (error) => { + server.log(proxyChainId, `Direct Source Socket Error: ${error.stack}`); + targetSocket.destroy(); + }); +}; +exports.direct = direct; +//# sourceMappingURL=direct.js.map \ No newline at end of file diff --git a/dist/direct.js.map b/dist/direct.js.map new file mode 100644 index 00000000..60973329 --- /dev/null +++ b/dist/direct.js.map @@ -0,0 +1 @@ +{"version":3,"file":"direct.js","sourceRoot":"","sources":["../src/direct.ts"],"names":[],"mappings":";;;;AAGA,gEAA2B;AAC3B,uCAA+B;AAG/B,mEAA8D;AAgB9D;;;;GAIG;AACI,MAAM,MAAM,GAAG,CAClB,EACI,OAAO,EACP,YAAY,EACZ,IAAI,EACJ,MAAM,EACN,WAAW,GACF,EACT,EAAE;IACN,MAAM,GAAG,GAAG,IAAI,cAAG,CAAC,aAAa,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;IAEhD,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE;QACf,MAAM,IAAI,KAAK,CAAC,0BAA0B,CAAC,CAAC;KAC/C;IAED,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE;QACX,MAAM,IAAI,KAAK,CAAC,sBAAsB,CAAC,CAAC;KAC3C;IAED,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE;QACjB,0BAA0B;QAC1B,YAAY,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;KAC9B;IAED,MAAM,OAAO,GAAG;QACZ,IAAI,EAAE,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC;QACtB,IAAI,EAAE,GAAG,CAAC,QAAQ;QAClB,YAAY,EAAE,WAAW,CAAC,YAAY;QACtC,MAAM,EAAE,WAAW,CAAC,QAAQ;QAC5B,MAAM,EAAE,WAAW,CAAC,SAAS;KAChC,CAAC;IAEF,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,GAAG,EAAE;QACzB,OAAO,CAAC,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;KAC5C;IAED,MAAM,YAAY,GAAG,kBAAG,CAAC,gBAAgB,CAAC,OAAO,EAAE,GAAG,EAAE;QACpD,IAAI;YACA,YAAY,CAAC,KAAK,CAAC,6CAA6C,CAAC,CAAC;SACrE;QAAC,OAAO,KAAK,EAAE;YACZ,YAAY,CAAC,OAAO,CAAC,KAAc,CAAC,CAAC;SACxC;IACL,CAAC,CAAC,CAAC;IAEH,IAAA,qCAAgB,EAAC,YAAY,EAAE,YAAY,CAAC,CAAC;IAE7C,YAAY,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;IAChC,YAAY,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;IAEhC,qEAAqE;IACrE,kFAAkF;IAClF,oEAAoE;IACpE,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE;QAC1B,YAAY,CAAC,MAAM,EAAE,CAAC;QAEtB,IAAI,YAAY,CAAC,QAAQ,EAAE;YACvB,YAAY,CAAC,GAAG,EAAE,CAAC;SACtB;IACL,CAAC,CAAC,CAAC;IAEH,aAAa;IACb,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE;QAC1B,YAAY,CAAC,MAAM,EAAE,CAAC;QAEtB,IAAI,YAAY,CAAC,QAAQ,EAAE;YACvB,YAAY,CAAC,GAAG,EAAE,CAAC;SACtB;IACL,CAAC,CAAC,CAAC;IAEH,MAAM,EAAE,YAAY,EAAE,GAAG,YAAY,CAAC;IAEtC,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE;QAC/B,MAAM,CAAC,GAAG,CAAC,YAAY,EAAE,oCAAoC,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC;QAE5E,YAAY,CAAC,OAAO,EAAE,CAAC;IAC3B,CAAC,CAAC,CAAC;IAEH,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE;QAC/B,MAAM,CAAC,GAAG,CAAC,YAAY,EAAE,+BAA+B,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC;QAEvE,YAAY,CAAC,OAAO,EAAE,CAAC;IAC3B,CAAC,CAAC,CAAC;AACP,CAAC,CAAC;AAlFW,QAAA,MAAM,UAkFjB"} \ No newline at end of file diff --git a/dist/forward.d.ts b/dist/forward.d.ts new file mode 100644 index 00000000..01eeab3f --- /dev/null +++ b/dist/forward.d.ts @@ -0,0 +1,31 @@ +/// +/// +import type dns from 'node:dns'; +import http from 'node:http'; +import type { URL } from 'node:url'; +export interface HandlerOpts { + upstreamProxyUrlParsed: URL; + ignoreUpstreamProxyCertificate: boolean; + localAddress?: string; + ipFamily?: number; + dnsLookup?: typeof dns['lookup']; +} +/** + * The request is read from the client and is resent. + * This is similar to Direct / Chain, however it uses the CONNECT protocol instead. + * Forward uses standard HTTP methods. + * + * ``` + * Client -> Apify (HTTP) -> Web + * Client <- Apify (HTTP) <- Web + * ``` + * + * or + * + * ``` + * Client -> Apify (HTTP) -> Upstream (HTTP) -> Web + * Client <- Apify (HTTP) <- Upstream (HTTP) <- Web + * ``` + */ +export declare const forward: (request: http.IncomingMessage, response: http.ServerResponse, handlerOpts: HandlerOpts) => Promise; +//# sourceMappingURL=forward.d.ts.map \ No newline at end of file diff --git a/dist/forward.d.ts.map b/dist/forward.d.ts.map new file mode 100644 index 00000000..460f90fc --- /dev/null +++ b/dist/forward.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"forward.d.ts","sourceRoot":"","sources":["../src/forward.ts"],"names":[],"mappings":";;AAAA,OAAO,KAAK,GAAG,MAAM,UAAU,CAAC;AAChC,OAAO,IAAI,MAAM,WAAW,CAAC;AAG7B,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAqBpC,MAAM,WAAW,WAAW;IACxB,sBAAsB,EAAE,GAAG,CAAC;IAC5B,8BAA8B,EAAE,OAAO,CAAC;IACxC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,OAAO,GAAG,CAAC,QAAQ,CAAC,CAAC;CACpC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,OAAO,YACP,KAAK,eAAe,YACnB,KAAK,cAAc,eAChB,WAAW,KAEzB,QAAQ,IAAI,CA4Fb,CAAC"} \ No newline at end of file diff --git a/dist/forward.js b/dist/forward.js new file mode 100644 index 00000000..1a8b96ef --- /dev/null +++ b/dist/forward.js @@ -0,0 +1,106 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.forward = void 0; +const tslib_1 = require("tslib"); +const node_http_1 = tslib_1.__importDefault(require("node:http")); +const node_https_1 = tslib_1.__importDefault(require("node:https")); +const node_stream_1 = tslib_1.__importDefault(require("node:stream")); +const node_util_1 = tslib_1.__importDefault(require("node:util")); +const statuses_1 = require("./statuses"); +const count_target_bytes_1 = require("./utils/count_target_bytes"); +const get_basic_1 = require("./utils/get_basic"); +const valid_headers_only_1 = require("./utils/valid_headers_only"); +const pipeline = node_util_1.default.promisify(node_stream_1.default.pipeline); +/** + * The request is read from the client and is resent. + * This is similar to Direct / Chain, however it uses the CONNECT protocol instead. + * Forward uses standard HTTP methods. + * + * ``` + * Client -> Apify (HTTP) -> Web + * Client <- Apify (HTTP) <- Web + * ``` + * + * or + * + * ``` + * Client -> Apify (HTTP) -> Upstream (HTTP) -> Web + * Client <- Apify (HTTP) <- Upstream (HTTP) <- Web + * ``` + */ +const forward = async (request, response, handlerOpts) => new Promise(async (resolve, reject) => { + const proxy = handlerOpts.upstreamProxyUrlParsed; + const origin = proxy ? proxy.origin : request.url; + const options = { + method: request.method, + headers: (0, valid_headers_only_1.validHeadersOnly)(request.rawHeaders), + insecureHTTPParser: true, + localAddress: handlerOpts.localAddress, + family: handlerOpts.ipFamily, + lookup: handlerOpts.dnsLookup, + }; + // In case of proxy the path needs to be an absolute URL + if (proxy) { + options.path = request.url; + try { + if (proxy.username || proxy.password) { + options.headers.push('proxy-authorization', (0, get_basic_1.getBasicAuthorizationHeader)(proxy)); + } + } + catch (error) { + reject(error); + return; + } + } + const requestCallback = async (clientResponse) => { + try { + // This is necessary to prevent Node.js throwing an error + let statusCode = clientResponse.statusCode; + if (statusCode < 100 || statusCode > 999) { + statusCode = statuses_1.badGatewayStatusCodes.STATUS_CODE_OUT_OF_RANGE; + } + // 407 is handled separately + if (clientResponse.statusCode === 407) { + reject(new Error('407 Proxy Authentication Required')); + return; + } + response.writeHead(statusCode, clientResponse.statusMessage, (0, valid_headers_only_1.validHeadersOnly)(clientResponse.rawHeaders)); + // `pipeline` automatically handles all the events and data + await pipeline(clientResponse, response); + resolve(); + } + catch { + // Client error, pipeline already destroys the streams, ignore. + resolve(); + } + }; + // We have to force cast `options` because @types/node doesn't support an array. + const client = origin.startsWith('https:') + ? node_https_1.default.request(origin, { + ...options, + rejectUnauthorized: handlerOpts.upstreamProxyUrlParsed ? !handlerOpts.ignoreUpstreamProxyCertificate : undefined, + }, requestCallback) + : node_http_1.default.request(origin, options, requestCallback); + client.once('socket', (socket) => { + // Socket can be re-used by multiple requests. + // That's why we need to track the previous stats. + socket.previousBytesRead = socket.bytesRead; + socket.previousBytesWritten = socket.bytesWritten; + (0, count_target_bytes_1.countTargetBytes)(request.socket, socket, (handler) => response.once('close', handler)); + }); + // Can't use pipeline here as it automatically destroys the streams + request.pipe(client); + client.on('error', (error) => { + var _a; + if (response.headersSent) { + return; + } + const statusCode = (_a = statuses_1.errorCodeToStatusCode[error.code]) !== null && _a !== void 0 ? _a : statuses_1.badGatewayStatusCodes.GENERIC_ERROR; + response.statusCode = !proxy && error.code === 'ENOTFOUND' ? 404 : statusCode; + response.setHeader('content-type', 'text/plain; charset=utf-8'); + response.end(node_http_1.default.STATUS_CODES[response.statusCode]); + resolve(); + }); +}); +exports.forward = forward; +//# sourceMappingURL=forward.js.map \ No newline at end of file diff --git a/dist/forward.js.map b/dist/forward.js.map new file mode 100644 index 00000000..2d88377a --- /dev/null +++ b/dist/forward.js.map @@ -0,0 +1 @@ +{"version":3,"file":"forward.js","sourceRoot":"","sources":["../src/forward.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/forward_socks.d.ts b/dist/forward_socks.d.ts new file mode 100644 index 00000000..dde64637 --- /dev/null +++ b/dist/forward_socks.d.ts @@ -0,0 +1,15 @@ +/// +import http from 'node:http'; +import type { URL } from 'node:url'; +export interface HandlerOpts { + upstreamProxyUrlParsed: URL; + localAddress?: string; +} +/** + * ``` + * Client -> Apify (HTTP) -> Upstream (SOCKS) -> Web + * Client <- Apify (HTTP) <- Upstream (SOCKS) <- Web + * ``` + */ +export declare const forwardSocks: (request: http.IncomingMessage, response: http.ServerResponse, handlerOpts: HandlerOpts) => Promise; +//# sourceMappingURL=forward_socks.d.ts.map \ No newline at end of file diff --git a/dist/forward_socks.d.ts.map b/dist/forward_socks.d.ts.map new file mode 100644 index 00000000..7fbfca04 --- /dev/null +++ b/dist/forward_socks.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"forward_socks.d.ts","sourceRoot":"","sources":["../src/forward_socks.ts"],"names":[],"mappings":";AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAoBpC,MAAM,WAAW,WAAW;IACxB,sBAAsB,EAAE,GAAG,CAAC;IAC5B,YAAY,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;;;;GAKG;AACH,eAAO,MAAM,YAAY,YACZ,KAAK,eAAe,YACnB,KAAK,cAAc,eAChB,WAAW,KAEzB,QAAQ,IAAI,CAiEb,CAAC"} \ No newline at end of file diff --git a/dist/forward_socks.js b/dist/forward_socks.js new file mode 100644 index 00000000..ed26164d --- /dev/null +++ b/dist/forward_socks.js @@ -0,0 +1,70 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.forwardSocks = void 0; +const tslib_1 = require("tslib"); +const node_http_1 = tslib_1.__importDefault(require("node:http")); +const node_stream_1 = tslib_1.__importDefault(require("node:stream")); +const node_util_1 = tslib_1.__importDefault(require("node:util")); +const socks_proxy_agent_1 = require("socks-proxy-agent"); +const statuses_1 = require("./statuses"); +const count_target_bytes_1 = require("./utils/count_target_bytes"); +const valid_headers_only_1 = require("./utils/valid_headers_only"); +const pipeline = node_util_1.default.promisify(node_stream_1.default.pipeline); +/** + * ``` + * Client -> Apify (HTTP) -> Upstream (SOCKS) -> Web + * Client <- Apify (HTTP) <- Upstream (SOCKS) <- Web + * ``` + */ +const forwardSocks = async (request, response, handlerOpts) => new Promise(async (resolve, reject) => { + const agent = new socks_proxy_agent_1.SocksProxyAgent(handlerOpts.upstreamProxyUrlParsed); + const options = { + method: request.method, + headers: (0, valid_headers_only_1.validHeadersOnly)(request.rawHeaders), + insecureHTTPParser: true, + localAddress: handlerOpts.localAddress, + agent, + }; + // Only handling "http" here - since everything else is handeled by tunnelSocks. + // We have to force cast `options` because @types/node doesn't support an array. + const client = node_http_1.default.request(request.url, options, async (clientResponse) => { + try { + // This is necessary to prevent Node.js throwing an error + let statusCode = clientResponse.statusCode; + if (statusCode < 100 || statusCode > 999) { + statusCode = statuses_1.badGatewayStatusCodes.STATUS_CODE_OUT_OF_RANGE; + } + // 407 is handled separately + if (clientResponse.statusCode === 407) { + reject(new Error('407 Proxy Authentication Required')); + return; + } + response.writeHead(statusCode, clientResponse.statusMessage, (0, valid_headers_only_1.validHeadersOnly)(clientResponse.rawHeaders)); + // `pipeline` automatically handles all the events and data + await pipeline(clientResponse, response); + resolve(); + } + catch { + // Client error, pipeline already destroys the streams, ignore. + resolve(); + } + }); + client.once('socket', (socket) => { + (0, count_target_bytes_1.countTargetBytes)(request.socket, socket); + }); + // Can't use pipeline here as it automatically destroys the streams + request.pipe(client); + client.on('error', (error) => { + var _a; + if (response.headersSent) { + return; + } + const statusCode = (_a = statuses_1.errorCodeToStatusCode[error.code]) !== null && _a !== void 0 ? _a : statuses_1.badGatewayStatusCodes.GENERIC_ERROR; + response.statusCode = statusCode; + response.setHeader('content-type', 'text/plain; charset=utf-8'); + response.end(node_http_1.default.STATUS_CODES[response.statusCode]); + resolve(); + }); +}); +exports.forwardSocks = forwardSocks; +//# sourceMappingURL=forward_socks.js.map \ No newline at end of file diff --git a/dist/forward_socks.js.map b/dist/forward_socks.js.map new file mode 100644 index 00000000..1ca6992f --- /dev/null +++ b/dist/forward_socks.js.map @@ -0,0 +1 @@ +{"version":3,"file":"forward_socks.js","sourceRoot":"","sources":["../src/forward_socks.ts"],"names":[],"mappings":";;;;AAAA,kEAA6B;AAC7B,sEAAiC;AAEjC,kEAA6B;AAE7B,yDAAoD;AAEpD,yCAA0E;AAC1E,mEAA8D;AAC9D,mEAA8D;AAE9D,MAAM,QAAQ,GAAG,mBAAI,CAAC,SAAS,CAAC,qBAAM,CAAC,QAAQ,CAAC,CAAC;AAgBjD;;;;;GAKG;AACI,MAAM,YAAY,GAAG,KAAK,EAC7B,OAA6B,EAC7B,QAA6B,EAC7B,WAAwB,EAEX,EAAE,CAAC,IAAI,OAAO,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE;IACtD,MAAM,KAAK,GAAG,IAAI,mCAAe,CAAC,WAAW,CAAC,sBAAsB,CAAC,CAAC;IAEtE,MAAM,OAAO,GAAY;QACrB,MAAM,EAAE,OAAO,CAAC,MAAO;QACvB,OAAO,EAAE,IAAA,qCAAgB,EAAC,OAAO,CAAC,UAAU,CAAC;QAC7C,kBAAkB,EAAE,IAAI;QACxB,YAAY,EAAE,WAAW,CAAC,YAAY;QACtC,KAAK;KACR,CAAC;IAEF,gFAAgF;IAChF,gFAAgF;IAChF,MAAM,MAAM,GAAG,mBAAI,CAAC,OAAO,CAAC,OAAO,CAAC,GAAI,EAAE,OAA4C,EAAE,KAAK,EAAE,cAAc,EAAE,EAAE;QAC7G,IAAI;YACA,yDAAyD;YACzD,IAAI,UAAU,GAAG,cAAc,CAAC,UAAW,CAAC;YAC5C,IAAI,UAAU,GAAG,GAAG,IAAI,UAAU,GAAG,GAAG,EAAE;gBACtC,UAAU,GAAG,gCAAqB,CAAC,wBAAwB,CAAC;aAC/D;YAED,4BAA4B;YAC5B,IAAI,cAAc,CAAC,UAAU,KAAK,GAAG,EAAE;gBACnC,MAAM,CAAC,IAAI,KAAK,CAAC,mCAAmC,CAAC,CAAC,CAAC;gBACvD,OAAO;aACV;YAED,QAAQ,CAAC,SAAS,CACd,UAAU,EACV,cAAc,CAAC,aAAa,EAC5B,IAAA,qCAAgB,EAAC,cAAc,CAAC,UAAU,CAAC,CAC9C,CAAC;YAEF,2DAA2D;YAC3D,MAAM,QAAQ,CACV,cAAc,EACd,QAAQ,CACX,CAAC;YAEF,OAAO,EAAE,CAAC;SACb;QAAC,MAAM;YACJ,+DAA+D;YAC/D,OAAO,EAAE,CAAC;SACb;IACL,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE,EAAE;QAC7B,IAAA,qCAAgB,EAAC,OAAO,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7C,CAAC,CAAC,CAAC;IAEH,mEAAmE;IACnE,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACrB,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,KAA4B,EAAE,EAAE;;QAChD,IAAI,QAAQ,CAAC,WAAW,EAAE;YACtB,OAAO;SACV;QAED,MAAM,UAAU,GAAG,MAAA,gCAAqB,CAAC,KAAK,CAAC,IAAK,CAAC,mCAAI,gCAAqB,CAAC,aAAa,CAAC;QAE7F,QAAQ,CAAC,UAAU,GAAG,UAAU,CAAC;QACjC,QAAQ,CAAC,SAAS,CAAC,cAAc,EAAE,2BAA2B,CAAC,CAAC;QAChE,QAAQ,CAAC,GAAG,CAAC,mBAAI,CAAC,YAAY,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC;QAErD,OAAO,EAAE,CAAC;IACd,CAAC,CAAC,CAAC;AACP,CAAC,CAAC,CAAC;AAtEU,QAAA,YAAY,gBAsEtB"} \ No newline at end of file diff --git a/dist/index.d.ts b/dist/index.d.ts new file mode 100644 index 00000000..12c7ff02 --- /dev/null +++ b/dist/index.d.ts @@ -0,0 +1,7 @@ +export * from './request_error'; +export * from './server'; +export * from './utils/redact_url'; +export * from './anonymize_proxy'; +export * from './tcp_tunnel_tools'; +export { CustomResponse } from './custom_response'; +//# sourceMappingURL=index.d.ts.map \ No newline at end of file diff --git a/dist/index.d.ts.map b/dist/index.d.ts.map new file mode 100644 index 00000000..15af00ba --- /dev/null +++ b/dist/index.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAC;AAChC,cAAc,UAAU,CAAC;AACzB,cAAc,oBAAoB,CAAC;AACnC,cAAc,mBAAmB,CAAC;AAClC,cAAc,oBAAoB,CAAC;AAEnC,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC"} \ No newline at end of file diff --git a/dist/index.js b/dist/index.js new file mode 100644 index 00000000..655d10e9 --- /dev/null +++ b/dist/index.js @@ -0,0 +1,9 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const tslib_1 = require("tslib"); +tslib_1.__exportStar(require("./request_error"), exports); +tslib_1.__exportStar(require("./server"), exports); +tslib_1.__exportStar(require("./utils/redact_url"), exports); +tslib_1.__exportStar(require("./anonymize_proxy"), exports); +tslib_1.__exportStar(require("./tcp_tunnel_tools"), exports); +//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/dist/index.js.map b/dist/index.js.map new file mode 100644 index 00000000..5e97fecb --- /dev/null +++ b/dist/index.js.map @@ -0,0 +1 @@ +{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,0DAAgC;AAChC,mDAAyB;AACzB,6DAAmC;AACnC,4DAAkC;AAClC,6DAAmC"} \ No newline at end of file diff --git a/dist/request_error.d.ts b/dist/request_error.d.ts new file mode 100644 index 00000000..26cf4a0f --- /dev/null +++ b/dist/request_error.d.ts @@ -0,0 +1,14 @@ +/** + * Represents custom request error. The message is emitted as HTTP response + * with a specific HTTP code and headers. + * If this error is thrown from the `prepareRequestFunction` function, + * the message and status code is sent to client. + * By default, the response will have Content-Type: text/plain + * and for the 407 status the Proxy-Authenticate header will be added. + */ +export declare class RequestError extends Error { + statusCode: number; + headers?: Record | undefined; + constructor(message: string, statusCode: number, headers?: Record | undefined); +} +//# sourceMappingURL=request_error.d.ts.map \ No newline at end of file diff --git a/dist/request_error.d.ts.map b/dist/request_error.d.ts.map new file mode 100644 index 00000000..bf468d06 --- /dev/null +++ b/dist/request_error.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"request_error.d.ts","sourceRoot":"","sources":["../src/request_error.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,qBAAa,YAAa,SAAQ,KAAK;IAGxB,UAAU,EAAE,MAAM;IAClB,OAAO,CAAC;gBAFf,OAAO,EAAE,MAAM,EACR,UAAU,EAAE,MAAM,EAClB,OAAO,CAAC,oCAAwB;CAO9C"} \ No newline at end of file diff --git a/dist/request_error.js b/dist/request_error.js new file mode 100644 index 00000000..02379cfd --- /dev/null +++ b/dist/request_error.js @@ -0,0 +1,32 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.RequestError = void 0; +/** + * Represents custom request error. The message is emitted as HTTP response + * with a specific HTTP code and headers. + * If this error is thrown from the `prepareRequestFunction` function, + * the message and status code is sent to client. + * By default, the response will have Content-Type: text/plain + * and for the 407 status the Proxy-Authenticate header will be added. + */ +class RequestError extends Error { + constructor(message, statusCode, headers) { + super(message); + Object.defineProperty(this, "statusCode", { + enumerable: true, + configurable: true, + writable: true, + value: statusCode + }); + Object.defineProperty(this, "headers", { + enumerable: true, + configurable: true, + writable: true, + value: headers + }); + this.name = RequestError.name; + Error.captureStackTrace(this, RequestError); + } +} +exports.RequestError = RequestError; +//# sourceMappingURL=request_error.js.map \ No newline at end of file diff --git a/dist/request_error.js.map b/dist/request_error.js.map new file mode 100644 index 00000000..c38e5c7d --- /dev/null +++ b/dist/request_error.js.map @@ -0,0 +1 @@ +{"version":3,"file":"request_error.js","sourceRoot":"","sources":["../src/request_error.ts"],"names":[],"mappings":";;;AAAA;;;;;;;GAOG;AACH,MAAa,YAAa,SAAQ,KAAK;IACnC,YACI,OAAe,EACR,UAAkB,EAClB,OAAgC;QAEvC,KAAK,CAAC,OAAO,CAAC,CAAC;;;;;mBAHR;;;;;;mBACA;;QAGP,IAAI,CAAC,IAAI,GAAG,YAAY,CAAC,IAAI,CAAC;QAE9B,KAAK,CAAC,iBAAiB,CAAC,IAAI,EAAE,YAAY,CAAC,CAAC;IAChD,CAAC;CACJ;AAXD,oCAWC"} \ No newline at end of file diff --git a/dist/server.d.ts b/dist/server.d.ts new file mode 100644 index 00000000..0784af05 --- /dev/null +++ b/dist/server.d.ts @@ -0,0 +1,212 @@ +/// +/// +/// +/// +/// +/// +/// +import { Buffer } from 'node:buffer'; +import type dns from 'node:dns'; +import { EventEmitter } from 'node:events'; +import http from 'node:http'; +import { URL } from 'node:url'; +import type { HandlerOpts as CustomResponseOpts } from './custom_response'; +import type { Socket } from './socket'; +export declare const SOCKS_PROTOCOLS: string[]; +export type ConnectionStats = { + srcTxBytes: number; + srcRxBytes: number; + trgTxBytes: number | null; + trgRxBytes: number | null; +}; +type HandlerOpts = { + server: Server; + id: number; + srcRequest: http.IncomingMessage; + srcResponse: http.ServerResponse | null; + srcHead: Buffer | null; + trgParsed: URL | null; + upstreamProxyUrlParsed: URL | null; + ignoreUpstreamProxyCertificate: boolean; + isHttp: boolean; + customResponseFunction?: CustomResponseOpts['customResponseFunction'] | null; + customConnectServer?: http.Server | null; + localAddress?: string; + ipFamily?: number; + dnsLookup?: typeof dns['lookup']; + customTag?: unknown; +}; +export type PrepareRequestFunctionOpts = { + connectionId: number; + request: http.IncomingMessage; + username: string; + password: string; + hostname: string; + port: number; + isHttp: boolean; +}; +export type PrepareRequestFunctionResult = { + customResponseFunction?: CustomResponseOpts['customResponseFunction']; + customConnectServer?: http.Server | null; + requestAuthentication?: boolean; + failMsg?: string; + upstreamProxyUrl?: string | null; + ignoreUpstreamProxyCertificate?: boolean; + localAddress?: string; + ipFamily?: number; + dnsLookup?: typeof dns['lookup']; + customTag?: unknown; +}; +type Promisable = T | Promise; +export type PrepareRequestFunction = (opts: PrepareRequestFunctionOpts) => Promisable; +/** + * Represents the proxy server. + * It emits the 'requestFailed' event on unexpected request errors, with the following parameter `{ error, request }`. + * It emits the 'connectionClosed' event when connection to proxy server is closed, with parameter `{ connectionId, stats }`. + */ +export declare class Server extends EventEmitter { + port: number; + host?: string; + prepareRequestFunction?: PrepareRequestFunction; + authRealm: unknown; + verbose: boolean; + server: http.Server; + lastHandlerId: number; + stats: { + httpRequestCount: number; + connectRequestCount: number; + trafficUsedInBytes: number; + }; + connections: Map; + /** + * Initializes a new instance of Server class. + * @param options + * @param [options.port] Port where the server will listen. By default 8000. + * @param [options.prepareRequestFunction] Custom function to authenticate proxy requests, + * provide URL to upstream proxy or potentially provide a function that generates a custom response to HTTP requests. + * It accepts a single parameter which is an object: + * ``` + * { + * connectionId: symbol, + * request: http.IncomingMessage, + * username: string, + * password: string, + * hostname: string, + * port: number, + * isHttp: boolean, + * } + * ``` + * and returns an object (or promise resolving to the object) with following form: + * ``` + * { + * requestAuthentication: boolean, + * upstreamProxyUrl: string, + * customResponseFunction: Function, + * } + * ``` + * If `upstreamProxyUrl` is a falsy value, no upstream proxy is used. + * If `prepareRequestFunction` is not set, the proxy server will not require any authentication + * and will not use any upstream proxy. + * If `customResponseFunction` is set, it will be called to generate a custom response to the HTTP request. + * It should not be used together with `upstreamProxyUrl`. + * @param [options.authRealm] Realm used in the Proxy-Authenticate header and also in the 'Server' HTTP header. By default it's `ProxyChain`. + * @param [options.verbose] If true, the server will output logs + */ + constructor(options?: { + port?: number; + host?: string; + prepareRequestFunction?: PrepareRequestFunction; + verbose?: boolean; + authRealm?: unknown; + }); + log(connectionId: unknown, str: string): void; + onClientError(err: NodeJS.ErrnoException, socket: Socket): void; + /** + * Assigns a unique ID to the socket and keeps the register up to date. + * Needed for abrupt close of the server. + */ + registerConnection(socket: Socket): void; + /** + * Registering total stats each server + */ + /** + * Handles incoming sockets, useful for error handling + */ + onConnection(socket: Socket): void; + /** + * Converts known errors to be instance of RequestError. + */ + normalizeHandlerError(error: NodeJS.ErrnoException): NodeJS.ErrnoException; + /** + * Handles normal HTTP request by forwarding it to target host or the upstream proxy. + */ + onRequest(request: http.IncomingMessage, response: http.ServerResponse): Promise; + /** + * Handles HTTP CONNECT request by setting up a tunnel either to target host or to the upstream proxy. + * @param request + * @param socket + * @param head The first packet of the tunneling stream (may be empty) + */ + onConnect(request: http.IncomingMessage, socket: Socket, head: Buffer): Promise; + /** + * Prepares handler options from a request. + * @see {prepareRequestHandling} + */ + getHandlerOpts(request: http.IncomingMessage): HandlerOpts; + /** + * Calls `this.prepareRequestFunction` with normalized options. + * @param request + * @param handlerOpts + */ + callPrepareRequestFunction(request: http.IncomingMessage, handlerOpts: HandlerOpts): Promise; + /** + * Authenticates a new request and determines upstream proxy URL using the user function. + * Returns a promise resolving to an object that can be used to run a handler. + * @param request + */ + prepareRequestHandling(request: http.IncomingMessage): Promise; + /** + * Sends a HTTP error response to the client. + * @param request + * @param error + */ + failRequest(request: http.IncomingMessage, error: NodeJS.ErrnoException): void; + /** + * Sends a simple HTTP response to the client and forcibly closes the connection. + * This invalidates the ServerResponse instance (if present). + * We don't know the state of the response anyway. + * Writing directly to the socket seems to be the easiest solution. + * @param socket + * @param statusCode + * @param headers + * @param message + */ + sendSocketResponse(socket: Socket, statusCode?: number, caseSensitiveHeaders?: {}, message?: string): void; + /** + * Starts listening at a port specified in the constructor. + */ + listen(callback?: (error: NodeJS.ErrnoException | null) => void): Promise; + /** + * Gets array of IDs of all active connections. + */ + getConnectionIds(): number[]; + /** + * Gets data transfer statistics of a specific proxy connection. + */ + getConnectionStats(connectionId: number): ConnectionStats | undefined; + /** + * Forcibly close a specific pending proxy connection. + */ + closeConnection(connectionId: number): void; + /** + * Forcibly closes pending proxy connections. + */ + closeConnections(): void; + /** + * Closes the proxy server. + * @param closeConnections If true, pending proxy connections are forcibly closed. + */ + close(closeConnections: boolean, callback?: (error: NodeJS.ErrnoException | null) => void): Promise; +} +export {}; +//# sourceMappingURL=server.d.ts.map \ No newline at end of file diff --git a/dist/server.d.ts.map b/dist/server.d.ts.map new file mode 100644 index 00000000..925e7c4f --- /dev/null +++ b/dist/server.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":";;;;;;;AACA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,KAAK,GAAG,MAAM,UAAU,CAAC;AAChC,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAO/B,OAAO,KAAK,EAAE,WAAW,IAAI,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAO3E,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAQvC,eAAO,MAAM,eAAe,UAA2D,CAAC;AAcxF,MAAM,MAAM,eAAe,GAAG;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B,CAAC;AAEF,KAAK,WAAW,GAAG;IACf,MAAM,EAAE,MAAM,CAAC;IACf,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,EAAE,IAAI,CAAC,eAAe,CAAC;IACjC,WAAW,EAAE,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC;IACxC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,SAAS,EAAE,GAAG,GAAG,IAAI,CAAC;IACtB,sBAAsB,EAAE,GAAG,GAAG,IAAI,CAAC;IACnC,8BAA8B,EAAE,OAAO,CAAC;IACxC,MAAM,EAAE,OAAO,CAAC;IAChB,sBAAsB,CAAC,EAAE,kBAAkB,CAAC,wBAAwB,CAAC,GAAG,IAAI,CAAC;IAC7E,mBAAmB,CAAC,EAAE,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;IACzC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,OAAO,GAAG,CAAC,QAAQ,CAAC,CAAC;IACjC,SAAS,CAAC,EAAE,OAAO,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,0BAA0B,GAAG;IACrC,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,IAAI,CAAC,eAAe,CAAC;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,OAAO,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,4BAA4B,GAAG;IACvC,sBAAsB,CAAC,EAAE,kBAAkB,CAAC,wBAAwB,CAAC,CAAC;IACtE,mBAAmB,CAAC,EAAE,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;IACzC,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,8BAA8B,CAAC,EAAE,OAAO,CAAC;IACzC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,OAAO,GAAG,CAAC,QAAQ,CAAC,CAAC;IACjC,SAAS,CAAC,EAAE,OAAO,CAAC;CACvB,CAAC;AAEF,KAAK,UAAU,CAAC,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;AACpC,MAAM,MAAM,sBAAsB,GAAG,CAAC,IAAI,EAAE,0BAA0B,KAAK,UAAU,CAAC,SAAS,GAAG,4BAA4B,CAAC,CAAC;AAEhI;;;;GAIG;AACH,qBAAa,MAAO,SAAQ,YAAY;IACpC,IAAI,EAAE,MAAM,CAAC;IAEb,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd,sBAAsB,CAAC,EAAE,sBAAsB,CAAC;IAEhD,SAAS,EAAE,OAAO,CAAC;IAEnB,OAAO,EAAE,OAAO,CAAC;IAEjB,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC;IAEpB,aAAa,EAAE,MAAM,CAAC;IAEtB,KAAK,EAAE;QAAE,gBAAgB,EAAE,MAAM,CAAC;QAAC,mBAAmB,EAAE,MAAM,CAAC;QAAC,kBAAkB,EAAG,MAAM,CAAC;KAAE,CAAC;IAE/F,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAEjC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAiCG;gBACS,OAAO,GAAE;QACjB,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,sBAAsB,CAAC,EAAE,sBAAsB,CAAC;QAChD,OAAO,CAAC,EAAE,OAAO,CAAC;QAClB,SAAS,CAAC,EAAE,OAAO,CAAC;KAClB;IA8BN,GAAG,CAAC,YAAY,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI;IAQ7C,aAAa,CAAC,GAAG,EAAE,MAAM,CAAC,cAAc,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI;IAW/D;;;OAGG;IACH,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IA2BxC;;OAEG;IAGH;;OAEG;IACH,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAmBlC;;OAEG;IACH,qBAAqB,CAAC,KAAK,EAAE,MAAM,CAAC,cAAc,GAAG,MAAM,CAAC,cAAc;IAY1E;;OAEG;IACG,SAAS,CAAC,OAAO,EAAE,IAAI,CAAC,eAAe,EAAE,QAAQ,EAAE,IAAI,CAAC,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;IA0B5F;;;;;OAKG;IACG,SAAS,CAAC,OAAO,EAAE,IAAI,CAAC,eAAe,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA+B3F;;;OAGG;IACH,cAAc,CAAC,OAAO,EAAE,IAAI,CAAC,eAAe,GAAG,WAAW;IA4D1D;;;;OAIG;IACG,0BAA0B,CAAC,OAAO,EAAE,IAAI,CAAC,eAAe,EAAE,WAAW,EAAE,WAAW,GAAG,OAAO,CAAC,4BAA4B,CAAC;IAuChI;;;;OAIG;IACG,sBAAsB,CAAC,OAAO,EAAE,IAAI,CAAC,eAAe,GAAG,OAAO,CAAC,WAAW,CAAC;IAsDjF;;;;OAIG;IACH,WAAW,CAAC,OAAO,EAAE,IAAI,CAAC,eAAe,EAAE,KAAK,EAAE,MAAM,CAAC,cAAc,GAAG,IAAI;IAiB9E;;;;;;;;;OASG;IACH,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,SAAM,EAAE,oBAAoB,KAAK,EAAE,OAAO,SAAK,GAAG,IAAI;IAsCnG;;OAEG;IACG,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC,cAAc,GAAG,IAAI,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IA4BrF;;OAEG;IACH,gBAAgB,IAAI,MAAM,EAAE;IAI5B;;OAEG;IACH,kBAAkB,CAAC,YAAY,EAAE,MAAM,GAAG,eAAe,GAAG,SAAS;IAgBrE;;OAEG;IACH,eAAe,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI;IAW3C;;OAEG;IACH,gBAAgB,IAAI,IAAI;IAUxB;;;OAGG;IACG,KAAK,CAAC,gBAAgB,EAAE,OAAO,EAAE,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC,cAAc,GAAG,IAAI,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;CAoBlH"} \ No newline at end of file diff --git a/dist/server.js b/dist/server.js new file mode 100644 index 00000000..35f29131 --- /dev/null +++ b/dist/server.js @@ -0,0 +1,579 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.Server = exports.SOCKS_PROTOCOLS = void 0; +const tslib_1 = require("tslib"); +/* eslint-disable no-use-before-define */ +const node_buffer_1 = require("node:buffer"); +const node_events_1 = require("node:events"); +const node_http_1 = tslib_1.__importDefault(require("node:http")); +const node_url_1 = require("node:url"); +const node_util_1 = tslib_1.__importDefault(require("node:util")); +const chain_1 = require("./chain"); +const chain_socks_1 = require("./chain_socks"); +const custom_connect_1 = require("./custom_connect"); +const custom_response_1 = require("./custom_response"); +const direct_1 = require("./direct"); +const forward_1 = require("./forward"); +const forward_socks_1 = require("./forward_socks"); +const request_error_1 = require("./request_error"); +const statuses_1 = require("./statuses"); +const count_target_bytes_1 = require("./utils/count_target_bytes"); +const nodeify_1 = require("./utils/nodeify"); +const normalize_url_port_1 = require("./utils/normalize_url_port"); +const parse_authorization_header_1 = require("./utils/parse_authorization_header"); +const redact_url_1 = require("./utils/redact_url"); +exports.SOCKS_PROTOCOLS = ['socks:', 'socks4:', 'socks4a:', 'socks5:', 'socks5h:']; +// TODO: +// - Implement this requirement from rfc7230 +// "A proxy MUST forward unrecognized header fields unless the field-name +// is listed in the Connection header field (Section 6.1) or the proxy +// is specifically configured to block, or otherwise transform, such +// fields. Other recipients SHOULD ignore unrecognized header fields. +// These requirements allow HTTP's functionality to be enhanced without +// requiring prior update of deployed intermediaries." +const DEFAULT_AUTH_REALM = 'ProxyChain'; +const DEFAULT_PROXY_SERVER_PORT = 8000; +/** + * Represents the proxy server. + * It emits the 'requestFailed' event on unexpected request errors, with the following parameter `{ error, request }`. + * It emits the 'connectionClosed' event when connection to proxy server is closed, with parameter `{ connectionId, stats }`. + */ +class Server extends node_events_1.EventEmitter { + /** + * Initializes a new instance of Server class. + * @param options + * @param [options.port] Port where the server will listen. By default 8000. + * @param [options.prepareRequestFunction] Custom function to authenticate proxy requests, + * provide URL to upstream proxy or potentially provide a function that generates a custom response to HTTP requests. + * It accepts a single parameter which is an object: + * ``` + * { + * connectionId: symbol, + * request: http.IncomingMessage, + * username: string, + * password: string, + * hostname: string, + * port: number, + * isHttp: boolean, + * } + * ``` + * and returns an object (or promise resolving to the object) with following form: + * ``` + * { + * requestAuthentication: boolean, + * upstreamProxyUrl: string, + * customResponseFunction: Function, + * } + * ``` + * If `upstreamProxyUrl` is a falsy value, no upstream proxy is used. + * If `prepareRequestFunction` is not set, the proxy server will not require any authentication + * and will not use any upstream proxy. + * If `customResponseFunction` is set, it will be called to generate a custom response to the HTTP request. + * It should not be used together with `upstreamProxyUrl`. + * @param [options.authRealm] Realm used in the Proxy-Authenticate header and also in the 'Server' HTTP header. By default it's `ProxyChain`. + * @param [options.verbose] If true, the server will output logs + */ + constructor(options = {}) { + super(); + Object.defineProperty(this, "port", { + enumerable: true, + configurable: true, + writable: true, + value: void 0 + }); + Object.defineProperty(this, "host", { + enumerable: true, + configurable: true, + writable: true, + value: void 0 + }); + Object.defineProperty(this, "prepareRequestFunction", { + enumerable: true, + configurable: true, + writable: true, + value: void 0 + }); + Object.defineProperty(this, "authRealm", { + enumerable: true, + configurable: true, + writable: true, + value: void 0 + }); + Object.defineProperty(this, "verbose", { + enumerable: true, + configurable: true, + writable: true, + value: void 0 + }); + Object.defineProperty(this, "server", { + enumerable: true, + configurable: true, + writable: true, + value: void 0 + }); + Object.defineProperty(this, "lastHandlerId", { + enumerable: true, + configurable: true, + writable: true, + value: void 0 + }); + Object.defineProperty(this, "stats", { + enumerable: true, + configurable: true, + writable: true, + value: void 0 + }); + Object.defineProperty(this, "connections", { + enumerable: true, + configurable: true, + writable: true, + value: void 0 + }); + if (options.port === undefined || options.port === null) { + this.port = DEFAULT_PROXY_SERVER_PORT; + } + else { + this.port = options.port; + } + this.host = options.host; + this.prepareRequestFunction = options.prepareRequestFunction; + this.authRealm = options.authRealm || DEFAULT_AUTH_REALM; + this.verbose = !!options.verbose; + this.server = node_http_1.default.createServer(); + this.server.on('clientError', this.onClientError.bind(this)); + this.server.on('request', this.onRequest.bind(this)); + this.server.on('connect', this.onConnect.bind(this)); + this.server.on('connection', this.onConnection.bind(this)); + this.lastHandlerId = 0; + this.stats = { + httpRequestCount: 0, + connectRequestCount: 0, + trafficUsedInBytes: 0 + }; + this.connections = new Map(); + } + log(connectionId, str) { + if (this.verbose) { + const logPrefix = connectionId != null ? `${String(connectionId)} | ` : ''; + // eslint-disable-next-line no-console + console.log(`ProxyServer[${this.port}]: ${logPrefix}${str}`); + } + } + onClientError(err, socket) { + this.log(socket.proxyChainId, `onClientError: ${err}`); + // https://nodejs.org/api/http.html#http_event_clienterror + if (err.code === 'ECONNRESET' || !socket.writable) { + return; + } + this.sendSocketResponse(socket, 400, {}, 'Invalid request'); + } + /** + * Assigns a unique ID to the socket and keeps the register up to date. + * Needed for abrupt close of the server. + */ + registerConnection(socket) { + const unique = this.lastHandlerId++; + socket.proxyChainId = unique; + this.connections.set(unique, socket); + socket.on('close', () => { + const socketStats = this.getConnectionStats(unique); + this.emit('connectionClosed', { + connectionId: unique, + stats: socketStats, + }); + if (socketStats) { + this.stats.trafficUsedInBytes += socketStats.srcRxBytes + socketStats.srcTxBytes + (socketStats.trgRxBytes || 0) + (socketStats.trgTxBytes || 0); + } + this.connections.delete(unique); + }); + // We have to manually destroy the socket if it timeouts. + // This will prevent connections from leaking and close them properly. + socket.on('timeout', () => { + socket.destroy(); + }); + } + /** + * Registering total stats each server + */ + /** + * Handles incoming sockets, useful for error handling + */ + onConnection(socket) { + // https://github.com/nodejs/node/issues/23858 + if (!socket.remoteAddress) { + socket.destroy(); + return; + } + this.registerConnection(socket); + // We need to consume socket errors, because the handlers are attached asynchronously. + // See https://github.com/apify/proxy-chain/issues/53 + socket.on('error', (err) => { + // Handle errors only if there's no other handler + if (this.listenerCount('error') === 1) { + this.log(socket.proxyChainId, `Source socket emitted error: ${err.stack || err}`); + } + }); + } + /** + * Converts known errors to be instance of RequestError. + */ + normalizeHandlerError(error) { + if (error.message === 'Username contains an invalid colon') { + return new request_error_1.RequestError('Invalid colon in username in upstream proxy credentials', statuses_1.badGatewayStatusCodes.AUTH_FAILED); + } + if (error.message === '407 Proxy Authentication Required') { + return new request_error_1.RequestError('Invalid upstream proxy credentials', statuses_1.badGatewayStatusCodes.AUTH_FAILED); + } + return error; + } + /** + * Handles normal HTTP request by forwarding it to target host or the upstream proxy. + */ + async onRequest(request, response) { + try { + const handlerOpts = await this.prepareRequestHandling(request); + handlerOpts.srcResponse = response; + const { proxyChainId } = request.socket; + if (handlerOpts.customResponseFunction) { + this.log(proxyChainId, 'Using handleCustomResponse()'); + await (0, custom_response_1.handleCustomResponse)(request, response, handlerOpts); + return; + } + if (handlerOpts.upstreamProxyUrlParsed && exports.SOCKS_PROTOCOLS.includes(handlerOpts.upstreamProxyUrlParsed.protocol)) { + this.log(proxyChainId, 'Using forwardSocks()'); + await (0, forward_socks_1.forwardSocks)(request, response, handlerOpts); + return; + } + this.log(proxyChainId, 'Using forward()'); + await (0, forward_1.forward)(request, response, handlerOpts); + } + catch (error) { + this.failRequest(request, this.normalizeHandlerError(error)); + } + } + /** + * Handles HTTP CONNECT request by setting up a tunnel either to target host or to the upstream proxy. + * @param request + * @param socket + * @param head The first packet of the tunneling stream (may be empty) + */ + async onConnect(request, socket, head) { + try { + const handlerOpts = await this.prepareRequestHandling(request); + handlerOpts.srcHead = head; + const data = { request, sourceSocket: socket, head, handlerOpts: handlerOpts, server: this, isPlain: false }; + if (handlerOpts.customConnectServer) { + socket.unshift(head); // See chain.ts for why we do this + await (0, custom_connect_1.customConnect)(socket, handlerOpts.customConnectServer); + return; + } + if (handlerOpts.upstreamProxyUrlParsed) { + if (exports.SOCKS_PROTOCOLS.includes(handlerOpts.upstreamProxyUrlParsed.protocol)) { + this.log(socket.proxyChainId, `Using chainSocks() => ${request.url}`); + await (0, chain_socks_1.chainSocks)(data); + return; + } + this.log(socket.proxyChainId, `Using chain() => ${request.url}`); + (0, chain_1.chain)(data); + return; + } + this.log(socket.proxyChainId, `Using direct() => ${request.url}`); + (0, direct_1.direct)(data); + } + catch (error) { + this.failRequest(request, this.normalizeHandlerError(error)); + } + } + /** + * Prepares handler options from a request. + * @see {prepareRequestHandling} + */ + getHandlerOpts(request) { + const handlerOpts = { + server: this, + id: request.socket.proxyChainId, + srcRequest: request, + srcHead: null, + trgParsed: null, + upstreamProxyUrlParsed: null, + ignoreUpstreamProxyCertificate: false, + isHttp: false, + srcResponse: null, + customResponseFunction: null, + customConnectServer: null, + }; + this.log(request.socket.proxyChainId, `!!! Handling ${request.method} ${request.url} HTTP/${request.httpVersion}`); + if (request.method === 'CONNECT') { + // CONNECT server.example.com:80 HTTP/1.1 + try { + handlerOpts.trgParsed = new node_url_1.URL(`connect://${request.url}`); + } + catch { + throw new request_error_1.RequestError(`Target "${request.url}" could not be parsed`, 400); + } + if (!handlerOpts.trgParsed.hostname || !handlerOpts.trgParsed.port) { + throw new request_error_1.RequestError(`Target "${request.url}" could not be parsed`, 400); + } + this.stats.connectRequestCount++; + } + else { + // The request should look like: + // GET http://server.example.com:80/some-path HTTP/1.1 + // Note that RFC 7230 says: + // "When making a request to a proxy, other than a CONNECT or server-wide + // OPTIONS request (as detailed below), a client MUST send the target + // URI in absolute-form as the request-target" + let parsed; + try { + parsed = new node_url_1.URL(request.url); + } + catch { + // If URL is invalid, throw HTTP 400 error + throw new request_error_1.RequestError(`Target "${request.url}" could not be parsed`, 400); + } + // Only HTTP is supported, other protocols such as HTTP or FTP must use the CONNECT method + if (parsed.protocol !== 'http:') { + throw new request_error_1.RequestError(`Only HTTP protocol is supported (was ${parsed.protocol})`, 400); + } + handlerOpts.trgParsed = parsed; + handlerOpts.isHttp = true; + this.stats.httpRequestCount++; + } + return handlerOpts; + } + /** + * Calls `this.prepareRequestFunction` with normalized options. + * @param request + * @param handlerOpts + */ + async callPrepareRequestFunction(request, handlerOpts) { + if (this.prepareRequestFunction) { + const funcOpts = { + connectionId: request.socket.proxyChainId, + request, + username: '', + password: '', + hostname: handlerOpts.trgParsed.hostname, + port: (0, normalize_url_port_1.normalizeUrlPort)(handlerOpts.trgParsed), + isHttp: handlerOpts.isHttp, + }; + // Authenticate the request using a user function (if provided) + const proxyAuth = request.headers['proxy-authorization']; + if (proxyAuth) { + const auth = (0, parse_authorization_header_1.parseAuthorizationHeader)(proxyAuth); + if (!auth) { + throw new request_error_1.RequestError('Invalid "Proxy-Authorization" header', 400); + } + // https://datatracker.ietf.org/doc/html/rfc7617#page-3 + // Note that both scheme and parameter names are matched case- + // insensitively. + if (auth.type.toLowerCase() !== 'basic') { + throw new request_error_1.RequestError('The "Proxy-Authorization" header must have the "Basic" type.', 400); + } + funcOpts.username = auth.username; + funcOpts.password = auth.password; + } + const result = await this.prepareRequestFunction(funcOpts); + return result !== null && result !== void 0 ? result : {}; + } + return {}; + } + /** + * Authenticates a new request and determines upstream proxy URL using the user function. + * Returns a promise resolving to an object that can be used to run a handler. + * @param request + */ + async prepareRequestHandling(request) { + const handlerOpts = this.getHandlerOpts(request); + const funcResult = await this.callPrepareRequestFunction(request, handlerOpts); + handlerOpts.localAddress = funcResult.localAddress; + 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) { + throw new request_error_1.RequestError(funcResult.failMsg || 'Proxy credentials required.', 407); + } + if (funcResult.upstreamProxyUrl) { + try { + handlerOpts.upstreamProxyUrlParsed = new node_url_1.URL(funcResult.upstreamProxyUrl); + } + catch (error) { + throw new Error(`Invalid "upstreamProxyUrl" provided: ${error} (was "${funcResult.upstreamProxyUrl}"`); + } + if (!['http:', 'https:', ...exports.SOCKS_PROTOCOLS].includes(handlerOpts.upstreamProxyUrlParsed.protocol)) { + throw new Error(`Invalid "upstreamProxyUrl" provided: URL must have one of the following protocols: "http", "https", ${exports.SOCKS_PROTOCOLS.map((p) => `"${p.replace(':', '')}"`).join(', ')} (was "${funcResult.upstreamProxyUrl}")`); + } + } + if (funcResult.ignoreUpstreamProxyCertificate !== undefined) { + handlerOpts.ignoreUpstreamProxyCertificate = funcResult.ignoreUpstreamProxyCertificate; + } + const { proxyChainId } = request.socket; + if (funcResult.customResponseFunction) { + this.log(proxyChainId, 'Using custom response function'); + handlerOpts.customResponseFunction = funcResult.customResponseFunction; + if (!handlerOpts.isHttp) { + throw new Error('The "customResponseFunction" option can only be used for HTTP requests.'); + } + if (typeof (handlerOpts.customResponseFunction) !== 'function') { + throw new Error('The "customResponseFunction" option must be a function.'); + } + } + if (handlerOpts.upstreamProxyUrlParsed) { + this.log(proxyChainId, `Using upstream proxy ${(0, redact_url_1.redactUrl)(handlerOpts.upstreamProxyUrlParsed)}`); + } + return handlerOpts; + } + /** + * Sends a HTTP error response to the client. + * @param request + * @param error + */ + failRequest(request, error) { + const { proxyChainId } = request.socket; + if (error.name === 'RequestError') { + const typedError = error; + this.log(proxyChainId, `Request failed (status ${typedError.statusCode}): ${error.message}`); + this.sendSocketResponse(request.socket, typedError.statusCode, typedError.headers, error.message); + } + else { + this.log(proxyChainId, `Request failed with error: ${error.stack || error}`); + this.sendSocketResponse(request.socket, 500, {}, 'Internal error in proxy server'); + this.emit('requestFailed', { error, request }); + } + this.log(proxyChainId, 'Closing because request failed with error'); + } + /** + * Sends a simple HTTP response to the client and forcibly closes the connection. + * This invalidates the ServerResponse instance (if present). + * We don't know the state of the response anyway. + * Writing directly to the socket seems to be the easiest solution. + * @param socket + * @param statusCode + * @param headers + * @param message + */ + sendSocketResponse(socket, statusCode = 500, caseSensitiveHeaders = {}, message = '') { + try { + const headers = Object.fromEntries(Object.entries(caseSensitiveHeaders).map(([name, value]) => [name.toLowerCase(), value])); + headers.connection = 'close'; + headers.date = (new Date()).toUTCString(); + headers['content-length'] = String(node_buffer_1.Buffer.byteLength(message)); + headers.server = headers.server || this.authRealm; + headers['content-type'] = headers['content-type'] || 'text/plain; charset=utf-8'; + if (statusCode === 407 && !headers['proxy-authenticate']) { + headers['proxy-authenticate'] = `Basic realm="${this.authRealm}"`; + } + let msg = `HTTP/1.1 ${statusCode} ${node_http_1.default.STATUS_CODES[statusCode] || 'Unknown Status Code'}\r\n`; + for (const [key, value] of Object.entries(headers)) { + msg += `${key}: ${value}\r\n`; + } + msg += `\r\n${message}`; + // Unfortunately it's not possible to send RST in Node.js yet. + // See https://github.com/nodejs/node/issues/27428 + socket.setTimeout(1000, () => { + socket.destroy(); + }); + // This sends FIN, meaning we still can receive data. + socket.end(msg); + } + catch (err) { + this.log(socket.proxyChainId, `Unhandled error in sendResponse(), will be ignored: ${err.stack || err}`); + } + } + /** + * Starts listening at a port specified in the constructor. + */ + async listen(callback) { + const promise = new Promise((resolve, reject) => { + // Unfortunately server.listen() is not a normal function that fails on error, + // so we need this trickery + const onError = (error) => { + this.log(null, `Listen failed: ${error}`); + removeListeners(); + reject(error); + }; + const onListening = () => { + this.port = this.server.address().port; + this.log(null, 'Listening...'); + removeListeners(); + resolve(); + }; + const removeListeners = () => { + this.server.removeListener('error', onError); + this.server.removeListener('listening', onListening); + }; + this.server.on('error', onError); + this.server.on('listening', onListening); + this.server.listen(this.port, this.host); + }); + return (0, nodeify_1.nodeify)(promise, callback); + } + /** + * Gets array of IDs of all active connections. + */ + getConnectionIds() { + return [...this.connections.keys()]; + } + /** + * Gets data transfer statistics of a specific proxy connection. + */ + getConnectionStats(connectionId) { + const socket = this.connections.get(connectionId); + if (!socket) + return undefined; + const targetStats = (0, count_target_bytes_1.getTargetStats)(socket); + const result = { + srcTxBytes: socket.bytesWritten, + srcRxBytes: socket.bytesRead, + trgTxBytes: targetStats.bytesWritten, + trgRxBytes: targetStats.bytesRead, + }; + return result; + } + /** + * Forcibly close a specific pending proxy connection. + */ + closeConnection(connectionId) { + this.log(null, 'Closing pending socket'); + const socket = this.connections.get(connectionId); + if (!socket) + return; + socket.destroy(); + this.log(null, `Destroyed pending socket`); + } + /** + * Forcibly closes pending proxy connections. + */ + closeConnections() { + this.log(null, 'Closing pending sockets'); + for (const socket of this.connections.values()) { + socket.destroy(); + } + this.log(null, `Destroyed ${this.connections.size} pending sockets`); + } + /** + * Closes the proxy server. + * @param closeConnections If true, pending proxy connections are forcibly closed. + */ + async close(closeConnections, callback) { + if (typeof closeConnections === 'function') { + callback = closeConnections; + closeConnections = false; + } + if (closeConnections) { + this.closeConnections(); + } + if (this.server) { + const { server } = this; + // @ts-expect-error Let's make sure we can't access the server anymore. + this.server = null; + const promise = node_util_1.default.promisify(server.close).bind(server)(); + return (0, nodeify_1.nodeify)(promise, callback); + } + return (0, nodeify_1.nodeify)(Promise.resolve(), callback); + } +} +exports.Server = Server; +//# sourceMappingURL=server.js.map \ No newline at end of file diff --git a/dist/server.js.map b/dist/server.js.map new file mode 100644 index 00000000..b2b1fe2c --- /dev/null +++ b/dist/server.js.map @@ -0,0 +1 @@ +{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/socket.d.ts b/dist/socket.d.ts new file mode 100644 index 00000000..ef524179 --- /dev/null +++ b/dist/socket.d.ts @@ -0,0 +1,11 @@ +/// +/// +import type net from 'node:net'; +import type tls from 'node:tls'; +type AdditionalProps = { + proxyChainId?: number; +}; +export type Socket = net.Socket & AdditionalProps; +export type TLSSocket = tls.TLSSocket & AdditionalProps; +export {}; +//# sourceMappingURL=socket.d.ts.map \ No newline at end of file diff --git a/dist/socket.d.ts.map b/dist/socket.d.ts.map new file mode 100644 index 00000000..76fd7e8a --- /dev/null +++ b/dist/socket.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"socket.d.ts","sourceRoot":"","sources":["../src/socket.ts"],"names":[],"mappings":";;AAAA,OAAO,KAAK,GAAG,MAAM,UAAU,CAAC;AAChC,OAAO,KAAK,GAAG,MAAM,UAAU,CAAC;AAEhC,KAAK,eAAe,GAAG;IAAE,YAAY,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAEjD,MAAM,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,GAAG,eAAe,CAAC;AAClD,MAAM,MAAM,SAAS,GAAG,GAAG,CAAC,SAAS,GAAG,eAAe,CAAC"} \ No newline at end of file diff --git a/dist/socket.js b/dist/socket.js new file mode 100644 index 00000000..ca957cb3 --- /dev/null +++ b/dist/socket.js @@ -0,0 +1,3 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +//# sourceMappingURL=socket.js.map \ No newline at end of file diff --git a/dist/socket.js.map b/dist/socket.js.map new file mode 100644 index 00000000..b3db2857 --- /dev/null +++ b/dist/socket.js.map @@ -0,0 +1 @@ +{"version":3,"file":"socket.js","sourceRoot":"","sources":["../src/socket.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/dist/statuses.d.ts b/dist/statuses.d.ts new file mode 100644 index 00000000..3dd9d9a1 --- /dev/null +++ b/dist/statuses.d.ts @@ -0,0 +1,46 @@ +type HttpStatusCode = number; +export declare const badGatewayStatusCodes: { + /** + * Upstream has timed out. + */ + readonly TIMEOUT: 504; + /** + * Upstream responded with non-200 status code. + */ + readonly NON_200: 590; + /** + * Upstream respondend with status code different than 100-999. + */ + readonly STATUS_CODE_OUT_OF_RANGE: 592; + /** + * DNS lookup failed - EAI_NODATA or EAI_NONAME. + */ + readonly NOT_FOUND: 593; + /** + * Upstream refused connection. + */ + readonly CONNECTION_REFUSED: 594; + /** + * Connection reset due to loss of connection or timeout. + */ + readonly CONNECTION_RESET: 595; + /** + * Trying to write on a closed socket. + */ + readonly BROKEN_PIPE: 596; + /** + * Incorrect upstream credentials. + */ + readonly AUTH_FAILED: 597; + /** + * Generic upstream error. + */ + readonly GENERIC_ERROR: 599; +}; +export declare const createCustomStatusHttpResponse: (statusCode: number, statusMessage: string, message?: string) => string; +export declare const errorCodeToStatusCode: { + [errorCode: string]: HttpStatusCode | undefined; +}; +export declare const socksErrorMessageToStatusCode: (socksErrorMessage: string) => (typeof badGatewayStatusCodes)[keyof typeof badGatewayStatusCodes]; +export {}; +//# sourceMappingURL=statuses.d.ts.map \ No newline at end of file diff --git a/dist/statuses.d.ts.map b/dist/statuses.d.ts.map new file mode 100644 index 00000000..ca498248 --- /dev/null +++ b/dist/statuses.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"statuses.d.ts","sourceRoot":"","sources":["../src/statuses.ts"],"names":[],"mappings":"AAEA,KAAK,cAAc,GAAG,MAAM,CAAC;AAE7B,eAAO,MAAM,qBAAqB;IAC9B;;OAEG;;IAEH;;OAEG;;IAEH;;OAEG;;IAEH;;OAEG;;IAEH;;OAEG;;IAEH;;OAEG;;IAEH;;OAEG;;IAEH;;OAEG;;IAEH;;OAEG;;CAEG,CAAC;AAWX,eAAO,MAAM,8BAA8B,eAAgB,MAAM,iBAAiB,MAAM,6BASvF,CAAC;AAGF,eAAO,MAAM,qBAAqB,EAAE;IAAC,CAAC,SAAS,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS,CAAA;CAM1E,CAAC;AAEX,eAAO,MAAM,6BAA6B,sBAAuB,MAAM,KAAG,CAAA,4BAA4B,EAAC,MAAM,4BAA4B,CASxI,CAAC"} \ No newline at end of file diff --git a/dist/statuses.js b/dist/statuses.js new file mode 100644 index 00000000..ce9fdd56 --- /dev/null +++ b/dist/statuses.js @@ -0,0 +1,81 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.socksErrorMessageToStatusCode = exports.errorCodeToStatusCode = exports.createCustomStatusHttpResponse = exports.badGatewayStatusCodes = void 0; +const node_http_1 = require("node:http"); +exports.badGatewayStatusCodes = { + /** + * Upstream has timed out. + */ + TIMEOUT: 504, + /** + * Upstream responded with non-200 status code. + */ + NON_200: 590, + /** + * Upstream respondend with status code different than 100-999. + */ + STATUS_CODE_OUT_OF_RANGE: 592, + /** + * DNS lookup failed - EAI_NODATA or EAI_NONAME. + */ + NOT_FOUND: 593, + /** + * Upstream refused connection. + */ + CONNECTION_REFUSED: 594, + /** + * Connection reset due to loss of connection or timeout. + */ + CONNECTION_RESET: 595, + /** + * Trying to write on a closed socket. + */ + BROKEN_PIPE: 596, + /** + * Incorrect upstream credentials. + */ + AUTH_FAILED: 597, + /** + * Generic upstream error. + */ + GENERIC_ERROR: 599, +}; +node_http_1.STATUS_CODES['590'] = 'Non Successful'; +node_http_1.STATUS_CODES['592'] = 'Status Code Out Of Range'; +node_http_1.STATUS_CODES['593'] = 'Not Found'; +node_http_1.STATUS_CODES['594'] = 'Connection Refused'; +node_http_1.STATUS_CODES['595'] = 'Connection Reset'; +node_http_1.STATUS_CODES['596'] = 'Broken Pipe'; +node_http_1.STATUS_CODES['597'] = 'Auth Failed'; +node_http_1.STATUS_CODES['599'] = 'Upstream Error'; +const createCustomStatusHttpResponse = (statusCode, statusMessage, message = '') => { + return [ + `HTTP/1.1 ${statusCode} ${statusMessage || node_http_1.STATUS_CODES[statusCode] || 'Unknown Status Code'}`, + 'Connection: close', + `Date: ${(new Date()).toUTCString()}`, + `Content-Length: ${Buffer.byteLength(message)}`, + ``, + message, + ].join('\r\n'); +}; +exports.createCustomStatusHttpResponse = createCustomStatusHttpResponse; +// https://nodejs.org/api/errors.html#common-system-errors +exports.errorCodeToStatusCode = { + ENOTFOUND: exports.badGatewayStatusCodes.NOT_FOUND, + ECONNREFUSED: exports.badGatewayStatusCodes.CONNECTION_REFUSED, + ECONNRESET: exports.badGatewayStatusCodes.CONNECTION_RESET, + EPIPE: exports.badGatewayStatusCodes.BROKEN_PIPE, + ETIMEDOUT: exports.badGatewayStatusCodes.TIMEOUT, +}; +const socksErrorMessageToStatusCode = (socksErrorMessage) => { + switch (socksErrorMessage) { + case 'Proxy connection timed out': + return exports.badGatewayStatusCodes.TIMEOUT; + case 'Socks5 Authentication failed': + return exports.badGatewayStatusCodes.AUTH_FAILED; + default: + return exports.badGatewayStatusCodes.GENERIC_ERROR; + } +}; +exports.socksErrorMessageToStatusCode = socksErrorMessageToStatusCode; +//# sourceMappingURL=statuses.js.map \ No newline at end of file diff --git a/dist/statuses.js.map b/dist/statuses.js.map new file mode 100644 index 00000000..02820bd1 --- /dev/null +++ b/dist/statuses.js.map @@ -0,0 +1 @@ +{"version":3,"file":"statuses.js","sourceRoot":"","sources":["../src/statuses.ts"],"names":[],"mappings":";;;AAAA,yCAAyC;AAI5B,QAAA,qBAAqB,GAAG;IACjC;;OAEG;IACH,OAAO,EAAE,GAAG;IACZ;;OAEG;IACH,OAAO,EAAE,GAAG;IACZ;;OAEG;IACH,wBAAwB,EAAE,GAAG;IAC7B;;OAEG;IACH,SAAS,EAAE,GAAG;IACd;;OAEG;IACH,kBAAkB,EAAE,GAAG;IACvB;;OAEG;IACH,gBAAgB,EAAE,GAAG;IACrB;;OAEG;IACH,WAAW,EAAE,GAAG;IAChB;;OAEG;IACH,WAAW,EAAE,GAAG;IAChB;;OAEG;IACH,aAAa,EAAE,GAAG;CACZ,CAAC;AAEX,wBAAY,CAAC,KAAK,CAAC,GAAG,gBAAgB,CAAC;AACvC,wBAAY,CAAC,KAAK,CAAC,GAAG,0BAA0B,CAAC;AACjD,wBAAY,CAAC,KAAK,CAAC,GAAG,WAAW,CAAC;AAClC,wBAAY,CAAC,KAAK,CAAC,GAAG,oBAAoB,CAAC;AAC3C,wBAAY,CAAC,KAAK,CAAC,GAAG,kBAAkB,CAAC;AACzC,wBAAY,CAAC,KAAK,CAAC,GAAG,aAAa,CAAC;AACpC,wBAAY,CAAC,KAAK,CAAC,GAAG,aAAa,CAAC;AACpC,wBAAY,CAAC,KAAK,CAAC,GAAG,gBAAgB,CAAC;AAEhC,MAAM,8BAA8B,GAAG,CAAC,UAAkB,EAAE,aAAqB,EAAE,OAAO,GAAG,EAAE,EAAE,EAAE;IACtG,OAAO;QACH,YAAY,UAAU,IAAI,aAAa,IAAI,wBAAY,CAAC,UAAU,CAAC,IAAI,qBAAqB,EAAE;QAC9F,mBAAmB;QACnB,SAAS,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE,EAAE;QACrC,mBAAmB,MAAM,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE;QAC/C,EAAE;QACF,OAAO;KACV,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;AACnB,CAAC,CAAC;AATW,QAAA,8BAA8B,kCASzC;AAEF,0DAA0D;AAC7C,QAAA,qBAAqB,GAAsD;IACpF,SAAS,EAAE,6BAAqB,CAAC,SAAS;IAC1C,YAAY,EAAE,6BAAqB,CAAC,kBAAkB;IACtD,UAAU,EAAE,6BAAqB,CAAC,gBAAgB;IAClD,KAAK,EAAE,6BAAqB,CAAC,WAAW;IACxC,SAAS,EAAE,6BAAqB,CAAC,OAAO;CAClC,CAAC;AAEJ,MAAM,6BAA6B,GAAG,CAAC,iBAAyB,EAAoE,EAAE;IACzI,QAAQ,iBAAiB,EAAE;QACvB,KAAK,4BAA4B;YAC7B,OAAO,6BAAqB,CAAC,OAAO,CAAC;QACzC,KAAK,8BAA8B;YAC/B,OAAO,6BAAqB,CAAC,WAAW,CAAC;QAC7C;YACI,OAAO,6BAAqB,CAAC,aAAa,CAAC;KAClD;AACL,CAAC,CAAC;AATW,QAAA,6BAA6B,iCASxC"} \ No newline at end of file diff --git a/dist/tcp_tunnel_tools.d.ts b/dist/tcp_tunnel_tools.d.ts new file mode 100644 index 00000000..4ca3898a --- /dev/null +++ b/dist/tcp_tunnel_tools.d.ts @@ -0,0 +1,6 @@ +export declare function createTunnel(proxyUrl: string, targetHost: string, options?: { + verbose?: boolean; + ignoreProxyCertificate?: boolean; +}, callback?: (error: Error | null, result?: string) => void): Promise; +export declare function closeTunnel(serverPath: string, closeConnections: boolean | undefined, callback: (error: Error | null, result?: boolean) => void): Promise; +//# sourceMappingURL=tcp_tunnel_tools.d.ts.map \ No newline at end of file diff --git a/dist/tcp_tunnel_tools.d.ts.map b/dist/tcp_tunnel_tools.d.ts.map new file mode 100644 index 00000000..51082739 --- /dev/null +++ b/dist/tcp_tunnel_tools.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"tcp_tunnel_tools.d.ts","sourceRoot":"","sources":["../src/tcp_tunnel_tools.ts"],"names":[],"mappings":"AAkBA,wBAAsB,YAAY,CAC9B,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,EAClB,OAAO,CAAC,EAAE;IACN,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,sBAAsB,CAAC,EAAE,OAAO,CAAC;CACpC,EACD,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI,EAAE,MAAM,CAAC,EAAE,MAAM,KAAK,IAAI,GAC1D,OAAO,CAAC,MAAM,CAAC,CAsEjB;AAED,wBAAsB,WAAW,CAC7B,UAAU,EAAE,MAAM,EAClB,gBAAgB,EAAE,OAAO,GAAG,SAAS,EACrC,QAAQ,EAAE,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI,EAAE,MAAM,CAAC,EAAE,OAAO,KAAK,IAAI,GAC1D,OAAO,CAAC,OAAO,CAAC,CA+BlB"} \ No newline at end of file diff --git a/dist/tcp_tunnel_tools.js b/dist/tcp_tunnel_tools.js new file mode 100644 index 00000000..777e54cc --- /dev/null +++ b/dist/tcp_tunnel_tools.js @@ -0,0 +1,104 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.closeTunnel = exports.createTunnel = void 0; +const tslib_1 = require("tslib"); +const node_net_1 = tslib_1.__importDefault(require("node:net")); +const node_url_1 = require("node:url"); +const chain_1 = require("./chain"); +const nodeify_1 = require("./utils/nodeify"); +const runningServers = {}; +const getAddress = (server) => { + const { address: host, port, family } = server.address(); + if (family === 'IPv6') { + return `[${host}]:${port}`; + } + return `${host}:${port}`; +}; +async function createTunnel(proxyUrl, targetHost, options, callback) { + const parsedProxyUrl = new node_url_1.URL(proxyUrl); + if (!['http:', 'https:'].includes(parsedProxyUrl.protocol)) { + throw new Error(`The proxy URL must have the "http" or "https" protocol (was "${proxyUrl}")`); + } + const url = new node_url_1.URL(`connect://${targetHost || ''}`); + if (!url.hostname) { + throw new Error('Missing target hostname'); + } + if (!url.port) { + throw new Error('Missing target port'); + } + const verbose = options && options.verbose; + const server = node_net_1.default.createServer(); + const log = (...args) => { + if (verbose) + console.log(...args); + }; + server.log = log; + server.on('connection', (sourceSocket) => { + var _a; + const remoteAddress = `${sourceSocket.remoteAddress}:${sourceSocket.remotePort}`; + const { connections } = runningServers[getAddress(server)]; + log(`new client connection from ${remoteAddress}`); + sourceSocket.on('close', (hadError) => { + connections.delete(sourceSocket); + log(`connection from ${remoteAddress} closed, hadError=${hadError}`); + }); + connections.add(sourceSocket); + (0, chain_1.chain)({ + request: { url: targetHost }, + sourceSocket, + handlerOpts: { + upstreamProxyUrlParsed: parsedProxyUrl, + ignoreUpstreamProxyCertificate: (_a = options === null || options === void 0 ? void 0 : options.ignoreProxyCertificate) !== null && _a !== void 0 ? _a : false, + }, + server: server, + isPlain: true, + }); + }); + const promise = new Promise((resolve, reject) => { + server.once('error', reject); + // Let the system pick a random listening port + server.listen(0, () => { + const address = getAddress(server); + server.off('error', reject); + runningServers[address] = { server, connections: new Set() }; + log('server listening to ', address); + resolve(address); + }); + }); + return (0, nodeify_1.nodeify)(promise, callback); +} +exports.createTunnel = createTunnel; +async function closeTunnel(serverPath, closeConnections, callback) { + const { hostname, port } = new node_url_1.URL(`tcp://${serverPath}`); + if (!hostname) + throw new Error('serverPath must contain hostname'); + if (!port) + throw new Error('serverPath must contain port'); + const promise = new Promise((resolve) => { + if (!runningServers[serverPath]) { + resolve(false); + return; + } + if (!closeConnections) { + resolve(true); + return; + } + for (const connection of runningServers[serverPath].connections) { + connection.destroy(); + } + resolve(true); + }) + .then(async (serverExists) => new Promise((resolve) => { + if (!serverExists) { + resolve(false); + return; + } + runningServers[serverPath].server.close(() => { + delete runningServers[serverPath]; + resolve(true); + }); + })); + return (0, nodeify_1.nodeify)(promise, callback); +} +exports.closeTunnel = closeTunnel; +//# sourceMappingURL=tcp_tunnel_tools.js.map \ No newline at end of file diff --git a/dist/tcp_tunnel_tools.js.map b/dist/tcp_tunnel_tools.js.map new file mode 100644 index 00000000..46a7d33c --- /dev/null +++ b/dist/tcp_tunnel_tools.js.map @@ -0,0 +1 @@ +{"version":3,"file":"tcp_tunnel_tools.js","sourceRoot":"","sources":["../src/tcp_tunnel_tools.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/dist/tsconfig.tsbuildinfo b/dist/tsconfig.tsbuildinfo new file mode 100644 index 00000000..0967aa23 --- /dev/null +++ b/dist/tsconfig.tsbuildinfo @@ -0,0 +1 @@ +{"program":{"fileNames":["../node_modules/typescript/lib/lib.es5.d.ts","../node_modules/typescript/lib/lib.es2015.d.ts","../node_modules/typescript/lib/lib.es2016.d.ts","../node_modules/typescript/lib/lib.es2017.d.ts","../node_modules/typescript/lib/lib.es2018.d.ts","../node_modules/typescript/lib/lib.es2019.d.ts","../node_modules/typescript/lib/lib.es2020.d.ts","../node_modules/typescript/lib/lib.es2021.d.ts","../node_modules/typescript/lib/lib.es2022.d.ts","../node_modules/typescript/lib/lib.esnext.d.ts","../node_modules/typescript/lib/lib.es2015.core.d.ts","../node_modules/typescript/lib/lib.es2015.collection.d.ts","../node_modules/typescript/lib/lib.es2015.generator.d.ts","../node_modules/typescript/lib/lib.es2015.iterable.d.ts","../node_modules/typescript/lib/lib.es2015.promise.d.ts","../node_modules/typescript/lib/lib.es2015.proxy.d.ts","../node_modules/typescript/lib/lib.es2015.reflect.d.ts","../node_modules/typescript/lib/lib.es2015.symbol.d.ts","../node_modules/typescript/lib/lib.es2015.symbol.wellknown.d.ts","../node_modules/typescript/lib/lib.es2016.array.include.d.ts","../node_modules/typescript/lib/lib.es2017.object.d.ts","../node_modules/typescript/lib/lib.es2017.sharedmemory.d.ts","../node_modules/typescript/lib/lib.es2017.string.d.ts","../node_modules/typescript/lib/lib.es2017.intl.d.ts","../node_modules/typescript/lib/lib.es2017.typedarrays.d.ts","../node_modules/typescript/lib/lib.es2018.asyncgenerator.d.ts","../node_modules/typescript/lib/lib.es2018.asynciterable.d.ts","../node_modules/typescript/lib/lib.es2018.intl.d.ts","../node_modules/typescript/lib/lib.es2018.promise.d.ts","../node_modules/typescript/lib/lib.es2018.regexp.d.ts","../node_modules/typescript/lib/lib.es2019.array.d.ts","../node_modules/typescript/lib/lib.es2019.object.d.ts","../node_modules/typescript/lib/lib.es2019.string.d.ts","../node_modules/typescript/lib/lib.es2019.symbol.d.ts","../node_modules/typescript/lib/lib.es2019.intl.d.ts","../node_modules/typescript/lib/lib.es2020.bigint.d.ts","../node_modules/typescript/lib/lib.es2020.date.d.ts","../node_modules/typescript/lib/lib.es2020.promise.d.ts","../node_modules/typescript/lib/lib.es2020.sharedmemory.d.ts","../node_modules/typescript/lib/lib.es2020.string.d.ts","../node_modules/typescript/lib/lib.es2020.symbol.wellknown.d.ts","../node_modules/typescript/lib/lib.es2020.intl.d.ts","../node_modules/typescript/lib/lib.es2020.number.d.ts","../node_modules/typescript/lib/lib.es2021.promise.d.ts","../node_modules/typescript/lib/lib.es2021.string.d.ts","../node_modules/typescript/lib/lib.es2021.weakref.d.ts","../node_modules/typescript/lib/lib.es2021.intl.d.ts","../node_modules/typescript/lib/lib.es2022.array.d.ts","../node_modules/typescript/lib/lib.es2022.error.d.ts","../node_modules/typescript/lib/lib.es2022.intl.d.ts","../node_modules/typescript/lib/lib.es2022.object.d.ts","../node_modules/typescript/lib/lib.es2022.sharedmemory.d.ts","../node_modules/typescript/lib/lib.es2022.string.d.ts","../node_modules/typescript/lib/lib.esnext.intl.d.ts","../node_modules/tslib/tslib.d.ts","../src/socket.ts","../src/statuses.ts","../src/utils/count_target_bytes.ts","../src/utils/decode_uri_component_safe.ts","../src/utils/get_basic.ts","../src/chain.ts","../node_modules/@types/node/compatibility/disposable.d.ts","../node_modules/@types/node/compatibility/indexable.d.ts","../node_modules/@types/node/compatibility/iterators.d.ts","../node_modules/@types/node/compatibility/index.d.ts","../node_modules/@types/node/ts5.6/globals.typedarray.d.ts","../node_modules/@types/node/ts5.6/buffer.buffer.d.ts","../node_modules/buffer/index.d.ts","../node_modules/undici-types/header.d.ts","../node_modules/undici-types/readable.d.ts","../node_modules/undici-types/file.d.ts","../node_modules/undici-types/fetch.d.ts","../node_modules/undici-types/formdata.d.ts","../node_modules/undici-types/connector.d.ts","../node_modules/undici-types/client.d.ts","../node_modules/undici-types/errors.d.ts","../node_modules/undici-types/dispatcher.d.ts","../node_modules/undici-types/global-dispatcher.d.ts","../node_modules/undici-types/global-origin.d.ts","../node_modules/undici-types/pool-stats.d.ts","../node_modules/undici-types/pool.d.ts","../node_modules/undici-types/handlers.d.ts","../node_modules/undici-types/balanced-pool.d.ts","../node_modules/undici-types/agent.d.ts","../node_modules/undici-types/mock-interceptor.d.ts","../node_modules/undici-types/mock-agent.d.ts","../node_modules/undici-types/mock-client.d.ts","../node_modules/undici-types/mock-pool.d.ts","../node_modules/undici-types/mock-errors.d.ts","../node_modules/undici-types/proxy-agent.d.ts","../node_modules/undici-types/api.d.ts","../node_modules/undici-types/cookies.d.ts","../node_modules/undici-types/patch.d.ts","../node_modules/undici-types/filereader.d.ts","../node_modules/undici-types/diagnostics-channel.d.ts","../node_modules/undici-types/websocket.d.ts","../node_modules/undici-types/content-type.d.ts","../node_modules/undici-types/cache.d.ts","../node_modules/undici-types/interceptors.d.ts","../node_modules/undici-types/index.d.ts","../node_modules/@types/node/globals.d.ts","../node_modules/@types/node/assert.d.ts","../node_modules/@types/node/assert/strict.d.ts","../node_modules/@types/node/async_hooks.d.ts","../node_modules/@types/node/buffer.d.ts","../node_modules/@types/node/child_process.d.ts","../node_modules/@types/node/cluster.d.ts","../node_modules/@types/node/console.d.ts","../node_modules/@types/node/constants.d.ts","../node_modules/@types/node/crypto.d.ts","../node_modules/@types/node/dgram.d.ts","../node_modules/@types/node/diagnostics_channel.d.ts","../node_modules/@types/node/dns.d.ts","../node_modules/@types/node/dns/promises.d.ts","../node_modules/@types/node/domain.d.ts","../node_modules/@types/node/dom-events.d.ts","../node_modules/@types/node/events.d.ts","../node_modules/@types/node/fs.d.ts","../node_modules/@types/node/fs/promises.d.ts","../node_modules/@types/node/http.d.ts","../node_modules/@types/node/http2.d.ts","../node_modules/@types/node/https.d.ts","../node_modules/@types/node/inspector.d.ts","../node_modules/@types/node/module.d.ts","../node_modules/@types/node/net.d.ts","../node_modules/@types/node/os.d.ts","../node_modules/@types/node/path.d.ts","../node_modules/@types/node/perf_hooks.d.ts","../node_modules/@types/node/process.d.ts","../node_modules/@types/node/punycode.d.ts","../node_modules/@types/node/querystring.d.ts","../node_modules/@types/node/readline.d.ts","../node_modules/@types/node/readline/promises.d.ts","../node_modules/@types/node/repl.d.ts","../node_modules/@types/node/stream.d.ts","../node_modules/@types/node/stream/promises.d.ts","../node_modules/@types/node/stream/consumers.d.ts","../node_modules/@types/node/stream/web.d.ts","../node_modules/@types/node/string_decoder.d.ts","../node_modules/@types/node/test.d.ts","../node_modules/@types/node/timers.d.ts","../node_modules/@types/node/timers/promises.d.ts","../node_modules/@types/node/tls.d.ts","../node_modules/@types/node/trace_events.d.ts","../node_modules/@types/node/tty.d.ts","../node_modules/@types/node/url.d.ts","../node_modules/@types/node/util.d.ts","../node_modules/@types/node/v8.d.ts","../node_modules/@types/node/vm.d.ts","../node_modules/@types/node/wasi.d.ts","../node_modules/@types/node/worker_threads.d.ts","../node_modules/@types/node/zlib.d.ts","../node_modules/@types/node/ts5.6/index.d.ts","../node_modules/socks/typings/common/constants.d.ts","../node_modules/socks/typings/common/util.d.ts","../node_modules/socks/typings/client/socksclient.d.ts","../node_modules/socks/typings/index.d.ts","../src/chain_socks.ts","../src/custom_connect.ts","../src/custom_response.ts","../src/direct.ts","../src/utils/is_hop_by_hop_header.ts","../src/utils/valid_headers_only.ts","../src/forward.ts","../node_modules/socks-proxy-agent/node_modules/agent-base/dist/helpers.d.ts","../node_modules/socks-proxy-agent/node_modules/agent-base/dist/index.d.ts","../node_modules/socks-proxy-agent/dist/index.d.ts","../src/forward_socks.ts","../src/request_error.ts","../src/utils/nodeify.ts","../src/utils/normalize_url_port.ts","../src/utils/parse_authorization_header.ts","../src/utils/redact_url.ts","../src/server.ts","../src/anonymize_proxy.ts","../src/tcp_tunnel_tools.ts","../src/index.ts","../node_modules/@types/estree/index.d.ts","../node_modules/@types/istanbul-lib-coverage/index.d.ts","../node_modules/@types/istanbul-lib-report/index.d.ts","../node_modules/@types/istanbul-reports/index.d.ts","../node_modules/@jest/expect-utils/build/index.d.ts","../node_modules/chalk/index.d.ts","../node_modules/@sinclair/typebox/typebox.d.ts","../node_modules/@jest/schemas/build/index.d.ts","../node_modules/pretty-format/build/index.d.ts","../node_modules/jest-diff/build/index.d.ts","../node_modules/jest-matcher-utils/build/index.d.ts","../node_modules/expect/build/index.d.ts","../node_modules/@types/jest/index.d.ts","../node_modules/@types/json-schema/index.d.ts","../node_modules/@types/json5/index.d.ts","../node_modules/@types/responselike/index.d.ts","../node_modules/@types/stack-utils/index.d.ts","../node_modules/@types/yargs-parser/index.d.ts","../node_modules/@types/yargs/index.d.ts","../node_modules/@types/yauzl/index.d.ts"],"fileInfos":[{"version":"8730f4bf322026ff5229336391a18bcaa1f94d4f82416c8b2f3954e2ccaae2ba","affectsGlobalScope":true},"dc47c4fa66b9b9890cf076304de2a9c5201e94b740cffdf09f87296d877d71f6","7a387c58583dfca701b6c85e0adaf43fb17d590fb16d5b2dc0a2fbd89f35c467","8a12173c586e95f4433e0c6dc446bc88346be73ffe9ca6eec7aa63c8f3dca7f9","5f4e733ced4e129482ae2186aae29fde948ab7182844c3a5a51dd346182c7b06","4b421cbfb3a38a27c279dec1e9112c3d1da296f77a1a85ddadf7e7a425d45d18","1fc5ab7a764205c68fa10d381b08417795fc73111d6dd16b5b1ed36badb743d9","746d62152361558ea6d6115cf0da4dd10ede041d14882ede3568bce5dc4b4f1f","d11a03592451da2d1065e09e61f4e2a9bf68f780f4f6623c18b57816a9679d17","aea179452def8a6152f98f63b191b84e7cbd69b0e248c91e61fb2e52328abe8c",{"version":"adb996790133eb33b33aadb9c09f15c2c575e71fb57a62de8bf74dbf59ec7dfb","affectsGlobalScope":true},{"version":"8cc8c5a3bac513368b0157f3d8b31cfdcfe78b56d3724f30f80ed9715e404af8","affectsGlobalScope":true},{"version":"cdccba9a388c2ee3fd6ad4018c640a471a6c060e96f1232062223063b0a5ac6a","affectsGlobalScope":true},{"version":"c5c05907c02476e4bde6b7e76a79ffcd948aedd14b6a8f56e4674221b0417398","affectsGlobalScope":true},{"version":"5f406584aef28a331c36523df688ca3650288d14f39c5d2e555c95f0d2ff8f6f","affectsGlobalScope":true},{"version":"22f230e544b35349cfb3bd9110b6ef37b41c6d6c43c3314a31bd0d9652fcec72","affectsGlobalScope":true},{"version":"7ea0b55f6b315cf9ac2ad622b0a7813315bb6e97bf4bb3fbf8f8affbca7dc695","affectsGlobalScope":true},{"version":"3013574108c36fd3aaca79764002b3717da09725a36a6fc02eac386593110f93","affectsGlobalScope":true},{"version":"eb26de841c52236d8222f87e9e6a235332e0788af8c87a71e9e210314300410a","affectsGlobalScope":true},{"version":"3be5a1453daa63e031d266bf342f3943603873d890ab8b9ada95e22389389006","affectsGlobalScope":true},{"version":"17bb1fc99591b00515502d264fa55dc8370c45c5298f4a5c2083557dccba5a2a","affectsGlobalScope":true},{"version":"7ce9f0bde3307ca1f944119f6365f2d776d281a393b576a18a2f2893a2d75c98","affectsGlobalScope":true},{"version":"6a6b173e739a6a99629a8594bfb294cc7329bfb7b227f12e1f7c11bc163b8577","affectsGlobalScope":true},{"version":"81cac4cbc92c0c839c70f8ffb94eb61e2d32dc1c3cf6d95844ca099463cf37ea","affectsGlobalScope":true},{"version":"b0124885ef82641903d232172577f2ceb5d3e60aed4da1153bab4221e1f6dd4e","affectsGlobalScope":true},{"version":"0eb85d6c590b0d577919a79e0084fa1744c1beba6fd0d4e951432fa1ede5510a","affectsGlobalScope":true},{"version":"da233fc1c8a377ba9e0bed690a73c290d843c2c3d23a7bd7ec5cd3d7d73ba1e0","affectsGlobalScope":true},{"version":"d154ea5bb7f7f9001ed9153e876b2d5b8f5c2bb9ec02b3ae0d239ec769f1f2ae","affectsGlobalScope":true},{"version":"bb2d3fb05a1d2ffbca947cc7cbc95d23e1d053d6595391bd325deb265a18d36c","affectsGlobalScope":true},{"version":"c80df75850fea5caa2afe43b9949338ce4e2de086f91713e9af1a06f973872b8","affectsGlobalScope":true},{"version":"9d57b2b5d15838ed094aa9ff1299eecef40b190722eb619bac4616657a05f951","affectsGlobalScope":true},{"version":"6c51b5dd26a2c31dbf37f00cfc32b2aa6a92e19c995aefb5b97a3a64f1ac99de","affectsGlobalScope":true},{"version":"6e7997ef61de3132e4d4b2250e75343f487903ddf5370e7ce33cf1b9db9a63ed","affectsGlobalScope":true},{"version":"2ad234885a4240522efccd77de6c7d99eecf9b4de0914adb9a35c0c22433f993","affectsGlobalScope":true},{"version":"5e5e095c4470c8bab227dbbc61374878ecead104c74ab9960d3adcccfee23205","affectsGlobalScope":true},{"version":"09aa50414b80c023553090e2f53827f007a301bc34b0495bfb2c3c08ab9ad1eb","affectsGlobalScope":true},{"version":"d7f680a43f8cd12a6b6122c07c54ba40952b0c8aa140dcfcf32eb9e6cb028596","affectsGlobalScope":true},{"version":"3787b83e297de7c315d55d4a7c546ae28e5f6c0a361b7a1dcec1f1f50a54ef11","affectsGlobalScope":true},{"version":"e7e8e1d368290e9295ef18ca23f405cf40d5456fa9f20db6373a61ca45f75f40","affectsGlobalScope":true},{"version":"faf0221ae0465363c842ce6aa8a0cbda5d9296940a8e26c86e04cc4081eea21e","affectsGlobalScope":true},{"version":"06393d13ea207a1bfe08ec8d7be562549c5e2da8983f2ee074e00002629d1871","affectsGlobalScope":true},{"version":"2768ef564cfc0689a1b76106c421a2909bdff0acbe87da010785adab80efdd5c","affectsGlobalScope":true},{"version":"b248e32ca52e8f5571390a4142558ae4f203ae2f94d5bac38a3084d529ef4e58","affectsGlobalScope":true},{"version":"6c55633c733c8378db65ac3da7a767c3cf2cf3057f0565a9124a16a3a2019e87","affectsGlobalScope":true},{"version":"fb4416144c1bf0323ccbc9afb0ab289c07312214e8820ad17d709498c865a3fe","affectsGlobalScope":true},{"version":"5b0ca94ec819d68d33da516306c15297acec88efeb0ae9e2b39f71dbd9685ef7","affectsGlobalScope":true},{"version":"34c839eaaa6d78c8674ae2c37af2236dee6831b13db7b4ef4df3ec889a04d4f2","affectsGlobalScope":true},{"version":"34478567f8a80171f88f2f30808beb7da15eac0538ae91282dd33dce928d98ed","affectsGlobalScope":true},{"version":"ab7d58e6161a550ff92e5aff755dc37fe896245348332cd5f1e1203479fe0ed1","affectsGlobalScope":true},{"version":"6bda95ea27a59a276e46043b7065b55bd4b316c25e70e29b572958fa77565d43","affectsGlobalScope":true},{"version":"aedb8de1abb2ff1095c153854a6df7deae4a5709c37297f9d6e9948b6806fa66","affectsGlobalScope":true},{"version":"a4da0551fd39b90ca7ce5f68fb55d4dc0c1396d589b612e1902f68ee090aaada","affectsGlobalScope":true},{"version":"11ffe3c281f375fff9ffdde8bbec7669b4dd671905509079f866f2354a788064","affectsGlobalScope":true},{"version":"52d1bb7ab7a3306fd0375c8bff560feed26ed676a5b0457fa8027b563aecb9a4","affectsGlobalScope":true},"a6a5253138c5432c68a1510c70fe78a644fe2e632111ba778e1978010d6edfec",{"version":"72181a1971593faa43471493af8e19ea739e12c58e11fd64ce001e965b4526d7","signature":"a03d32439203511c57876bb23e962511b4f069b06800861a7c67d4948584d7d4"},{"version":"6edf50c2113472dcba8dd6223df71f1c041d15ddbf0aa030b66780ff165595e4","signature":"acbcfa9387696e0acfe021d791d214d38be05167537da09d187d3a4fdbd599d1"},{"version":"e0ef3aef59604cb619a725c925fea1cc21db0fd5e2fe3015407c319d7ddcdf6e","signature":"31eaed72d2072540d0be49cbc81aba184de817b5a76c724b5dfbac046db12ed0"},{"version":"f00315922d62f47e4115169e802c0cd7c482f5fbf44e34da440457cbb57d3889","signature":"cff7429bac5bb14f2b856bf6937c41d695b3e4dd7f1030099c528dcffda3d34f"},{"version":"9212f643310c9d923b75d0a1a6ec9bea451664e900cc1e9d77caf5b408984346","signature":"a30ccb3156938b78ff7580ae18fd8c6597fe5dd927ad754db01da8783a7052a3"},{"version":"74d90c52c671d774d93a68075c173b0d92f2b8dabe68a1bfde0e9eb55b50678c","signature":"2b4d346165280a15c121f90364f23ad6df16247b356255f914daf5fb2caca0c4"},{"version":"6c7176368037af28cb72f2392010fa1cef295d6d6744bca8cfb54985f3a18c3e","affectsGlobalScope":true},{"version":"ab41ef1f2cdafb8df48be20cd969d875602483859dc194e9c97c8a576892c052","affectsGlobalScope":true},{"version":"437e20f2ba32abaeb7985e0afe0002de1917bc74e949ba585e49feba65da6ca1","affectsGlobalScope":true},"21d819c173c0cf7cc3ce57c3276e77fd9a8a01d35a06ad87158781515c9a438a",{"version":"613b21ccdf3be6329d56e6caa13b258c842edf8377be7bc9f014ed14cdcfc308","affectsGlobalScope":true},{"version":"d2662405c15ec112ebc0c3ec787edb82d58d6acb1a9d109317d7bf9cff9d09a7","affectsGlobalScope":true},"8e9c23ba78aabc2e0a27033f18737a6df754067731e69dc5f52823957d60a4b6","5929864ce17fba74232584d90cb721a89b7ad277220627cc97054ba15a98ea8f","7180c03fd3cb6e22f911ce9ba0f8a7008b1a6ddbe88ccf16a9c8140ef9ac1686","25c8056edf4314820382a5fdb4bb7816999acdcb929c8f75e3f39473b87e85bc","54cb85a47d760da1c13c00add10d26b5118280d44d58e6908d8e89abbd9d7725","3e4825171442666d31c845aeb47fcd34b62e14041bb353ae2b874285d78482aa","c6fd2c5a395f2432786c9cb8deb870b9b0e8ff7e22c029954fabdd692bff6195","a967bfe3ad4e62243eb604bf956101e4c740f5921277c60debaf325c1320bf88","e9775e97ac4877aebf963a0289c81abe76d1ec9a2a7778dbe637e5151f25c5f3","471e1da5a78350bc55ef8cef24eb3aca6174143c281b8b214ca2beda51f5e04a","cadc8aced301244057c4e7e73fbcae534b0f5b12a37b150d80e5a45aa4bebcbd","385aab901643aa54e1c36f5ef3107913b10d1b5bb8cbcd933d4263b80a0d7f20","9670d44354bab9d9982eca21945686b5c24a3f893db73c0dae0fd74217a4c219","db3435f3525cd785bf21ec6769bf8da7e8a776be1a99e2e7efb5f244a2ef5fee","c3b170c45fc031db31f782e612adf7314b167e60439d304b49e704010e7bafe5","40383ebef22b943d503c6ce2cb2e060282936b952a01bea5f9f493d5fb487cc7","4893a895ea92c85345017a04ed427cbd6a1710453338df26881a6019432febdd","3a84b7cb891141824bd00ef8a50b6a44596aded4075da937f180c90e362fe5f6","13f6f39e12b1518c6650bbb220c8985999020fe0f21d818e28f512b7771d00f9","9b5369969f6e7175740bf51223112ff209f94ba43ecd3bb09eefff9fd675624a","4fe9e626e7164748e8769bbf74b538e09607f07ed17c2f20af8d680ee49fc1da","24515859bc0b836719105bb6cc3d68255042a9f02a6022b3187948b204946bd2","33203609eba548914dc83ddf6cadbc0bcb6e8ef89f6d648ca0908ae887f9fcc5","0db18c6e78ea846316c012478888f33c11ffadab9efd1cc8bcc12daded7a60b6","89167d696a849fce5ca508032aabfe901c0868f833a8625d5a9c6e861ef935d2","e53a3c2a9f624d90f24bf4588aacd223e7bec1b9d0d479b68d2f4a9e6011147f","339dc5265ee5ed92e536a93a04c4ebbc2128f45eeec6ed29f379e0085283542c","9f0a92164925aa37d4a5d9dd3e0134cff8177208dba55fd2310cd74beea40ee2","8bfdb79bf1a9d435ec48d9372dc93291161f152c0865b81fc0b2694aedb4578d","2e85db9e6fd73cfa3d7f28e0ab6b55417ea18931423bd47b409a96e4a169e8e6","c46e079fe54c76f95c67fb89081b3e399da2c7d109e7dca8e4b58d83e332e605","d32275be3546f252e3ad33976caf8c5e842c09cb87d468cb40d5f4cf092d1acc","4a0c3504813a3289f7fb1115db13967c8e004aa8e4f8a9021b95285502221bd1",{"version":"1a2e588ce04b57f262959afb54933563431bf75304cfda6165703fe08f4018c5","affectsGlobalScope":true},"c775b106d611ae2c068ed8429a132608d10007918941311214892dcd4a571ad7","7394959e5a741b185456e1ef5d64599c36c60a323207450991e7a42e08911419","75eb536b960b85f75e21490beeab53ea616646a995ad203e1af532d67a774fb6",{"version":"36d0976d3dad74078f707af107b5082dbe42ffcadb3442ff140c36c8a33b4887","affectsGlobalScope":true},"51bb58ef3a22fdc49a2d338a852050855d1507f918d4d7fa77a68d72fee9f780","7646ad748a9ca15bf43d4c88f83cc851c67f8ec9c1186295605b59ba6bb36dcb",{"version":"cef8931bc129687165253f0642427c2a72705a4613b3ac461b9fa78c7cdaef32","affectsGlobalScope":true},"5524481e56c48ff486f42926778c0a3cce1cc85dc46683b92b1271865bcf015a","47b62c294beb69daa5879f052e416b02e6518f3e4541ae98adbfb27805dd6711","f8375506002c556ec412c7e2a5a9ece401079ee5d9eb2c1372e9f5377fac56c7","8edd6482bd72eca772f9df15d05c838dd688cdbd4d62690891fca6578cfda6fe","07ba29a1a495b710aea48a4cf19ae12b3cbda2a8e9ac62192af477027a99e8de","6dead64c944504250dd2fc9095231f36887cfc1534f1ff57737c19f92d165c91","b9a4824bb83f25d6d227394db2ed99985308cf2a3a35f0d6d39aa72b15473982",{"version":"6e57c0b7b3d2716fbc0ca28aa23f62bc997ad534d1369f3853dcb9d453d1fb91","affectsGlobalScope":true},{"version":"b84f34005e497dbc0c1948833818cdb38e8c01ff4f88d810b4d70aa2e6c52916","affectsGlobalScope":true},"8e8e284b3832911aeede987e4d74cf0a00f2b03896b2fd3bf924344cc0f96b3c","37d37474a969ab1b91fc332eb6a375885dfd25279624dfa84dea48c9aedf4472","957905d33a09ce85efd84a65819cdd22eefdd64959afacbdcfe5f36b0d7a7bbe","f1a79b6047d006548185e55478837dfbcdd234d6fe51532783f5dffd401cfb2b","565fda33feca88f4b5db23ba8e605da1fd28b6d63292d276bdbd2afe6cd4c490","e822320b448edce0c7ede9cbeada034c72e1f1c8c8281974817030564c63dcb1",{"version":"c5ea83ef86cc930db2ed42cafeef63013c59720cdc127b23feeb77df412950b9","affectsGlobalScope":true},"f23e3d484de54d235bf702072100b541553a1df2550bad691fe84995e15cf7be","821c79b046e40d54a447bebd9307e70b86399a89980a87bbc98114411169e274","17bc38afc78d40b2f54af216c0cc31a4bd0c6897a5945fa39945dfc43260be2c",{"version":"d201b44ff390c220a94fb0ff6a534fe9fa15b44f8a86d0470009cdde3a3e62ab","affectsGlobalScope":true},{"version":"d44445141f204d5672c502a39c1124bcf1df225eba05df0d2957f79122be87b5","affectsGlobalScope":true},"de905bc5f7e7a81cb420e212b95ab5e3ab840f93e0cfa8ce879f6e7fa465d4a2","bc2ff43214898bc6d53cab92fb41b5309efec9cbb59a0650525980aee994de2b","bede3143eeddca3b8ec3592b09d7eb02042f9e195251040c5146eac09b173236","64a40cf4ec8a7a29db2b4bc35f042e5be8537c4be316e5221f40f30ca8ed7051","294c082d609e6523520290db4f1d54114ebc83643fb42abd965be5bcc5d9416b","cf7d740e39bd8adbdc7840ee91bef0af489052f6467edfcefb7197921757ec3b","37ba7b45141a45ce6e80e66f2a96c8a5ab1bcef0fc2d0f56bb58df96ec67e972","125d792ec6c0c0f657d758055c494301cc5fdb327d9d9d5960b3f129aff76093",{"version":"63c3208a57f10a4f89944c80a6cdb31faff343e41a2d3e06831c621788969fa7","affectsGlobalScope":true},"b85151402164ab7cb665e58df5c1a29aa25ea4ed3a367f84a15589e7d7a9c8ca","89eb8abe2b5c146fbb8f3bf72f4e91de3541f2fb559ad5fed4ad5bf223a3dedb",{"version":"bc6cb10764a82f3025c0f4822b8ad711c16d1a5c75789be2d188d553b69b2d48","affectsGlobalScope":true},"41d510caf7ed692923cb6ef5932dc9cf1ed0f57de8eb518c5bab8358a21af674","2751c5a6b9054b61c9b03b3770b2d39b1327564672b63e3485ac03ffeb28b4f6","dc058956a93388aab38307b7b3b9b6379e1021e73a244aab6ac9427dc3a252a7","f33302cf240672359992c356f2005d395b559e176196d03f31a28cc7b01e69bc",{"version":"3ce25041ff6ae06c08fcaccd5fcd9baf4ca6e80e6cb5a922773a1985672e74c2","affectsGlobalScope":true},{"version":"652c0de14329a834ff06af6ad44670fac35849654a464fd9ae36edb92a362c12","affectsGlobalScope":true},"3b1e178016d3fc554505ae087c249b205b1c50624d482c542be9d4682bab81fc","5db7c5bb02ef47aaaec6d262d50c4e9355c80937d649365c343fa5e84569621d","cf45d0510b661f1da461479851ff902f188edb111777c37055eff12fa986a23a",{"version":"ec9a5f06328f61e09f44d6781d1bd862475f9900c16cef82621a46305def3c4d","affectsGlobalScope":true},"37bef1064b7d015aeaa7c0716fe23a0b3844abe2c0a3df7144153ca8445fe0da","75bd411256302c183207051fd198b4e0dbab45d28a6daf04d3ad61f70a2c8e90","d7c7fd205f57abd1705b98d7f4b47b1dd8f256a3c9a98efb872d75ef27e9e59e","407c3e1ea5aef8aadab1780651e4f2281a6382aa81db218f589af5ac3abc6cab","aa7e6b64b597c32a65662e0b455e641748ab4ecc71aa31e4c8ad0678ca65bc5a","8ef8425c7726a1bd5ab733fb1c8f682b5c116f7b1cf0a1a20bfaf6e6e368b459",{"version":"4eb3e7d54973491a6e25fb8013505696681c4bc1b8f8cbc5cb653ed27cfdbc10","signature":"ff99072d083f57c2ed6b076bc7dc5cbde72f36092361189a03b4fe5956a36890"},{"version":"ebbe85599e9b22451bf16fd340e1608c88b05e244ccfff49c648515f497395d1","signature":"d478c50fc50b9e9a6289ec3c85571cfe1d062b73fab7c92b6a4bb9593aef71de"},{"version":"dc9c95f98107c2e20d3b2eabe19df076fccb801d65abbe31f95843d860fa1051","signature":"7adb3e9f00f210fb1f0c38512fcaa1bd57e806950f0c3abd07c0086e6fe6cbd3"},{"version":"cbcd119ef7f3dcf3105e9e4e0bf7daf2634bfc8c5bb36d9e0b82f5dfb0429055","signature":"830e16a4df39cd19bfa17d971f4cc2e09a1becbdb8bf79bac852ca968ac39764"},{"version":"9191d75779e059274da2ad16b23b48609a8164910d2263d0e32621cdf1c91630","signature":"217710e1ccf61405510ce30d295d29c8a2b00efcf2a56ed335e8e1b53014bb21"},{"version":"99d1356ed44b41b84a9b9437c4e3aa3b6a2d422db67262c29b99c96f0b0412eb","signature":"be5f408346d5affec7cb59e50d913ed162c881d450193494318872e124c85742"},{"version":"fe13e3e5a016554ee0f02addf03e0c0c1636b2cf02944475ea6279b293b12618","signature":"5a42eeb772c0c062022424b8ff45d1a806c6d45d8c1f872c0b944432624e4244"},"d159a1bf12aed961258abcb9bc26ceaa615db573662be64f7e9ce136db1fb265","86cc3effc64d89018e0615a7026f9298c17d162cde9bce65b9c4b5b2aebdb40a","a79453ed56ff690ab4b0b96bbdcb8cc0025e162dd893813b669b41cf84947c28",{"version":"bcbb1abaccd386d3fee7bd05610ec543159d18513990dacd757b06a55b183d4a","signature":"16a2a9d29fcbe8f4418a5962781973749b0ffe59cb312bee696c05b4c6111957"},{"version":"9bd37ae0b5f0d5159f6a5df4f3652bf6460662a223a3485b3e98dbf2fcbd7ba8","signature":"2eb1ba3cf40253113faf59cbc92e489d4cded2025d41a58dc1a30ef471a9f4f7"},{"version":"d085a2b4e9a4cc5849c4b38cc308d37a84072ad3f7f37cdf34023d5d2fbfb613","signature":"7a55cfecd45f6a037540e1daa4719e093bb33d5d2f64e9dc4599d52d73e08975"},{"version":"40b372843ae835ed0eca6d1e1da2fa4e2b9ce857f7a6307598a59ace8ab7e749","signature":"a66a9824009380d232d281b3ddcc761f648ab3c5488f02aab73a2baee7e58fbd"},{"version":"27c3423ba5d450f51322e6df40d03cf9b41b66eef47dc0f8470f0ea249c53af8","signature":"2aeaa721bab17d5ee774b73a96a6c3b657707a30bf58c5ca9546dd3c3b0dd13a"},{"version":"daf38f6a266cfc9f3e8fe5901b14ec8d620338add927ed1bf32ccf6722b3168f","signature":"9c8b60ee26a38cb0b91b67816e0f7b5636dcd6b758a1eb00a4adaf6159d3b9c2"},{"version":"a5daefb282425beec19b6e42dddd518d6313b06aaab60ed451ca14c2b77dc620","signature":"59ec811a5db90b36bcc21149545495287aff5f873c7990d4516748de65ed26d3"},{"version":"b13597004cddc30abda1bc857d6518cdc353138f67cd3107074c2ab083f04f4d","signature":"3cc648ca5e34a7b3fdadd0bc86e10c4a24ac62cdc9798a0356713e34a031a713"},{"version":"b7909fc03146c795f5cc7b2ddc291ab7801eed60bab1ff190fbacecf54bb7922","signature":"1fb911064d4e0bfef48d76d39e4fc90a42e3bb272869fa88cb91c5938c440cd2"},{"version":"36a5ac659c3e3fa743219212ce4b703aa2fb648a75196646273e556bd42df6d5","signature":"1ba3a0102d506375398797630f9786b8fbba4d04e173cdbf6994d58e1131e22d"},"151ff381ef9ff8da2da9b9663ebf657eac35c4c9a19183420c05728f31a6761d","035a5df183489c2e22f3cf59fc1ed2b043d27f357eecc0eb8d8e840059d44245","a4809f4d92317535e6b22b01019437030077a76fec1d93b9881c9ed4738fcc54","5f53fa0bd22096d2a78533f94e02c899143b8f0f9891a46965294ee8b91a9434","763e521cf114b80e0dd0e21ca49b9f8ae62e8999555a5e7bade8ce36b33001c2","0d14fa22c41fdc7277e6f71473b20ebc07f40f00e38875142335d5b63cdfc9d2","3054ef91b855e005b9c4681399e9d64d2a7b07a22d539314d794f09e53b876a7","ffcc5500e77223169833fc6eb59b3a507944a1f89574e0a1276b0ea7fc22c4a4","22f13de9e2fe5f0f4724797abd3d34a1cdd6e47ef81fc4933fea3b8bf4ad524b","e3ba509d3dce019b3190ceb2f3fc88e2610ab717122dabd91a9efaa37804040d","cda0cb09b995489b7f4c57f168cd31b83dcbaa7aad49612734fb3c9c73f6e4f2","f72f8428f3c1caa22e9c247d046603b85b442c0dae7b77a7a0bc092c18867cb7",{"version":"195f63105abc03e72b6a176e3e34dfb5ac932b55db378fdc7874b1617e24b465","affectsGlobalScope":true},"f3d8c757e148ad968f0d98697987db363070abada5f503da3c06aefd9d4248c1","96d14f21b7652903852eef49379d04dbda28c16ed36468f8c9fa08f7c14c9538","3cfb0cb51cc2c2e1b313d7c4df04dbf7e5bda0a133c6b309bf6af77cf614b971","ab82804a14454734010dcdcd43f564ff7b0389bee4c5692eec76ff5b30d4cf66","bae8d023ef6b23df7da26f51cea44321f95817c190342a36882e93b80d07a960","26a770cec4bd2e7dbba95c6e536390fffe83c6268b78974a93727903b515c4e7","74d5a87c3616cd5d8691059d531504403aa857e09cbaecb1c64dfb9ace0db185"],"options":{"allowSyntheticDefaultImports":true,"alwaysStrict":true,"declaration":true,"declarationMap":true,"emitDecoratorMetadata":true,"esModuleInterop":true,"experimentalDecorators":true,"importHelpers":true,"importsNotUsedAsValues":0,"module":1,"newLine":1,"noEmitHelpers":true,"noFallthroughCasesInSwitch":true,"noImplicitOverride":true,"noImplicitReturns":true,"noUnusedLocals":true,"noUnusedParameters":true,"outDir":"./","preserveConstEnums":true,"removeComments":false,"sourceMap":true,"strict":true,"target":6,"useDefineForClassFields":true},"fileIdsList":[[67,105],[67,105,184],[67,105,179],[67,105,180],[67,105,186,189],[67,102,105],[67,104,105],[67,105,110,138],[67,105,106,117,118,125,135,146],[67,105,106,107,117,125],[62,63,64,67,105],[67,105,108,147],[67,105,109,110,118,126],[67,105,110,135,143],[67,105,111,113,117,125],[67,104,105,112],[67,105,113,114],[67,105,115,117],[67,104,105,117],[67,105,117,118,119,135,146],[67,105,117,118,119,132,135,138],[67,100,105],[67,105,113,117,120,125,135,146],[67,105,117,118,120,121,125,135,143,146],[67,105,120,122,135,143,146],[67,105,117,123],[67,105,124,146,151],[67,105,113,117,125,135],[67,105,126],[67,105,127],[67,104,105,128],[67,105,129,145,151],[67,105,130],[67,105,131],[67,105,117,132,133],[67,105,132,134,147,149],[67,105,117,135,136,138],[67,105,137,138],[67,105,135,136],[67,105,138],[67,105,139],[67,105,135,140],[67,105,117,141,142],[67,105,141,142],[67,105,110,125,135,143],[67,105,144],[105],[65,66,67,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152],[67,105,125,145],[67,105,120,131,146],[67,105,110,147],[67,105,135,148],[67,105,124,149],[67,105,150],[67,105,117,119,128,135,138,146,149,151],[67,105,135,152],[67,105,120,135,153],[67,105,195],[67,105,117,135,153],[67,105,182,188],[67,105,186],[67,105,183,187],[67,105,185],[67,105,120,125,146,153,157,166],[67,105,120,122,135,153],[67,105,120,125,135,143,153,165],[67,105,117,135,153,154,155],[67,105,125,135,153],[67,105,154],[67,105,156],[67,77,81,105,146],[67,77,105,135,146],[67,72,105],[67,74,77,105,143,146],[67,105,125,143],[67,105,153],[67,72,105,153],[67,74,77,105,125,146],[67,69,70,73,76,105,117,135,146],[67,69,75,105],[67,73,77,105,138,146,153],[67,93,105,153],[67,71,72,105,153],[67,77,105],[67,71,72,73,74,75,76,77,78,79,81,82,83,84,85,86,87,88,89,90,91,92,94,95,96,97,98,99,105],[67,77,84,85,105],[67,75,77,85,86,105],[67,76,105],[67,69,72,77,105],[67,77,81,85,86,105],[67,81,105],[67,75,77,80,105,146],[67,69,74,75,77,81,84,105],[67,105,135],[67,72,77,93,105,151,153],[55,67,105,120,125,146,170,174],[55,56,57,58,60,67,105,113,117,120,122,146],[55,56,57,58,67,105,117,120,125,146,157],[55,67,105,120,125,147],[55,67,105,120],[55,56,58,67,105,113,117,125,146],[55,57,58,60,67,105,113,120,122,135,146,147,163],[55,57,58,67,105,120,135,146,147,163,167],[55,67,105,160,169,173,174,175,176],[55,67,105],[55,56,57,58,61,67,105,113,117,120,125,146,147,158,159,160,161,164,168,169,170,171,172,173],[55,67,105,125,143],[55,61,67,105,125,146,170],[55,67,105,125],[55,59,67,105,146],[55,67,105,146],[55,67,105,120,162],[105,120,125],[56,105,113,117,146],[56,105,117,120,146],[120,125],[105,120],[56,105,113,117],[113,120,146],[120,146],[160,169,173,174,175,176],[56,105,113,117,120,146,160],[125,143],[125],[146]],"referencedMap":[[182,1],[185,2],[184,1],[178,1],[179,1],[180,3],[181,4],[190,5],[191,1],[192,1],[102,6],[103,6],[104,7],[105,8],[106,9],[107,10],[62,1],[65,11],[63,1],[64,1],[108,12],[109,13],[110,14],[111,15],[112,16],[113,17],[114,17],[116,1],[115,18],[117,19],[118,20],[119,21],[101,22],[120,23],[121,24],[122,25],[123,26],[124,27],[125,28],[126,29],[127,30],[128,31],[129,32],[130,33],[131,34],[132,35],[133,35],[134,36],[135,37],[137,38],[136,39],[138,40],[139,41],[140,42],[141,43],[142,44],[143,45],[144,46],[67,47],[66,1],[153,48],[145,49],[146,50],[147,51],[148,52],[149,53],[150,54],[151,55],[152,56],[193,57],[194,1],[195,1],[196,58],[197,59],[68,1],[183,1],[189,60],[187,61],[188,62],[186,63],[167,64],[165,65],[166,66],[156,67],[154,68],[155,69],[157,70],[55,1],[12,1],[11,1],[2,1],[13,1],[14,1],[15,1],[16,1],[17,1],[18,1],[19,1],[20,1],[3,1],[4,1],[24,1],[21,1],[22,1],[23,1],[25,1],[26,1],[27,1],[5,1],[28,1],[29,1],[30,1],[31,1],[6,1],[35,1],[32,1],[33,1],[34,1],[36,1],[7,1],[37,1],[42,1],[43,1],[38,1],[39,1],[40,1],[41,1],[8,1],[47,1],[44,1],[45,1],[46,1],[48,1],[9,1],[49,1],[50,1],[51,1],[52,1],[53,1],[1,1],[10,1],[54,1],[84,71],[91,72],[83,71],[98,73],[75,74],[74,75],[97,76],[92,77],[95,78],[77,79],[76,80],[72,81],[71,76],[94,82],[73,83],[78,84],[79,1],[82,84],[69,1],[100,85],[99,84],[86,86],[87,87],[89,88],[85,89],[88,90],[93,76],[80,91],[81,92],[90,93],[70,94],[96,95],[175,96],[61,97],[158,98],[159,99],[160,100],[161,101],[164,102],[168,103],[177,104],[169,105],[174,106],[56,107],[57,100],[176,108],[58,109],[59,105],[60,110],[162,105],[170,105],[171,111],[172,105],[173,111],[163,112]],"exportedModulesMap":[[182,1],[185,2],[184,1],[178,1],[179,1],[180,3],[181,4],[190,5],[191,1],[192,1],[102,6],[103,6],[104,7],[105,8],[106,9],[107,10],[62,1],[65,11],[63,1],[64,1],[108,12],[109,13],[110,14],[111,15],[112,16],[113,17],[114,17],[116,1],[115,18],[117,19],[118,20],[119,21],[101,22],[120,23],[121,24],[122,25],[123,26],[124,27],[125,28],[126,29],[127,30],[128,31],[129,32],[130,33],[131,34],[132,35],[133,35],[134,36],[135,37],[137,38],[136,39],[138,40],[139,41],[140,42],[141,43],[142,44],[143,45],[144,46],[67,47],[66,1],[153,48],[145,49],[146,50],[147,51],[148,52],[149,53],[150,54],[151,55],[152,56],[193,57],[194,1],[195,1],[196,58],[197,59],[68,1],[183,1],[189,60],[187,61],[188,62],[186,63],[167,64],[165,65],[166,66],[156,67],[154,68],[155,69],[157,70],[55,1],[12,1],[11,1],[2,1],[13,1],[14,1],[15,1],[16,1],[17,1],[18,1],[19,1],[20,1],[3,1],[4,1],[24,1],[21,1],[22,1],[23,1],[25,1],[26,1],[27,1],[5,1],[28,1],[29,1],[30,1],[31,1],[6,1],[35,1],[32,1],[33,1],[34,1],[36,1],[7,1],[37,1],[42,1],[43,1],[38,1],[39,1],[40,1],[41,1],[8,1],[47,1],[44,1],[45,1],[46,1],[48,1],[9,1],[49,1],[50,1],[51,1],[52,1],[53,1],[1,1],[10,1],[54,1],[84,71],[91,72],[83,71],[98,73],[75,74],[74,75],[97,76],[92,77],[95,78],[77,79],[76,80],[72,81],[71,76],[94,82],[73,83],[78,84],[79,1],[82,84],[69,1],[100,85],[99,84],[86,86],[87,87],[89,88],[85,89],[88,90],[93,76],[80,91],[81,92],[90,93],[70,94],[96,95],[175,113],[61,114],[158,115],[159,116],[160,117],[161,118],[164,119],[168,120],[177,121],[174,122],[56,123],[58,124],[60,125],[171,125],[173,125]],"semanticDiagnosticsPerFile":[182,185,184,178,179,180,181,190,191,192,102,103,104,105,106,107,62,65,63,64,108,109,110,111,112,113,114,116,115,117,118,119,101,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,137,136,138,139,140,141,142,143,144,67,66,153,145,146,147,148,149,150,151,152,193,194,195,196,197,68,183,189,187,188,186,167,165,166,156,154,155,157,55,12,11,2,13,14,15,16,17,18,19,20,3,4,24,21,22,23,25,26,27,5,28,29,30,31,6,35,32,33,34,36,7,37,42,43,38,39,40,41,8,47,44,45,46,48,9,49,50,51,52,53,1,10,54,84,91,83,98,75,74,97,92,95,77,76,72,71,94,73,78,79,82,69,100,99,86,87,89,85,88,93,80,81,90,70,96,175,61,158,159,160,161,164,168,177,169,174,56,57,176,58,59,60,162,170,171,172,173,163]},"version":"4.9.5"} \ No newline at end of file diff --git a/dist/utils/count_target_bytes.d.ts b/dist/utils/count_target_bytes.d.ts new file mode 100644 index 00000000..cb026eef --- /dev/null +++ b/dist/utils/count_target_bytes.d.ts @@ -0,0 +1,18 @@ +/// +import type net from 'node:net'; +type Stats = { + bytesWritten: number | null; + bytesRead: number | null; +}; +/** + * Socket object extended with previous read and written bytes. + * Necessary due to target socket re-use. + */ +export type SocketWithPreviousStats = net.Socket & { + previousBytesWritten?: number; + previousBytesRead?: number; +}; +export declare const countTargetBytes: (source: net.Socket, target: SocketWithPreviousStats, registerCloseHandler?: ((handler: () => void) => void) | undefined) => void; +export declare const getTargetStats: (socket: net.Socket) => Stats; +export {}; +//# sourceMappingURL=count_target_bytes.d.ts.map \ No newline at end of file diff --git a/dist/utils/count_target_bytes.d.ts.map b/dist/utils/count_target_bytes.d.ts.map new file mode 100644 index 00000000..660bbaf7 --- /dev/null +++ b/dist/utils/count_target_bytes.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"count_target_bytes.d.ts","sourceRoot":"","sources":["../../src/utils/count_target_bytes.ts"],"names":[],"mappings":";AAAA,OAAO,KAAK,GAAG,MAAM,UAAU,CAAC;AAOhC,KAAK,KAAK,GAAG;IAAE,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAC;AAEvE;;;GAGG;AACH,MAAM,MAAM,uBAAuB,GAAG,GAAG,CAAC,MAAM,GAAG;IAAE,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAAC,iBAAiB,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAajH,eAAO,MAAM,gBAAgB,WACjB,IAAI,MAAM,UACV,uBAAuB,oCACE,MAAM,IAAI,KAAK,IAAI,kBACrD,IAmCF,CAAC;AAEF,eAAO,MAAM,cAAc,WAAY,IAAI,MAAM,KAAG,KAWnD,CAAC"} \ No newline at end of file diff --git a/dist/utils/count_target_bytes.js b/dist/utils/count_target_bytes.js new file mode 100644 index 00000000..68605eb1 --- /dev/null +++ b/dist/utils/count_target_bytes.js @@ -0,0 +1,53 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.getTargetStats = exports.countTargetBytes = void 0; +const targetBytesWritten = Symbol('targetBytesWritten'); +const targetBytesRead = Symbol('targetBytesRead'); +const targets = Symbol('targets'); +const calculateTargetStats = Symbol('calculateTargetStats'); +// @ts-expect-error TS is not aware that `source` is used in the assertion. +// eslint-disable-next-line @typescript-eslint/no-empty-function +function typeSocket(source) { } +const countTargetBytes = (source, target, registerCloseHandler) => { + typeSocket(source); + source[targetBytesWritten] = source[targetBytesWritten] || 0; + source[targetBytesRead] = source[targetBytesRead] || 0; + source[targets] = source[targets] || new Set(); + source[targets].add(target); + const closeHandler = () => { + source[targetBytesWritten] += (target.bytesWritten - (target.previousBytesWritten || 0)); + source[targetBytesRead] += (target.bytesRead - (target.previousBytesRead || 0)); + source[targets].delete(target); + }; + if (!registerCloseHandler) { + registerCloseHandler = (handler) => target.once('close', handler); + } + registerCloseHandler(closeHandler); + if (!source[calculateTargetStats]) { + source[calculateTargetStats] = () => { + let bytesWritten = source[targetBytesWritten]; + let bytesRead = source[targetBytesRead]; + for (const socket of source[targets]) { + bytesWritten += (socket.bytesWritten - (socket.previousBytesWritten || 0)); + bytesRead += (socket.bytesRead - (socket.previousBytesRead || 0)); + } + return { + bytesWritten, + bytesRead, + }; + }; + } +}; +exports.countTargetBytes = countTargetBytes; +const getTargetStats = (socket) => { + typeSocket(socket); + if (socket[calculateTargetStats]) { + return socket[calculateTargetStats](); + } + return { + bytesWritten: null, + bytesRead: null, + }; +}; +exports.getTargetStats = getTargetStats; +//# sourceMappingURL=count_target_bytes.js.map \ No newline at end of file diff --git a/dist/utils/count_target_bytes.js.map b/dist/utils/count_target_bytes.js.map new file mode 100644 index 00000000..1eec5a37 --- /dev/null +++ b/dist/utils/count_target_bytes.js.map @@ -0,0 +1 @@ +{"version":3,"file":"count_target_bytes.js","sourceRoot":"","sources":["../../src/utils/count_target_bytes.ts"],"names":[],"mappings":";;;AAEA,MAAM,kBAAkB,GAAG,MAAM,CAAC,oBAAoB,CAAC,CAAC;AACxD,MAAM,eAAe,GAAG,MAAM,CAAC,iBAAiB,CAAC,CAAC;AAClD,MAAM,OAAO,GAAG,MAAM,CAAC,SAAS,CAAC,CAAC;AAClC,MAAM,oBAAoB,GAAG,MAAM,CAAC,sBAAsB,CAAC,CAAC;AAiB5D,2EAA2E;AAC3E,gEAAgE;AAChE,SAAS,UAAU,CAAC,MAAe,IAA0C,CAAC;AAEvE,MAAM,gBAAgB,GAAG,CAC5B,MAAkB,EAClB,MAA+B,EAC/B,oBAAoD,EAChD,EAAE;IACN,UAAU,CAAC,MAAM,CAAC,CAAC;IAEnB,MAAM,CAAC,kBAAkB,CAAC,GAAG,MAAM,CAAC,kBAAkB,CAAC,IAAI,CAAC,CAAC;IAC7D,MAAM,CAAC,eAAe,CAAC,GAAG,MAAM,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC;IACvD,MAAM,CAAC,OAAO,CAAC,GAAG,MAAM,CAAC,OAAO,CAAC,IAAI,IAAI,GAAG,EAAE,CAAC;IAE/C,MAAM,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IAE5B,MAAM,YAAY,GAAG,GAAG,EAAE;QACtB,MAAM,CAAC,kBAAkB,CAAC,IAAI,CAAC,MAAM,CAAC,YAAY,GAAG,CAAC,MAAM,CAAC,oBAAoB,IAAI,CAAC,CAAC,CAAC,CAAC;QACzF,MAAM,CAAC,eAAe,CAAC,IAAI,CAAC,MAAM,CAAC,SAAS,GAAG,CAAC,MAAM,CAAC,iBAAiB,IAAI,CAAC,CAAC,CAAC,CAAC;QAChF,MAAM,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IACnC,CAAC,CAAC;IACF,IAAI,CAAC,oBAAoB,EAAE;QACvB,oBAAoB,GAAG,CAAC,OAAmB,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;KACjF;IACD,oBAAoB,CAAC,YAAY,CAAC,CAAC;IAEnC,IAAI,CAAC,MAAM,CAAC,oBAAoB,CAAC,EAAE;QAC/B,MAAM,CAAC,oBAAoB,CAAC,GAAG,GAAG,EAAE;YAChC,IAAI,YAAY,GAAG,MAAM,CAAC,kBAAkB,CAAC,CAAC;YAC9C,IAAI,SAAS,GAAG,MAAM,CAAC,eAAe,CAAC,CAAC;YAExC,KAAK,MAAM,MAAM,IAAI,MAAM,CAAC,OAAO,CAAC,EAAE;gBAClC,YAAY,IAAI,CAAC,MAAM,CAAC,YAAY,GAAG,CAAC,MAAM,CAAC,oBAAoB,IAAI,CAAC,CAAC,CAAC,CAAC;gBAC3E,SAAS,IAAI,CAAC,MAAM,CAAC,SAAS,GAAG,CAAC,MAAM,CAAC,iBAAiB,IAAI,CAAC,CAAC,CAAC,CAAC;aACrE;YAED,OAAO;gBACH,YAAY;gBACZ,SAAS;aACZ,CAAC;QACN,CAAC,CAAC;KACL;AACL,CAAC,CAAC;AAvCW,QAAA,gBAAgB,oBAuC3B;AAEK,MAAM,cAAc,GAAG,CAAC,MAAkB,EAAS,EAAE;IACxD,UAAU,CAAC,MAAM,CAAC,CAAC;IAEnB,IAAI,MAAM,CAAC,oBAAoB,CAAC,EAAE;QAC9B,OAAO,MAAM,CAAC,oBAAoB,CAAC,EAAE,CAAC;KACzC;IAED,OAAO;QACH,YAAY,EAAE,IAAI;QAClB,SAAS,EAAE,IAAI;KAClB,CAAC;AACN,CAAC,CAAC;AAXW,QAAA,cAAc,kBAWzB"} \ No newline at end of file diff --git a/dist/utils/decode_uri_component_safe.d.ts b/dist/utils/decode_uri_component_safe.d.ts new file mode 100644 index 00000000..aac7a7f9 --- /dev/null +++ b/dist/utils/decode_uri_component_safe.d.ts @@ -0,0 +1,2 @@ +export declare const decodeURIComponentSafe: (encodedURIComponent: string) => string; +//# sourceMappingURL=decode_uri_component_safe.d.ts.map \ No newline at end of file diff --git a/dist/utils/decode_uri_component_safe.d.ts.map b/dist/utils/decode_uri_component_safe.d.ts.map new file mode 100644 index 00000000..c330627c --- /dev/null +++ b/dist/utils/decode_uri_component_safe.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"decode_uri_component_safe.d.ts","sourceRoot":"","sources":["../../src/utils/decode_uri_component_safe.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,sBAAsB,wBAAyB,MAAM,KAAG,MAMpE,CAAC"} \ No newline at end of file diff --git a/dist/utils/decode_uri_component_safe.js b/dist/utils/decode_uri_component_safe.js new file mode 100644 index 00000000..1f8b19dd --- /dev/null +++ b/dist/utils/decode_uri_component_safe.js @@ -0,0 +1,13 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.decodeURIComponentSafe = void 0; +const decodeURIComponentSafe = (encodedURIComponent) => { + try { + return decodeURIComponent(encodedURIComponent); + } + catch { + return encodedURIComponent; + } +}; +exports.decodeURIComponentSafe = decodeURIComponentSafe; +//# sourceMappingURL=decode_uri_component_safe.js.map \ No newline at end of file diff --git a/dist/utils/decode_uri_component_safe.js.map b/dist/utils/decode_uri_component_safe.js.map new file mode 100644 index 00000000..ddeb7f85 --- /dev/null +++ b/dist/utils/decode_uri_component_safe.js.map @@ -0,0 +1 @@ +{"version":3,"file":"decode_uri_component_safe.js","sourceRoot":"","sources":["../../src/utils/decode_uri_component_safe.ts"],"names":[],"mappings":";;;AAAO,MAAM,sBAAsB,GAAG,CAAC,mBAA2B,EAAU,EAAE;IAC1E,IAAI;QACA,OAAO,kBAAkB,CAAC,mBAAmB,CAAC,CAAC;KAClD;IAAC,MAAM;QACJ,OAAO,mBAAmB,CAAC;KAC9B;AACL,CAAC,CAAC;AANW,QAAA,sBAAsB,0BAMjC"} \ No newline at end of file diff --git a/dist/utils/get_basic.d.ts b/dist/utils/get_basic.d.ts new file mode 100644 index 00000000..ab5621b2 --- /dev/null +++ b/dist/utils/get_basic.d.ts @@ -0,0 +1,3 @@ +import type { URL } from 'node:url'; +export declare const getBasicAuthorizationHeader: (url: URL) => string; +//# sourceMappingURL=get_basic.d.ts.map \ No newline at end of file diff --git a/dist/utils/get_basic.d.ts.map b/dist/utils/get_basic.d.ts.map new file mode 100644 index 00000000..667fc0d0 --- /dev/null +++ b/dist/utils/get_basic.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"get_basic.d.ts","sourceRoot":"","sources":["../../src/utils/get_basic.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAIpC,eAAO,MAAM,2BAA2B,QAAS,GAAG,KAAG,MAUtD,CAAC"} \ No newline at end of file diff --git a/dist/utils/get_basic.js b/dist/utils/get_basic.js new file mode 100644 index 00000000..427c7db0 --- /dev/null +++ b/dist/utils/get_basic.js @@ -0,0 +1,15 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.getBasicAuthorizationHeader = void 0; +const decode_uri_component_safe_1 = require("./decode_uri_component_safe"); +const getBasicAuthorizationHeader = (url) => { + const username = (0, decode_uri_component_safe_1.decodeURIComponentSafe)(url.username); + const password = (0, decode_uri_component_safe_1.decodeURIComponentSafe)(url.password); + const auth = `${username}:${password}`; + if (username.includes(':')) { + throw new Error('Username contains an invalid colon'); + } + return `Basic ${Buffer.from(auth).toString('base64')}`; +}; +exports.getBasicAuthorizationHeader = getBasicAuthorizationHeader; +//# sourceMappingURL=get_basic.js.map \ No newline at end of file diff --git a/dist/utils/get_basic.js.map b/dist/utils/get_basic.js.map new file mode 100644 index 00000000..d6dc834e --- /dev/null +++ b/dist/utils/get_basic.js.map @@ -0,0 +1 @@ +{"version":3,"file":"get_basic.js","sourceRoot":"","sources":["../../src/utils/get_basic.ts"],"names":[],"mappings":";;;AAEA,2EAAqE;AAE9D,MAAM,2BAA2B,GAAG,CAAC,GAAQ,EAAU,EAAE;IAC5D,MAAM,QAAQ,GAAG,IAAA,kDAAsB,EAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IACtD,MAAM,QAAQ,GAAG,IAAA,kDAAsB,EAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IACtD,MAAM,IAAI,GAAG,GAAG,QAAQ,IAAI,QAAQ,EAAE,CAAC;IAEvC,IAAI,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE;QACxB,MAAM,IAAI,KAAK,CAAC,oCAAoC,CAAC,CAAC;KACzD;IAED,OAAO,SAAS,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;AAC3D,CAAC,CAAC;AAVW,QAAA,2BAA2B,+BAUtC"} \ No newline at end of file diff --git a/dist/utils/is_hop_by_hop_header.d.ts b/dist/utils/is_hop_by_hop_header.d.ts new file mode 100644 index 00000000..ed2bab35 --- /dev/null +++ b/dist/utils/is_hop_by_hop_header.d.ts @@ -0,0 +1,2 @@ +export declare const isHopByHopHeader: (header: string) => boolean; +//# sourceMappingURL=is_hop_by_hop_header.d.ts.map \ No newline at end of file diff --git a/dist/utils/is_hop_by_hop_header.d.ts.map b/dist/utils/is_hop_by_hop_header.d.ts.map new file mode 100644 index 00000000..f2926935 --- /dev/null +++ b/dist/utils/is_hop_by_hop_header.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"is_hop_by_hop_header.d.ts","sourceRoot":"","sources":["../../src/utils/is_hop_by_hop_header.ts"],"names":[],"mappings":"AAYA,eAAO,MAAM,gBAAgB,WAAY,MAAM,KAAG,OAAyD,CAAC"} \ No newline at end of file diff --git a/dist/utils/is_hop_by_hop_header.js b/dist/utils/is_hop_by_hop_header.js new file mode 100644 index 00000000..61ad4afd --- /dev/null +++ b/dist/utils/is_hop_by_hop_header.js @@ -0,0 +1,17 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.isHopByHopHeader = void 0; +// As per HTTP specification, hop-by-hop headers should be consumed but the proxy, and not forwarded +const hopByHopHeaders = [ + 'connection', + 'keep-alive', + 'proxy-authenticate', + 'proxy-authorization', + 'te', + 'trailer', + 'transfer-encoding', + 'upgrade', +]; +const isHopByHopHeader = (header) => hopByHopHeaders.includes(header.toLowerCase()); +exports.isHopByHopHeader = isHopByHopHeader; +//# sourceMappingURL=is_hop_by_hop_header.js.map \ No newline at end of file diff --git a/dist/utils/is_hop_by_hop_header.js.map b/dist/utils/is_hop_by_hop_header.js.map new file mode 100644 index 00000000..e252b590 --- /dev/null +++ b/dist/utils/is_hop_by_hop_header.js.map @@ -0,0 +1 @@ +{"version":3,"file":"is_hop_by_hop_header.js","sourceRoot":"","sources":["../../src/utils/is_hop_by_hop_header.ts"],"names":[],"mappings":";;;AAAA,oGAAoG;AACpG,MAAM,eAAe,GAAG;IACpB,YAAY;IACZ,YAAY;IACZ,oBAAoB;IACpB,qBAAqB;IACrB,IAAI;IACJ,SAAS;IACT,mBAAmB;IACnB,SAAS;CACZ,CAAC;AAEK,MAAM,gBAAgB,GAAG,CAAC,MAAc,EAAW,EAAE,CAAC,eAAe,CAAC,QAAQ,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC,CAAC;AAA/F,QAAA,gBAAgB,oBAA+E"} \ No newline at end of file diff --git a/dist/utils/nodeify.d.ts b/dist/utils/nodeify.d.ts new file mode 100644 index 00000000..026d75a0 --- /dev/null +++ b/dist/utils/nodeify.d.ts @@ -0,0 +1,2 @@ +export declare const nodeify: (promise: Promise, callback?: ((error: Error | null, result?: T | undefined) => void) | undefined) => Promise; +//# sourceMappingURL=nodeify.d.ts.map \ No newline at end of file diff --git a/dist/utils/nodeify.d.ts.map b/dist/utils/nodeify.d.ts.map new file mode 100644 index 00000000..969a9290 --- /dev/null +++ b/dist/utils/nodeify.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"nodeify.d.ts","sourceRoot":"","sources":["../../src/utils/nodeify.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,OAAO,+CAAqD,KAAK,GAAG,IAAI,6BAAiB,IAAI,4BAczG,CAAC"} \ No newline at end of file diff --git a/dist/utils/nodeify.js b/dist/utils/nodeify.js new file mode 100644 index 00000000..7e7543ac --- /dev/null +++ b/dist/utils/nodeify.js @@ -0,0 +1,17 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.nodeify = void 0; +// Replacement for Bluebird's Promise.nodeify() +const nodeify = async (promise, callback) => { + if (typeof callback !== 'function') + return promise; + promise.then((result) => callback(null, result), callback).catch((error) => { + // Need to .catch because it doesn't crash the process on Node.js 14 + process.nextTick(() => { + throw error; + }); + }); + return promise; +}; +exports.nodeify = nodeify; +//# sourceMappingURL=nodeify.js.map \ No newline at end of file diff --git a/dist/utils/nodeify.js.map b/dist/utils/nodeify.js.map new file mode 100644 index 00000000..1433ca1d --- /dev/null +++ b/dist/utils/nodeify.js.map @@ -0,0 +1 @@ +{"version":3,"file":"nodeify.js","sourceRoot":"","sources":["../../src/utils/nodeify.ts"],"names":[],"mappings":";;;AAAA,+CAA+C;AACxC,MAAM,OAAO,GAAG,KAAK,EAAK,OAAmB,EAAE,QAAoD,EAAc,EAAE;IACtH,IAAI,OAAO,QAAQ,KAAK,UAAU;QAAE,OAAO,OAAO,CAAC;IAEnD,OAAO,CAAC,IAAI,CACR,CAAC,MAAM,EAAE,EAAE,CAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,EAClC,QAAQ,CACX,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;QACd,oEAAoE;QACpE,OAAO,CAAC,QAAQ,CAAC,GAAG,EAAE;YAClB,MAAM,KAAK,CAAC;QAChB,CAAC,CAAC,CAAC;IACP,CAAC,CAAC,CAAC;IAEH,OAAO,OAAO,CAAC;AACnB,CAAC,CAAC;AAdW,QAAA,OAAO,WAclB"} \ No newline at end of file diff --git a/dist/utils/normalize_url_port.d.ts b/dist/utils/normalize_url_port.d.ts new file mode 100644 index 00000000..5ed4d9ab --- /dev/null +++ b/dist/utils/normalize_url_port.d.ts @@ -0,0 +1,3 @@ +import type { URL } from 'node:url'; +export declare const normalizeUrlPort: (url: URL) => number; +//# sourceMappingURL=normalize_url_port.d.ts.map \ No newline at end of file diff --git a/dist/utils/normalize_url_port.d.ts.map b/dist/utils/normalize_url_port.d.ts.map new file mode 100644 index 00000000..491134b5 --- /dev/null +++ b/dist/utils/normalize_url_port.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"normalize_url_port.d.ts","sourceRoot":"","sources":["../../src/utils/normalize_url_port.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAWpC,eAAO,MAAM,gBAAgB,QAAS,GAAG,KAAG,MAU3C,CAAC"} \ No newline at end of file diff --git a/dist/utils/normalize_url_port.js b/dist/utils/normalize_url_port.js new file mode 100644 index 00000000..fc4d4a74 --- /dev/null +++ b/dist/utils/normalize_url_port.js @@ -0,0 +1,22 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.normalizeUrlPort = void 0; +// https://url.spec.whatwg.org/#default-port +const mapping = { + 'ftp:': 21, + 'http:': 80, + 'https:': 443, + 'ws:': 80, + 'wss:': 443, +}; +const normalizeUrlPort = (url) => { + if (url.port) { + return Number(url.port); + } + if (url.protocol in mapping) { + return mapping[url.protocol]; + } + throw new Error(`Unexpected protocol: ${url.protocol}`); +}; +exports.normalizeUrlPort = normalizeUrlPort; +//# sourceMappingURL=normalize_url_port.js.map \ No newline at end of file diff --git a/dist/utils/normalize_url_port.js.map b/dist/utils/normalize_url_port.js.map new file mode 100644 index 00000000..dfb0b98e --- /dev/null +++ b/dist/utils/normalize_url_port.js.map @@ -0,0 +1 @@ +{"version":3,"file":"normalize_url_port.js","sourceRoot":"","sources":["../../src/utils/normalize_url_port.ts"],"names":[],"mappings":";;;AAEA,4CAA4C;AAC5C,MAAM,OAAO,GAAG;IACZ,MAAM,EAAE,EAAE;IACV,OAAO,EAAE,EAAE;IACX,QAAQ,EAAE,GAAG;IACb,KAAK,EAAE,EAAE;IACT,MAAM,EAAE,GAAG;CACd,CAAC;AAEK,MAAM,gBAAgB,GAAG,CAAC,GAAQ,EAAU,EAAE;IACjD,IAAI,GAAG,CAAC,IAAI,EAAE;QACV,OAAO,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;KAC3B;IAED,IAAI,GAAG,CAAC,QAAQ,IAAI,OAAO,EAAE;QACzB,OAAO,OAAO,CAAC,GAAG,CAAC,QAAgC,CAAC,CAAC;KACxD;IAED,MAAM,IAAI,KAAK,CAAC,wBAAwB,GAAG,CAAC,QAAQ,EAAE,CAAC,CAAC;AAC5D,CAAC,CAAC;AAVW,QAAA,gBAAgB,oBAU3B"} \ No newline at end of file diff --git a/dist/utils/parse_authorization_header.d.ts b/dist/utils/parse_authorization_header.d.ts new file mode 100644 index 00000000..0dd1fc88 --- /dev/null +++ b/dist/utils/parse_authorization_header.d.ts @@ -0,0 +1,9 @@ +interface Authorization { + type: string; + data: string; + username?: string; + password?: string; +} +export declare const parseAuthorizationHeader: (header: string) => Authorization | null; +export {}; +//# sourceMappingURL=parse_authorization_header.d.ts.map \ No newline at end of file diff --git a/dist/utils/parse_authorization_header.d.ts.map b/dist/utils/parse_authorization_header.d.ts.map new file mode 100644 index 00000000..e815181a --- /dev/null +++ b/dist/utils/parse_authorization_header.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"parse_authorization_header.d.ts","sourceRoot":"","sources":["../../src/utils/parse_authorization_header.ts"],"names":[],"mappings":"AASA,UAAU,aAAa;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,eAAO,MAAM,wBAAwB,WAAY,MAAM,KAAG,aAAa,GAAG,IA+CzE,CAAC"} \ No newline at end of file diff --git a/dist/utils/parse_authorization_header.js b/dist/utils/parse_authorization_header.js new file mode 100644 index 00000000..4b5259a8 --- /dev/null +++ b/dist/utils/parse_authorization_header.js @@ -0,0 +1,53 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.parseAuthorizationHeader = void 0; +const node_buffer_1 = require("node:buffer"); +const splitAt = (string, index) => { + return [ + index === -1 ? '' : string.substring(0, index), + index === -1 ? '' : string.substring(index + 1), + ]; +}; +const parseAuthorizationHeader = (header) => { + if (header) { + header = header.trim(); + } + if (!header) { + return null; + } + const [type, data] = splitAt(header, header.indexOf(' ')); + // https://datatracker.ietf.org/doc/html/rfc7617#page-3 + // Note that both scheme and parameter names are matched case- + // insensitively. + if (type.toLowerCase() !== 'basic') { + return { type, data }; + } + const auth = node_buffer_1.Buffer.from(data, 'base64').toString(); + // https://datatracker.ietf.org/doc/html/rfc7617#page-5 + // To receive authorization, the client + // + // 1. obtains the user-id and password from the user, + // + // 2. constructs the user-pass by concatenating the user-id, a single + // colon (":") character, and the password, + // + // 3. encodes the user-pass into an octet sequence (see below for a + // discussion of character encoding schemes), + // + // 4. and obtains the basic-credentials by encoding this octet sequence + // using Base64 ([RFC4648], Section 4) into a sequence of US-ASCII + // characters ([RFC0020]). + // Note: + // If there's a colon : missing, we imply that the user-pass string is just a username. + // This is a non-spec behavior. At Apify there are clients that rely on this. + // If you want this behavior changed, please open an issue. + const [username, password] = auth.includes(':') ? splitAt(auth, auth.indexOf(':')) : [auth, '']; + return { + type, + data, + username, + password, + }; +}; +exports.parseAuthorizationHeader = parseAuthorizationHeader; +//# sourceMappingURL=parse_authorization_header.js.map \ No newline at end of file diff --git a/dist/utils/parse_authorization_header.js.map b/dist/utils/parse_authorization_header.js.map new file mode 100644 index 00000000..c044a38c --- /dev/null +++ b/dist/utils/parse_authorization_header.js.map @@ -0,0 +1 @@ +{"version":3,"file":"parse_authorization_header.js","sourceRoot":"","sources":["../../src/utils/parse_authorization_header.ts"],"names":[],"mappings":";;;AAAA,6CAAqC;AAErC,MAAM,OAAO,GAAG,CAAC,MAAc,EAAE,KAAa,EAAE,EAAE;IAC9C,OAAO;QACH,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,EAAE,KAAK,CAAC;QAC9C,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,GAAG,CAAC,CAAC;KAClD,CAAC;AACN,CAAC,CAAC;AASK,MAAM,wBAAwB,GAAG,CAAC,MAAc,EAAwB,EAAE;IAC7E,IAAI,MAAM,EAAE;QACR,MAAM,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC;KAC1B;IAED,IAAI,CAAC,MAAM,EAAE;QACT,OAAO,IAAI,CAAC;KACf;IAED,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC;IAE1D,uDAAuD;IACvD,8DAA8D;IAC9D,iBAAiB;IACjB,IAAI,IAAI,CAAC,WAAW,EAAE,KAAK,OAAO,EAAE;QAChC,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;KACzB;IAED,MAAM,IAAI,GAAG,oBAAM,CAAC,IAAI,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,QAAQ,EAAE,CAAC;IAEpD,uDAAuD;IACvD,uCAAuC;IACvC,EAAE;IACF,sDAAsD;IACtD,EAAE;IACF,sEAAsE;IACtE,+CAA+C;IAC/C,EAAE;IACF,oEAAoE;IACpE,iDAAiD;IACjD,EAAE;IACF,wEAAwE;IACxE,sEAAsE;IACtE,8BAA8B;IAE9B,QAAQ;IACR,uFAAuF;IACvF,6EAA6E;IAC7E,2DAA2D;IAC3D,MAAM,CAAC,QAAQ,EAAE,QAAQ,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;IAEhG,OAAO;QACH,IAAI;QACJ,IAAI;QACJ,QAAQ;QACR,QAAQ;KACX,CAAC;AACN,CAAC,CAAC;AA/CW,QAAA,wBAAwB,4BA+CnC"} \ No newline at end of file diff --git a/dist/utils/redact_url.d.ts b/dist/utils/redact_url.d.ts new file mode 100644 index 00000000..618a0563 --- /dev/null +++ b/dist/utils/redact_url.d.ts @@ -0,0 +1,3 @@ +import { URL } from 'node:url'; +export declare const redactUrl: (url: string | URL, passwordReplacement?: string) => string; +//# sourceMappingURL=redact_url.d.ts.map \ No newline at end of file diff --git a/dist/utils/redact_url.d.ts.map b/dist/utils/redact_url.d.ts.map new file mode 100644 index 00000000..869b4e8a --- /dev/null +++ b/dist/utils/redact_url.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"redact_url.d.ts","sourceRoot":"","sources":["../../src/utils/redact_url.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAE/B,eAAO,MAAM,SAAS,QAAS,MAAM,GAAG,GAAG,mCAAuC,MAUjF,CAAC"} \ No newline at end of file diff --git a/dist/utils/redact_url.js b/dist/utils/redact_url.js new file mode 100644 index 00000000..4db0a74e --- /dev/null +++ b/dist/utils/redact_url.js @@ -0,0 +1,15 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.redactUrl = void 0; +const node_url_1 = require("node:url"); +const redactUrl = (url, passwordReplacement = '') => { + if (typeof url !== 'object') { + url = new node_url_1.URL(url); + } + if (url.password) { + return url.href.replace(`:${url.password}`, `:${passwordReplacement}`); + } + return url.href; +}; +exports.redactUrl = redactUrl; +//# sourceMappingURL=redact_url.js.map \ No newline at end of file diff --git a/dist/utils/redact_url.js.map b/dist/utils/redact_url.js.map new file mode 100644 index 00000000..19e3aa5f --- /dev/null +++ b/dist/utils/redact_url.js.map @@ -0,0 +1 @@ +{"version":3,"file":"redact_url.js","sourceRoot":"","sources":["../../src/utils/redact_url.ts"],"names":[],"mappings":";;;AAAA,uCAA+B;AAExB,MAAM,SAAS,GAAG,CAAC,GAAiB,EAAE,mBAAmB,GAAG,YAAY,EAAU,EAAE;IACvF,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE;QACzB,GAAG,GAAG,IAAI,cAAG,CAAC,GAAG,CAAC,CAAC;KACtB;IAED,IAAI,GAAG,CAAC,QAAQ,EAAE;QACd,OAAO,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,GAAG,CAAC,QAAQ,EAAE,EAAE,IAAI,mBAAmB,EAAE,CAAC,CAAC;KAC1E;IAED,OAAO,GAAG,CAAC,IAAI,CAAC;AACpB,CAAC,CAAC;AAVW,QAAA,SAAS,aAUpB"} \ No newline at end of file diff --git a/dist/utils/valid_headers_only.d.ts b/dist/utils/valid_headers_only.d.ts new file mode 100644 index 00000000..1bf1f951 --- /dev/null +++ b/dist/utils/valid_headers_only.d.ts @@ -0,0 +1,5 @@ +/** + * @see https://nodejs.org/api/http.html#http_message_rawheaders + */ +export declare const validHeadersOnly: (rawHeaders: string[]) => string[]; +//# sourceMappingURL=valid_headers_only.d.ts.map \ No newline at end of file diff --git a/dist/utils/valid_headers_only.d.ts.map b/dist/utils/valid_headers_only.d.ts.map new file mode 100644 index 00000000..d7e1374e --- /dev/null +++ b/dist/utils/valid_headers_only.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"valid_headers_only.d.ts","sourceRoot":"","sources":["../../src/utils/valid_headers_only.ts"],"names":[],"mappings":"AAIA;;GAEG;AACH,eAAO,MAAM,gBAAgB,eAAgB,MAAM,EAAE,KAAG,MAAM,EAgC7D,CAAC"} \ No newline at end of file diff --git a/dist/utils/valid_headers_only.js b/dist/utils/valid_headers_only.js new file mode 100644 index 00000000..1abe0e83 --- /dev/null +++ b/dist/utils/valid_headers_only.js @@ -0,0 +1,36 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.validHeadersOnly = void 0; +const node_http_1 = require("node:http"); +const is_hop_by_hop_header_1 = require("./is_hop_by_hop_header"); +/** + * @see https://nodejs.org/api/http.html#http_message_rawheaders + */ +const validHeadersOnly = (rawHeaders) => { + const result = []; + let containsHost = false; + for (let i = 0; i < rawHeaders.length; i += 2) { + const name = rawHeaders[i]; + const value = rawHeaders[i + 1]; + try { + (0, node_http_1.validateHeaderName)(name); + (0, node_http_1.validateHeaderValue)(name, value); + } + catch { + continue; + } + if ((0, is_hop_by_hop_header_1.isHopByHopHeader)(name)) { + continue; + } + if (name.toLowerCase() === 'host') { + if (containsHost) { + continue; + } + containsHost = true; + } + result.push(name, value); + } + return result; +}; +exports.validHeadersOnly = validHeadersOnly; +//# sourceMappingURL=valid_headers_only.js.map \ No newline at end of file diff --git a/dist/utils/valid_headers_only.js.map b/dist/utils/valid_headers_only.js.map new file mode 100644 index 00000000..2e2cff6f --- /dev/null +++ b/dist/utils/valid_headers_only.js.map @@ -0,0 +1 @@ +{"version":3,"file":"valid_headers_only.js","sourceRoot":"","sources":["../../src/utils/valid_headers_only.ts"],"names":[],"mappings":";;;AAAA,yCAAoE;AAEpE,iEAA0D;AAE1D;;GAEG;AACI,MAAM,gBAAgB,GAAG,CAAC,UAAoB,EAAY,EAAE;IAC/D,MAAM,MAAM,GAAG,EAAE,CAAC;IAElB,IAAI,YAAY,GAAG,KAAK,CAAC;IAEzB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,UAAU,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE;QAC3C,MAAM,IAAI,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC;QAC3B,MAAM,KAAK,GAAG,UAAU,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;QAEhC,IAAI;YACA,IAAA,8BAAkB,EAAC,IAAI,CAAC,CAAC;YACzB,IAAA,+BAAmB,EAAC,IAAI,EAAE,KAAK,CAAC,CAAC;SACpC;QAAC,MAAM;YACJ,SAAS;SACZ;QAED,IAAI,IAAA,uCAAgB,EAAC,IAAI,CAAC,EAAE;YACxB,SAAS;SACZ;QAED,IAAI,IAAI,CAAC,WAAW,EAAE,KAAK,MAAM,EAAE;YAC/B,IAAI,YAAY,EAAE;gBACd,SAAS;aACZ;YAED,YAAY,GAAG,IAAI,CAAC;SACvB;QAED,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;KAC5B;IAED,OAAO,MAAM,CAAC;AAClB,CAAC,CAAC;AAhCW,QAAA,gBAAgB,oBAgC3B"} \ No newline at end of file