Как отключить кодировку «Transfer-Encoding: chunked» в Varnish?

Используя 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

вывод лаклога: https://gist.github.com/onethumb/e66a2bc4727a3a5340b6

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


person Don MacAskill    schedule 13.05.2014    source источник
comment
тоже проблема, дождитесь обновления не минуя стримера.   -  person Moshe L    schedule 05.11.2014


Ответы (3)


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

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

Пример:

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);
        }
}
person Nils Goroll    schedule 14.05.2014
comment
Удалил мой ответ, твой лучше - person Brandon Wamboldt; 14.05.2014
comment
Я уже пробовал set beresp.do_stream=false с тем же результатом. :( - person Don MacAskill; 14.05.2014
comment
Я должен уточнить, теперь, когда я провел больше тестов. beresp.do_stream=false повышает вероятность успеха, но ошибки все же возникают. У меня частота возникновения этой проблемы составляет не менее 10 %, даже если для параметра do_stream установлено значение false. - person Don MacAskill; 15.05.2014
comment
@BrandonWamboldt Ваш ответ великолепен, поскольку в нем деликатно указан относительный код, который может быть полезен для новичков (у них может не хватить репутации, необходимой для просмотра ответа). Спасибо и +1 Здесь в любом случае =) - person okm; 20.05.2014
comment
@DonMacAskill Просто любопытно, попробуйте включить ESI в соответствии с моим ответом и посмотрите, сработает ли это. У меня 100% успеха. - person Brandon Wamboldt; 20.05.2014
comment
Я думаю, что beresp.do_stream влияет только на запросы, пока объект не будет кэширован. Будущие ответы всегда разбиваются на части, если только исходный сервер не включил заголовок Content-Length (т. е. не разбил ответ на части). - person Ted Percival; 18.11.2015

Так что решение совсем не интуитивное, но надо включить 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 отправляет фрагментированную кодировку передачи, если установлен флаг RES_CHUNKED.

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

person Brandon Wamboldt    schedule 14.05.2014

Обновился с лака 4.0 до 5.2 и теперь корректно работает и по 1-му запросу.

person Piotr Jankowski    schedule 06.10.2017