芝麻web文件管理V1.00
编辑当前文件:/home/sditechnicalteam/public_html/vendor/php-http/client-common/src/Plugin/RedirectPlugin.php
*/ final class RedirectPlugin implements Plugin { /** * Rule on how to redirect, change method for the new request. * * @var array */ private $redirectCodes = [ 300 => [ 'switch' => [ 'unless' => ['GET', 'HEAD'], 'to' => 'GET', ], 'multiple' => true, 'permanent' => false, ], 301 => [ 'switch' => [ 'unless' => ['GET', 'HEAD'], 'to' => 'GET', ], 'multiple' => false, 'permanent' => true, ], 302 => [ 'switch' => [ 'unless' => ['GET', 'HEAD'], 'to' => 'GET', ], 'multiple' => false, 'permanent' => false, ], 303 => [ 'switch' => [ 'unless' => ['GET', 'HEAD'], 'to' => 'GET', ], 'multiple' => false, 'permanent' => false, ], 307 => [ 'switch' => false, 'multiple' => false, 'permanent' => false, ], 308 => [ 'switch' => false, 'multiple' => false, 'permanent' => true, ], ]; /** * Determine how header should be preserved from old request. * * @var bool|array * * true will keep all previous headers (default value) * false will ditch all previous headers * string[] will keep only headers with the specified names */ private $preserveHeader; /** * Store all previous redirect from 301 / 308 status code. * * @var array */ private $redirectStorage = []; /** * Whether the location header must be directly used for a multiple redirection status code (300). * * @var bool */ private $useDefaultForMultiple; /** * @var string[][] Chain identifier => list of URLs for this chain */ private $circularDetection = []; /** * @var StreamFactoryInterface|null */ private $streamFactory; /** * @param array{'preserve_header'?: bool|string[], 'use_default_for_multiple'?: bool, 'strict'?: bool} $config * * Configuration options: * - preserve_header: True keeps all headers, false remove all of them, an array is interpreted as a list of header names to keep * - use_default_for_multiple: Whether the location header must be directly used for a multiple redirection status code (300) * - strict: When true, redirect codes 300, 301, 302 will not modify request method and body * - stream_factory: If set, must be a PSR-17 StreamFactoryInterface - if not set, we try to discover one */ public function __construct(array $config = []) { $resolver = new OptionsResolver(); $resolver->setDefaults([ 'preserve_header' => true, 'use_default_for_multiple' => true, 'strict' => false, 'stream_factory' => null, ]); $resolver->setAllowedTypes('preserve_header', ['bool', 'array']); $resolver->setAllowedTypes('use_default_for_multiple', 'bool'); $resolver->setAllowedTypes('strict', 'bool'); $resolver->setAllowedTypes('stream_factory', [StreamFactoryInterface::class, 'null']); $resolver->setNormalizer('preserve_header', function (OptionsResolver $resolver, $value) { if (is_bool($value) && false === $value) { return []; } return $value; }); $resolver->setDefault('stream_factory', function (Options $options): ?StreamFactoryInterface { return $this->guessStreamFactory(); }); $options = $resolver->resolve($config); $this->preserveHeader = $options['preserve_header']; $this->useDefaultForMultiple = $options['use_default_for_multiple']; if ($options['strict']) { $this->redirectCodes[300]['switch'] = false; $this->redirectCodes[301]['switch'] = false; $this->redirectCodes[302]['switch'] = false; } $this->streamFactory = $options['stream_factory']; } /** * {@inheritdoc} */ public function handleRequest(RequestInterface $request, callable $next, callable $first): Promise { // Check in storage if (array_key_exists((string) $request->getUri(), $this->redirectStorage)) { $uri = $this->redirectStorage[(string) $request->getUri()]['uri']; $statusCode = $this->redirectStorage[(string) $request->getUri()]['status']; $redirectRequest = $this->buildRedirectRequest($request, $uri, $statusCode); return $first($redirectRequest); } return $next($request)->then(function (ResponseInterface $response) use ($request, $first): ResponseInterface { $statusCode = $response->getStatusCode(); if (!array_key_exists($statusCode, $this->redirectCodes)) { return $response; } $uri = $this->createUri($response, $request); $redirectRequest = $this->buildRedirectRequest($request, $uri, $statusCode); $chainIdentifier = spl_object_hash((object) $first); if (!array_key_exists($chainIdentifier, $this->circularDetection)) { $this->circularDetection[$chainIdentifier] = []; } $this->circularDetection[$chainIdentifier][] = (string) $request->getUri(); if (in_array((string) $redirectRequest->getUri(), $this->circularDetection[$chainIdentifier], true)) { throw new CircularRedirectionException('Circular redirection detected', $request, $response); } if ($this->redirectCodes[$statusCode]['permanent']) { $this->redirectStorage[(string) $request->getUri()] = [ 'uri' => $uri, 'status' => $statusCode, ]; } // Call redirect request synchronously return $first($redirectRequest)->wait(); }); } /** * The default only needs to be determined if no value is provided. */ public function guessStreamFactory(): ?StreamFactoryInterface { if (class_exists(Psr17FactoryDiscovery::class)) { try { return Psr17FactoryDiscovery::findStreamFactory(); } catch (\Throwable $t) { // ignore and try other options } } if (class_exists(Psr17Factory::class)) { return new Psr17Factory(); } if (class_exists(Utils::class)) { return new class() implements StreamFactoryInterface { public function createStream(string $content = ''): StreamInterface { return Utils::streamFor($content); } public function createStreamFromFile(string $filename, string $mode = 'r'): StreamInterface { throw new \RuntimeException('Internal error: this method should not be needed'); } public function createStreamFromResource($resource): StreamInterface { throw new \RuntimeException('Internal error: this method should not be needed'); } }; } return null; } private function buildRedirectRequest(RequestInterface $originalRequest, UriInterface $targetUri, int $statusCode): RequestInterface { $originalRequest = $originalRequest->withUri($targetUri); if (false !== $this->redirectCodes[$statusCode]['switch'] && !in_array($originalRequest->getMethod(), $this->redirectCodes[$statusCode]['switch']['unless'], true)) { $originalRequest = $originalRequest->withMethod($this->redirectCodes[$statusCode]['switch']['to']); if ('GET' === $this->redirectCodes[$statusCode]['switch']['to'] && $this->streamFactory) { // if we found a stream factory, remove the request body. otherwise leave the body there. $originalRequest = $originalRequest->withoutHeader('content-type'); $originalRequest = $originalRequest->withoutHeader('content-length'); $originalRequest = $originalRequest->withBody($this->streamFactory->createStream()); } } if (is_array($this->preserveHeader)) { $headers = array_keys($originalRequest->getHeaders()); foreach ($headers as $name) { if (!in_array($name, $this->preserveHeader, true)) { $originalRequest = $originalRequest->withoutHeader($name); } } } return $originalRequest; } /** * Creates a new Uri from the old request and the location header. * * @throws HttpException If location header is not usable (missing or incorrect) * @throws MultipleRedirectionException If a 300 status code is received and default location cannot be resolved (doesn't use the location header or not present) */ private function createUri(ResponseInterface $redirectResponse, RequestInterface $originalRequest): UriInterface { if ($this->redirectCodes[$redirectResponse->getStatusCode()]['multiple'] && (!$this->useDefaultForMultiple || !$redirectResponse->hasHeader('Location'))) { throw new MultipleRedirectionException('Cannot choose a redirection', $originalRequest, $redirectResponse); } if (!$redirectResponse->hasHeader('Location')) { throw new HttpException('Redirect status code, but no location header present in the response', $originalRequest, $redirectResponse); } $location = $redirectResponse->getHeaderLine('Location'); $parsedLocation = parse_url($location); if (false === $parsedLocation || '' === $location) { throw new HttpException(sprintf('Location "%s" could not be parsed', $location), $originalRequest, $redirectResponse); } $uri = $originalRequest->getUri(); // Redirections can either use an absolute uri or a relative reference https://www.rfc-editor.org/rfc/rfc3986#section-4.2 // If relative, we need to check if we have an absolute path or not $path = array_key_exists('path', $parsedLocation) ? $parsedLocation['path'] : ''; if (!array_key_exists('host', $parsedLocation) && '/' !== $location[0]) { // the target is a relative-path reference, we need to merge it with the base path $originalPath = $uri->getPath(); if ('' === $path) { $path = $originalPath; } elseif (($pos = strrpos($originalPath, '/')) !== false) { $path = substr($originalPath, 0, $pos + 1).$path; } else { $path = '/'.$path; } /* replace '/./' or '/foo/../' with '/' */ $re = ['#(/\./)#', '#/(?!\.\.)[^/]+/\.\./#']; for ($n = 1; $n > 0; $path = preg_replace($re, '/', $path, -1, $n)) { if (null === $path) { throw new HttpException(sprintf('Failed to resolve Location %s', $location), $originalRequest, $redirectResponse); } } } if (null === $path) { throw new HttpException(sprintf('Failed to resolve Location %s', $location), $originalRequest, $redirectResponse); } $uri = $uri ->withPath($path) ->withQuery(array_key_exists('query', $parsedLocation) ? $parsedLocation['query'] : '') ->withFragment(array_key_exists('fragment', $parsedLocation) ? $parsedLocation['fragment'] : '') ; if (array_key_exists('scheme', $parsedLocation)) { $uri = $uri->withScheme($parsedLocation['scheme']); } if (array_key_exists('host', $parsedLocation)) { $uri = $uri->withHost($parsedLocation['host']); } if (array_key_exists('port', $parsedLocation)) { $uri = $uri->withPort($parsedLocation['port']); } elseif (array_key_exists('host', $parsedLocation)) { $uri = $uri->withPort(null); } return $uri; } }