RequestCompressionMiddleware.php 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. <?php
  2. namespace Aws;
  3. use GuzzleHttp\Psr7;
  4. use Psr\Http\Message\RequestInterface;
  5. /**
  6. * Used to compress request payloads if the service/operation support it.
  7. *
  8. * IMPORTANT: this middleware must be added after the "build" step.
  9. *
  10. * @internal
  11. */
  12. class RequestCompressionMiddleware
  13. {
  14. private $api;
  15. private $minimumCompressionSize;
  16. private $nextHandler;
  17. private $encodings;
  18. private $encoding;
  19. private $encodingMap = [
  20. 'gzip' => 'gzencode'
  21. ];
  22. /**
  23. * Create a middleware wrapper function.
  24. *
  25. * @return callable
  26. */
  27. public static function wrap(array $config)
  28. {
  29. return function (callable $handler) use ($config) {
  30. return new self($handler, $config);
  31. };
  32. }
  33. public function __construct(callable $nextHandler, $config)
  34. {
  35. $this->minimumCompressionSize = $this->determineMinimumCompressionSize($config);
  36. $this->api = $config['api'];
  37. $this->nextHandler = $nextHandler;
  38. }
  39. public function __invoke(CommandInterface $command, RequestInterface $request)
  40. {
  41. if (isset($command['@request_min_compression_size_bytes'])
  42. && is_int($command['@request_min_compression_size_bytes'])
  43. && $this->isValidCompressionSize($command['@request_min_compression_size_bytes'])
  44. ) {
  45. $this->minimumCompressionSize = $command['@request_min_compression_size_bytes'];
  46. }
  47. $nextHandler = $this->nextHandler;
  48. $operation = $this->api->getOperation($command->getName());
  49. $compressionInfo = isset($operation['requestcompression'])
  50. ? $operation['requestcompression']
  51. : null;
  52. if (!$this->shouldCompressRequestBody(
  53. $compressionInfo,
  54. $command,
  55. $operation,
  56. $request
  57. )) {
  58. return $nextHandler($command, $request);
  59. }
  60. $this->encodings = $compressionInfo['encodings'];
  61. $request = $this->compressRequestBody($request);
  62. return $nextHandler($command, $request);
  63. }
  64. private function compressRequestBody(
  65. RequestInterface $request
  66. ) {
  67. $fn = $this->determineEncoding();
  68. if (is_null($fn)) {
  69. return $request;
  70. }
  71. $body = $request->getBody()->getContents();
  72. $compressedBody = $fn($body);
  73. return $request->withBody(Psr7\Utils::streamFor($compressedBody))
  74. ->withHeader('content-encoding', $this->encoding);
  75. }
  76. private function determineEncoding()
  77. {
  78. foreach ($this->encodings as $encoding) {
  79. if (isset($this->encodingMap[$encoding])) {
  80. $this->encoding = $encoding;
  81. return $this->encodingMap[$encoding];
  82. }
  83. }
  84. return null;
  85. }
  86. private function shouldCompressRequestBody(
  87. $compressionInfo,
  88. $command,
  89. $operation,
  90. $request
  91. ){
  92. if ($compressionInfo) {
  93. if (isset($command['@disable_request_compression'])
  94. && $command['@disable_request_compression'] === true
  95. ) {
  96. return false;
  97. } elseif ($this->hasStreamingTraitWithoutRequiresLength($command, $operation)
  98. ) {
  99. return true;
  100. }
  101. $requestBodySize = $request->hasHeader('content-length')
  102. ? (int) $request->getHeaderLine('content-length')
  103. : $request->getBody()->getSize();
  104. if ($requestBodySize >= $this->minimumCompressionSize) {
  105. return true;
  106. }
  107. }
  108. return false;
  109. }
  110. private function hasStreamingTraitWithoutRequiresLength($command, $operation)
  111. {
  112. foreach ($operation->getInput()->getMembers() as $name => $member) {
  113. if (isset($command[$name])
  114. && !empty($member['streaming'])
  115. && empty($member['requiresLength'])
  116. ){
  117. return true;
  118. }
  119. }
  120. return false;
  121. }
  122. private function determineMinimumCompressionSize($config) {
  123. if (is_callable($config['request_min_compression_size_bytes'])) {
  124. $minCompressionSz = $config['request_min_compression_size_bytes']();
  125. } else {
  126. $minCompressionSz = $config['request_min_compression_size_bytes'];
  127. }
  128. if ($this->isValidCompressionSize($minCompressionSz)) {
  129. return $minCompressionSz;
  130. }
  131. }
  132. private function isValidCompressionSize($compressionSize)
  133. {
  134. if (is_numeric($compressionSize)
  135. && ($compressionSize >= 0 && $compressionSize <= 10485760)
  136. ) {
  137. return true;
  138. }
  139. throw new \InvalidArgumentException(
  140. 'The minimum request compression size must be a '
  141. . 'non-negative integer value between 0 and 10485760 bytes, inclusive.'
  142. );
  143. }
  144. }