Как отключить кодировку "Передача-кодирование: закодированная" в лаке?

Используя Varnish 4, у меня есть набор бэкэндов, которые отвечают действительным заголовком Content-Length и заголовком Transfer-Encoding.

При первом попадании клиента, а не на клиенте с этими заголовками, Varnish отбрасывает заголовок Content-Length и добавляет Transfer-Encoding: chunked к ответу. (Интересно, что полезная нагрузка не содержит каких-либо куски в нем - это один непрерывный полезной нагрузки).

Это создает серьезные проблемы для клиентов, таких как Flash-видеоплееры, которые пытаются выполнить анализ сегмента, ширины полосы и т.д. на основе заголовка Content-Length. Их анализ терпит неудачу, и они не могут делать такие вещи, как потоки с несколькими битрейтами и т.д.

Я пробовал несколько полу-очевидных вещей вроде:

  • beresp.do_stream = true
  • beresp.do_gzip = false
  • unset req.http.Accept-Encoding

Пример ответа на бэкэнд:

HTTP/1.1 200 OK
Cache-Control: public, max-age=600
Content-Type: video/mp4
Date: Tue, 13 May 2014 19:44:35 GMT
Server: Apache
Content-Length: 796618
Connection: keep-alive

Пример ответа на лак:

HTTP/1.1 200 OK
Server: Apache
Cache-Control: public, max-age=600
Content-Type: video/mp4
Date: Tue, 13 May 2014 23:10:06 GMT
X-Varnish: 2
Age: 0
Transfer-Encoding: chunked
Accept-Ranges: bytes

Последующие нагрузки объекта действительно включая Content-Length заголовок, просто не первый груз в кэш.

VCL: https://gist.github.com/onethumb/e64a405cc579909cace1

вывод varnishlog: https://gist.github.com/onethumb/e66a2bc4727a3a5340b6

Varnish Trac: https://www.varnish-cache.org/trac/ticket/1506

Ответ 1

Пока do_stream = false будет делать то, что вы хотите.

Избегание кодирования с чередованием для случая, когда бэкэнд посылает unchunked, является возможным улучшением будущего для лака.

Пример:

sub vcl_backend_response {
        if(beresp.http.Content-Type ~ "video") {
                set beresp.do_stream = false;
                set beresp.do_gzip = false;
                //set resp.http.Content-Length = beresp.http.Content-Length;
        }
        if(beresp.http.Edge-Control == "no-store") {
                set beresp.uncacheable = true;
                set beresp.ttl = 60s;
                set beresp.http.Smug-Cacheable = "No";
                return(deliver);
        }
}

Ответ 2

Таким образом, решение не является интуитивно понятным, но вы должны включить обработку esi:

sub vcl_backend_response {
        set beresp.do_esi = true;

        if(beresp.http.Content-Type ~ "video") {
                set beresp.do_stream = true;
                set beresp.do_gzip = false;
                //set resp.http.Content-Length = beresp.http.Content-Length;
        }
        if(beresp.http.Edge-Control == "no-store") {
                set beresp.uncacheable = true;
                set beresp.ttl = 60s;
                set beresp.http.Smug-Cacheable = "No";
                return(deliver);
        }
}

Итак, я обнаружил это, просмотрев исходный код.

В частности, Varnish делает это:

if (!req->disable_esi && req->obj->esidata != NULL) {
    /* In ESI mode, we can't know the aggregate length */
    req->res_mode &= ~RES_LEN;
    req->res_mode |= RES_ESI;
}

В приведенном выше коде устанавливается флаг res_mode.

Немного позже:

if (!(req->res_mode & (RES_LEN|RES_CHUNKED|RES_EOF))) {
    /* We havn't chosen yet, do so */
    if (!req->wantbody) {
        /* Nothing */
    } else if (req->http->protover >= 11) {
        req->res_mode |= RES_CHUNKED;
    } else {
        req->res_mode |= RES_EOF;
        req->doclose = SC_TX_EOF;
    }
}

Это устанавливает флаг res_mode в RES_CHUNKED, если HTTP-протокол HTTP/1.1 или выше (что в вашем примере) , а флаг res_mode не установлен. Теперь даже позже:

if (req->res_mode & RES_CHUNKED)
    http_SetHeader(req->resp, "Transfer-Encoding: chunked");

Varnish отправляет кодировку передачи chuncked, если установлен флаг RES_CHUNKED.

только, который я вижу, чтобы эффективно отключить это, включив режим ESI. Он отключается несколькими другими способами, но это непрактично (например, для запросов HTTP HEAD или страниц с кодом статуса 304).

Ответ 3

Обновление с лака 4.0 до 5.2, и теперь это правильно работает и для первого запроса.