Skip to content
This repository was archived by the owner on Jan 6, 2024. It is now read-only.

Add async capability #11

Merged
merged 8 commits into from
Nov 5, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,22 @@
"require": {
"php": ">=5.5.0",
"php-http/httplug": "dev-master",
"php-http/httplug-async": "^0.1",
"guzzlehttp/guzzle": "^6.0"
},
"require-dev": {
"ext-curl": "*",
"php-http/adapter-integration-tests": "^0.2@dev"
"php-http/adapter-integration-tests": "dev-feature/async-test"
},
"repositories": [
{
"type": "vcs",
"url": "https://github.com/joelwurtz/adapter-integration-tests"
}
],
"provide": {
"php-http/client-implementation": "1.0"
"php-http/client-implementation": "1.0",
"php-http/async-client-implementation": "1.0"
},
"autoload": {
"psr-4": {
Expand Down
48 changes: 13 additions & 35 deletions src/Guzzle6HttpAdapter.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,17 @@
use GuzzleHttp\Client;
use GuzzleHttp\ClientInterface;
use GuzzleHttp\Exception as GuzzleExceptions;
use GuzzleHttp\Promise\PromiseInterface;
use Http\Client\Exception;
use Http\Client\Exception\HttpException;
use Http\Client\Exception\NetworkException;
use Http\Client\Exception\RequestException;
use Http\Client\Exception\TransferException;
use Http\Client\HttpAsyncClient;
use Http\Client\HttpClient;
use Psr\Http\Message\RequestInterface;

/**
* @author David de Boer <[email protected]>
*/
class Guzzle6HttpAdapter implements HttpClient
class Guzzle6HttpAdapter implements HttpClient, HttpAsyncClient
{
/**
* @var ClientInterface
Expand All @@ -44,42 +43,21 @@ public function __construct(ClientInterface $client = null)
*/
public function sendRequest(RequestInterface $request)
{
try {
return $this->client->send($request);
} catch (GuzzleExceptions\SeekException $e) {
throw new RequestException($e->getMessage(), $request, $e);
} catch (GuzzleExceptions\GuzzleException $e) {
throw $this->handleException($e);
$promise = $this->sendAsyncRequest($request);
$promise->wait();

if ($promise->getState() == PromiseInterface::REJECTED) {
throw $promise->getException();
}

return $promise->getResponse();
}

/**
* Converts a Guzzle exception into an Httplug exception.
*
* @param GuzzleExceptions\GuzzleException $exception
*
* @return Exception
* {@inheritdoc}
*/
private function handleException(GuzzleExceptions\GuzzleException $exception)
public function sendAsyncRequest(RequestInterface $request)
{
if ($exception instanceof GuzzleExceptions\ConnectException) {
return new NetworkException($exception->getMessage(), $exception->getRequest(), $exception);
}

if ($exception instanceof GuzzleExceptions\RequestException) {
// Make sure we have a response for the HttpException
if ($exception->hasResponse()) {
return new HttpException(
$exception->getMessage(),
$exception->getRequest(),
$exception->getResponse(),
$exception
);
}

return new RequestException($exception->getMessage(), $exception->getRequest(), $exception);
}

return new TransferException($exception->getMessage(), 0, $exception);
return new Guzzle6Promise($this->client->sendAsync($request), $request);
}
}
151 changes: 151 additions & 0 deletions src/Guzzle6Promise.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
<?php

namespace Http\Adapter;

use GuzzleHttp\Exception as GuzzleExceptions;
use GuzzleHttp\Promise\PromiseInterface;
use Http\Client\Exception as HttplugException;
use Http\Client\Promise;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;

class Guzzle6Promise implements Promise
{
/**
* @var \GuzzleHttp\Promise\PromiseInterface
*/
private $promise;

/**
* @var string State of the promise
*/
private $state;

/**
* @var ResponseInterface
*/
private $response;

/**
* @var HttplugException
*/
private $exception;

/**
* @var RequestInterface
*/
private $request;

public function __construct(PromiseInterface $promise, RequestInterface $request)
{
$this->request = $request;
$this->state = self::PENDING;
$this->promise = $promise->then(function ($response) {
$this->response = $response;
$this->state = self::FULFILLED;

return $response;
}, function ($reason) use ($request) {
if ($reason instanceof HttplugException) {
$this->state = self::REJECTED;
$this->exception = $reason;

throw $this->exception;
}

if (!($reason instanceof GuzzleExceptions\GuzzleException)) {
throw new \RuntimeException("Invalid reason");
}

$this->state = self::REJECTED;
$this->exception = $this->handleException($reason, $request);

throw $this->exception;
});
}

/**
* {@inheritdoc}
*/
public function then(callable $onFulfilled = null, callable $onRejected = null)
{
return new static($this->promise->then($onFulfilled, $onRejected), $this->request);
}

/**
* {@inheritdoc}
*/
public function getState()
{
return $this->state;
}


/**
* {@inheritdoc}
*/
public function getResponse()
{
if (self::FULFILLED !== $this->state) {
throw new \LogicException("Response not available for the current state");
}

return $this->response;
}

/**
* {@inheritdoc}
*/
public function getException()
{
if (self::REJECTED !== $this->state) {
throw new \LogicException("Error not available for the current state");
}

return $this->exception;
}

/**
* {@inheritdoc}
*/
public function wait()
{
$this->promise->wait(false);
}

/**
* Converts a Guzzle exception into an Httplug exception.
*
* @param GuzzleExceptions\GuzzleException $exception
* @param RequestInterface $request
*
* @return HttplugException
*/
private function handleException(GuzzleExceptions\GuzzleException $exception, RequestInterface $request)
{
if ($exception instanceof GuzzleExceptions\SeekException) {
return new HttplugException\RequestException($exception->getMessage(), $request, $exception);
}

if ($exception instanceof GuzzleExceptions\ConnectException) {
return new HttplugException\NetworkException($exception->getMessage(), $exception->getRequest(), $exception);
}

if ($exception instanceof GuzzleExceptions\RequestException) {
// Make sure we have a response for the HttpException
if ($exception->hasResponse()) {
return new HttplugException\HttpException(
$exception->getMessage(),
$exception->getRequest(),
$exception->getResponse(),
$exception
);
}

return new HttplugException\RequestException($exception->getMessage(), $exception->getRequest(), $exception);
}

return new HttplugException\TransferException($exception->getMessage(), 0, $exception);
}
}

30 changes: 30 additions & 0 deletions tests/Guzzle6CurlHttpAsyncAdapterTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php

/*
* This file is part of the Http Adapter package.
*
* (c) Eric GELOEN <[email protected]>
*
* For the full copyright and license information, please read the LICENSE
* file that was distributed with this source code.
*/

namespace Http\Adapter\Tests;

use GuzzleHttp\Handler\CurlHandler;

/**
* @requires PHP 5.5
*
* @author GeLo <[email protected]>
*/
class Guzzle6CurlHttpAsyncAdapterTest extends Guzzle6HttpAsyncAdapterTest
{
/**
* {@inheritdoc}
*/
protected function createHandler()
{
return new CurlHandler();
}
}
3 changes: 2 additions & 1 deletion tests/Guzzle6HttpAdapterTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,12 @@

use GuzzleHttp\Client;
use Http\Adapter\Guzzle6HttpAdapter;
use Http\Client\Tests\HttpClientTest;

/**
* @author GeLo <[email protected]>
*/
abstract class Guzzle6HttpAdapterTest extends HttpAdapterTest
abstract class Guzzle6HttpAdapterTest extends HttpClientTest
{
/**
* {@inheritdoc}
Expand Down
37 changes: 37 additions & 0 deletions tests/Guzzle6HttpAsyncAdapterTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?php

/*
* This file is part of the Http Adapter package.
*
* (c) Eric GELOEN <[email protected]>
*
* For the full copyright and license information, please read the LICENSE
* file that was distributed with this source code.
*/

namespace Http\Adapter\Tests;

use GuzzleHttp\Client;
use Http\Adapter\Guzzle6HttpAdapter;
use Http\Client\Tests\HttpAsyncClientTest;

/**
* @author GeLo <[email protected]>
*/
abstract class Guzzle6HttpAsyncAdapterTest extends HttpAsyncClientTest
{
/**
* {@inheritdoc}
*/
protected function createHttpAsyncClient()
{
return new Guzzle6HttpAdapter(new Client(['handler' => $this->createHandler()]));
}

/**
* Returns a handler for the client
*
* @return object
*/
abstract protected function createHandler();
}
28 changes: 28 additions & 0 deletions tests/Guzzle6MultiCurlHttpAsyncAdapterTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php

/*
* This file is part of the Http Adapter package.
*
* (c) Eric GELOEN <[email protected]>
*
* For the full copyright and license information, please read the LICENSE
* file that was distributed with this source code.
*/

namespace Http\Adapter\Tests;

use GuzzleHttp\Handler\CurlMultiHandler;

/**
* @author GeLo <[email protected]>
*/
class Guzzle6MultiCurlHttpAsyncAdapterTest extends Guzzle6HttpAsyncAdapterTest
{
/**
* {@inheritdoc}
*/
protected function createHandler()
{
return new CurlMultiHandler();
}
}
Loading