Fail a content provider that makes no progress

write_content_with_progress() advances its offset only by what the provider
writes, so a provider that reported success without writing anything and
without calling done() was handed the same offset and length again on the next
pass. With the peer still connected it spun there, re-entering the provider as
fast as the loop could run.

make_file_body()'s provider was one way to reach this and was fixed in #2566,
but any user-supplied provider can do the same. Treat a pass that makes no
progress as a short body, which is how done() called early is already handled.
This commit is contained in:
yhirose committed 2026-08-26 23:05:19 -04:00
1 parent f2b338ae2e
commit c58061ea81
2 files changed
+48

No files matched your search

+11
View File
@@ -8401,6 +8401,8 @@ inline bool write_content_with_progress(Stream &strm,
data_sink.done = [&]() { finished = true; };
while (offset < end_offset && !finished && !is_shutting_down()) {
auto last_offset = offset;
if (!strm.wait_writable() || !strm.is_peer_alive()) {
error = Error::Write;
return false;
@@ -8411,6 +8413,15 @@ inline bool write_content_with_progress(Stream &strm,
error = Error::Write;
return false;
}
// A provider that reports success without writing anything and without
// reporting itself done gets handed the same offset and length again on
// the next pass, so it would spin here for as long as the peer stays
// connected. Treat making no progress as a short body, like done() early.
if (!finished && offset == last_offset) {
error = Error::Write;
return false;
}
}
if (offset < end_offset) { // done() called early, or is_shutting_down()