| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147 | <?phpnamespace Aws\S3;use Aws\Api\Service;use Aws\CommandInterface;use GuzzleHttp\Psr7;use InvalidArgumentException;use Psr\Http\Message\RequestInterface;use Psr\Http\Message\StreamInterface;/** * Apply required or optional checksums to requests before sending. * * IMPORTANT: This middleware must be added after the "build" step. * * @internal */class ApplyChecksumMiddleware{    use CalculatesChecksumTrait;    private static $sha256AndMd5 = [        'PutObject',        'UploadPart',    ];    /** @var Service */    private $api;    private $nextHandler;    /**     * Create a middleware wrapper function.     *     * @param Service $api     * @return callable     */    public static function wrap(Service $api)    {        return function (callable $handler) use ($api) {            return new self($handler, $api);        };    }    public function __construct(callable $nextHandler, Service $api)    {        $this->api = $api;        $this->nextHandler = $nextHandler;    }    public function __invoke(        CommandInterface $command,        RequestInterface $request    ) {        $next = $this->nextHandler;        $name = $command->getName();        $body = $request->getBody();        //Checks if AddContentMD5 has been specified for PutObject or UploadPart        $addContentMD5 = $command['AddContentMD5'] ?? null;        $op = $this->api->getOperation($command->getName());        $checksumInfo = $op['httpChecksum'] ?? [];        $checksumMemberName = array_key_exists('requestAlgorithmMember', $checksumInfo)            ? $checksumInfo['requestAlgorithmMember']            : "";        $requestedAlgorithm = $command[$checksumMemberName] ?? null;        if (!empty($checksumMemberName) && !empty($requestedAlgorithm)) {            $requestedAlgorithm = strtolower($requestedAlgorithm);            $checksumMember = $op->getInput()->getMember($checksumMemberName);            $supportedAlgorithms = isset($checksumMember['enum'])                ? array_map('strtolower', $checksumMember['enum'])                : null;            if (is_array($supportedAlgorithms)                && in_array($requestedAlgorithm, $supportedAlgorithms)            ) {                $request = $this->addAlgorithmHeader($requestedAlgorithm, $request, $body);            } else {                throw new InvalidArgumentException(                    "Unsupported algorithm supplied for input variable {$checksumMemberName}."                    . "  Supported checksums for this operation include: "                    . implode(", ", $supportedAlgorithms) . "."                );            }            return $next($command, $request);        }        if (!empty($checksumInfo)) {        //if the checksum member is absent, check if it's required        $checksumRequired = $checksumInfo['requestChecksumRequired'] ?? null;            if ((!empty($checksumRequired))                || (in_array($name, self::$sha256AndMd5) && $addContentMD5)            ) {                //S3Express doesn't support MD5; default to crc32 instead                if ($this->isS3Express($command)) {                    $request = $this->addAlgorithmHeader('crc32', $request, $body);                } elseif (!$request->hasHeader('Content-MD5')) {                    // Set the content MD5 header for operations that require it.                    $request = $request->withHeader(                        'Content-MD5',                        base64_encode(Psr7\Utils::hash($body, 'md5', true))                    );                }                return $next($command, $request);            }        }        if (in_array($name, self::$sha256AndMd5) && $command['ContentSHA256']) {            // Set the content hash header if provided in the parameters.            $request = $request->withHeader(                'X-Amz-Content-Sha256',                $command['ContentSHA256']            );        }        return $next($command, $request);    }    /**     * @param string $requestedAlgorithm     * @param RequestInterface $request     * @param StreamInterface $body     * @return RequestInterface     */    private function addAlgorithmHeader(        string $requestedAlgorithm,        RequestInterface $request,        StreamInterface $body    ) {        $headerName = "x-amz-checksum-{$requestedAlgorithm}";        if (!$request->hasHeader($headerName)) {            $encoded = $this->getEncodedValue($requestedAlgorithm, $body);            $request = $request->withHeader($headerName, $encoded);        }        return $request;    }    /**     * @param CommandInterface $command     * @return bool     */    private function isS3Express(CommandInterface $command): bool    {        return isset($command['@context']['signing_service'])            && $command['@context']['signing_service'] === 's3express';    }}
 |