/**
* @coroutine
*
* @param \Icicle\Http\Message\Request $request
* @param mixed[] $options
*
* @return \Generator
*
* @resolve \Icicle\Http\Message\Response
*/
public function send(Request $request, array $options = []) : \Generator
{
$timeout = isset($options['timeout']) ? (double) $options['timeout'] : self::DEFAULT_TIMEOUT;
$cryptoMethod = isset($options['crypto_method']) ? (int) $options['crypto_method'] : self::DEFAULT_CRYPTO_METHOD;
$request = $request->withHeader('Connection', 'close');
$count = 0;
try {
do {
$uri = $request->getUri();
$host = $uri->getHost();
$port = $uri->getPort();
if ('' === $host || 0 === $port) {
throw new InvalidArgumentError('Request URI must have a host and port.');
}
/** @var \Icicle\Socket\Socket $socket */
$socket = (yield from Dns\connect($uri->getHost(), $uri->getPort(), $options));
if ($uri->getScheme() === 'https') {
yield from $socket->enableCrypto($cryptoMethod, $timeout);
}
/** @var \Icicle\Http\Message\Response $response */
$response = (yield from $this->requester->send($socket, $request, $options));
if ($this->follow) {
switch ($response->getStatusCode()) {
case Response::SEE_OTHER:
$request = $request->withMethod($request->getMethod() === 'HEAD' ? 'HEAD' : 'GET');
// No break.
// No break.
case Response::MOVED_PERMANENTLY:
case Response::FOUND:
case Response::TEMPORARY_REDIRECT:
case Response::PERMANENT_REDIRECT:
$socket->close();
// Close original connection.
if (++$count > $this->maxRedirects) {
throw new RedirectException(sprintf('Too many redirects encountered (max redirects: %d).', $this->maxRedirects));
}
if (!$response->hasHeader('Location')) {
throw new RedirectException('No Location header found in redirect response.');
}
$request = $request->withUri(new BasicUri($response->getHeader('Location')));
$response = null;
// Let's go around again!
}
}
} while (null === $response);
} finally {
$request->getBody()->close();
}
return $response;
}