httpd: retransmit a continued chunk from where it was sent

A file larger than the output buffer is sent in two phases: the first
buffer goes out in MSS-sized pieces, with o_idx walking through it, and
what is left is then streamed from flash a chunk at a time into outbuf
from offset 0.

The continuation branch never reset o_idx, so it kept the offset the first
phase had left it at, while uip_rexmit() retransmits from outbuf + o_idx.
A lost continuation segment was therefore retransmitted as the bytes 1460
further into the buffer, which the client accepts as the missing data, and
with o_idx + slen past the 2500-byte buffer it also read whatever xdata
follows outbuf.

Reproduced on a SWTGW218AS by dropping 15% of the switch's TCP segments:
seven of eight fetches of a 21 kB static came back the right length and
the wrong content, each first differing at a fixed phase of the 1460-byte
chunk. With o_idx reset the retransmission repeats the chunk it sent.

The o_idx += slen right after slen = 0 went too: it added nothing.

(cherry picked from commit 789d29423288f89411a41673e26ce1854b296c3d)
This commit is contained in:
d00f committed 2026-09-08 16:22:07 +02:00
1 parent 6488433584
commit 8d7faf5312
1 file changed
+2 -2
+2 -2
View File
@@ -865,7 +865,6 @@ void httpd_appcall(void)
o_idx += uip_mss(); o_idx += uip_mss();
} else { } else {
slen = 0; slen = 0;
o_idx += slen;
} }
s->tstate = TSTATE_ACKED; s->tstate = TSTATE_ACKED;
@@ -886,7 +885,8 @@ void httpd_appcall(void)
flash_region.addr = cont_addr; flash_region.addr = cont_addr;
flash_region.len = slen; flash_region.len = slen;
flash_read_bulk(outbuf); flash_read_bulk(outbuf);
uip_send(outbuf, slen); o_idx = 0;
uip_send(outbuf + o_idx, slen);
cont_len -= slen; cont_len -= slen;
cont_addr += slen; cont_addr += slen;
s->tstate = TSTATE_TX; s->tstate = TSTATE_TX;