public function stream(InputStreamInterface $in) : \Generator { if ($this->disconnected) { throw new WebSocketException('Cannot send message to disconnected web socket'); } $eof = $in->eof(); $i = 0; while (!$eof) { $chunk = (yield from $in->read(8188)); $eof = $in->eof(); yield from $this->sendFrame(new Frame($i++ === 0 ? Frame::BINARY : Frame::CONTINUATION, $chunk, $eof)); } }
/** * Read next inbound frame from the socket. * * @return Frame or false when connection has been terminated. */ protected function readNextFrane() : \Generator { try { list($a, $b) = array_map('ord', str_split(yield from Stream::readBuffer($this->stream, 2, true), 1)); $finished = $a & Frame::FINISHED ? true : false; $opcode = (int) ($a & Frame::OPCODE); $masked = $b & Frame::MASKED ? true : false; $len = (int) ($b & Frame::LENGTH); if ($this->masked && !$masked) { throw new WebSocketException('Client messages must be masked'); } // Parse extended length fields: if ($len === 0x7e) { $len = unpack('n', yield from Stream::readBuffer($this->stream, 2, true))[1]; } elseif ($len === 0x7f) { $lp = unpack('N2', yield from Stream::readBuffer($this->stream, 8, true)); // 32 bit int check: if (PHP_INT_MAX === 0x7fffffff) { if ($lp[1] !== 0 || $lp[2] < 0) { throw new WebSocketException('Max payload size exceeded'); } $len = $lp[2]; } else { $len = $lp[1] << 32 | $lp[2]; if ($len < 0) { throw new WebSocketException('Cannot use most significant bit in 64 bit length field'); } } } if ($len < 0) { throw new WebSocketException('Payload length must not be negative'); } // Read and unmask data in chunks: if ($this->masked) { $key = str_split(yield from Stream::readBuffer($this->stream, 4, true), 1); } $data = ''; $read = 0; $i = 0; while ($read < $len && !$this->stream->eof()) { $data .= (yield from $this->stream->read($len - $read)); if ($this->masked) { for ($read = \strlen($data); $i < $read; $i++) { $data[$i] = $data[$i] ^ $key[$i % 4]; } } else { $read = \strlen($data); } } if (\strlen($data) !== $len) { throw new WebSocketException(sprintf('Expected %u bytes, received %u bytes', $len, \strlen($data))); } return new Frame($opcode, $data, $finished); } catch (StreamException $e) { if ($this->stream->eof()) { return false; } throw $e; } catch (WebSocketException $e) { if ($this->stream->eof()) { return false; } throw $e; } }
protected function broadcastBinary(InputStreamInterface $in, Client $sender = NULL, bool $excludeSender = true) : \Generator { // Copy clients array to avoid sending partial messages to clients that connect during broadcast. $clients = $this->clients; $eof = $in->eof(); $i = 0; while (!$eof) { $chunk = (yield from $in->read(8188)); $eof = $in->eof(); yield from $this->broadcastFrame(new Frame($i++ === 0 ? Frame::BINARY : Frame::CONTINUATION, $chunk, $eof), $clients, $sender, $excludeSender); } }