1
0
mirror of https://github.com/guzzle/guzzle.git synced 2025-02-24 01:53:58 +01:00

WIP work on on_stats

This commit is contained in:
Michael Dowling 2015-07-30 23:26:08 -07:00
parent 2e92238902
commit ec1f1a8b56
7 changed files with 227 additions and 18 deletions

View File

@ -7,6 +7,7 @@ use GuzzleHttp\Promise\FulfilledPromise;
use GuzzleHttp\Promise\RejectedPromise;
use GuzzleHttp\Psr7;
use GuzzleHttp\Psr7\LazyOpenStream;
use GuzzleHttp\TransferStats;
use Psr\Http\Message\RequestInterface;
/**
@ -94,6 +95,10 @@ class CurlFactory implements CurlFactoryInterface
EasyHandle $easy,
CurlFactoryInterface $factory
) {
if (isset($easy->options['on_stats'])) {
self::invokeStats($easy);
}
if (!$easy->response || $easy->errno) {
return self::finishError($handler, $easy, $factory);
}
@ -110,6 +115,19 @@ class CurlFactory implements CurlFactoryInterface
return new FulfilledPromise($easy->response);
}
private static function invokeStats(EasyHandle $easy)
{
$curlStats = curl_getinfo($easy->handle);
$stats = new TransferStats(
$easy->request,
$easy->response,
$curlStats['total_time'],
$easy->errno,
$curlStats
);
call_user_func($easy->options['on_stats'], $stats);
}
private static function finishError(
callable $handler,
EasyHandle $easy,

View File

@ -4,6 +4,7 @@ namespace GuzzleHttp\Handler;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Promise\PromiseInterface;
use GuzzleHttp\Promise\RejectedPromise;
use GuzzleHttp\TransferStats;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;
@ -80,18 +81,16 @@ class MockHandler implements \Countable
? new RejectedPromise($response)
: \GuzzleHttp\Promise\promise_for($response);
if (!$this->onFulfilled && !$this->onRejected) {
return $response;
}
return $response->then(
function ($value) {
function ($value) use ($request, $options) {
$this->invokeStats($request, $options, $value);
if ($this->onFulfilled) {
call_user_func($this->onFulfilled, $value);
}
return $value;
},
function ($reason) {
function ($reason) use ($request, $options) {
$this->invokeStats($request, $options, null, $reason);
if ($this->onRejected) {
call_user_func($this->onRejected, $reason);
}
@ -149,4 +148,16 @@ class MockHandler implements \Countable
{
return count($this->queue);
}
private function invokeStats(
RequestInterface $request,
array $options,
ResponseInterface $response = null,
$reason = null
) {
if (isset($options['on_stats'])) {
$stats = new TransferStats($request, $response, 0, $reason);
call_user_func($options['on_stats'], $stats);
}
}
}

View File

@ -7,7 +7,9 @@ use GuzzleHttp\Promise\FulfilledPromise;
use GuzzleHttp\Promise\RejectedPromise;
use GuzzleHttp\Promise\PromiseInterface;
use GuzzleHttp\Psr7;
use GuzzleHttp\TransferStats;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\StreamInterface;
/**
@ -32,6 +34,8 @@ class StreamHandler
usleep($options['delay'] * 1000);
}
$startTime = isset($options['on_stats']) ? microtime(true) : null;
try {
// Does not support the expect header.
$request = $request->withoutHeader('Expect');
@ -42,8 +46,12 @@ class StreamHandler
$request = $request->withHeader('Content-Length', 0);
}
$stream = $this->createStream($request, $options);
return $this->createResponse($request, $options, $stream);
return $this->createResponse(
$request,
$options,
$this->createStream($request, $options),
$startTime
);
} catch (\InvalidArgumentException $e) {
throw $e;
} catch (\Exception $e) {
@ -56,16 +64,37 @@ class StreamHandler
) {
$e = new ConnectException($e->getMessage(), $request, $e);
}
return new RejectedPromise(
RequestException::wrapException($request, $e)
$e = RequestException::wrapException($request, $e);
$this->invokeStatsFunction($options, $request, $startTime, null, $e);
return new RejectedPromise($e);
}
}
private function invokeStatsFunction(
array $options,
RequestInterface $request,
$startTime,
ResponseInterface $response = null,
$error = null
) {
if (isset($options['on_stats'])) {
$stats = new TransferStats(
$request,
$response,
microtime(true) - $startTime,
$error,
[]
);
call_user_func($options['on_stats'], $stats);
}
}
private function createResponse(
RequestInterface $request,
array $options,
$stream
$stream,
$startTime
) {
$hdrs = $this->lastHeaders;
$this->lastHeaders = [];
@ -93,6 +122,8 @@ class StreamHandler
$this->drain($stream, $sink);
}
$this->invokeStatsFunction($options, $request, $startTime, $response, null);
return new FulfilledPromise($response);
}

118
src/TransferStats.php Normal file
View File

@ -0,0 +1,118 @@
<?php
namespace GuzzleHttp;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\UriInterface;
/**
* Represents data at the point after it was transferred either successfully
* or after a network error.
*/
final class TransferStats
{
private $request;
private $response;
private $transferTime;
private $handlerStats;
private $handlerErrorData;
/**
* @param RequestInterface $request Request that was sent.
* @param ResponseInterface $response Response received (if any)
* @param null $transferTime Total handler transfer time.
* @param mixed $handlerErrorData Handler error data.
* @param array $handlerStats Handler specific stats.
*/
public function __construct(
RequestInterface $request,
ResponseInterface $response = null,
$transferTime = null,
$handlerErrorData = null,
$handlerStats = []
) {
$this->request = $request;
$this->response = $response;
$this->transferTime = $transferTime;
$this->handlerErrorData = $handlerErrorData;
$this->handlerStats = $handlerStats;
}
/**
* @return RequestInterface
*/
public function getRequest()
{
return $this->request;
}
/**
* @return ResponseInterface|null
*/
public function getResponse()
{
return $this->response;
}
/**
* @return bool
*/
public function hasResponse()
{
return $this->response !== null;
}
/**
* Gets handler specific error data.
*
* @return mixed
*/
public function getHandlerErrorData()
{
return $this->handlerErrorData;
}
/**
* Get the effective URI the request was sent to
*
* @return UriInterface
*/
public function getEffectiveUri()
{
return $this->request->getUri();
}
/**
* Get the estimated time the request was being transferred by the handler.
*
* @return float Time in seconds.
*/
public function getTransferTime()
{
return $this->transferTime;
}
/**
* Gets an array of handler stat data.
*
* @return array
*/
public function getHandlerStats()
{
return $this->handlerStats;
}
/**
* Get a specific handler stat from the handler by name.
*
* @param string $stat Handler specific transfer stat to retrieve.
*
* @return mixed|null
*/
public function getHandlerStat($stat)
{
return isset($this->handlerStats[$stat])
? $this->handlerStats[$stat]
: null;
}
}

