Skip to content

Commit cab1d5d

Browse files
committed
fix empty strings returned from request body streams being interpreted as eof
1 parent 2cb8277 commit cab1d5d

File tree

4 files changed

+33
-3
lines changed

4 files changed

+33
-3
lines changed

src/Psr18/Client.php

+4-1
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,10 @@ protected function initRequest(RequestInterface $request, ResponseHeaderParser $
8585

8686
$ch->setopt(CURLOPT_UPLOAD, true);
8787
$ch->setopt(CURLOPT_READFUNCTION, function ($ch, $fd, $length) use ($requestBody) {
88-
return $requestBody->read($length);
88+
do {
89+
$chunk = $requestBody->read($length);
90+
} while ($chunk === "" && $requestBody->eof() === false);
91+
return $chunk;
8992
});
9093

9194
$ch->setopt(CURLOPT_WRITEFUNCTION, /** @throws Throwable */ function ($ch, $data) {

tests/HttpClientTest.php

+27
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,33 @@ public function testThrowRequestExceptionIfRequestBodyThrows(): void
144144
$this->client->sendRequest($request);
145145
}
146146

147+
public function testRequestBodyStreamCanReturnEmptyChunks(): void
148+
{
149+
$requestBody = new PredefinedChunkStream([
150+
"something",
151+
"",
152+
"else"
153+
]);
154+
155+
$chunks = [];
156+
$this->curlHandle->setOnAfterRead(function ($chunk) use (&$chunks) {
157+
$chunks[] = $chunk;
158+
});
159+
160+
$request = $this->requestFactory->createRequest("POST", "https://example.com")
161+
->withBody($requestBody);
162+
163+
$this->client->sendRequest($request);
164+
165+
// Last chunk is empty
166+
$this->assertEmpty(array_pop($chunks));
167+
168+
// No empty chunks in between
169+
foreach ($chunks as $chunk) {
170+
$this->assertNotEmpty($chunk);
171+
}
172+
}
173+
147174
#[TestWith([CURLE_COULDNT_CONNECT, "CURLE_COULDNT_CONNECT"], "CURLE_COULDNT_CONNECT")]
148175
#[TestWith([CURLE_COULDNT_RESOLVE_HOST, "CURLE_COULDNT_RESOLVE_HOST"], "CURLE_COULDNT_RESOLVE_HOST")]
149176
#[TestWith([CURLE_COULDNT_RESOLVE_PROXY, "CURLE_COULDNT_RESOLVE_PROXY"], "CURLE_COULDNT_RESOLVE_PROXY")]

tests/Stream/PredefinedChunkStream.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ public function isReadable(): bool
126126
*/
127127
public function read(int $length): string
128128
{
129-
while (strlen($this->buffer) === 0 && count($this->chunks) > 0) {
129+
if (strlen($this->buffer) === 0 && count($this->chunks) > 0) {
130130
$chunk = array_shift($this->chunks);
131131
if ($chunk instanceof Throwable) {
132132
throw $chunk;

tests/Util/TestCurlHandle.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ public function doExec(): string|bool
7171
$chunk = $this->options[CURLOPT_READFUNCTION](null, $this->options[CURLOPT_INFILE] ?? null, 8192);
7272
$this->requestBodySink?->write($chunk);
7373
$uploaded += strlen($chunk);
74-
$this->onAfterRead?->call($this);
74+
$this->onAfterRead?->call($this, $chunk);
7575
$this->progressUpdate(0, 0, $this->getRequestHeader("Content-Length") ?? 0, $uploaded);
7676
} while (strlen($chunk) > 0);
7777
}

0 commit comments

Comments
 (0)