20 const HISTORY_HEADER =
'X-Guzzle-Redirect-History';
22 const STATUS_HISTORY_HEADER =
'X-Guzzle-Redirect-Status-History';
24 public static $defaultSettings = [
26 'protocols' => [
'http',
'https'],
29 'track_redirects' =>
false,
38 public function __construct(callable $nextHandler)
40 $this->nextHandler = $nextHandler;
51 $fn = $this->nextHandler;
53 if (empty($options[
'allow_redirects'])) {
54 return $fn($request, $options);
57 if ($options[
'allow_redirects'] ===
true) {
58 $options[
'allow_redirects'] = self::$defaultSettings;
59 } elseif (!is_array($options[
'allow_redirects'])) {
60 throw new \InvalidArgumentException(
'allow_redirects must be true, false, or array');
63 $options[
'allow_redirects'] += self::$defaultSettings;
66 if (empty($options[
'allow_redirects'][
'max'])) {
67 return $fn($request, $options);
70 return $fn($request, $options)
71 ->then(
function (ResponseInterface $response) use ($request, $options) {
72 return $this->checkRedirect($request, $options, $response);
83 public function checkRedirect(
84 RequestInterface $request,
94 $this->guardMax($request, $options);
95 $nextRequest = $this->modifyRequest($request, $options, $response);
97 if (isset($options[
'allow_redirects'][
'on_redirect'])) {
99 $options[
'allow_redirects'][
'on_redirect'],
102 $nextRequest->getUri()
107 $promise = $this($nextRequest, $options);
110 if (!empty($options[
'allow_redirects'][
'track_redirects'])) {
111 return $this->withTracking(
113 (
string) $nextRequest->getUri(),
128 return $promise->
then(
133 $historyHeader = $response->
getHeader(self::HISTORY_HEADER);
134 $statusHeader = $response->
getHeader(self::STATUS_HISTORY_HEADER);
135 array_unshift($historyHeader, $uri);
136 array_unshift($statusHeader, $statusCode);
137 return $response->
withHeader(self::HISTORY_HEADER, $historyHeader)
138 ->withHeader(self::STATUS_HISTORY_HEADER, $statusHeader);
150 private function guardMax(RequestInterface $request, array &$options)
152 $current = isset($options[
'__redirect_count'])
153 ? $options[
'__redirect_count']
155 $options[
'__redirect_count'] = $current + 1;
156 $max = $options[
'allow_redirects'][
'max'];
158 if ($options[
'__redirect_count'] > $max) {
159 throw new TooManyRedirectsException(
160 "Will not follow more than {$max} redirects",
173 public function modifyRequest(
174 RequestInterface $request,
180 $protocols = $options[
'allow_redirects'][
'protocols'];
186 if ($statusCode == 303 ||
187 ($statusCode <= 302 && !$options[
'allow_redirects'][
'strict'])
189 $modify[
'method'] =
'GET';
190 $modify[
'body'] =
'';
193 $uri = $this->redirectUri($request, $response, $protocols);
194 if (isset($options[
'idn_conversion']) && ($options[
'idn_conversion'] !==
false)) {
195 $idnOptions = ($options[
'idn_conversion'] ===
true) ? IDNA_DEFAULT : $options[
'idn_conversion'];
196 $uri = Utils::idnUriConvert($uri, $idnOptions);
199 $modify[
'uri'] = $uri;
200 Psr7\rewind_body($request);
204 if ($options[
'allow_redirects'][
'referer']
205 && $modify[
'uri']->getScheme() === $request->getUri()->getScheme()
207 $uri = $request->getUri()->withUserInfo(
'');
208 $modify[
'set_headers'][
'Referer'] = (string) $uri;
210 $modify[
'remove_headers'][] =
'Referer';
214 if ($request->getUri()->getHost() !== $modify[
'uri']->getHost()) {
215 $modify[
'remove_headers'][] =
'Authorization';
218 return Psr7\modify_request($request, $modify);
230 private function redirectUri(
235 $location = Psr7\UriResolver::resolve(
241 if (!in_array($location->getScheme(), $protocols)) {
244 'Redirect URI, %s, does not use one of the allowed redirect protocols: %s',
246 implode(
', ', $protocols)