Skip to content

Commit 050dc63

Browse files
Merge branch '5.0' into 5.1
* 5.0: minor #37121 [Contracts] Add missing "extra.thanks" entries in composer.json (nicolas-grekas) [Process] Fix Permission Denied error when writing sf_proc_00 lock files on Windows fix handling null as empty data No need to create an issue when creating a PR Use ">=" for the "php" requirement [HttpClient] Fix promise behavior in HttplugClient [Console] Fixes question input encoding on Windows
2 parents 4b296e8 + 9eec6ed commit 050dc63

File tree

3 files changed

+124
-3
lines changed

3 files changed

+124
-3
lines changed

HttplugClient.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
namespace Symfony\Component\HttpClient;
1313

1414
use GuzzleHttp\Promise\Promise as GuzzlePromise;
15+
use GuzzleHttp\Promise\RejectedPromise;
1516
use Http\Client\Exception\NetworkException;
1617
use Http\Client\Exception\RequestException;
1718
use Http\Client\HttpAsyncClient;
@@ -22,7 +23,6 @@
2223
use Http\Message\StreamFactory;
2324
use Http\Message\UriFactory;
2425
use Http\Promise\Promise;
25-
use Http\Promise\RejectedPromise;
2626
use Nyholm\Psr7\Factory\Psr17Factory;
2727
use Nyholm\Psr7\Request;
2828
use Nyholm\Psr7\Uri;
@@ -114,7 +114,7 @@ public function sendAsyncRequest(RequestInterface $request): Promise
114114
try {
115115
$response = $this->sendPsr7Request($request, true);
116116
} catch (NetworkException $e) {
117-
return new RejectedPromise($e);
117+
return new HttplugPromise(new RejectedPromise($e));
118118
}
119119

120120
$waitLoop = $this->waitLoop;

Response/HttplugPromise.php

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,12 @@ public function getState(): string
5454
*/
5555
public function wait($unwrap = true)
5656
{
57-
return $this->promise->wait($unwrap);
57+
$result = $this->promise->wait($unwrap);
58+
59+
while ($result instanceof HttplugPromiseInterface || $result instanceof GuzzlePromiseInterface) {
60+
$result = $result->wait($unwrap);
61+
}
62+
63+
return $result;
5864
}
5965
}

Tests/HttplugClientTest.php

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,18 @@
1111

1212
namespace Symfony\Component\HttpClient\Tests;
1313

14+
use GuzzleHttp\Promise\FulfilledPromise as GuzzleFulfilledPromise;
1415
use Http\Client\Exception\NetworkException;
1516
use Http\Client\Exception\RequestException;
17+
use Http\Promise\FulfilledPromise;
1618
use Http\Promise\Promise;
1719
use PHPUnit\Framework\TestCase;
1820
use Psr\Http\Message\ResponseInterface;
21+
use Symfony\Component\HttpClient\Exception\TransportException;
1922
use Symfony\Component\HttpClient\HttplugClient;
23+
use Symfony\Component\HttpClient\MockHttpClient;
2024
use Symfony\Component\HttpClient\NativeHttpClient;
25+
use Symfony\Component\HttpClient\Response\MockResponse;
2126
use Symfony\Contracts\HttpClient\Test\TestHttpServer;
2227

2328
class HttplugClientTest extends TestCase
@@ -152,4 +157,114 @@ public function testRequestException()
152157
$this->expectException(RequestException::class);
153158
$client->sendRequest($client->createRequest('BAD.METHOD', 'http://localhost:8057'));
154159
}
160+
161+
public function testRetry404()
162+
{
163+
$client = new HttplugClient(new NativeHttpClient());
164+
165+
$successCallableCalled = false;
166+
$failureCallableCalled = false;
167+
168+
$promise = $client
169+
->sendAsyncRequest($client->createRequest('GET', 'http://localhost:8057/404'))
170+
->then(
171+
function (ResponseInterface $response) use (&$successCallableCalled, $client) {
172+
$this->assertSame(404, $response->getStatusCode());
173+
$successCallableCalled = true;
174+
175+
return $client->sendAsyncRequest($client->createRequest('GET', 'http://localhost:8057'));
176+
},
177+
function (\Exception $exception) use (&$failureCallableCalled) {
178+
$failureCallableCalled = true;
179+
180+
throw $exception;
181+
}
182+
)
183+
;
184+
185+
$response = $promise->wait(true);
186+
187+
$this->assertTrue($successCallableCalled);
188+
$this->assertFalse($failureCallableCalled);
189+
$this->assertSame(200, $response->getStatusCode());
190+
}
191+
192+
public function testRetryNetworkError()
193+
{
194+
$client = new HttplugClient(new NativeHttpClient());
195+
196+
$successCallableCalled = false;
197+
$failureCallableCalled = false;
198+
199+
$promise = $client
200+
->sendAsyncRequest($client->createRequest('GET', 'http://localhost:8057/chunked-broken'))
201+
->then(function (ResponseInterface $response) use (&$successCallableCalled) {
202+
$successCallableCalled = true;
203+
204+
return $response;
205+
}, function (\Exception $exception) use (&$failureCallableCalled, $client) {
206+
$this->assertSame(NetworkException::class, \get_class($exception));
207+
$this->assertSame(TransportException::class, \get_class($exception->getPrevious()));
208+
$failureCallableCalled = true;
209+
210+
return $client->sendAsyncRequest($client->createRequest('GET', 'http://localhost:8057'));
211+
})
212+
;
213+
214+
$response = $promise->wait(true);
215+
216+
$this->assertFalse($successCallableCalled);
217+
$this->assertTrue($failureCallableCalled);
218+
$this->assertSame(200, $response->getStatusCode());
219+
}
220+
221+
public function testRetryEarlierError()
222+
{
223+
$isFirstRequest = true;
224+
$errorMessage = 'Error occurred before making the actual request.';
225+
226+
$client = new HttplugClient(new MockHttpClient(function () use (&$isFirstRequest, $errorMessage) {
227+
if ($isFirstRequest) {
228+
$isFirstRequest = false;
229+
throw new TransportException($errorMessage);
230+
}
231+
232+
return new MockResponse('OK', ['http_code' => 200]);
233+
}));
234+
235+
$request = $client->createRequest('GET', 'http://test');
236+
237+
$successCallableCalled = false;
238+
$failureCallableCalled = false;
239+
240+
$promise = $client
241+
->sendAsyncRequest($request)
242+
->then(
243+
function (ResponseInterface $response) use (&$successCallableCalled) {
244+
$successCallableCalled = true;
245+
246+
return $response;
247+
},
248+
function (\Exception $exception) use ($errorMessage, &$failureCallableCalled, $client, $request) {
249+
$this->assertSame(NetworkException::class, \get_class($exception));
250+
$this->assertSame($errorMessage, $exception->getMessage());
251+
$failureCallableCalled = true;
252+
253+
// Ensure arbitrary levels of promises work.
254+
return (new FulfilledPromise(null))->then(function () use ($client, $request) {
255+
return (new GuzzleFulfilledPromise(null))->then(function () use ($client, $request) {
256+
return $client->sendAsyncRequest($request);
257+
});
258+
});
259+
}
260+
)
261+
;
262+
263+
$response = $promise->wait(true);
264+
265+
$this->assertFalse($successCallableCalled);
266+
$this->assertTrue($failureCallableCalled);
267+
$this->assertSame(200, $response->getStatusCode());
268+
$this->assertSame('OK', (string) $response->getBody());
269+
}
155270
}

0 commit comments

Comments
 (0)