103 lines
2.2 KiB
TypeScript
103 lines
2.2 KiB
TypeScript
import { hri } from "human-readable-ids";
|
|
import Debug from "debug";
|
|
import Client from "./client";
|
|
import { TunnelAgent } from "./agent";
|
|
import { Options } from "../server";
|
|
|
|
// Manage sets of clients
|
|
// A client is a "user session" established to service a remote localtunnel client
|
|
export class ClientManager {
|
|
public opt: Options;
|
|
public clients: Map<string, Client>;
|
|
public stats: { tunnels: number };
|
|
public debug: any;
|
|
|
|
constructor(opt: Options) {
|
|
this.opt = opt || {};
|
|
|
|
// id -> client instance
|
|
this.clients = new Map();
|
|
|
|
// statistics
|
|
this.stats = {
|
|
tunnels: 0,
|
|
};
|
|
|
|
this.debug = Debug("lt:ClientManager");
|
|
}
|
|
|
|
// create a new tunnel with `id`
|
|
// if the id is already used, a random id is assigned
|
|
// if the tunnel could not be created, throws an error
|
|
async newClient(id: string) {
|
|
const clients = this.clients;
|
|
const stats = this.stats;
|
|
|
|
// can't ask for id already is use
|
|
if (clients.has(id)) {
|
|
id = hri.random();
|
|
}
|
|
|
|
const maxSockets = this.opt.max_tcp_sockets;
|
|
|
|
const agent = new TunnelAgent({
|
|
clientId: id,
|
|
maxSockets: 10,
|
|
});
|
|
|
|
const client = new Client({ id, agent });
|
|
|
|
// add to clients map immediately to avoid races with other clients requesting same id
|
|
clients.set(id, client);
|
|
|
|
client.once("close", (e) => {
|
|
console.log(`\n\n\n\x1b[33m==>Closing: \x1b[0m\n`);
|
|
console.log(e);
|
|
this.removeClient(id);
|
|
});
|
|
|
|
// try/catch used here to remove client id
|
|
try {
|
|
const info = await agent.listen();
|
|
|
|
++stats.tunnels;
|
|
|
|
return {
|
|
id: id,
|
|
port: info.port,
|
|
max_conn_count: maxSockets,
|
|
url: "",
|
|
};
|
|
} catch (err) {
|
|
this.removeClient(id);
|
|
// rethrow error for upstream to handle
|
|
throw err;
|
|
}
|
|
}
|
|
|
|
getClient(id: string) {
|
|
return this.clients.get(id);
|
|
}
|
|
|
|
hasClient(id: string) {
|
|
return !!this.getClient(id);
|
|
}
|
|
|
|
removeClient(id: string) {
|
|
this.debug("removing client: %s", id);
|
|
|
|
const client = this.getClient(id);
|
|
|
|
if (!client) {
|
|
console.log(`\n\n\n\x1b[33m==>NO CLIENT \x1b[0m\n`);
|
|
return;
|
|
}
|
|
|
|
--this.stats.tunnels;
|
|
|
|
delete this.clients[id];
|
|
|
|
client.close();
|
|
}
|
|
}
|