private function redirect(RequestCycle $cycle, Uri $newUri)
{
if (in_array($newUri->__toString(), $cycle->redirectHistory)) {
$this->fail($cycle, new InfiniteRedirectException(sprintf('Infinite redirect detected while following Location header: %s', $newUri)));
return;
}
$request = $cycle->request;
$response = $cycle->response;
$cycle->previousResponse = clone $response;
$cycle->response = null;
$refererUri = $request->getUri();
$cycle->uri = $newUri;
$authority = $this->generateAuthorityFromUri($newUri);
$socketCheckoutUri = $newUri->getScheme() . "://{$authority}";
$request->setUri($newUri->__toString());
$host = $this->removePortFromHost($authority);
$request->setHeader('Host', $host);
$this->assignApplicableRequestCookies($request, $cycle->options);
/**
* If this is a 302/303 we need to follow the location with a GET if the
* original request wasn't GET/HEAD. Otherwise, be sure to rewind the body
* if it's an Iterator instance because we'll need to send it again.
*
* @link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.3
*/
$method = $request->getMethod();
$status = $response->getStatus();
if (($status == 302 || $status == 303) && !($method == 'GET' || $method == 'HEAD')) {
$request->setMethod('GET');
$request->removeHeader('Transfer-Encoding');
$request->removeHeader('Content-Length');
$request->removeHeader('Content-Type');
$request->setBody(null);
} elseif (($body = $request->getBody()) && $body instanceof \Iterator) {
$body->rewind();
}
if ($cycle->options[self::OP_AUTO_REFERER]) {
$this->assignRedirectRefererHeader($refererUri, $newUri, $request);
}
$cycle->futureResponse->update([Notify::REDIRECT, $refererUri, (string) $newUri]);
if ($this->shouldCloseSocketAfterResponse($request, $response)) {
// If the HTTP response dictated a connection close we need
// to inform the socket pool and checkout a new connection.
@fclose($cycle->socket);
$this->socketPool->clear($cycle->socket);
$cycle->socket = null;
$cycle->socketCheckoutUri = $socketCheckoutUri;
$futureSocket = $this->socketPool->checkout($socketCheckoutUri, $cycle->options);
$futureSocket->when(function ($error, $result) use($cycle) {
$this->onSocketResolve($cycle, $error, $result);
});
} elseif ($cycle->socketCheckoutUri === $socketCheckoutUri) {
// Woot! We can reuse the socket we already have \o/
$this->onSocketResolve($cycle, $error = null, $cycle->socket);
} else {
// Store the original socket and don't check it back into the
// pool until we are finished with all redirects. This helps
// prevent redirect checkout requests from being queued and
// causing unwanted waiting to complete redirects than need to
// traverse multiple host names.
$cycle->socketCheckoutUri = $socketCheckoutUri;
$cycle->redirectedSockets[] = $cycle->socket;
$cycle->socket = null;
$futureSocket = $this->socketPool->checkout($socketCheckoutUri, $cycle->options);
$futureSocket->when(function ($error, $result) use($cycle) {
$this->onSocketResolve($cycle, $error, $result);
});
}
}