private function buildIncomingStream(Message $message, float $timeout = 0) : \Generator
{
$body = $message->getBody();
if ($body instanceof SeekableStream && $body->isOpen()) {
yield from $body->seek(0);
}
if (!$body->isReadable()) {
return $message;
}
if (strtolower($message->getHeader('Transfer-Encoding') === 'chunked')) {
$stream = new ChunkedDecoder($this->hwm);
$coroutine = new Coroutine(Stream\pipe($body, $stream, true, 0, null, $timeout));
$coroutine->done(null, [$stream, 'close']);
$message = $message->withBody($stream);
} elseif ($message->hasHeader('Content-Length')) {
$length = (int) $message->getHeader('Content-Length');
if (0 > $length) {
throw new MessageException(Response::BAD_REQUEST, 'Content-Length header invalid.');
}
$stream = new MemoryStream($this->hwm);
if (0 === $length) {
yield from $stream->end();
} else {
$coroutine = new Coroutine(Stream\pipe($body, $stream, true, $length, null, $timeout));
$coroutine->done(null, [$stream, 'close']);
}
$message = $message->withBody($stream);
} elseif ($message instanceof Request) {
switch ($message->getMethod()) {
case 'POST':
case 'PUT':
// Post and put messages must have content length or be transfer encoded.
throw new MessageException(Response::LENGTH_REQUIRED, 'Content-Length header required.');
default:
// Assume 0 length body.
$stream = new MemoryStream();
yield from $stream->end();
// Creates empty request body.
return $message->withBody($stream);
}
} elseif (strtolower($message->getHeader('Connection')) !== 'close') {
throw new MessageException(Response::LENGTH_REQUIRED, 'Content-Length header required.');
}
$contentEncoding = strtolower($message->getHeader('Content-Encoding'));
switch ($contentEncoding) {
case 'deflate':
$stream = new ZlibDecoder(ZlibDecoder::DEFLATE, $this->hwm);
break;
case 'gzip':
$stream = new ZlibDecoder(ZlibDecoder::GZIP, $this->hwm);
break;
case '':
// No content encoding.
return $message;
default:
throw new MessageException(Response::BAD_REQUEST, sprintf('Unsupported content encoding received: %s', $contentEncoding));
}
$coroutine = new Coroutine(Stream\pipe($message->getBody(), $stream, true, 0, null, $timeout));
$coroutine->done(null, [$stream, 'close']);
return $message->withBody($stream);
}