Как отключить кодировку "Передача-кодирование: закодированная" в лаке?
Используя 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, и теперь это правильно работает и для первого запроса.