import { Transport } from "./communication/Transport.js"; import { Datagram, Request, Response, RequestStub } from "../Independents/Datagram/datagram-types.js" import { DatagramShort, isRequest, isResponse } from "../Independents/Datagram/datagram.js"; import { HostName, HostNameTemplate } from "../Independents/DNS/HostName.js"; import { OpenPromise } from "../Independents/util/OpenPromise.js"; import { RouteDatagram } from "./datagram-routing.js"; import { Gateway } from "./Gateway.js"; import { BurpaStateLens, CBurpastateWrapper } from "./burpastate.js"; import { Subscription } from "../Independents/util/Subscription.js"; import { Vagrant } from "./Host.js"; export type RequestResponse = { request: RequestStub, subscription: Subscription }; export type ResponseHookFn = (response: Response) => void; export type HostNames = { [host: string]: HostName }; /** * Represents a remote gateway accessible via a communication channel (aka Transport) * such as a socket. It can match outgoing datagrams with incomming datagrams to * provide a mechanism for the request/response pattern. */ export class GatewayProxy { /** * Eeach request datagram is marked with a request number, unique * to the Transport. By providing the same request number in the response * datagram, requests and responses can be paired. */ Transport: Transport = undefined as unknown as Transport; responseHooks: { [reqno: number]: ResponseHookFn[] } = {} constructor(public remoteAliases: HostNames, public gateway: Gateway, public hostTemplate: HostNameTemplate, public secret: string) { } addResponseHook(reqno: number, callback: ResponseHookFn) { if (!this.responseHooks[reqno]) { this.responseHooks[reqno] = []; } this.responseHooks[reqno].push(callback); } onReceiveDatagram(datagram: Datagram) { if (isRequest(datagram)) { if (datagram.path == "/children" && datagram.method == "POST" && this.gateway.isRecipient(datagram)) { this.addResponseHook(datagram.reqno, (response) => { if (response.status == 201) { //console.log(`DETECTED SPAWNING A NEW ALIAS AT ${datagram.to} RESULTING IN ${response.body.host} REPLACING ${response.body.replaces}`); RemoveGatewayProxyAliases(new CBurpastateWrapper(this.gateway.whole), response.body.host); this.remoteAliases[response.body.host] = new HostName(response.body.host); // delete this.aliases[response.body.replaces]; } }) } } RouteDatagram(new CBurpastateWrapper(this.gateway.whole), datagram); // onReceiveDatagram(datagram); } transmitDatagram(datagram: Datagram) { let dbg = this.Transport.debug; let { localGateway, remoteGateway, type } = dbg; //console.warn("Routed", dbg, datagram.trail, DatagramShort(datagram)); this.gateway.state.log("Routed", `from ${localGateway} to ${remoteGateway} via ${type} datagram:${DatagramShort(datagram)}`); if (isRequest(datagram)) { // this.reportSentRequest(datagram) } else if (isResponse(datagram)) { let fns = this.responseHooks[datagram.reqno]; if (fns) { for (let fn of fns) { fn(datagram); } delete this.responseHooks[datagram.reqno] } } this.Transport.transmitDatagram(datagram); } } function RemoveGatewayProxyAliases(state: CBurpastateWrapper, alias: string) { for (let rgw of Object.keys(state.whole.persistent.hosts)) { let gw = state.getHost(rgw); if (gw instanceof Gateway) { for (let gwp of gw.extension.downstreamGateways) { delete gwp.remoteAliases[alias]; } } } }