<?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; }}