This repository has been archived on 2023-04-19. You can view files and clone it, but cannot push or open issues or pull requests.
odproxy-deprecated/index.ts

183 lines
4.1 KiB
TypeScript

import http, { IncomingMessage, ServerResponse } from "http";
import httpProxy from 'http-proxy';
import fs from "fs";
import child_process, { ChildProcess } from "child_process";
import kill from "tree-kill";
import axios from "axios";
let config = require("./config.json");
class ServiceManager {
proxy = httpProxy.createProxy();
server = http.createServer((req, res) => this.listen(req, res));
services: Service[] = [];
stopped: boolean = false;
async setup() {
this.server.listen(config.listen);
}
async stop() {
if(this.stopped) return;
this.stopped = true;
this.services.forEach(s => s.stop());
if(config.socket) fs.rmSync(config.listen);
}
async listen(req: IncomingMessage, res: ServerResponse) {
let oriHost = req.headers.host;
let host = oriHost.indexOf(":") ? oriHost.split(':')[0] : oriHost;
for(let i in this.services) {
if(this.services[i].hosts.indexOf(host) !== -1) {
if(this.services[i].socket) {
this.proxy.web(req, res, {target: {
socketPath: await this.services[i].start(),
host: "localhost"
}});
} else {
this.proxy.web(req, res, {target: await this.services[i].start()});
}
return;
}
}
console.warn("Host", host, "not found!");
res.statusCode = 404;
res.end();
}
}
class Service implements ServiceConfig {
hosts: string[];
type: ServiceType;
start_cmd: string;
stop_cmd: string;
listen: string;
socket: boolean;
waiting: boolean = false;
waiting_clients: Function[] = [];
child?: ChildProcess;
timeout?: number;
timeout_check?: NodeJS.Timeout;
last_activity: Date;
man: ServiceManager;
alive: boolean = false;
constructor(man: ServiceManager, config: ServiceConfig) {
this.man = man;
this.hosts = config.hosts;
this.type = config.type;
this.start_cmd = config.start_cmd;
this.stop_cmd = config.stop_cmd;
this.socket = config.socket;
this.listen = config.listen;
this.timeout = config.timeout ? config.timeout*1000 : 0;
man.services.push(this);
}
async start() {
this.last_activity = new Date;
if(this.timeout && !this.waiting) {
if(this.timeout_check) clearTimeout(this.timeout_check);
this.timeout_check = setTimeout(() => {
if((new Date).getTime() > this.last_activity.getTime()+this.timeout) this.stop();
}, this.timeout);
}
if(!this.child) {
if(this.socket) if(fs.existsSync(this.listen)) fs.rmSync(this.listen);
this.child = child_process.exec(this.start_cmd);
console.log(`Started service ${this.hosts[0]}`);
}
if(!this.alive) {
await this.life_check();
this.alive = true;
}
return this.listen;
}
async stop() {
if(this.socket) if(fs.existsSync(this.listen)) fs.rmSync(this.listen);
if(this.alive) {
this.alive = false;
if(this.type == "process") kill(this.child.pid, "SIGUSR2");
if(this.type == "control") child_process.exec(this.stop_cmd);
this.child = null;
console.log(`Stopped service ${this.hosts[0]}`);
}
}
async life_check() {
if(!this.waiting) {
this.waiting = true;
let done = () => {
clearInterval(int);
this.waiting = false;
for(let i in this.waiting_clients) {
this.waiting_clients[i]();
}
};
let int = setInterval(async () => {
if(this.socket) {
if(fs.existsSync(this.listen)) done();
} else {
try {
if([200, 301, 302].indexOf((await axios.get(this.listen)).status) !== -1) done();
} catch(e) {}
}
}, 100);
}
await new Promise((resolve) => {
this.waiting_clients.push(resolve);
});
}
}
type ServiceType = "control" | "process";
interface ServiceConfig {
hosts: string[];
type: ServiceType;
start_cmd: string;
stop_cmd?: string;
listen: string;
socket: boolean;
timeout?: number;
};
let start = async (config: ServiceConfig[]) => {
let man = new ServiceManager;
await man.setup();
config.forEach(s => {
new Service(man, s);
});
let exit = async (e: any) => {
if(e) console.log(e);
man.stop();
process.exit(0);
};
process.on('exit', exit);
process.on('beforeExit', exit);
process.on('SIGINT', exit);
process.on('SIGUSR1', exit);
process.on('SIGUSR2', exit);
process.on('SIGTERM', exit);
process.on('uncaughtException', console.error);
};
start(config.map);