Skip to content
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
49 changes: 48 additions & 1 deletion src/Illuminate/Http/Client/Factory.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace Illuminate\Http\Client;

use Closure;
use GuzzleHttp\Middleware;
use GuzzleHttp\Promise\Create;
use GuzzleHttp\Promise\PromiseInterface;
use GuzzleHttp\Psr7\Response as Psr7Response;
Expand All @@ -28,6 +29,13 @@ class Factory
*/
protected $dispatcher;

/**
* The middleware to apply to every request.
*
* @var array
*/
protected $globalMiddleware = [];

/**
* The stub callables that will handle requests.
*
Expand Down Expand Up @@ -76,6 +84,45 @@ public function __construct(Dispatcher $dispatcher = null)
$this->stubCallbacks = collect();
}

/**
* Add middleware to apply to every request.
*
* @param callable $middleware
* @return $this
*/
public function globalMiddleware($middleware)
{
$this->globalMiddleware[] = $middleware;

return $this;
}

/**
* Add request middleware to apply to every request.
*
* @param callable $middleware
* @return $this
*/
public function globalRequestMiddleware($middleware)
{
$this->globalMiddleware[] = Middleware::mapRequest($middleware);

return $this;
}

/**
* Add response middleware to apply to every request.
*
* @param callable $middleware
* @return $this
*/
public function globalResponseMiddleware($middleware)
{
$this->globalMiddleware[] = Middleware::mapResponse($middleware);

return $this;
}

/**
* Create a new response instance for use during stubbing.
*
Expand Down Expand Up @@ -353,7 +400,7 @@ public function recorded($callback = null)
*/
protected function newPendingRequest()
{
return new PendingRequest($this);
return new PendingRequest($this, $this->globalMiddleware);
}

/**
Expand Down
32 changes: 30 additions & 2 deletions src/Illuminate/Http/Client/PendingRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
use GuzzleHttp\Exception\RequestException;
use GuzzleHttp\Exception\TransferException;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Middleware;
use GuzzleHttp\UriTemplate\UriTemplate;
use Illuminate\Contracts\Support\Arrayable;
use Illuminate\Http\Client\Events\ConnectionFailed;
Expand Down Expand Up @@ -216,12 +217,13 @@ class PendingRequest
* Create a new HTTP Client instance.
*
* @param \Illuminate\Http\Client\Factory|null $factory
* @param array $middleware
* @return void
*/
public function __construct(Factory $factory = null)
public function __construct(Factory $factory = null, $middleware = [])
{
$this->factory = $factory;
$this->middleware = new Collection;
$this->middleware = new Collection($middleware);

$this->asJson();

Expand Down Expand Up @@ -626,6 +628,32 @@ public function withMiddleware(callable $middleware)
return $this;
}

/**
* Add new request middleware the client handler stack.
*
* @param callable $middleware
* @return $this
*/
public function withRequestMiddleware(callable $middleware)
{
$this->middleware->push(Middleware::mapRequest($middleware));

return $this;
}

/**
* Add new response middleware the client handler stack.
*
* @param callable $middleware
* @return $this
*/
public function withResponseMiddleware(callable $middleware)
{
$this->middleware->push(Middleware::mapResponse($middleware));

return $this;
}

