PluginProbe
Media Cloud Sync / 1.3.11
Media Cloud Sync v1.3.11
1.4.0 1.3.12 1.3.11 1.3.10 trunk 1.0.0 1.0.1 1.0.2 1.0.3 1.1.0 1.1.1 1.2.0 1.2.10 1.2.11 1.2.12 1.2.13 1.2.2 1.2.3 1.2.4 1.2.5 1.2.6 1.2.7 1.2.8 1.2.9 1.3.0 All 34 releases
media-cloud-sync / includes / sdk / s3 / GuzzleHttp / RedirectMiddleware.php

RedirectMiddleware.php in Media Cloud Sync 1.3.11, at includes/sdk/s3/GuzzleHttp/RedirectMiddleware.php

163 lines 7.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace Dudlewebs\WPMCS\s3\GuzzleHttp;
4
5 use Dudlewebs\WPMCS\s3\GuzzleHttp\Exception\BadResponseException;
6 use Dudlewebs\WPMCS\s3\GuzzleHttp\Exception\TooManyRedirectsException;
7 use Dudlewebs\WPMCS\s3\GuzzleHttp\Promise\PromiseInterface;
8 use Dudlewebs\WPMCS\s3\Psr\Http\Message\RequestInterface;
9 use Dudlewebs\WPMCS\s3\Psr\Http\Message\ResponseInterface;
10 use Dudlewebs\WPMCS\s3\Psr\Http\Message\UriInterface;
11 /**
12 * Request redirect middleware.
13 *
14 * Apply this middleware like other middleware using
15 * {@see \GuzzleHttp\Middleware::redirect()}.
16 *
17 * @final
18 */
19 class RedirectMiddleware
20 {
21 public const HISTORY_HEADER = 'X-Guzzle-Redirect-History';
22 public const STATUS_HISTORY_HEADER = 'X-Guzzle-Redirect-Status-History';
23 /**
24 * @var array
25 */
26 public static $defaultSettings = ['max' => 5, 'protocols' => ['http', 'https'], 'strict' => \false, 'referer' => \false, 'track_redirects' => \false];
27 /**
28 * @var callable(RequestInterface, array): PromiseInterface
29 */
30 private $nextHandler;
31 /**
32 * @param callable(RequestInterface, array): PromiseInterface $nextHandler Next handler to invoke.
33 */
34 public function __construct(callable $nextHandler)
35 {
36 $this->nextHandler = $nextHandler;
37 }
38 public function __invoke(RequestInterface $request, array $options) : PromiseInterface
39 {
40 $fn = $this->nextHandler;
41 if (empty($options['allow_redirects'])) {
42 return $fn($request, $options);
43 }
44 if ($options['allow_redirects'] === \true) {
45 $options['allow_redirects'] = self::$defaultSettings;
46 } elseif (!\is_array($options['allow_redirects'])) {
47 throw new \InvalidArgumentException('allow_redirects must be true, false, or array');
48 } else {
49 // Merge the default settings with the provided settings
50 $options['allow_redirects'] += self::$defaultSettings;
51 }
52 if (empty($options['allow_redirects']['max'])) {
53 return $fn($request, $options);
54 }
55 return $fn($request, $options)->then(function (ResponseInterface $response) use($request, $options) {
56 return $this->checkRedirect($request, $options, $response);
57 });
58 }
59 /**
60 * @return ResponseInterface|PromiseInterface
61 */
62 public function checkRedirect(RequestInterface $request, array $options, ResponseInterface $response)
63 {
64 if (\strpos((string) $response->getStatusCode(), '3') !== 0 || !$response->hasHeader('Location')) {
65 return $response;
66 }
67 $this->guardMax($request, $response, $options);
68 $nextRequest = $this->modifyRequest($request, $options, $response);
69 // If authorization is handled by curl, unset it if URI is cross-origin.
70 if (Psr7\UriComparator::isCrossOrigin($request->getUri(), $nextRequest->getUri()) && \defined('\\CURLOPT_HTTPAUTH')) {
71 unset($options['curl'][\CURLOPT_HTTPAUTH], $options['curl'][\CURLOPT_USERPWD]);
72 }
73 if (isset($options['allow_redirects']['on_redirect'])) {
74 $options['allow_redirects']['on_redirect']($request, $response, $nextRequest->getUri());
75 }
76 $promise = $this($nextRequest, $options);
77 // Add headers to be able to track history of redirects.
78 if (!empty($options['allow_redirects']['track_redirects'])) {
79 return $this->withTracking($promise, (string) $nextRequest->getUri(), $response->getStatusCode());
80 }
81 return $promise;
82 }
83 /**
84 * Enable tracking on promise.
85 */
86 private function withTracking(PromiseInterface $promise, string $uri, int $statusCode) : PromiseInterface
87 {
88 return $promise->then(static function (ResponseInterface $response) use($uri, $statusCode) {
89 // Note that we are pushing to the front of the list as this
90 // would be an earlier response than what is currently present
91 // in the history header.
92 $historyHeader = $response->getHeader(self::HISTORY_HEADER);
93 $statusHeader = $response->getHeader(self::STATUS_HISTORY_HEADER);
94 \array_unshift($historyHeader, $uri);
95 \array_unshift($statusHeader, (string) $statusCode);
96 return $response->withHeader(self::HISTORY_HEADER, $historyHeader)->withHeader(self::STATUS_HISTORY_HEADER, $statusHeader);
97 });
98 }
99 /**
100 * Check for too many redirects.
101 *
102 * @throws TooManyRedirectsException Too many redirects.
103 */
104 private function guardMax(RequestInterface $request, ResponseInterface $response, array &$options) : void
105 {
106 $current = $options['__redirect_count'] ?? 0;
107 $options['__redirect_count'] = $current + 1;
108 $max = $options['allow_redirects']['max'];
109 if ($options['__redirect_count'] > $max) {
110 throw new TooManyRedirectsException("Will not follow more than {$max} redirects", $request, $response);
111 }
112 }
113 public function modifyRequest(RequestInterface $request, array $options, ResponseInterface $response) : RequestInterface
114 {
115 // Request modifications to apply.
116 $modify = [];
117 $protocols = $options['allow_redirects']['protocols'];
118 // Use a GET request if this is an entity enclosing request and we are
119 // not forcing RFC compliance, but rather emulating what all browsers
120 // would do.
121 $statusCode = $response->getStatusCode();
122 if ($statusCode == 303 || $statusCode <= 302 && !$options['allow_redirects']['strict']) {
123 $safeMethods = ['GET', 'HEAD', 'OPTIONS'];
124 $requestMethod = $request->getMethod();
125 $modify['method'] = \in_array($requestMethod, $safeMethods) ? $requestMethod : 'GET';
126 $modify['body'] = '';
127 }
128 $uri = self::redirectUri($request, $response, $protocols);
129 if (isset($options['idn_conversion']) && $options['idn_conversion'] !== \false) {
130 $idnOptions = $options['idn_conversion'] === \true ? \IDNA_DEFAULT : $options['idn_conversion'];
131 $uri = Utils::idnUriConvert($uri, $idnOptions);
132 }
133 $modify['uri'] = $uri;
134 Psr7\Message::rewindBody($request);
135 // Add the Referer header if it is told to do so and only
136 // add the header if we are not redirecting from https to http.
137 if ($options['allow_redirects']['referer'] && $modify['uri']->getScheme() === $request->getUri()->getScheme()) {
138 $uri = $request->getUri()->withUserInfo('');
139 $modify['set_headers']['Referer'] = (string) $uri;
140 } else {
141 $modify['remove_headers'][] = 'Referer';
142 }
143 // Remove Authorization and Cookie headers if URI is cross-origin.
144 if (Psr7\UriComparator::isCrossOrigin($request->getUri(), $modify['uri'])) {
145 $modify['remove_headers'][] = 'Authorization';
146 $modify['remove_headers'][] = 'Cookie';
147 }
148 return Psr7\Utils::modifyRequest($request, $modify);
149 }
150 /**
151 * Set the appropriate URL on the request based on the location header.
152 */
153 private static function redirectUri(RequestInterface $request, ResponseInterface $response, array $protocols) : UriInterface
154 {
155 $location = Psr7\UriResolver::resolve($request->getUri(), new Psr7\Uri($response->getHeaderLine('Location')));
156 // Ensure that the redirect URI is allowed based on the protocols.
157 if (!\in_array($location->getScheme(), $protocols)) {
158 throw new BadResponseException(\sprintf('Redirect URI, %s, does not use one of the allowed redirect protocols: %s', $location, \implode(', ', $protocols)), $request, $response);
159 }
160 return $location;
161 }
162 }
163