import { updateRoots } from "./burpastate.js"; import { Request, Response, Datagram, RequestInit, RequestInfo, Message, MessageInfo, MessageInit, RequestInitObject, DatagramInitObject } from "../Independents/Datagram/datagram-types.js" import { CRequest, DatagramShort } from "../Independents/Datagram/datagram.js" import { Host, HostExtension, HostExtensions, NamingFn } from "./Host.js"; import { generateGatewayName } from "../Independents/RememberableNames/random-gateway-name.js"; import { Transport } from "./communication/Transport.js" import { Either, Left, Right } from "../Independents/util/Either.js" import { HostName, HostNameTemplate } from "../Independents/DNS/HostName.js"; import { WebsocketTransportClient } from "./communication/WebsocketTransportClient.js"; import { LocalTransportClient } from "./communication/LocalTransport.js"; import { CHostOwnershipRecord } from "./Host.js"; import { GatewayProxy } from "./GatewayProxy.js"; //import { isDownstreamOf } from "../Independents/DNS/Resolve.js"; import { TBurpaState, CBurpastateWrapper } from "./burpastate.js"; import { Lens } from "functional"; import { Subscription } from "../Independents/util/Subscription.js"; import { processType } from "../Independents/util/misc.js"; import { exitCode } from "process"; import { OpenPromise } from "../index.js"; export class SpawningError { constructor(public name: string, public msg: string) { } } export class Gateway extends Host { //tempAlias: HostName | null = null; getGateway() { return this; } // createTemporaryAlias(parentGw: string): HostName { // let name = `temp5555${RandomString()}-${parentGw}`; // let hn = new HostName(name); // this.aliases.push(name); // this.tempAlias = hn; // return hn; // } // get tempAlias(): HostName { // for (let a of this.aliases) { // if (a.startsWith("temp5555") || a.startsWith("temp6666")) { // return new HostName(a); // } // } // throw "no temp alias" // } addChild(name: string, secret: string, registred: boolean, gateway: "gateway" | "vagrant"): CHostOwnershipRecord { return this._addChild(gateway, name, secret, registred, gateway); } get extension(): GatewayHostExtension { let e = HostExtensions[this.hostId] as GatewayHostExtension; if (!e) { e = new GatewayHostExtension(this.state, this.self.burpaport); HostExtensions[this.hostId] = e; } return e; } // async spawnThisGatewayAsFarUpstreamAsNeeded(templateForHostToBe: HostNameTemplate): Promise { // // if (templateForHostToBe.isDownstreamOf(this.state.hostName("local"))) { // // return this.hostId; // // } // if (this.extension.upstreamGateway == null) { // await this.registerAtUpstream(templateForHostToBe.nameTemplate) // TODO! Does not work // } // return this.alias.name; // } getNamingFunction(): NamingFn { return generateGatewayName; } // isDownstreamOf(parent: string) { // for (let a of this.aliases) { // if (isDownstreamOf(a, parent)) { // return true; // } // } // return false; // } collectGarbage() { super.collectGarbage(); // let remove: GatewayProxy[] = []; // for (let gwp of this.downstreamGateways) { // if (Object.keys(gwp.aliases).length == 0 || (gwp.Transport.lastHeartbeat != -1 && gwp.Transport.lastHeartbeat < Date.now() - 60000)) { // remove.push(gwp); // } // } // RemoveItems(this.downstreamGateways, remove); } isRecipient(datagram: Datagram) { let to = datagram.to; for (let a of this.aliases2) { if (to == a) { return true; } } return false; } async onReceiveDatagram(datagram: Datagram) { //}, hook?: (response: Promise) => void, requestTransport?: Transport): void { if (this.isRecipient(datagram)) { super.onReceiveDatagram(datagram); return; } throw new Error(`Not recipient`); } // async register(name: string): Promise> { // return Promise.resolve(Left(new SpawningError("todo", "todo"))); // } getDownstreamProxy(host: string) { for (let gwp of this.extension.downstreamGateways) { if (gwp.remoteAliases[host]) { return gwp; } } return null; } dbgDownstreamTransports() { return this.extension.downstreamGateways.map(e => Object.keys(e.remoteAliases).join(",")).join(";"); } getGatewayProxy(name: string): GatewayProxy { let gw = this.findGatewayProxy(name); if (gw) { return gw; } throw new Error(`Cannot find transport for gateway "${name}" from gateway ${this.alias.name}. Have downstream Transports for "${this.dbgDownstreamTransports()}"`) } findGatewayProxy(name: string): GatewayProxy | null { if (this.alias.parent == name) { return this.extension.upstreamGateway; } return this.getDownstreamProxy(name); } assureUpstreamTransport(seed: string, sampleTemplate: string, secret: string): void { let nt = new HostNameTemplate(sampleTemplate); if (this.extension.upstreamGateway) { throw new Error("Already upstream transport") return; // throw new Error("Why create a transport when there is one?") // let previous = Object.keys(this.extension.upstreamGateway.remoteAliases); // if (previous.length !== 1) { // throw new Error(`How can an upstream transport have other than one aliases ${previous}?`); // } // if (previous[0] !== nameTemplate.parent) { // throw new Error(`Upstream gateway missmatch ${previous[0]} !== ${nameTemplate.parent}`) // } // return true; } let gwp = new GatewayProxy({ [nt.parent]: new HostName(nt.parent) }, this, nt, secret); this.createClientTransport(seed, gwp); this.extension.upstreamGateway = gwp; return; } createClientTransport(seed: string, gw: GatewayProxy): Transport { if (processType() == "nodejs") { return new LocalTransportClient(gw); } //console.warn("CREATING WEBSOCKETCLIENT Transport"); return new WebsocketTransportClient(seed, false, gw); } /** * Spawns an alias for this gateway and registers it at the upstream gateway * * @param nameTemplate The host name of the new alias. May include wildcards. * @param secret This secret will be used to verify that future changes are authorized * @returns The new host name (without wildcards). */ async registerAtUpstream(sampleTemplate: string): Promise { //debugger let sample = this.state.hostName(sampleTemplate); if (sample.isLocal()) { return this.hostId; } // console.error("NONLOCAL GATEWAY NEEDED FOR",sampleTemplate); if (this.extension.registration) { return this.extension.registration; } this.extension.registration = new OpenPromise(); //let temp = `{gw$}-gw.burpa.net` let seed = new HostNameTemplate("{gw$}"); // let tempAlias = `${this.hostId}-${hnt.parent}`; // ?? new HostName(""); // this.whole.persistent.aliases[tempAlias] = this.hostId; let secret = ""; this.assureUpstreamTransport(this.state.getSerialId(), sampleTemplate, secret); let request1 = new CRequest( { port: "burpasys", from: this.hostId, to: "*", method: "PUT", path: `/children/${this.hostId}`, body: { secret } } ); let request2 = new CRequest( { port: "burpasys", from: this.hostId, to: "*", method: "POST", path: "/children", body: { replaces: this.hostId, secret } } ); //console.warn(request2.from) let response1: Subscription; let response2: Subscription; response1 = this.reportSentRequest(request1); response2 = this.reportSentRequest(request2); await response1; let response = await response2.current; if (response.status !== 201) { this.state.log("Error", `Spawn failed. ${DatagramShort(response)}`); } this.whole.persistent.aliases[response.body.host as string] = this.hostId; updateRoots(this.state, response.body); this.extension.registration.resolve(response.body.host) return await this.extension.registration; } } function RemoveItems(arr: any[], remove: any[]) { for (var i = remove.length - 1; i >= 0; i--) arr.splice(remove[i], 1); } export class GatewayHostExtension extends HostExtension { registration: OpenPromise | null = null; upstreamGateway: GatewayProxy = undefined as unknown as GatewayProxy; downstreamGateways: GatewayProxy[] = [] }