123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147 |
- <?php
- namespace 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';
- }
- }
|