PageRenderTime 37ms CodeModel.GetById 27ms RepoModel.GetById 1ms app.codeStats 0ms

/sites/all/modules/contrib/civicrm/vendor/guzzlehttp/guzzle/src/RedirectMiddleware.php

https://gitlab.com/virtualrealms/d7civicrm
PHP | 237 lines | 155 code | 29 blank | 53 comment | 19 complexity | 5f7cef791a1837874c931193e307e5d3 MD5 | raw file
  1. <?php
  2. namespace GuzzleHttp;
  3. use GuzzleHttp\Exception\BadResponseException;
  4. use GuzzleHttp\Exception\TooManyRedirectsException;
  5. use GuzzleHttp\Promise\PromiseInterface;
  6. use GuzzleHttp\Psr7;
  7. use Psr\Http\Message\RequestInterface;
  8. use Psr\Http\Message\ResponseInterface;
  9. use Psr\Http\Message\UriInterface;
  10. /**
  11. * Request redirect middleware.
  12. *
  13. * Apply this middleware like other middleware using
  14. * {@see GuzzleHttp\Middleware::redirect()}.
  15. */
  16. class RedirectMiddleware
  17. {
  18. const HISTORY_HEADER = 'X-Guzzle-Redirect-History';
  19. const STATUS_HISTORY_HEADER = 'X-Guzzle-Redirect-Status-History';
  20. public static $defaultSettings = [
  21. 'max' => 5,
  22. 'protocols' => ['http', 'https'],
  23. 'strict' => false,
  24. 'referer' => false,
  25. 'track_redirects' => false,
  26. ];
  27. /** @var callable */
  28. private $nextHandler;
  29. /**
  30. * @param callable $nextHandler Next handler to invoke.
  31. */
  32. public function __construct(callable $nextHandler)
  33. {
  34. $this->nextHandler = $nextHandler;
  35. }
  36. /**
  37. * @param RequestInterface $request
  38. * @param array $options
  39. *
  40. * @return PromiseInterface
  41. */
  42. public function __invoke(RequestInterface $request, array $options)
  43. {
  44. $fn = $this->nextHandler;
  45. if (empty($options['allow_redirects'])) {
  46. return $fn($request, $options);
  47. }
  48. if ($options['allow_redirects'] === true) {
  49. $options['allow_redirects'] = self::$defaultSettings;
  50. } elseif (!is_array($options['allow_redirects'])) {
  51. throw new \InvalidArgumentException('allow_redirects must be true, false, or array');
  52. } else {
  53. // Merge the default settings with the provided settings
  54. $options['allow_redirects'] += self::$defaultSettings;
  55. }
  56. if (empty($options['allow_redirects']['max'])) {
  57. return $fn($request, $options);
  58. }
  59. return $fn($request, $options)
  60. ->then(function (ResponseInterface $response) use ($request, $options) {
  61. return $this->checkRedirect($request, $options, $response);
  62. });
  63. }
  64. /**
  65. * @param RequestInterface $request
  66. * @param array $options
  67. * @param ResponseInterface|PromiseInterface $response
  68. *
  69. * @return ResponseInterface|PromiseInterface
  70. */
  71. public function checkRedirect(
  72. RequestInterface $request,
  73. array $options,
  74. ResponseInterface $response
  75. ) {
  76. if (substr($response->getStatusCode(), 0, 1) != '3'
  77. || !$response->hasHeader('Location')
  78. ) {
  79. return $response;
  80. }
  81. $this->guardMax($request, $options);
  82. $nextRequest = $this->modifyRequest($request, $options, $response);
  83. if (isset($options['allow_redirects']['on_redirect'])) {
  84. call_user_func(
  85. $options['allow_redirects']['on_redirect'],
  86. $request,
  87. $response,
  88. $nextRequest->getUri()
  89. );
  90. }
  91. /** @var PromiseInterface|ResponseInterface $promise */
  92. $promise = $this($nextRequest, $options);
  93. // Add headers to be able to track history of redirects.
  94. if (!empty($options['allow_redirects']['track_redirects'])) {
  95. return $this->withTracking(
  96. $promise,
  97. (string) $nextRequest->getUri(),
  98. $response->getStatusCode()
  99. );
  100. }
  101. return $promise;
  102. }
  103. private function withTracking(PromiseInterface $promise, $uri, $statusCode)
  104. {
  105. return $promise->then(
  106. function (ResponseInterface $response) use ($uri, $statusCode) {
  107. // Note that we are pushing to the front of the list as this
  108. // would be an earlier response than what is currently present
  109. // in the history header.
  110. $historyHeader = $response->getHeader(self::HISTORY_HEADER);
  111. $statusHeader = $response->getHeader(self::STATUS_HISTORY_HEADER);
  112. array_unshift($historyHeader, $uri);
  113. array_unshift($statusHeader, $statusCode);
  114. return $response->withHeader(self::HISTORY_HEADER, $historyHeader)
  115. ->withHeader(self::STATUS_HISTORY_HEADER, $statusHeader);
  116. }
  117. );
  118. }
  119. private function guardMax(RequestInterface $request, array &$options)
  120. {
  121. $current = isset($options['__redirect_count'])
  122. ? $options['__redirect_count']
  123. : 0;
  124. $options['__redirect_count'] = $current + 1;
  125. $max = $options['allow_redirects']['max'];
  126. if ($options['__redirect_count'] > $max) {
  127. throw new TooManyRedirectsException(
  128. "Will not follow more than {$max} redirects",
  129. $request
  130. );
  131. }
  132. }
  133. /**
  134. * @param RequestInterface $request
  135. * @param array $options
  136. * @param ResponseInterface $response
  137. *
  138. * @return RequestInterface
  139. */
  140. public function modifyRequest(
  141. RequestInterface $request,
  142. array $options,
  143. ResponseInterface $response
  144. ) {
  145. // Request modifications to apply.
  146. $modify = [];
  147. $protocols = $options['allow_redirects']['protocols'];
  148. // Use a GET request if this is an entity enclosing request and we are
  149. // not forcing RFC compliance, but rather emulating what all browsers
  150. // would do.
  151. $statusCode = $response->getStatusCode();
  152. if ($statusCode == 303 ||
  153. ($statusCode <= 302 && $request->getBody() && !$options['allow_redirects']['strict'])
  154. ) {
  155. $modify['method'] = 'GET';
  156. $modify['body'] = '';
  157. }
  158. $modify['uri'] = $this->redirectUri($request, $response, $protocols);
  159. Psr7\rewind_body($request);
  160. // Add the Referer header if it is told to do so and only
  161. // add the header if we are not redirecting from https to http.
  162. if ($options['allow_redirects']['referer']
  163. && $modify['uri']->getScheme() === $request->getUri()->getScheme()
  164. ) {
  165. $uri = $request->getUri()->withUserInfo('', '');
  166. $modify['set_headers']['Referer'] = (string) $uri;
  167. } else {
  168. $modify['remove_headers'][] = 'Referer';
  169. }
  170. // Remove Authorization header if host is different.
  171. if ($request->getUri()->getHost() !== $modify['uri']->getHost()) {
  172. $modify['remove_headers'][] = 'Authorization';
  173. }
  174. return Psr7\modify_request($request, $modify);
  175. }
  176. /**
  177. * Set the appropriate URL on the request based on the location header
  178. *
  179. * @param RequestInterface $request
  180. * @param ResponseInterface $response
  181. * @param array $protocols
  182. *
  183. * @return UriInterface
  184. */
  185. private function redirectUri(
  186. RequestInterface $request,
  187. ResponseInterface $response,
  188. array $protocols
  189. ) {
  190. $location = Psr7\UriResolver::resolve(
  191. $request->getUri(),
  192. new Psr7\Uri($response->getHeaderLine('Location'))
  193. );
  194. // Ensure that the redirect URI is allowed based on the protocols.
  195. if (!in_array($location->getScheme(), $protocols)) {
  196. throw new BadResponseException(
  197. sprintf(
  198. 'Redirect URI, %s, does not use one of the allowed redirect protocols: %s',
  199. $location,
  200. implode(', ', $protocols)
  201. ),
  202. $request,
  203. $response
  204. );
  205. }
  206. return $location;
  207. }
  208. }