Esempio n. 1
0
 /**
  * @coroutine
  *
  * Flushes the contents of the internal buffer to the underlying stream.
  *
  * @return \Generator
  *
  * @resolve int Number of bytes written to the stream.
  *
  * @throws \Icicle\Awaitable\Exception\TimeoutException If the operation times out.
  * @throws \Icicle\Stream\Exception\UnwritableException If the stream is no longer writable.
  * @throws \Icicle\Stream\Exception\ClosedException If the stream is unexpectedly closed.
  */
 public function flush() : \Generator
 {
     if ($this->buffer->isEmpty()) {
         return 0;
     }
     return yield from $this->stream->write($this->buffer->drain(), $this->timeout);
 }
Esempio n. 2
0
 /**
  * @coroutine
  *
  * @param string $data
  * @param float|int $timeout
  * @param bool $end
  *
  * @return \Generator
  *
  * @resolve int Number of bytes written to the stream.
  *
  * @throws \Icicle\Stream\Exception\UnwritableException If the stream is no longer writable.
  */
 protected function send(string $data, float $timeout = 0, bool $end = false) : \Generator
 {
     if (!$this->isWritable()) {
         throw new UnwritableException('The stream is no longer writable.');
     }
     $this->buffer->push($data);
     if (null !== $this->delayed && !$this->buffer->isEmpty()) {
         $delayed = $this->delayed;
         $this->delayed = null;
         $delayed->resolve($this->remove());
     }
     if ($end) {
         if ($this->buffer->isEmpty()) {
             $this->free();
         } else {
             $this->writable = false;
         }
     }
     if (0 !== $this->hwm && $this->buffer->getLength() > $this->hwm) {
         $awaitable = new Delayed($this->onCancelled = $this->onCancelled ?: function () {
             $this->free();
         });
         $this->queue->push($awaitable);
         if ($timeout) {
             $awaitable = $awaitable->timeout($timeout);
         }
         (yield $awaitable);
     }
     return strlen($data);
 }
Esempio n. 3
0
 /**
  * {@inheritdoc}
  *
  * @throws \Icicle\Http\Exception\MessageException If an invalid chunk length is found.
  */
 protected function send(string $data, float $timeout = 0, bool $end = false) : \Generator
 {
     $this->buffer->push($data);
     $data = '';
     while (!$this->buffer->isEmpty()) {
         if (0 === $this->length) {
             // Read chunk length.
             if (false === ($position = $this->buffer->search("\r\n"))) {
                 return yield from parent::send($data, $timeout, $end);
             }
             $length = rtrim($this->buffer->remove($position + 2), "\r\n");
             if ($position = strpos($length, ';')) {
                 $length = substr($length, 0, $position);
             }
             if (!preg_match('/^[a-f0-9]+$/i', $length)) {
                 yield from parent::send('', $timeout, true);
                 throw new MessageException(Response::BAD_REQUEST, 'Invalid chunk length.');
             }
             $this->length = hexdec($length) + 2;
             if (2 === $this->length) {
                 // Termination chunk.
                 $end = true;
             }
         }
         if (2 < $this->length) {
             // Read chunk.
             $buffer = $this->buffer->remove($this->length - 2);
             $this->length -= strlen($buffer);
             $data .= $buffer;
         }
         if (2 >= $this->length) {
             // Remove \r\n after chunk.
             $this->length -= strlen($this->buffer->remove($this->length));
         }
     }
     return yield from parent::send($data, $timeout, $end);
 }
Esempio n. 4
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);
 }