]> mop.ddnsfree.com - git repositories - brisk.git/commitdiff
fix the content codings announced on the stream
authorMatteo Nastasi <nastasi@alternativeoutput.it>
Sun, 13 Sep 2026 11:31:15 +0000 (13:31 +0200)
committerMatteo Nastasi <nastasi@alternativeoutput.it>
Sun, 13 Sep 2026 11:31:15 +0000 (13:31 +0200)
Three inconsistencies between what the headers promised and what went
out on the socket:

- headers_render() announced "Content-Encoding: chunked" on every
  streaming response the client had not asked to compress. chunked is a
  transfer coding, not a content coding. Apache rewrote the response and
  it went unnoticed; nginx forwards the header as it stands, and a
  browser that meets a content coding it does not know refuses the whole
  body. Transfer-Encoding: chunked, right below, already says it.

- the deflate stream was built without stating the window, and the
  filter then emits raw deflate, while Content-Encoding: deflate
  promises the zlib wrapper of RFC 1950. Whoever asked for deflate got
  bytes that could not be inflated as announced.

- get_encoding() compared the tokens of Accept-Encoding as they came out
  of explode(), with their leading space and their quality attached.
  "gzip, deflate, br" only ever matched its first token, and a coding
  refused with q=0 was taken as accepted.

Checked on the daemon socket, reading the raw stream and inflating it:
gzip and deflate both come out whole across several chunks, every chunk
self contained, the blocks paired and nothing duplicated; with no
Accept-Encoding no Content-Encoding is sent at all.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014M1jiEq9cHdE5SE5j6vFuE

web/Obj/sac-a-push.phh
web/Obj/zlibstream.phh

index 06b3c0bdc915b44cd233cf52850e1736449ab5a1..393490b3fda811e2569522ac3756b136699731fa 100644 (file)
@@ -342,9 +342,13 @@ function headers_render($header, $len)
             if (!isset($hset['expires'])) {
                 $s .= "Expires: Mon, 26 Jul 1997 05:00:00 GMT\r\n";
             }
-            if (!isset($hset['content-encoding'])) {
-                $s .= "Content-Encoding: chunked\r\n";
-            }
+            /* No Content-Encoding here: "chunked" is not a content coding
+               but a transfer coding, and it used to be emitted whenever the
+               client asked for no compression. Apache rewrote the response and
+               it went unnoticed; nginx forwards it as it stands, and a browser
+               that meets a content coding it does not know refuses the whole
+               body (ERR_CONTENT_DECODING_FAILED). The line below states the
+               transfer coding, which is the one that is really in use. */
             $s .= "Transfer-Encoding: chunked\r\n";
         }
         $s .= $cookies;
@@ -374,7 +378,24 @@ function get_encoding($header)
 {
     $enc = "plain";
     if (isset($header['Accept-Encoding'])) {
-        $acc = explode(',', $header['Accept-Encoding']);
+        /* "gzip, deflate, br" splits into tokens that carry a leading space
+           and may carry a quality: comparing them as they come made every
+           coding but the first one unreachable, and let through a coding the
+           client had refused with q=0. */
+        $acc = array();
+        foreach (explode(',', $header['Accept-Encoding']) as $tok) {
+            $par = explode(';', $tok);
+            $name = strtolower(trim($par[0]));
+            $qual = 1.0;
+            for ($i = 1 ; $i < count($par) ; $i++) {
+                if (preg_match('/^\s*q\s*=\s*([0-9.]+)\s*$/', $par[$i], $m)) {
+                    $qual = floatval($m[1]);
+                }
+            }
+            if ($qual > 0.0) {
+                $acc[] = $name;
+            }
+        }
 
         if (array_search('gzip', $acc) !== FALSE) {
             $enc = 'gzip';
index cd5b5834dfff5ac96cfa8cdcf37893ed0f4e411d..2fc927f28489c304205aeb8fe3b4c4ba4b925d02 100644 (file)
@@ -37,7 +37,14 @@ class ZLibStream {
             $thiz->head = "\037\213\010\000\000\000\000\000\000\003";
         }
         else if ($type  == 'deflate') {
-            if (($thiz->filter = stream_filter_append($thiz->s[1], "zlib.deflate", STREAM_FILTER_READ)) == FALSE) {
+            /* The window has to be stated: left to itself the filter emits a
+               raw deflate stream (no 78 9c header), while Content-Encoding:
+               deflate promises the zlib wrapper of RFC 1950. A positive window
+               asks for that wrapper; gzip above asks for the raw stream with a
+               negative one, and prepends its own header by hand. */
+            $params = array('level' => 6, 'window' => 15, 'memory' => 9);
+
+            if (($thiz->filter = stream_filter_append($thiz->s[1], "zlib.deflate", STREAM_FILTER_READ, $params)) == FALSE) {
                 return (FALSE);
             }
         }