| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211 | <?phpnamespace Aws\Handler\GuzzleV5;use Exception;use GuzzleHttp\Client;use GuzzleHttp\ClientInterface;use GuzzleHttp\Event\EndEvent;use GuzzleHttp\Exception\ConnectException;use GuzzleHttp\Exception\RequestException;use GuzzleHttp\Message\ResponseInterface as GuzzleResponse;use GuzzleHttp\Promise;use GuzzleHttp\Psr7\Response as Psr7Response;use GuzzleHttp\Stream\Stream;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,        'expect'            => true,        'cert'              => 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|Promise\PromiseInterface     * @throws \GuzzleHttp\Exception\GuzzleException     */    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) use ($options) {                // If we got a 'sink' that's a path, set the response body to                // the contents of the file. This will build the resulting                // exception with more information.                if ($exception instanceof RequestException) {                    if (isset($options['sink'])) {                        if (!($options['sink'] instanceof Psr7StreamInterface)) {                            $exception->getResponse()->setBody(                                Stream::factory(                                    file_get_contents($options['sink'])                                )                            );                        }                    }                }                // Reject with information about the error.                return new Promise\RejectedPromise($this->prepareErrorData($exception));            }        );    }    private function createGuzzleRequest(Psr7Request $psrRequest, array $options)    {        $ringConfig = [];        $statsCallback = isset($options['http_stats_receiver'])            ? $options['http_stats_receiver']            : null;        unset($options['http_stats_receiver']);        // 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        );        if (is_callable($statsCallback)) {            $request->getEmitter()->on(                'end',                function (EndEvent $event) use ($statsCallback) {                    $statsCallback($event->getTransferInfo());                }            );        }        // 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;    }}
 |