| 
<?phpnamespace Aws\Handler\GuzzleV5;
 
 use Aws\Sdk;
 use Exception;
 use GuzzleHttp\Client;
 use GuzzleHttp\ClientInterface;
 use GuzzleHttp\Exception\ConnectException;
 use GuzzleHttp\Exception\RequestException;
 use GuzzleHttp\Message\ResponseInterface as GuzzleResponse;
 use GuzzleHttp\Promise;
 use GuzzleHttp\Psr7\Response as Psr7Response;
 use Psr\Http\Message\RequestInterface as Psr7Request;
 use Psr\Http\Message\StreamInterface as Psr7StreamInterface;
 
 /**
 * A request handler that sends PSR-7-compatible requests with Guzzle 5.
 *
 * The handler accepts a PSR-7 Request object and an array of transfer options
 * and returns a Guzzle 6 Promise. The promise is either resolved with a
 * PSR-7 Response object or rejected with an array of error data.
 *
 * @codeCoverageIgnore
 */
 class GuzzleHandler
 {
 private static $validOptions = [
 'proxy'           => true,
 'verify'          => true,
 'timeout'         => true,
 'debug'           => true,
 'connect_timeout' => true,
 'stream'          => true,
 'delay'           => true,
 'sink'            => true,
 ];
 
 /** @var ClientInterface */
 private $client;
 
 /**
 * @param ClientInterface $client
 */
 public function __construct(ClientInterface $client = null)
 {
 $this->client = $client ?: new Client();
 }
 
 /**
 * @param Psr7Request $request
 * @param array       $options
 *
 * @return Promise\Promise
 */
 public function __invoke(Psr7Request $request, array $options = [])
 {
 // Create and send a Guzzle 5 request
 $guzzlePromise = $this->client->send(
 $this->createGuzzleRequest($request, $options)
 );
 
 $promise = new Promise\Promise(
 function () use ($guzzlePromise) {
 try {
 $guzzlePromise->wait();
 } catch (\Exception $e) {
 // The promise is already delivered when the exception is
 // thrown, so don't rethrow it.
 }
 },
 [$guzzlePromise, 'cancel']
 );
 
 $guzzlePromise->then([$promise, 'resolve'], [$promise, 'reject']);
 
 return $promise->then(
 function (GuzzleResponse $response) {
 // Adapt the Guzzle 5 Future to a Guzzle 6 ResponsePromise.
 return $this->createPsr7Response($response);
 },
 function (Exception $exception) {
 // Reject with information about the error.
 return new Promise\RejectedPromise($this->prepareErrorData($exception));
 }
 );
 }
 
 private function createGuzzleRequest(Psr7Request $psrRequest, array $options)
 {
 $ringConfig = [];
 
 // Remove unsupported options.
 foreach (array_keys($options) as $key) {
 if (!isset(self::$validOptions[$key])) {
 unset($options[$key]);
 }
 }
 
 // Handle delay option.
 if (isset($options['delay'])) {
 $ringConfig['delay'] = $options['delay'];
 unset($options['delay']);
 }
 
 // Prepare sink option.
 if (isset($options['sink'])) {
 $ringConfig['save_to'] = ($options['sink'] instanceof Psr7StreamInterface)
 ? new GuzzleStream($options['sink'])
 : $options['sink'];
 unset($options['sink']);
 }
 
 // Ensure that all requests are async and lazy like Guzzle 6.
 $options['future'] = 'lazy';
 
 // Create the Guzzle 5 request from the provided PSR7 request.
 $request = $this->client->createRequest(
 $psrRequest->getMethod(),
 $psrRequest->getUri(),
 $options
 );
 
 // For the request body, adapt the PSR stream to a Guzzle stream.
 $body = $psrRequest->getBody();
 if ($body->getSize() === 0) {
 $request->setBody(null);
 } else {
 $request->setBody(new GuzzleStream($body));
 }
 
 $request->setHeaders($psrRequest->getHeaders());
 
 $request->setHeader(
 'User-Agent',
 $request->getHeader('User-Agent')
 . ' ' . Client::getDefaultUserAgent()
 );
 
 // Make sure the delay is configured, if provided.
 if ($ringConfig) {
 foreach ($ringConfig as $k => $v) {
 $request->getConfig()->set($k, $v);
 }
 }
 
 return $request;
 }
 
 private function createPsr7Response(GuzzleResponse $response)
 {
 if ($body = $response->getBody()) {
 $body = new PsrStream($body);
 }
 
 return new Psr7Response(
 $response->getStatusCode(),
 $response->getHeaders(),
 $body,
 $response->getReasonPhrase()
 );
 }
 
 private function prepareErrorData(Exception $e)
 {
 $error = [
 'exception'        => $e,
 'connection_error' => false,
 'response'         => null,
 ];
 
 if ($e instanceof ConnectException) {
 $error['connection_error'] = true;
 }
 
 if ($e instanceof RequestException && $e->getResponse()) {
 $error['response'] = $this->createPsr7Response($e->getResponse());
 }
 
 return $error;
 }
 }
 
 |