1
0
mirror of https://github.com/guzzle/guzzle.git synced 2025-02-25 02:22:57 +01:00

Trying to flesh out intercepting events

This commit is contained in:
Michael Dowling 2013-08-01 12:49:43 -07:00
parent 75eca7f10d
commit ee0756ff06
8 changed files with 222 additions and 36 deletions

View File

@ -10,9 +10,9 @@ interface AdapterInterface
/**
* Transfers one or more HTTP requests and populates responses
*
* @param array $requests Array of {@see \Guzzle\Http\Message\RequestInterface} objects
* @param Transaction $transaction Hash of request to response object with an associated client
*
* @return Transaction Returns a hash mapping RequestInterface to ResponseInterface objects or AdapterExceptions
*/
public function send(array $requests);
public function send(Transaction $transaction);
}

View File

@ -45,15 +45,15 @@ class CurlAdapter extends AbstractAdapter
$this->factory = isset($options['factory']) ? $options['factory'] : CurlFactory::getInstance();
}
public function send(array $requests)
public function send(Transaction $transaction)
{
$context = [
'transaction' => new Transaction(),
'transaction' => $transaction,
'handles' => new \SplObjectStorage(),
'multi' => $this->checkoutMultiHandle()
];
foreach ($requests as $request) {
foreach ($transaction as $request) {
try {
$this->prepare($request, $context);
} catch (RequestException $e) {
@ -69,11 +69,9 @@ class CurlAdapter extends AbstractAdapter
private function prepare(RequestInterface $request, array $context)
{
$response = $this->messageFactory->createResponse();
$handle = $this->factory->createHandle($request, $response);
$handle = $this->factory->createHandle($request, $context['transaction'][$request]);
$this->checkCurlResult(curl_multi_add_handle($context['multi'], $handle));
$context['handles'][$request] = $handle;
$context['transaction'][$request] = $response;
}
/**
@ -117,7 +115,6 @@ class CurlAdapter extends AbstractAdapter
try {
$this->isCurlException($request, $curl);
// Emit request.sent
} catch (RequestException $e) {
$context['transaction'][$request] = $e;
}

View File

@ -12,29 +12,27 @@ use Guzzle\Stream\Stream;
*/
class StreamAdapter extends AbstractAdapter
{
public function send(array $requests)
public function send(Transaction $transaction)
{
$result = new Transaction();
foreach ($requests as $request) {
foreach ($transaction as $request) {
try {
$result[$request] = $this->createResponse($request);
$this->createResponse($request, $transaction[$request]);
} catch (RequestException $e) {
$result[$request] = $e;
$transaction[$request] = $e;
}
}
return $result;
return $transaction;
}
/**
* @param RequestInterface $request
* @return ResponseInterface
* @param RequestInterface $request Request to send
* @param ResponseInterface $response Response to populate
* @throws \LogicException if you attempt to stream and specify a write_to option
*/
private function createResponse(RequestInterface $request)
private function createResponse(RequestInterface $request, ResponseInterface $response)
{
$stream = $this->createStream($request, $http_response_header);
$response = $this->messageFactory->createResponse();
// Track the response headers of the request
if (isset($http_response_header)) {
@ -46,8 +44,6 @@ class StreamAdapter extends AbstractAdapter
} else{
$this->applySaveToBody($request, $response, $stream);
}
return $response;
}
/**

View File

@ -22,11 +22,11 @@ class StreamingProxyAdapter implements AdapterInterface
$this->streamingAdapter = $streamingAdapter;
}
public function send(array $requests)
public function send(Transaction $transaction)
{
$streaming = $default = array();
foreach ($requests as $request) {
foreach ($transaction as $request) {
if ($request->getTransferOptions()['streaming']) {
$streaming[] = $request;
} else {
@ -34,14 +34,25 @@ class StreamingProxyAdapter implements AdapterInterface
}
}
if ($default) {
$result = $this->defaultAdapter->send($default);
if ($streaming) {
$result->addAll($this->streamingAdapter->send($streaming));
}
return $result;
} elseif ($streaming) {
return $this->streamingAdapter->send($streaming);
if (!$streaming) {
return $this->defaultAdapter->send($transaction);
}
$streamingTransaction = new Transaction($transaction->getClient());
foreach ($streaming as $request) {
$streamingTransaction[$request] = $transaction[$request];
}
$this->streamingAdapter->send($streamingTransaction);
if ($default) {
$defaultTransaction = new Transaction($transaction->getClient());
foreach ($default as $request) {
$defaultTransaction[$request] = $transaction[$request];
}
$streamingTransaction->addAll($this->defaultAdapter->send($defaultTransaction));
}
return $streamingTransaction;
}
}

View File

@ -2,6 +2,7 @@
namespace Guzzle\Http\Adapter;
use Guzzle\Http\ClientInterface;
use Guzzle\Http\Message\RequestInterface;
use Guzzle\Http\Message\ResponseInterface;
use Guzzle\Http\Exception\RequestException;
@ -11,6 +12,17 @@ use Guzzle\Http\Exception\RequestException;
*/
class Transaction extends \SplObjectStorage
{
/** @var ClientInterface */
private $client;
/**
* @param ClientInterface $client Client that is used to send the requests
*/
public function __construct(ClientInterface $client)
{
$this->client = $client;
}
public function offsetSet($object, $data = null)
{
if (!($object instanceof RequestInterface)) {
@ -89,4 +101,14 @@ class Transaction extends \SplObjectStorage
return $hash;
}
/**
* Get the client that sent the requests
*
* @return ClientInterface
*/
public function getClient()
{
return $this->client;
}
}

View File

@ -5,13 +5,17 @@ namespace Guzzle\Http;
use Guzzle\Common\Collection;
use Guzzle\Common\HasDispatcher;
use Guzzle\Common\Version;
use Guzzle\Http\Event\AfterSendEvent;
use Guzzle\Http\Event\BeforeSendEvent;
use Guzzle\Http\Adapter\AdapterInterface;
use Guzzle\Http\Adapter\StreamAdapter;
use Guzzle\Http\Adapter\StreamingProxyAdapter;
use Guzzle\Http\Adapter\Curl\CurlAdapter;
use Guzzle\Http\Adapter\Transaction;
use Guzzle\Http\Exception\AdapterException;
use Guzzle\Http\Exception\BatchException;
use Guzzle\Http\Message\RequestInterface;
use Guzzle\Http\Message\ResponseInterface;
use Guzzle\Url\Url;
use Guzzle\Url\UriTemplate;
use Guzzle\Http\Message\MessageFactory;
@ -184,10 +188,17 @@ class Client implements ClientInterface
public function send(RequestInterface $request)
{
$transaction = $this->adapter->send(array($request));
if (!isset($transaction[$request])) {
throw new \LogicException('The client HTTP adapter did not populate a response for the request');
} elseif ($transaction[$request] instanceof AdapterException) {
$transaction = new Transaction($this);
$event = $this->preSend($request);
if ($response = $event->getResponse()) {
$transaction[$request] = $response;
} else {
$transaction[$request] = $this->messageFactory->createResponse();
$this->adapter->send($transaction);
}
$this->afterSend($request, $transaction);
if ($transaction[$request] instanceof \Exception) {
throw $transaction[$request];
}
@ -196,7 +207,28 @@ class Client implements ClientInterface
public function batch(array $requests)
{
$transaction = $this->adapter->send($requests);
$transaction = new Transaction($this);
$intercepted = new Transaction($this);
foreach ($requests as $request) {
$event = $this->preSend($request);
if ($response = $event->getResponse()) {
$intercepted[$request] = $response;
} else {
$transaction[$request] = $this->messageFactory->createResponse();
}
}
if (count($transaction)) {
$this->adapter->send($transaction);
}
$transaction->addAll($intercepted);
foreach ($requests as $request) {
$this->afterSend($request, $transaction);
}
if ($transaction->hasExceptions()) {
throw new BatchException($transaction, $this);
}
@ -214,6 +246,41 @@ class Client implements ClientInterface
return 'Guzzle/' . Version::VERSION . ' curl/' . curl_version()['version'] . ' PHP/' . PHP_VERSION;
}
/**
* Emits events before a request is sent
*
* @param RequestInterface $request Request about to be sent
*
* @return BeforeSendEvent
*/
private function preSend(RequestInterface $request)
{
return $request->getEventDispatcher()->dispatch(
'request.before_send',
new BeforeSendEvent($request, $this->messageFactory)
);
}
/**
* Performs validation and emits events after a request has been sent
*
* @param RequestInterface $request Request that sent
* @param Transaction $transaction Transaction
* @throws \LogicException if the transaction loses the request for some reason after the request.after_send event
*/
private function afterSend(RequestInterface $request, Transaction $transaction)
{
if (!isset($transaction[$request])) {
throw new \LogicException('The request is not associated with the transaction');
}
$event = $request->getEventDispatcher()->dispatch(
'request.after_send',
new AfterSendEvent($request, $transaction[$request], $this->messageFactory)
);
$transaction[$request] = $event->getResult();
}
/**
* Expand a URI template
*

View File

@ -0,0 +1,54 @@
<?php
namespace Guzzle\Http\Event;
use Guzzle\Common\Event;
use Guzzle\Http\Message\MessageFactoryInterface;
use Guzzle\Http\Message\RequestInterface;
use Guzzle\Http\Message\ResponseInterface;
class AfterSendEvent extends Event
{
public function __construct(RequestInterface $request, $result, MessageFactoryInterface $factory)
{
parent::__construct([
'request' => $request,
'message_factory' => $factory,
'result' => $result
]);
}
public function getRequest()
{
return $this['request'];
}
public function getMessageFactory()
{
return $this['message_factory'];
}
public function setResult($result)
{
if (!($result instanceof ResponseInterface) && !($result instanceof \Exception)) {
throw new \InvalidArgumentException('Result must be a ResponseInterface or Exception object');
}
$this['result'] = $result;
}
public function getResult()
{
return $this['result'];
}
public function hasResponse()
{
return $this['result'] instanceof ResponseInterface;
}
public function hasException()
{
return $this['result'] instanceof \Exception;
}
}

View File

@ -0,0 +1,39 @@
<?php
namespace Guzzle\Http\Event;
use Guzzle\Common\Event;
use Guzzle\Http\Message\MessageFactoryInterface;
use Guzzle\Http\Message\RequestInterface;
use Guzzle\Http\Message\ResponseInterface;
class BeforeSendEvent extends Event
{
public function __construct(RequestInterface $request, MessageFactoryInterface $factory)
{
parent::__construct([
'request' => $request,
'message_factory' => $factory
]);
}
public function getRequest()
{
return $this['request'];
}
public function getMessageFactory()
{
return $this['message_factory'];
}
public function getResponse()
{
return $this['response'];
}
public function setResponse(ResponseInterface $response)
{
$this['response'] = $response;
}
}