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); }); } }