로그인
KO
apps/caramail-chat / src / Net / WsLink.php · commit 6eebcdb
<?phpnamespace App\Net;use Evenement\EventEmitter;use PhpWasmNetwork\Connection;use PhpWasmNetwork\Event;use PhpWasmNetwork\Network;/** * A Link over a WebSocket (wss://…, port 443: it passes every firewall and proxy), with php-wasm-network, JumPHP's * network library: the browser's WebSocket in the page, a non-blocking socket on a desktop. The chat server * (caramail-server, its WebSocket port) speaks the little of the relay protocol needed: the client joins the room * "CHAT", where the server is peer 0; each binary message carries the applet's TCP bytes unchanged. * * poll() each frame (the window does it). */final class WsLink extends EventEmitter implements Link{    public const ROOM = 'CHAT';    private ?Connection $net = null;    private bool $open = false;    private bool $closed = false;    public function __construct(private readonly string $url)    {    }    public static function isUrl(string $server): bool    {        return (bool)preg_match('#^wss?://#i', $server);    }    public function poll(): void    {        if ($this->closed) {            return;        }        if ($this->net === null) {            // opened at the first poll: the window is drawn before a desktop connection's TLS handshake            $this->net = Network::connect($this->url, ['transport' => 'ws']);        }        foreach ($this->net->poll() as $event) {            switch ($event->type) {                case Event::OPEN:                    $this->net->joinRoom(self::ROOM);                    break;                case Event::ROOM:                    if (!$this->open) {                        $this->open = true;                        $this->emit('open');                    }                    break;                case Event::MESSAGE:                    $this->emit('data', [$event->data]);                    break;                case Event::ERROR:                    $info = $event->info();                    $this->emit('error', [(string)($info['message'] ?? $info['code'] ?? 'network error')]);                    break;                case Event::CLOSE:                    $this->finish('closed');                    return;            }        }        if ($this->net->state() === Connection::CLOSED) {            $this->finish('closed');        }    }    public function write(string $bytes): void    {        if ($this->open && $this->net !== null) {            $this->net->send($bytes, 0);        }    }    public function close(): void    {        $this->net?->close();        $this->finish('closed');    }    private function finish(string $why): void    {        if ($this->closed) {            return;        }        $this->closed = true;        $this->open = false;        $this->emit('close', [$why]);    }}