Skip to content

Fix infinite loop in uploadChunked() when the transport never reads past the end of the stream - #160

Merged
freekmurze merged 1 commit into
spatie:mainfrom
geisi:fix-upload-chunked-eof-loop
Jul 26, 2026
Merged

Fix infinite loop in uploadChunked() when the transport never reads past the end of the stream#160
freekmurze merged 1 commit into
spatie:mainfrom
geisi:fix-upload-chunked-eof-loop

Conversation

@geisi

@geisi geisi commented Jul 25, 2026

Copy link
Copy Markdown
Contributor

Fixes #159.

uploadChunked() looped on $stream->eof(), but PHP only sets a stream's EOF flag once a read attempts to move past its end. Guzzle >= 7.15 ("Bound cURL upload reads to the declared Content-Length") and libcurl >= 8.7 clamp request body reads to the declared size and never read past the end, so eof() stays false after the stream is fully consumed and the loop appends empty chunks forever.

This PR terminates the loop on the read position (tell() < getSize()) when the stream size is known, and keeps the eof() fallback for size-less streams (pipes via PumpStream), which still terminate correctly because their final chunk read does attempt to move past the end.

The regression test consumes each request body exactly to its declared size — the way Guzzle >= 7.15 / libcurl >= 8.7 behave — via a handler-stack middleware over a MockHandler. Against the previous implementation it fails with an exhausted mock queue (the empty appends keep coming); with this change the upload finishes with the expected chunk sequence.

Full analysis and an A/B reproduction (same host, only Guzzle version changed) are in #159.

@freekmurze
freekmurze merged commit 2d4c4a8 into spatie:main Jul 26, 2026
10 checks passed
@freekmurze

Copy link
Copy Markdown
Member

Thanks for the excellent debugging work here, Tim. The write-up in #159 made this trivial to verify, and the middleware-based regression test that consumes exactly the declared Content-Length is a really clean way to reproduce the Guzzle >= 7.15 behaviour.

Released as 1.25.0.

@geisi
geisi deleted the fix-upload-chunked-eof-loop branch July 26, 2026 11:48
@geisi

geisi commented Jul 26, 2026

Copy link
Copy Markdown
Contributor Author

Thank you Freek :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

uploadChunked() loops forever with Guzzle >= 7.15 (and libcurl >= 8.7): eof() is never reached

2 participants