/**
  * {@inheritdoc}
  */
 public function sendBinary(ReadableStream $stream, int $priority = 0) : Awaitable
 {
     return $this->writer->execute(function () use($stream) {
         $type = Frame::BINARY;
         $reserved = Frame::RESERVED1;
         $context = $this->deflate->getCompressionContext();
         $len = 0;
         try {
             $chunk = (yield $stream->readBuffer(4092));
             while (null !== ($next = (yield $stream->readBuffer(4092)))) {
                 $chunk = \deflate_add($context, $chunk, \ZLIB_SYNC_FLUSH);
                 $len += (yield $this->writeFrame(new Frame($type, $chunk, false, $reserved)));
                 $chunk = $next;
                 $type = Frame::CONTINUATION;
                 $reserved = 0;
             }
             if ($chunk !== null) {
                 $chunk = \substr(\deflate_add($context, $chunk, $this->deflate->getCompressionFlushMode()), 0, -4);
                 $len += (yield $this->writeFrame(new Frame($type, $chunk, true, $reserved)));
             }
             return $len;
         } finally {
             $stream->close();
         }
     }, $priority);
 }
Exemplo n.º 2
0
 /**
  * Parse the next HTTP response from the given stream.
  * 
  * @param ReadableStream $stream
  * @param string $line HTTP response line (mighty already be received while checking for 100 continue).
  * @param bool $dropBody Drop response body (needed when the response is caused by a HEAD request).
  * @return HttpResponse
  * 
  * @throws StreamClosedException When no HTTP response line could be parsed.
  */
 public function parseResponse(ReadableStream $stream, string $line = null, bool $dropBody = false) : \Generator
 {
     if ($line === null) {
         $i = 0;
         do {
             if ($i++ > 3 || null === ($line = (yield $stream->readLine()))) {
                 throw new StreamClosedException('Stream closed before HTTP response line was read');
             }
         } while ($line === '');
     }
     $m = null;
     if (!\preg_match("'^HTTP/(1\\.[01])\\s+([1-5][0-9]{2})(.*)\$'i", \trim($line), $m)) {
         throw new StreamClosedException('Invalid HTTP response line received');
     }
     $response = new HttpResponse();
     $response = $response->withProtocolVersion($m[1]);
     $response = $response->withStatus((int) $m[2], \trim($m[3]));
     $response = (yield from $this->parseHeaders($stream, $response));
     if ($dropBody || Http::isResponseWithoutBody($response->getStatusCode())) {
         $body = new StringBody();
         if (!$dropBody) {
             $response = $response->withHeader('Content-Length', '0');
         }
     } else {
         $body = Body::fromMessage($stream, $response);
     }
     static $remove = ['TE', 'Trailer'];
     foreach ($remove as $name) {
         $response = $response->withoutHeader($name);
     }
     return $response->withBody($body);
 }
Exemplo n.º 3
0
 /**
  * {@inheritdoc}
  */
 public function discard() : Awaitable
 {
     return new Coroutine(function () {
         $len = 0;
         try {
             while (null !== ($chunk = (yield $this->stream->read()))) {
                 $len += \strlen($chunk);
             }
             return $len;
         } finally {
             $this->stream->close();
         }
     });
 }
Exemplo n.º 4
0
 protected function streamReader(ReadableStream $stream) : \Generator
 {
     $contents = '';
     try {
         $channel = $stream->channel(8192, $this->length);
         while (null !== ($chunk = (yield $channel->receive()))) {
             $contents .= $chunk;
         }
     } finally {
         if ($this->closeSource) {
             $stream->close();
         }
     }
     return $contents;
 }
Exemplo n.º 5
0
 /**
  * Read next chunk from temp file.
  * 
  * Fall back to reading from buffered source stream if contents of the temp file have been read.
  */
 protected function readNextChunk() : \Generator
 {
     $chunk = (yield $this->stream->readBuffer($this->bufferSize));
     if ($chunk === null) {
         $chunk = (yield $this->source->readBuffer($this->bufferSize));
         if ($chunk === null) {
             $this->body->computeSize();
             $this->source->close();
         } else {
             $this->body->incrementOffset((yield $this->stream->write($chunk)));
         }
         $chunk = (yield $this->stream->readBuffer($this->bufferSize));
     }
     return $chunk;
 }
Exemplo n.º 6
0
 public function parseRequest(ReadableStream $stream) : \Generator
 {
     $i = 0;
     do {
         if ($i++ > 3 || null === ($line = (yield $stream->readLine()))) {
             throw new StreamClosedException('Stream closed before HTTP request line was read');
         }
     } while ($line === '');
     $m = null;
     if (!\preg_match("'^(\\S+)\\s+(.+)\\s+HTTP/(1\\.[01])\$'i", \trim($line), $m)) {
         throw new StreamClosedException('Invalid HTTP request line received');
     }
     $request = new HttpRequest($m[2], $m[1], [], $m[3]);
     $request = $request->withRequestTarget(\trim($m[2]));
     $request = (yield from $this->parseHeaders($stream, $request));
     $body = Body::fromMessage($stream, $request);
     static $remove = ['Content-Encoding', 'Trailer'];
     foreach ($remove as $name) {
         $request = $request->withoutHeader($name);
     }
     return $request->withBody($body);
 }
