0.3.2 --- download_to_file reports the writes the disk refused - #19
Merged
Sunrisepeak merged 3 commits intoSep 29, 2026
Merged
Conversation
download_to_file called ofs.write for each body chunk without looking at the stream, added each chunk's size to bytesWritten from what the network delivered, and set bytesWritten after ofs.close() without looking at that either. On a full disk (ENOSPC) the transfer therefore succeeded: a 420,831,054 byte download left 220,979,200 bytes on disk while bytesWritten said 420,831,054 and ok() was true, and the caller blamed the source (a checksum mismatch) for what was the local disk. Each chunk is now flushed and the stream checked. On the first failure the transfer stops reading, error becomes "write <path>: <reason>" with the reason taken from errno, and the connection is dropped instead of returned to the pool with the rest of the body still on it. Closing the file is checked the same way. Additive: DownloadToFileResult::writeFailed says the fault is the destination and not the source (a failed write, a failed close, or a file that could not be opened), and bytesReceived carries the network count. bytesWritten now counts bytes the file accepted; the two are equal for every transfer that did not fail to write. Tests: a download into /dev/full (Linux) against the in-process TLS server asserts not ok, writeFailed, bytesWritten == 0, the ENOSPC text, that reading stopped after the first refused chunk, and that the next request opens a new connection. Verified by mutation: with the stream checks removed it reports ok() and bytesWritten == 65536.
Version, CHANGELOG entry, and the README install lines name this release.
The index now requires mcpp >= 2026.9.18.3, and MCPP_VERSION pinned 2026.8.29.1,
so every `mcpp` invocation that needed the index ended at
error: index requires mcpp >= 2026.9.18.3 but this is mcpp 2026.8.29.1 [E0006]
That is the failure the "Smoke-test the project templates" step has reported on
master since the scheduled run of 2026-09-21. MCPP_VERSION is the only place the
version is named; both jobs' install steps and both cache keys read it.
Sunrisepeak
added a commit
to mcpplibs/mcpp-index
that referenced
this pull request
Sep 29, 2026
xpkg 0.0.60 (openxlings/libxpkg#45): ExecutionContext::hook_log -- a hook's child processes write to a log file instead of the caller's terminal -- and pkginfo.build_dep reads the variable xlings exports for a namespaced name. tinyhttps 0.3.2 (mcpplibs/tinyhttps#19): download_to_file reports a write the disk refused (writeFailed, bytesWritten counts what reached the file, bytesReceived what the network delivered). The GitHub tag tarballs and the GitCode mirror copies (mcpp-res/xpkg, mcpp-res/tinyhttps) were downloaded in full and are byte-identical: xpkg 348d78e8...ce5a, tinyhttps 6c9af965...fe5b. Added to all three platform blocks of each recipe; checked by loading both files with lua.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
HttpClient::download_to_file(0.2.9 through 0.3.1) calledofs.write(...)for each body chunk without checking the stream, counteddownloaded += nfrom the network, and setresult.bytesWritten = downloadedafterofs.close()without checking that either. On a full disk (ENOSPC) the transfer therefore "succeeded". Measured on a real download: 420,831,054 bytes received, 220,979,200 bytes left on disk,bytesWritten == 420,831,054,ok() == true. The caller (xlings) then blamed the source ("sha256 mismatch") instead of the local disk.Change (0.3.2)
errorbecomeswrite <path>: <reason>(the reason iserrnothroughstd::generic_category, e.g.No space left on device), and the connection is dropped instead of being returned to the pool with the rest of the body still on it.close()is checked the same way, since buffered bytes only reach the file at flush.DownloadToFileResult, both additive and placed after the existing fields:bool writeFailedis true when the fault is the destination and not the source: a failed write, a failed close, or a file that could not be opened (that error string is unchanged:Cannot open file: <path>).std::int64_t bytesReceivedis the number of body bytes the connection delivered.bytesWrittennow counts bytes the file accepted, where it used to count bytes the network delivered. The two are equal for every transfer that did not fail to write. When a write fails, the chunk that failed is not counted, though the file may hold part of it, so the value is a floor on the file's size and never above it. This is the one meaning change.http.cppmgains a global module fragment with<cerrno>;import stddoes not carry theerrnomacro.mcpp.toml, a CHANGELOG entry, and the README install lines.Tests
tests/test_pool.cppuses the existing in-process TLS server:ADownloadIntoAFullDeviceFailsAsALocalWriteAndCountsNothing(Linux only, skipped without/dev/full) downloads 64 KiB into/dev/fulland asserts: notok(),writeFailed,statusCode == 200,bytesWritten == 0,0 < bytesReceived < 64 KiB(reading stopped), the error starts withwrite /dev/full:and carries the ENOSPC text, and a following download to a good path succeeds on a new connection (accepts() == 2), so the dirty connection was not reused.ADestinationThatCannotBeOpenedIsALocalFailurepinswriteFailedfor the open failure.Checked by mutation: with the stream checks removed, the first test fails on
ok()(true) andbytesWritten(65536).mcpp test: all four test binaries pass locally (test_framing,test_pool27 tests,test_downloadandtest_resolver, which reach the network).tools/template_smoke.shpasses.