View File

@ -5,6 +5,7 @@ use GuzzleHttp\Handler\MockHandler;
use GuzzleHttp\Promise\PromiseInterface;
use GuzzleHttp\Psr7\Request;
use GuzzleHttp\Psr7\Response;
use GuzzleHttp\TransferStats;
/**
* @covers \GuzzleHttp\Handler\MockHandler
@ -43,7 +44,6 @@ class MockHandlerTest extends \PHPUnit_Framework_TestCase
$mock = new MockHandler([$e]);
$request = new Request('GET', 'http://example.com');
$p = $mock($request, []);
$this->assertEquals(PromiseInterface::REJECTED, $p->getState());
try {
$p->wait();
$this->fail();
@ -113,4 +113,35 @@ class MockHandlerTest extends \PHPUnit_Framework_TestCase
$request = new Request('GET', 'http://example.com');
$mock($request, ['http_errors' => true])->wait();
}
public function testInvokesOnStatsFunctionForResponse()
{
$res = new Response();
$mock = new MockHandler([$res]);
$request = new Request('GET', 'http://example.com');
$stats = null;
$onStats = function (TransferStats $s) use (&$stats) {
$stats = $s;
};
$p = $mock($request, ['on_stats' => $onStats]);
$p->wait();
$this->assertSame($res, $stats->getResponse());
$this->assertSame($request, $stats->getRequest());
}
public function testInvokesOnStatsFunctionForError()
{
$e = new \Exception('a');
$c = null;
$mock = new MockHandler([$e], null, function ($v) use (&$c) { $c = $v; });
$request = new Request('GET', 'http://example.com');
$stats = null;
$onStats = function (TransferStats $s) use (&$stats) {
$stats = $s;
};
$mock($request, ['on_stats' => $onStats])->wait(false);
$this->assertSame($e, $stats->getHandlerErrorData());
$this->assertSame(null, $stats->getResponse());
$this->assertSame($request, $stats->getRequest());
}
}

View File

@ -145,7 +145,7 @@ class MiddlewareTest extends \PHPUnit_Framework_TestCase
}));
$comp = $stack->resolve();
$p = $comp(new Request('PUT', 'http://www.google.com'), []);
$this->assertInstanceOf('GuzzleHttp\Promise\FulfilledPromise', $p);
$this->assertInstanceOf('GuzzleHttp\Promise\PromiseInterface', $p);
}
public function testMapsResponse()

View File

@ -25,7 +25,7 @@ class PrepareBodyMiddlewareTest extends \PHPUnit_Framework_TestCase
$stack->push($m);
$comp = $stack->resolve();
$p = $comp(new Request('PUT', 'http://www.google.com', [], '123'), []);
$this->assertInstanceOf('GuzzleHttp\Promise\FulfilledPromise', $p);
$this->assertInstanceOf('GuzzleHttp\Promise\PromiseInterface', $p);
$response = $p->wait();
$this->assertEquals(200, $response->getStatusCode());
}
@ -47,7 +47,7 @@ class PrepareBodyMiddlewareTest extends \PHPUnit_Framework_TestCase
$stack->push($m);
$comp = $stack->resolve();
$p = $comp(new Request('PUT', 'http://www.google.com', [], $body), []);
$this->assertInstanceOf('GuzzleHttp\Promise\FulfilledPromise', $p);
$this->assertInstanceOf('GuzzleHttp\Promise\PromiseInterface', $p);
$response = $p->wait();
$this->assertEquals(200, $response->getStatusCode());
}
@ -67,7 +67,7 @@ class PrepareBodyMiddlewareTest extends \PHPUnit_Framework_TestCase
$stack->push($m);
$comp = $stack->resolve();
$p = $comp(new Request('PUT', 'http://www.google.com', [], $bd), []);
$this->assertInstanceOf('GuzzleHttp\Promise\FulfilledPromise', $p);
$this->assertInstanceOf('GuzzleHttp\Promise\PromiseInterface', $p);
$response = $p->wait();
$this->assertEquals(200, $response->getStatusCode());
}
@ -103,7 +103,7 @@ class PrepareBodyMiddlewareTest extends \PHPUnit_Framework_TestCase
$p = $comp(new Request('PUT', 'http://www.google.com', [], $bd), [
'expect' => $value
]);
$this->assertInstanceOf('GuzzleHttp\Promise\FulfilledPromise', $p);
$this->assertInstanceOf('GuzzleHttp\Promise\PromiseInterface', $p);
$response = $p->wait();
$this->assertEquals(200, $response->getStatusCode());
}
@ -126,7 +126,7 @@ class PrepareBodyMiddlewareTest extends \PHPUnit_Framework_TestCase
new Request('PUT', 'http://www.google.com', ['Expect' => 'Foo'], $bd),
['expect' => true]
);
$this->assertInstanceOf('GuzzleHttp\Promise\FulfilledPromise', $p);
$this->assertInstanceOf('GuzzleHttp\Promise\PromiseInterface', $p);
$response = $p->wait();
$this->assertEquals(200, $response->getStatusCode());
}