Several framing errors in Transfer-Encoding: chunked responses were
reported as a normal end of body or decoded wrong, so a caller could
not tell a cut-off download from a complete one unless it knew the
size beforehand, which chunked responses usually do not provide.
- EOF between chunks looked like the last chunk: fetch_getln() returned an empty line, the parse loop did not run, chunksize stayed 0 and http_new_chunk() reported the terminating zero-size chunk. Require at least one hex digit in the size.
- The chunk size was truncated to int on return: a size that is a multiple of 2^32 came back as 0 (end of body) and 0xffffffff as -1 (error). Return only whether data follows; the caller reads the size from io->chunksize.
- Parsing the size could overflow: the shift had no bound, so a long hex string wrapped and threw off the framing for the rest of the response. Reject the size once another digit would exceed SIZE_MAX.
- EOF inside a chunk looked like the end of the body: http_fillbuf() only checked for fetch_bufread() == -1, so a peer closing mid-chunk left an empty buffer and the reader saw a clean EOF. Treat 0 as EPROTO.
- A bad chunk terminator was never recorded: a missing or wrong CRLF after the chunk data returned -1 without setting io->error, so the read failed with errno 0 and later reads did not see the failure. Set io->error to EPROTO, or to the read's errno.
- A read error while waiting for a chunk header was always reported as EPROTO, so a stall between chunks did not show up as a timeout. Keep the read's errno, and use EPROTO only for a malformed header.
- fetch_getln() could not be called again after a failed read: it dropped the part of the line it already had, so a size line split across two reads was parsed as a shorter one, and when nothing had been read yet it left conn->line NULL for the next call to write through. This matters now that fetch(1), which clears fetchRestartCalls, reads on after an EINTR from a chunk header instead of failing. Keep the partial line and point line at the buffer before reading.
- A line the peer cut off before its newline was returned as complete when the connection closed, so a chunk size line cut after a leading "0" ended the body, and a truncated status, header or FTP reply line was parsed as whatever was left. Fail with EPROTO; an empty remainder still ends the input as before.
Every malformed or truncated chunked body now ends as a read error
(ferror() with EPROTO or the socket errno) instead of a successful
EOF, so fetch(1)'s restart logic can retry it and other consumers
report the failure instead of accepting a partial file.
MFC after: 1 week
Sponsored by: Rubicon Communications, LLC ("Netgate")