/**
* Add a new "before sending" callback to the request.
*
Expand Down
123 changes: 123 additions & 0 deletions tests/Http/HttpClientTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
use Illuminate\Http\Client\ResponseSequence;
use Illuminate\Http\Response as HttpResponse;
use Illuminate\Support\Arr;
use Illuminate\Support\Carbon;
use Illuminate\Support\Collection;
use Illuminate\Support\Fluent;
use Illuminate\Support\Str;
Expand All @@ -28,6 +29,7 @@
use OutOfBoundsException;
use PHPUnit\Framework\AssertionFailedError;
use PHPUnit\Framework\TestCase;
use Psr\Http\Message\ResponseInterface;
use RuntimeException;
use Symfony\Component\VarDumper\VarDumper;

Expand Down Expand Up @@ -2316,4 +2318,125 @@ public function testTheTransferStatsAreCustomizableOnFake(): void

$this->assertTrue($onStatsFunctionCalled);
}

public function testItCanAddGlobalMiddleware()
{
Carbon::setTestNow(now()->startOfDay());
$requests = [];
$responses = [];
$this->factory->fake(function ($r) use (&$requests) {
$requests[] = $r;

Carbon::setTestNow(now()->addSeconds(6 * count($requests)));

return $this->factory::response('expected content');
});

$this->factory->globalMiddleware(Middleware::mapRequest(function ($request) {
// Test manipulating headers on outgoing request...
return $request->withHeader('User-Agent', 'Laravel Framework/1.0')
->withAddedHeader('shared', 'global')
->withHeader('list', ['item-1', 'item-2'])
->withAddedHeader('list', ['item-3']);
}))->globalMiddleware(Middleware::mapResponse(function ($response) use (&$requests) {
// Test adding headers in incoming response..
return $response->withHeader('X-Count', (string) count($requests));
}))->globalMiddleware(function ($handler) {
// Test wrapping request in timing function...
return function ($request, $options) use ($handler) {
$startedAt = now();

return $handler($request, $options)->then(function (ResponseInterface $response) use ($startedAt) {
return $response->withHeader('X-Duration', "{$startedAt->diffInSeconds(now())} seconds");
});
};
});
$responses[] = $this->factory->post('http://forge.laravel.com');
$responses[] = $this->factory->withHeader('shared', 'local')->post('http://vapor.laravel.com');

$this->assertCount(2, $requests);
$this->assertCount(2, $responses);

$this->assertSame(['Laravel Framework/1.0'], $requests[0]->header('User-Agent'));
$this->assertSame(['item-1', 'item-2', 'item-3'], $requests[0]->header('list'));
$this->assertSame(['global'], $requests[0]->header('shared'));
$this->assertSame('1', $responses[0]->header('X-Count'));
$this->assertSame('6 seconds', $responses[0]->header('X-Duration'));

$this->assertSame(['Laravel Framework/1.0'], $requests[1]->header('User-Agent'));
$this->assertSame(['item-1', 'item-2', 'item-3'], $requests[1]->header('list'));
$this->assertSame(['local', 'global'], $requests[1]->header('shared'));
$this->assertSame('2', $responses[1]->header('X-Count'));
$this->assertSame('12 seconds', $responses[1]->header('X-Duration'));
}

public function testItCanAddGlobalRequestMiddleware()
{
$requests = [];
$this->factory->fake(function ($r) use (&$requests) {
$requests[] = $r;

return Factory::response('expected content');
});

$this->factory->globalRequestMiddleware(function ($request) {
return $request->withHeader('User-Agent', 'Laravel Framework/1.0');
});
$this->factory->post('http://forge.laravel.com');
$this->factory->post('http://laravel.com');

$this->assertSame(['Laravel Framework/1.0'], $requests[0]->header('User-Agent'));
$this->assertSame(['Laravel Framework/1.0'], $requests[1]->header('User-Agent'));
}

public function testItCanAddGlobalResponseMiddleware()
{
$responses = [];
$this->factory->fake(function ($r) use (&$request) {
return Factory::response('expected content');
});

$this->factory->globalResponseMiddleware(function ($response) {
return $response->withHeader('X-Foo', 'Bar');
});
$responses[] = $this->factory->post('http://forge.laravel.com');
$responses[] = $this->factory->post('http://laravel.com');

$this->assertSame('Bar', $responses[0]->header('X-Foo'));
$this->assertSame('Bar', $responses[1]->header('X-Foo'));
}

public function testItCanAddRequestMiddleware()
{
$requests = [];
$this->factory->fake(function ($r) use (&$requests) {
$requests[] = $r;

return Factory::response('expected content');
});

$this->factory->withRequestMiddleware(function ($request) {
return $request->withHeader('User-Agent', 'Laravel Framework/1.0');
})->post('http://forge.laravel.com');
$this->factory->post('http://laravel.com');

$this->assertSame(['Laravel Framework/1.0'], $requests[0]->header('User-Agent'));
$this->assertSame(['GuzzleHttp/7'], $requests[1]->header('User-Agent'));
}

public function testItCanAddResponseMiddleware()
{
$responses = [];
$this->factory->fake(function ($r) use (&$request) {
return Factory::response('expected content');
});

$responses[] = $this->factory->withResponseMiddleware(function ($response) {
return $response->withHeader('X-Foo', 'Bar');
})->post('http://forge.laravel.com');
$responses[] = $this->factory->post('http://laravel.com');

$this->assertSame('Bar', $responses[0]->header('X-Foo'));
$this->assertSame('', $responses[1]->header('X-Foo'));
}
}