/** * @coroutine * * @param int $maxSize Max frame size. * @param float|int $timeout * * @return \Generator * * @resolve \Icicle\WebSocket\Protocol\Rfc6455Frame * * @throws \Icicle\WebSocket\Exception\FrameException */ public function read(int $maxSize, float $timeout = 0) : \Generator { $buffer = new Buffer(); try { do { $buffer->push(yield from $this->socket->read(0, null, $timeout)); } while ($buffer->getLength() < 2); $bytes = unpack('Cflags/Clength', $buffer->shift(2)); $rsv = $bytes['flags'] & self::RSV_MASK; $opcode = $bytes['flags'] & self::OPCODE_MASK; $final = (bool) ($bytes['flags'] & self::FIN_MASK); $masked = (bool) ($bytes['length'] & self::MASK_FLAG_MASK); $size = $bytes['length'] & self::LENGTH_MASK; if ($masked === $this->masked) { throw new FrameException(sprintf('Received %s frame.', $masked ? 'masked' : 'unmasked')); } if ($size === self::TWO_BYTE_LENGTH_FLAG) { while ($buffer->getLength() < 2) { $buffer->push(yield from $this->socket->read(0, null, $timeout)); } $bytes = unpack('nlength', $buffer->shift(2)); $size = $bytes['length']; if ($size < self::TWO_BYTE_LENGTH_FLAG) { throw new FrameException('Frame format error.'); } } elseif ($size === self::EIGHT_BYTE_LENGTH_FLAG) { while ($buffer->getLength() < 8) { $buffer->push(yield from $this->socket->read(0, null, $timeout)); } $bytes = unpack('Nhigh/Nlow', $buffer->shift(8)); $size = $bytes['high'] << 32 | $bytes['low']; if ($size < self::TWO_BYTE_MAX_LENGTH) { throw new FrameException('Frame format error.'); } } if ($size > $maxSize) { throw new PolicyException('Frame size exceeded max allowed size.'); } if ($masked) { while ($buffer->getLength() < self::MASK_LENGTH) { $buffer->push(yield from $this->socket->read(0, null, $timeout)); } $mask = $buffer->shift(self::MASK_LENGTH); } while ($buffer->getLength() < $size) { $buffer->push(yield from $this->socket->read(0, null, $timeout)); } $data = $buffer->shift($size); if ($masked) { $data ^= str_repeat($mask, (int) (($size + self::MASK_LENGTH - 1) / self::MASK_LENGTH)); } } finally { if (!$buffer->isEmpty()) { $this->socket->unshift((string) $buffer); } } return new Frame($opcode, $data, $rsv, $final); }
/** * @param \Icicle\Stream\Structures\Buffer $buffer * @param \Icicle\Socket\Socket $socket * @param float|int $timeout * * @return \Generator * * @throws \Icicle\Http\Exception\MessageException * @throws \Icicle\Http\Exception\ParseException */ protected function readHeaders(Buffer $buffer, Socket $socket, float $timeout = 0) : \Generator { $size = 0; $headers = []; do { while (false === ($position = $buffer->search("\r\n"))) { if ($buffer->getLength() >= $this->maxSize) { throw new MessageException(Response::REQUEST_HEADER_TOO_LARGE, sprintf('Message header exceeded maximum size of %d bytes.', $this->maxSize)); } $buffer->push(yield from $socket->read(0, null, $timeout)); } $length = $position + 2; $line = $buffer->shift($length); if (2 === $length) { return $headers; } $size += $length; $parts = explode(':', $line, 2); if (2 !== count($parts)) { throw new ParseException('Found header without colon.'); } list($name, $value) = $parts; $name = Message\decode($name); $value = Message\decode(trim($value)); // No check for case as Message class will automatically combine similarly named headers. if (!isset($headers[$name])) { $headers[$name] = [$value]; } else { $headers[$name][] = $value; } } while ($size < $this->maxSize); throw new MessageException(Response::REQUEST_HEADER_TOO_LARGE, sprintf('Message header exceeded maximum size of %d bytes.', $this->maxSize)); }