如何在Varnish中禁用"Transfer-Encoding:chunked"编码?

Don*_*ill 12 http chunked varnish transfer-encoding http-content-length

使用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
Run Code Online (Sandbox Code Playgroud)

样品清漆响应:

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
Run Code Online (Sandbox Code Playgroud)

对象的后续加载包括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

小智 6

暂时,do_stream = false会做你想要的.

避免对后端发送unchunked的情况进行分块编码,这是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);
        }
}
Run Code Online (Sandbox Code Playgroud)


Bra*_*ldt 3

所以解决方案一点也不直观,但是你必须启用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);
        }
}
Run Code Online (Sandbox Code Playgroud)

于是我通过浏览源码发现了这一点。

具体来说,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;
}
Run Code Online (Sandbox Code Playgroud)

上面的代码设置了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;
    }
}
Run Code Online (Sandbox Code Playgroud)

这会将res_mode标志设置为RES_CHUNKEDif HTTP 协议为HTTP/1.1或更高版本(在您的示例中)并且 res_mode 标志未设置。现在甚至更晚:

if (req->res_mode & RES_CHUNKED)
    http_SetHeader(req->resp, "Transfer-Encoding: chunked");
Run Code Online (Sandbox Code Playgroud)

RES_CHUNKED如果设置了该标志,Varnish 会发送分块传输编码。

我认为有效禁用此功能的唯一方法是启用 ESI 模式。它可以通过其他几种方式禁用,但这些方式并不实用(例如,对于 HTTP HEAD 请求或具有 304 状态代码的页面)。