Esempio n. 1
0
 /**
  * @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);
 }
Esempio n. 2
0
 /**
  * Returns bytes from the buffer based on the current length or current search byte.
  *
  * @return string
  */
 private function remove() : string
 {
     if (null !== $this->byte && false !== ($position = $this->buffer->search($this->byte))) {
         if (0 === $this->length || $position < $this->length) {
             return $this->buffer->shift($position + 1);
         }
         return $this->buffer->shift($this->length);
     }
     if (0 === $this->length) {
         return $this->buffer->drain();
     }
     return $this->buffer->shift($this->length);
 }
Esempio n. 3
0
 /**
  * @coroutine
  *
  * Reads and parses characters from the stream according to a format.
  *
  * The format string is of the same format as `sscanf()`.
  *
  * @param string $format The parse format.
  * @param float|int $timeout Number of seconds until the returned promise is rejected with a TimeoutException
  *     if no data is received. Use 0 for no timeout.
  *
  * @return \Generator
  *
  * @resolve array An array of parsed values.
  *
  * @throws \Icicle\Awaitable\Exception\TimeoutException If the operation times out.
  * @throws \Icicle\Stream\Exception\UnreadableException If the stream is no longer readable.
  * @throws \Icicle\Stream\Exception\ClosedException If the stream is unexpectedly closed.
  *
  * @see http://php.net/sscanf
  */
 public function scan(string $format, float $timeout = 0) : \Generator
 {
     // Read from the stream chunk by chunk, attempting to satisfy the format
     // string each time until the format successfully parses or the end of
     // the stream is reached.
     while (true) {
         $result = sscanf((string) $this->buffer, $format . '%n');
         $length = $result ? array_pop($result) : null;
         // If the format string was satisfied, consume the used characters and
         // return the parsed results.
         if ($length !== null && $length < $this->buffer->getLength()) {
             $this->buffer->shift($length);
             return $result;
         }
         // Read more into the buffer if possible.
         if ($this->stream->isReadable()) {
             $this->buffer->push(yield from $this->stream->read(0, null, $timeout));
         } else {
             // Format string can't be satisfied.
             return [];
         }
     }
 }
Esempio n. 4
0
 /**
  * @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));
 }