$wret = @fwrite($new_socket, $response, $response_l);
if ($wret < $response_l) {
printf("TROUBLES WITH FWRITE: %d\n", $wret);
- $user->rd_cache_set(mb_substr($content, $wret, $response_l - $wret, "ASCII"));
+ /* The remainder has to be sliced out of $response, not of $content:
+ $wret is an offset into $response, which carries the http headers
+ and the chunk framing ahead of the content (and, on a compressed
+ stream, the deflated bytes instead of the plain ones). Resuming
+ from $content dropped the header bytes and left the chunk shorter
+ than its declared length and without its terminator, which is what
+ made the javascript parser lose the framing for good. */
+ $user->rd_cache_set(mb_substr($response, $wret, $response_l - $wret, "ASCII"));
}
else {
$user->rd_cache_set("");
function chunked_fini()
{
- return sprintf("0\r\n");
+ /* The last chunk is "0" CRLF plus the CRLF that closes the (empty)
+ trailer section: without the second one the response stays open and
+ whoever parses it downstream waits for a continuation that never
+ comes. See stream_close(), which emits the same sequence. */
+ return sprintf("0\r\n\r\n");
}
for ($to_be_proc = $s_in_l, $max_fail = 0 ; $to_be_proc > 0 && $max_fail < 2 ; $max_fail++) {
if ($to_be_proc > 0) {
$max_fail = 0;
- if (($ct = fwrite($this->s[0], $s_in)) == FALSE)
+ /* Only the part that is still missing gets written: passing
+ $s_in whole again after a short write fed the already
+ compressed bytes to the deflater a second time, and the
+ client inflated a chunk with duplicated content in it. */
+ if (($ct = fwrite($this->s[0], mb_substr($s_in, $s_in_l - $to_be_proc,
+ $to_be_proc, 'ASCII'))) == FALSE)
return FALSE;
$to_be_proc -= $ct;
$wret = @fwrite($new_socket, $response, $response_l);
if ($wret < $response_l) {
printf("TROUBLES WITH FWRITE: %d\n", $wret);
- $user->rd_cache_set(mb_substr($content, $wret, $response_l - $wret, "ASCII"));
+ /* The remainder has to be sliced out of $response, not of $content:
+ $wret is an offset into $response, which carries the http headers
+ and the chunk framing ahead of the content (and, on a compressed
+ stream, the deflated bytes instead of the plain ones). Resuming
+ from $content dropped the header bytes and left the chunk shorter
+ than its declared length and without its terminator, which is what
+ made the javascript parser lose the framing for good. */
+ $user->rd_cache_set(mb_substr($response, $wret, $response_l - $wret, "ASCII"));
}
else {
$user->rd_cache_set("");