/** * @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; }