/vendor/php-http/client-common/src/Plugin/DecoderPlugin.php

https://github.com/linuxserver/Heimdall · PHP · 144 lines · 76 code · 22 blank · 46 comment · 10 complexity · 0bfacafb7cce61857fabcc40ec7fbef5 MD5 · raw file

  1. <?php
  2. namespace Http\Client\Common\Plugin;
  3. use Http\Client\Common\Plugin;
  4. use Http\Message\Encoding;
  5. use Psr\Http\Message\RequestInterface;
  6. use Psr\Http\Message\ResponseInterface;
  7. use Psr\Http\Message\StreamInterface;
  8. use Symfony\Component\OptionsResolver\OptionsResolver;
  9. /**
  10. * Allow to decode response body with a chunk, deflate, compress or gzip encoding.
  11. *
  12. * If zlib is not installed, only chunked encoding can be handled.
  13. *
  14. * If Content-Encoding is not disabled, the plugin will add an Accept-Encoding header for the encoding methods it supports.
  15. *
  16. * @author Joel Wurtz <joel.wurtz@gmail.com>
  17. */
  18. final class DecoderPlugin implements Plugin
  19. {
  20. /**
  21. * @var bool Whether this plugin decode stream with value in the Content-Encoding header (default to true).
  22. *
  23. * If set to false only the Transfer-Encoding header will be used
  24. */
  25. private $useContentEncoding;
  26. /**
  27. * @param array $config {
  28. *
  29. * @var bool $use_content_encoding Whether this plugin should look at the Content-Encoding header first or only at the Transfer-Encoding (defaults to true).
  30. * }
  31. */
  32. public function __construct(array $config = [])
  33. {
  34. $resolver = new OptionsResolver();
  35. $resolver->setDefaults([
  36. 'use_content_encoding' => true,
  37. ]);
  38. $resolver->setAllowedTypes('use_content_encoding', 'bool');
  39. $options = $resolver->resolve($config);
  40. $this->useContentEncoding = $options['use_content_encoding'];
  41. }
  42. /**
  43. * {@inheritdoc}
  44. */
  45. public function handleRequest(RequestInterface $request, callable $next, callable $first)
  46. {
  47. $encodings = extension_loaded('zlib') ? ['gzip', 'deflate'] : ['identity'];
  48. if ($this->useContentEncoding) {
  49. $request = $request->withHeader('Accept-Encoding', $encodings);
  50. }
  51. $encodings[] = 'chunked';
  52. $request = $request->withHeader('TE', $encodings);
  53. return $next($request)->then(function (ResponseInterface $response) {
  54. return $this->decodeResponse($response);
  55. });
  56. }
  57. /**
  58. * Decode a response body given its Transfer-Encoding or Content-Encoding value.
  59. *
  60. * @param ResponseInterface $response Response to decode
  61. *
  62. * @return ResponseInterface New response decoded
  63. */
  64. private function decodeResponse(ResponseInterface $response)
  65. {
  66. $response = $this->decodeOnEncodingHeader('Transfer-Encoding', $response);
  67. if ($this->useContentEncoding) {
  68. $response = $this->decodeOnEncodingHeader('Content-Encoding', $response);
  69. }
  70. return $response;
  71. }
  72. /**
  73. * Decode a response on a specific header (content encoding or transfer encoding mainly).
  74. *
  75. * @param string $headerName Name of the header
  76. * @param ResponseInterface $response Response
  77. *
  78. * @return ResponseInterface A new instance of the response decoded
  79. */
  80. private function decodeOnEncodingHeader($headerName, ResponseInterface $response)
  81. {
  82. if ($response->hasHeader($headerName)) {
  83. $encodings = $response->getHeader($headerName);
  84. $newEncodings = [];
  85. while ($encoding = array_pop($encodings)) {
  86. $stream = $this->decorateStream($encoding, $response->getBody());
  87. if (false === $stream) {
  88. array_unshift($newEncodings, $encoding);
  89. continue;
  90. }
  91. $response = $response->withBody($stream);
  92. }
  93. if (\count($newEncodings) > 0) {
  94. $response = $response->withHeader($headerName, $newEncodings);
  95. } else {
  96. $response = $response->withoutHeader($headerName);
  97. }
  98. }
  99. return $response;
  100. }
  101. /**
  102. * Decorate a stream given an encoding.
  103. *
  104. * @param string $encoding
  105. * @param StreamInterface $stream
  106. *
  107. * @return StreamInterface|false A new stream interface or false if encoding is not supported
  108. */
  109. private function decorateStream($encoding, StreamInterface $stream)
  110. {
  111. if ('chunked' === strtolower($encoding)) {
  112. return new Encoding\DechunkStream($stream);
  113. }
  114. if ('deflate' === strtolower($encoding)) {
  115. return new Encoding\DecompressStream($stream);
  116. }
  117. if ('gzip' === strtolower($encoding)) {
  118. return new Encoding\GzipDecodeStream($stream);
  119. }
  120. return false;
  121. }
  122. }