Exemplo n.º 7
0
 protected function parseHeaders(ReadableStream $stream, HttpMessage $message) : \Generator
 {
     $remaining = $this->maxHeaderSize;
     try {
         while (null !== ($line = (yield $stream->readLine($remaining)))) {
             if (\trim($line) === '') {
                 break;
             }
             $remaining -= \strlen($line);
             $parts = \explode(':', $line, 2);
             if (!isset($parts[1])) {
                 throw new \RuntimeException(\sprintf('Malformed HTTP header received: "%s"', $line));
             }
             $message = $message->withAddedHeader(\trim($parts[0]), \trim($parts[1]));
         }
     } catch (StreamException $e) {
         throw new StatusException(Http::REQUEST_HEADER_FIELDS_TOO_LARGE, 'Maximum HTTP header size exceeded', [], $e);
     }
     if ($line === null) {
         throw new \RuntimeException('Premature end of HTTP headers detected');
     }
     return $message;
 }
Exemplo n.º 8
0
 /**
  * Create the input stream being used to read decoded body data from the remote peer.
  */
 protected function createInputStream() : EntityStream
 {
     if ($this->chunked) {
         $stream = new ChunkDecodedStream($this->stream);
     } elseif ($this->length > 0) {
         $stream = new LimitStream($this->stream, $this->length);
     } elseif ($this->closeSupported) {
         $stream = $this->stream;
     } else {
         if ($this->cascadeClose) {
             $this->stream->close();
         }
         return new EntityStream(new ReadableMemoryStream(), true, $this->expectContinue);
     }
     return new EntityStream($stream, $this->cascadeClose, $this->expectContinue);
 }
Exemplo n.º 9
0
 /**
  * {@inheritdoc}
  */
 public function getReadableStream() : Awaitable
 {
     if ($this->temp) {
         $this->temp->rewind();
         return new Success(new BufferedBodyStream($this->temp, $this->stream, $this->bufferSize, $this));
     }
     if ($this->size !== null) {
         return new Success(new ReadableMemoryStream($this->buffer));
     }
     return new Coroutine(function () {
         $buffer = (yield $this->stream->readBuffer($this->bufferSize));
         $len = \strlen($buffer);
         if ($len < $this->bufferSize) {
             $this->buffer = $buffer;
             $this->size = $len;
             return new ReadableMemoryStream($buffer);
         }
         $this->temp = (yield LoopConfig::currentFilesystem()->tempStream());
         $this->offset += (yield $this->temp->write($buffer));
         return new BufferedBodyStream($this->temp, $this->stream, $this->bufferSize, $this);
     });
 }
Exemplo n.º 10
0
 /**
  * Create the input stream being used to read decoded body data from the remote peer.
  * 
  * @return ReadableStream
  */
 protected function createInputStream() : \Generator
 {
     if ($this->expectContinue) {
         (yield $this->expectContinue->write("HTTP/1.1 100 Continue\r\n"));
     }
     if ($this->chunked) {
         $stream = new ChunkDecodedStream($this->stream, $this->cascadeClose);
     } elseif ($this->length > 0) {
         $stream = new LimitStream($this->stream, $this->length, $this->cascadeClose);
     } else {
         if ($this->cascadeClose) {
             $this->stream->close();
         }
         return new ReadableMemoryStream();
     }
     switch ($this->compression) {
         case self::COMPRESSION_GZIP:
             return new ReadableInflateStream($stream, \ZLIB_ENCODING_GZIP);
         case self::COMPRESSION_DEFLATE:
             return new ReadableInflateStream($stream, \ZLIB_ENCODING_DEFLATE);
     }
     return $stream;
 }
Exemplo n.º 11
0
 /**
  * Stream a binary WebSocket message.
  * 
  * @param ReadableStream $stream
  * @param int $priority
  * @return int Number of transmitted bytes.
  * 
  * @throws \InvalidArgumentException When the text is not UTF-8 encoded.
  */
 public function sendBinary(ReadableStream $stream, int $priority = 0) : Awaitable
 {
     return $this->writer->execute(function () use($stream) {
         $type = Frame::BINARY;
         $len = 0;
         try {
             $chunk = (yield $stream->readBuffer(4092));
             while (null !== ($next = (yield $stream->readBuffer(4092)))) {
                 $len += (yield $this->writeFrame(new Frame($type, $chunk, false)));
                 $chunk = $next;
                 $type = Frame::CONTINUATION;
             }
             if ($chunk !== null) {
                 $len += (yield $this->writeFrame(new Frame($type, $chunk)));
             }
             return $len;
         } finally {
             $stream->close();
         }
     }, $priority);
 }