UNPKG

kaven-utils

Version:

Utils for Node.js.

161 lines (160 loc) 6.1 kB
/******************************************************************** * @author: Kaven * @email: kaven@wuwenkai.com * @website: http://blog.kaven.xyz * @file: [Kaven-Utils] /src/net/proxy/AbstractProxyServer.ts * @create: 2022-04-18 13:40:29.991 * @modify: 2025-10-14 22:58:04.864 * @version: 6.1.0 * @times: 50 * @lines: 200 * @copyright: Copyright © 2022-2025 Kaven. All Rights Reserved. * @description: [description] * @license: [license] ********************************************************************/ import { Strings_CR_LF, ToFileSize } from "kaven-basic"; import { createConnection } from "node:net"; import { HttpProxyUser } from "./HttpProxyUser.js"; import { EOL } from "node:os"; export class AbstractProxyServer { socketClientMap = new Map(); EnableLog = true; EnableError = true; Verbose = false; Authorization; Logger; Log(data) { if (this.EnableLog) { this.Logger?.Info(data); } } Error(data) { if (this.EnableError) { this.Logger?.Error(data); } } async onData(client, data) { if (client.Parser === undefined) { return; } client.Parser.Add(data); const req = client.Parser.TryGet(); if (req === undefined) { return; } if (this.Verbose && req.Index < 2) { client.Log(data.toString()); } if (this.Authorization !== undefined) { const info = req.GetAuthorizationInfo(this.Authorization.RequestHeaderName); const ok = await this.Authorization.Authenticate(info); if (!ok) { const res = this.Authorization.GetResponse(); client.UserSocket.write(res.ToBuffer()); return; } } if (!req.IsHttpRequest) { client.Parser = undefined; } if (req.IsHttpConnect) { client.Parser = undefined; } await this.handleHttpRequest(client, req); } writeToServer(req, proxyToServerSocket) { const requestTargetUrl = req.StartLine?.RequestTarget; if (requestTargetUrl === undefined) { proxyToServerSocket.write(req.ToBuffer()); return; } // absolute form -> absolute path // req.StartLine.RequestTarget = new KavenUrl(requestTargetUrl.PathAndParameters); req.StartLine.UseCompleteTarget = false; // headerLines.push("X-Forwarded-Proto: http"); // headerLines.push(`X-Forwarded-For: ${userIp}`); // headerLines.push(`X-Forwarded-Port: ${userPort}`); // headerLines.push(`X-Forwarded-Host: ${requestTargetUrl.DomainName}`); const buffer = req.ToBuffer(); proxyToServerSocket.write(buffer); } connect(port, host) { return new Promise((resolve, reject) => { const socket = createConnection(port, host, () => { resolve(socket); }); socket.on("error", err => { reject(err); }); }); } async handleHttpRequest(client, req) { try { if (client.ServerSocket !== undefined) { this.writeToServer(req, client.ServerSocket); return; } client.Server = `${req.Address.DomainName}:${req.Port}`; const userSocket = client.UserSocket; const host = req.Address.DomainName; const port = req.Port; // Creating a connection from proxy to destination server const proxyToServerSocket = await this.connect(port, host); client.Log("Proxy to server set up"); if (req.IsHttpRequest) { if (req.IsHttpConnect) { userSocket.write("HTTP/1.1 200 Connection Established\r\n\r\n"); userSocket.pipe(proxyToServerSocket); proxyToServerSocket.pipe(userSocket); } else { this.writeToServer(req, proxyToServerSocket); // proxyToServerSocket.on("data", data => { // InternalLogger.Warn(data.toString()); // userSocket.write(data); // }); proxyToServerSocket.pipe(userSocket); } } else { proxyToServerSocket.write(req.ToBuffer()); userSocket.pipe(proxyToServerSocket); proxyToServerSocket.pipe(userSocket); } client.ServerSocket = proxyToServerSocket; proxyToServerSocket.on("error", (err) => { client.Error(`Proxy to server error: ${err}`); }).once("close", () => { userSocket.destroy(); this.Stop(client, "proxyToServerSocket closed"); }); } catch (ex) { client.Error(ex); client.Destroy(); } } onSocketConnected(userSocket) { const client = new HttpProxyUser(userSocket); this.socketClientMap.set(userSocket, client); client.Log("New client connected to proxy"); userSocket.on("data", (data) => { this.onData(client, data); }); userSocket.on("error", (err) => { client.Error(`Client to proxy error: ${err}`); }).once("close", () => { this.Stop(client, "userSocket closed"); }); } Stop(client, msg = "") { if (this.socketClientMap.delete(client.UserSocket)) { client.Log([ msg, `${Strings_CR_LF}user -> proxy: read ${ToFileSize(client.UserSocket.bytesRead)}, write ${ToFileSize(client.UserSocket.bytesWritten)}`, `${Strings_CR_LF}proxy -> server: read ${ToFileSize(client.ServerSocket?.bytesRead ?? 0)}, write ${ToFileSize(client.ServerSocket?.bytesWritten ?? 0)}`, ].join(EOL)); } client.Destroy(); } }