kaven-utils
Version:
Utils for Node.js.
156 lines (155 loc) • 6.05 kB
JavaScript
/********************************************************************
* @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();
}
}