ES
games/xencenter-xenadmin-xcp-jumphp / src / Xapi / StreamTransport.php · commit fb1b7c9
<?phpnamespace App\Xapi;use JumPHP\Async\Task;/** * XAPI over real streams: one HttpChannel per channel name ('calls', 'events', 'rrd'), each its own keep-alive connection, opened * through a connector: the engine's Tcp (plain HTTP, port 80: a socket on the desktop; in the browser the JumPHP runtime on the * user's own network, or the server's bridge), an SSH tunnel (`ssh -W 127.0.0.1:80` through the host's own sshd: the system's ssh * on the desktop, OpenSSH in WebAssembly in the browser), or TLS (HTTPS, port 443, desktop). */final class StreamTransport implements Transport{    /** @var array<string, HttpChannel> */    private array $channels = [];    /** @var list<TlsStream> */    private array $tls = [];    /** @param \Closure(): object $open opens one stream to the XAPI server */    public function __construct(private \Closure $open, private string $hostHeader, private string $description)    {    }    /** A plain TCP connection ($tcp: the engine's Tcp, or an SshConnector of $engine->ssh()->via()). */    public static function tcp(object $tcp, string $host, int $port, string $description): self    {        return new self(fn () => $tcp->connect($host, $port, 15.0), $host, $description);    }    public static function tls(string $host, int $port): self    {        $transport = null;        $transport = new self(function () use ($host, $port, &$transport): object {            $stream = new TlsStream($host, $port);            $transport->tls[] = $stream;            return $stream;        }, $host, "HTTPS $host:$port");        return $transport;    }    public function request(string $method, string $path, string $body = '', array $headers = [], string $channel = 'calls'): Task    {        $this->channels[$channel] ??= new HttpChannel($this->open, $this->hostHeader, $channel === 'events' ? 90.0 : 40.0);        return $this->channels[$channel]->request($method, $path, $body, $headers);    }    /** @return array{0: \Closure(): object, 1: string} how a stream of one's own is opened (HttpStreamTransfer: XVA files), and the Host header */    public function connector(): array    {        return [$this->open, $this->hostHeader];    }    public function poll(float $dt): void    {        foreach ($this->tls as $i => $stream) {            $stream->poll();            if ($stream->isClosed()) {                unset($this->tls[$i]);            }        }        $this->tls = array_values($this->tls);        foreach ($this->channels as $channel) {            $channel->poll($dt);        }    }    public function close(): void    {        foreach ($this->channels as $channel) {            $channel->close();        }        $this->channels = [];        foreach ($this->tls as $stream) {            $stream->close();        }        $this->tls = [];    }    public function describe(): string    {        return $this->description;    }}