Skip to content

Commit 3946aa0

Browse files
wundiiawu
andauthored
fix: HttpClient Error handling (#24)
Co-authored-by: awu <[email protected]>
1 parent e7c7997 commit 3946aa0

File tree

2 files changed

+65
-0
lines changed

2 files changed

+65
-0
lines changed

src/Stream/CurlMultiHandler.php

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,9 @@ public function execute(): void
8181
if ($isRunning) {
8282
curl_multi_select($multiHandle);
8383
}
84+
85+
$this->verifyCurlHandle($multiHandle);
86+
8487
} while ($this->header->isEmpty() && $isRunning && $status === CURLM_OK);
8588

8689
$this->multiHandle = $multiHandle;
@@ -111,6 +114,8 @@ public function contentIterator(): iterable
111114
curl_multi_select($this->multiHandle);
112115
}
113116

117+
$this->verifyCurlHandle($this->multiHandle);
118+
114119
while (!$this->write->isEmpty()) {
115120
yield $this->write->read();
116121
}
@@ -132,4 +137,21 @@ public function contentIterator(): iterable
132137
$this->header = null;
133138
$this->write = null;
134139
}
140+
141+
private function verifyCurlHandle(CurlMultiHandle $curlMultiHandle): void
142+
{
143+
$info = curl_multi_info_read($curlMultiHandle);
144+
if ($info === false) {
145+
return;
146+
}
147+
148+
$handle = $info['handle'] ?? null;
149+
if (!$handle instanceof CurlHandle) {
150+
throw new RuntimeException('Internal HttpClient: cURL handle info read returned an invalid handle.');
151+
}
152+
153+
if (curl_errno($handle) !== 0) {
154+
throw new RuntimeException('Internal HttpClient: cURL handle execution failed with error: ' . curl_error($handle));
155+
}
156+
}
135157
}

tests/Stream/CurlMultiHandlerTest.php

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,12 @@
1010
use Thenativeweb\Eventsourcingdb\Stream\CurlMultiHandler;
1111
use Thenativeweb\Eventsourcingdb\Stream\Queue;
1212
use Thenativeweb\Eventsourcingdb\Stream\Request;
13+
use Thenativeweb\Eventsourcingdb\Tests\ClientTestTrait;
1314

1415
final class CurlMultiHandlerTest extends TestCase
1516
{
17+
use ClientTestTrait;
18+
1619
public function getPropertyValue(object $object, string $propertyName): mixed
1720
{
1821
$reflectionClass = new ReflectionClass($object);
@@ -21,6 +24,11 @@ public function getPropertyValue(object $object, string $propertyName): mixed
2124
return $reflectionProperty->getValue($object);
2225
}
2326

27+
public function removeLineBrakes(string $line): string
28+
{
29+
return preg_replace('/\r\n|\r|\n/', '', $line);
30+
}
31+
2432
public function testAbortInWithPositiveValue(): void
2533
{
2634
$curlMultiHandler = new CurlMultiHandler();
@@ -78,6 +86,41 @@ public function testExecuteThrowsIfHandleMissing(): void
7886
$curlMultiHandler->execute();
7987
}
8088

89+
public function testExecuteThrowsIfHostNotExists(): void
90+
{
91+
$this->expectException(RuntimeException::class);
92+
$this->expectExceptionMessageMatches("#Internal HttpClient: cURL handle execution failed with error: Failed to connect to [^ ]+ port 1234 after \d+ ms: Couldn't connect to server#");
93+
94+
$host = $this->container->getHost();
95+
$baseUrl = "http://{$host}:1234";
96+
97+
$request = new Request(
98+
'GET',
99+
$baseUrl,
100+
);
101+
$curlMultiHandler = new CurlMultiHandler();
102+
$curlMultiHandler->addHandle($request);
103+
$curlMultiHandler->execute();
104+
}
105+
106+
public function testExecuteSendsRequestAndParsesHttpHeadersCorrectly(): void
107+
{
108+
$request = new Request(
109+
'GET',
110+
$this->container->getBaseUrl() . '/api/v1/ping',
111+
);
112+
$curlMultiHandler = new CurlMultiHandler();
113+
$curlMultiHandler->addHandle($request);
114+
$curlMultiHandler->execute();
115+
116+
$headerQueue = $curlMultiHandler->getHeaderQueue();
117+
118+
$this->assertGreaterThanOrEqual(8, $headerQueue->getIterator()->count());
119+
$this->assertSame('HTTP/1.1 200 OK', $this->removeLineBrakes($headerQueue->read()));
120+
$this->assertSame('Cache-Control: no-store, no-cache, must-revalidate, proxy-revalidate', $this->removeLineBrakes($headerQueue->read()));
121+
$this->assertSame('Content-Type: application/json', $this->removeLineBrakes($headerQueue->read()));
122+
}
123+
81124
public function testContentIteratorThrowsIfMultiHandleMissing(): void
82125
{
83126
$this->expectException(RuntimeException::class);

0 commit comments

Comments
 (0)