#php #amazon-s3 #redirect #guzzle
Вопрос:
Я использую API, который получает запрос POST с некоторыми параметрами и использует их для создания файла в S3. Этот API возвращает ответ на перенаправление 303 с Location:
указанием подписанного URL-адреса для доступа к файлу S3.
Этот файл работает при доступе к API, например, через Postman, однако при доступе к нему через GuzzleHttp (v7.4) он завершается ошибкой SignatureDoesNotMatch
.
При отладке я использовал код:
$client = new Client([ 'allow_redirects' =gt; true, 'on_stats' =gt; function (TransferStats $stats) { var_dump($stats-gt;getEffectiveUri()); } ]); $client-gt;post('https://api.example.com', [ 'json' =gt; [ ... ] ]);
Делая это, я подтвердил, что URL-адрес правильный, и копирование/вставка точного URL-адреса, к которому осуществляется доступ, действительно работает. Однако при использовании GuzzleHttp это вообще не работает.
Обновление: Разработчик API сообщил мне, что эта проблема возникла также из-за того, что они использовали версию 2 подписи AWS S3. Теперь они изменили его на v4, что заставляет мой код работать как есть (я думаю, что у них могли быть те же проблемы с другими клиентами).
Ответ №1:
Проблема оказалась в том, что, когда Guzzle следует перенаправлениям, он сохраняет большую часть исходных заголовков запросов. Однако вычисленная подпись Amazon также проверяет подпись на соответствие (по крайней мере, некоторым) заголовкам. Оскорбительным заголовком был Content-Type
заголовок, который все еще отправлялся, хотя запрос больше не содержал никакого содержимого.
Чтобы исправить это, я создал новое промежуточное программное обеспечение Guzzle:
use PsrHttpMessageRequestInterface; class RemoveContentType { public function __invoke(callable $handler) { return function (RequestInterface $request, array $options) use ($handler) { if ($request-gt;getHeaderLine('Content-Type') amp;amp; $request-gt;getBody()-gt;getSize() === 0) { return $handler($request-gt;withoutHeader('Content-Type'), $options); } return $handler($request, $options); }; } }
это приведет к удалению типа содержимого из запроса с пустым телом.
Затем я изменил свой код, чтобы использовать это промежуточное программное обеспечение:
$stack = HandlerStack::create(); $stack-gt;push(new RemoveContentType ()); $client = new Client([ 'handler' =gt; $stack, 'allow_redirects' =gt; true, ]); $client-gt;post('https://api.example.com', [ 'json' =gt; [ ... ] ]);
Это, наконец, решило мою проблему.