UNPKG

kaven-utils

Version:

Utils for Node.js.

156 lines (155 loc) 6.05 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: 2024-11-01 10:48:07.314 * @version: 5.4.5 * @times: 45 * @lines: 198 * @copyright: Copyright © 2022-2024 Kaven. All Rights Reserved. * @description: [description] * @license: [license] ********************************************************************/ import { Strings_CR_LF, FileSize } from "kaven-basic"; import { createConnection } from "node:net"; import { HttpProxyUser } from "./HttpProxyUser.js"; import { InternalLogger } from "../../KavenUtility.Internal.js"; export class AbstractProxyServer { socketClientMap = new Map(); EnableLog = true; EnableError = true; Verbose = false; Authorization; Log(...args) { if (this.EnableLog) { InternalLogger()?.Info(...args); } } Error(...args) { if (this.EnableError) { InternalLogger()?.Error(...args); } } 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 ${FileSize(client.UserSocket.bytesRead)}, write ${FileSize(client.UserSocket.bytesWritten)}`, `${Strings_CR_LF}proxy -> server: read ${FileSize(client.ServerSocket?.bytesRead ?? 0)}, write ${FileSize(client.ServerSocket?.bytesWritten ?? 0)}`); } client.Destroy(); } }