| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120 | <?phpnamespace Aws\S3;use Aws\CommandInterface;use Psr\Http\Message\RequestInterface;/** * Used to update the host used for S3 requests in the case of using a * "bucket endpoint" or CNAME bucket. * * IMPORTANT: this middleware must be added after the "build" step. * * @internal */class BucketEndpointMiddleware{    private static $exclusions = ['GetBucketLocation' => true];    private $nextHandler;    /**     * Create a middleware wrapper function.     *     * @return callable     */    public static function wrap()    {        return function (callable $handler) {            return new self($handler);        };    }    public function __construct(callable $nextHandler)    {        $this->nextHandler = $nextHandler;    }    public function __invoke(CommandInterface $command, RequestInterface $request)    {        $nextHandler = $this->nextHandler;        $bucket = $command['Bucket'];        if ($bucket && !isset(self::$exclusions[$command->getName()])) {            $request = $this->modifyRequest($request, $command);        }        return $nextHandler($command, $request);    }    /**     * Performs a one-time removal of Bucket from path, then if     * the bucket name is duplicated in the path, performs additional     * removal which is dependent on the number of occurrences of the bucket     * name in a path-like format in the key name.     *     * @return string     */    private function removeBucketFromPath($path, $bucket, $key)    {        $occurrencesInKey = $this->getBucketNameOccurrencesInKey($key, $bucket);        do {            $len = strlen($bucket) + 1;            if (substr($path, 0, $len) === "/{$bucket}") {                $path = substr($path, $len);            }        } while (substr_count($path, "/{$bucket}") > $occurrencesInKey + 1);        return $path ?: '/';    }    private function removeDuplicateBucketFromHost($host, $bucket)    {        if (substr_count($host, $bucket) > 1) {            while (strpos($host, "{$bucket}.{$bucket}") === 0) {                $hostArr = explode('.', $host);                array_shift($hostArr);                $host = implode('.', $hostArr);            }        }        return $host;    }    private function getBucketNameOccurrencesInKey($key, $bucket)    {        $occurrences = 0;        if (empty($key)) {            return $occurrences;        }        $segments = explode('/', $key);        foreach($segments as $segment) {            if (strpos($segment, $bucket) === 0) {                $occurrences++;            }        }        return $occurrences;    }    private function modifyRequest(        RequestInterface $request,        CommandInterface $command    ) {        $key = isset($command['Key']) ? $command['Key'] : null;        $uri = $request->getUri();        $path = $uri->getPath();        $host = $uri->getHost();        $bucket = $command['Bucket'];        $path = $this->removeBucketFromPath($path, $bucket, $key);        $host = $this->removeDuplicateBucketFromHost($host, $bucket);        // Modify the Key to make sure the key is encoded, but slashes are not.        if ($key) {            $path = S3Client::encodeKey(rawurldecode($path));        }        return $request->withUri(            $uri->withHost($host)                ->withPath($path)        );    }}
 |