Skip to content

Commit 670aed4

Browse files
[HttpClient] resolve promise chains on HttplugClient::wait()
1 parent 6e7f325 commit 670aed4

File tree

3 files changed

+45
-28
lines changed

3 files changed

+45
-28
lines changed

src/Symfony/Component/HttpClient/HttplugClient.php

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ public function __construct(HttpClientInterface $client = null, ResponseFactoryI
6868
$this->client = $client ?? HttpClient::create();
6969
$this->responseFactory = $responseFactory;
7070
$this->streamFactory = $streamFactory ?? ($responseFactory instanceof StreamFactoryInterface ? $responseFactory : null);
71-
$this->promisePool = new \SplObjectStorage();
71+
$this->promisePool = \function_exists('GuzzleHttp\Promise\queue') ? new \SplObjectStorage() : null;
7272

7373
if (null !== $this->responseFactory && null !== $this->streamFactory) {
7474
return;
@@ -102,7 +102,7 @@ public function sendRequest(RequestInterface $request): Psr7ResponseInterface
102102
*/
103103
public function sendAsyncRequest(RequestInterface $request): Promise
104104
{
105-
if (!class_exists(GuzzlePromise::class)) {
105+
if (!$this->promisePool) {
106106
throw new \LogicException(sprintf('You cannot use "%s()" as the "guzzlehttp/promises" package is not installed. Try running "composer require guzzlehttp/promises".', __METHOD__));
107107
}
108108

@@ -112,19 +112,17 @@ public function sendAsyncRequest(RequestInterface $request): Promise
112112
return new RejectedPromise($e);
113113
}
114114

115-
$cancel = function () use ($response) {
116-
$response->cancel();
117-
unset($this->promisePool[$response]);
118-
};
119-
120115
$promise = new GuzzlePromise(function () use ($response) {
121116
$this->pendingResponse = $response;
122117
$this->wait();
123-
}, $cancel);
118+
}, function () use ($response) {
119+
$response->cancel();
120+
unset($this->promisePool[$response]);
121+
});
124122

125123
$this->promisePool[$response] = [$request, $promise];
126124

127-
return new HttplugPromise($promise, $cancel);
125+
return new HttplugPromise($promise);
128126
}
129127

130128
/**
@@ -136,13 +134,19 @@ public function sendAsyncRequest(RequestInterface $request): Promise
136134
*/
137135
public function wait(float $maxDuration = null, float $idleTimeout = null): int
138136
{
137+
if (!$this->promisePool) {
138+
return 0;
139+
}
140+
139141
$pendingResponse = $this->pendingResponse;
140142
$this->pendingResponse = null;
143+
$guzzleQueue = \GuzzleHttp\Promise\queue();
141144

142-
if (null !== $maxDuration) {
145+
if (0.0 === $remainingDuration = $maxDuration) {
146+
$idleTimeout = 0.0;
147+
} elseif (null !== $maxDuration) {
143148
$startTime = microtime(true);
144149
$idleTimeout = max(0.0, min($maxDuration / 5, $idleTimeout ?? $maxDuration));
145-
$remainingDuration = $maxDuration;
146150
}
147151

148152
do {
@@ -177,8 +181,10 @@ public function wait(float $maxDuration = null, float $idleTimeout = null): int
177181
}
178182
}
179183

184+
$guzzleQueue->run();
185+
180186
if ($pendingResponse === $response) {
181-
return \count($this->promisePool);
187+
return $this->promisePool->count();
182188
}
183189

184190
check_duration:
@@ -188,7 +194,7 @@ public function wait(float $maxDuration = null, float $idleTimeout = null): int
188194
}
189195
}
190196

191-
if (!$count = \count($this->promisePool)) {
197+
if (!$count = $this->promisePool->count()) {
192198
return 0;
193199
}
194200
} while (null !== $maxDuration && 0 < $remainingDuration);

src/Symfony/Component/HttpClient/Response/HttplugPromise.php

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,10 @@
2323
final class HttplugPromise implements HttplugPromiseInterface
2424
{
2525
private $promise;
26-
private $cancel;
2726

28-
public function __construct(GuzzlePromiseInterface $promise, callable $cancel = null)
27+
public function __construct(GuzzlePromiseInterface $promise)
2928
{
3029
$this->promise = $promise;
31-
$this->cancel = $cancel;
3230
}
3331

3432
public function then(callable $onFulfilled = null, callable $onRejected = null): self
@@ -58,16 +56,4 @@ public function wait($unwrap = true)
5856
{
5957
return $this->promise->wait($unwrap);
6058
}
61-
62-
public function __destruct()
63-
{
64-
if ($this->cancel) {
65-
($this->cancel)();
66-
}
67-
}
68-
69-
public function __wakeup()
70-
{
71-
throw new \BadMethodCallException('Cannot unserialize '.__CLASS__);
72-
}
7359
}

src/Symfony/Component/HttpClient/Tests/HttplugClientTest.php

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,31 @@ public function testSendAsyncRequest()
7575
$this->assertSame('HTTP/1.1', $body['SERVER_PROTOCOL']);
7676
}
7777

78+
public function testWait()
79+
{
80+
$client = new HttplugClient(new NativeHttpClient());
81+
82+
$successCallableCalled = false;
83+
$failureCallableCalled = false;
84+
$client->sendAsyncRequest($client->createRequest('GET', 'http://localhost:8057/timeout-body'))
85+
->then(function (ResponseInterface $response) use (&$successCallableCalled) {
86+
$successCallableCalled = true;
87+
88+
return $response;
89+
}, function (\Exception $exception) use (&$failureCallableCalled) {
90+
$failureCallableCalled = true;
91+
92+
throw $exception;
93+
});
94+
95+
$client->wait(0);
96+
$this->assertFalse($successCallableCalled, '$promise->then() should not be called yet.');
97+
98+
$client->wait();
99+
$this->assertTrue($successCallableCalled, '$promise->then() should have been called.');
100+
$this->assertFalse($failureCallableCalled, 'Failure callable should not be called when request is successful.');
101+
}
102+
78103
public function testPostRequest()
79104
{
80105
$client = new HttplugClient(new NativeHttpClient());

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy