如何在 Varnish 4.0 中传递数据 (header?)

How to pass data (header?) through in Varnish 4.0

我正在使用 devicedetect.vclX-UA-Device header 发送到我的应用程序,因此它知道要呈现哪个布局。 varnish 将为此 header 设置的可能值是 mobiledesktop.

在出去的路上,这个 header 被转换为 Vary: User-Agent

现在,作为一个独立的独立项目,我需要在 resp object 上设置另一个 header(它在发送到我们的 Golang 代理之前发送到客户)。此 header 将被称为 X-Analytics-Device,并且可能的值为 botmobiletabletdesktop

后端服务器不需要X-Analytics-Device做任何事情。只有我们的 Go 代理会在将它发送给客户端之前解析并删除这个 header。

问题是,我需要根据 vcl_recv 中子程序 call devicedetect; 的结果设置 X-Analytics-Device header。我需要最终将它设置在 vcl_deliver 中的 resp 上,我需要知道传递数据的最佳方式。

我能想到的唯一可行的方法(基于我对 Varnish 的有限理解)是我需要设置一些其他的 header,然后再访问它。

可能是这样的(我暂时省略了 bot):

if (req.http.X-UA-Device ~ "^mobile") {
  set req.http.X-UA-Device        = "mobile";
  set req.http.X-Analytics-Device = "mobile";

} elseif (req.http.X-UA-Device ~ "^tablet") {
  set req.http.X-UA-Device        = "desktop";
  set req.http.X-Analytics-Device = "tablet";

} else {
  set req.http.X-UA-Device        = "desktop";
  set req.http.X-Analytics-Device = "desktop";
}

在此之后...我不知道。 vcl_deliver需要这么设置吗?

set resp.http.X-Analytics-Device = req.http.X-Analytics-Device;

它是如何从 resp 传递到 req 的?如果命中或未命中会怎样?那有关系吗?这是否会尝试将此 header 缓存在 varnish 中(显然不应该缓存)?

我这样做的主要恐惧是有太多的移动部分我不知道最好的方法。

最终结果是...每个请求都需要检查设备,并且在输出时需要设置 header,而该值不会与清漆中的数据一起缓存,并且虽然将它发送到后端没有什么坏处,但不需要它。

这是我在上面添加 pseudo-code 行之前的完整 VCL。

vcl 4.0;

backend default {
  .host = "127.0.0.1";
  .port = "8080";
}

import std;

include "purge.vcl";
include "devicedetect.vcl";

acl purge {
  "localhost";
  "127.0.0.1";
  "10.0.0.0"/8;
}

sub vcl_recv {
  call devicedetect;
  if (req.http.X-UA-Device ~ "^mobile") {
    set req.http.X-UA-Device = "mobile";
  } else {
    set req.http.X-UA-Device = "desktop";
  }

  if (req.restarts == 0) {
    if (req.http.X-Forwarded-For) {
      set req.http.X-Forwarded-For = req.http.X-Forwarded-For + ", " + client.ip;
    } else {
      set req.http.X-Forwarded-For = client.ip;
    }
  }

  if (req.method !~ "^(GET|HEAD|PUT|POST|OPTIONS|DELETE)$") {
    return (synth(405));
  }

  # never cache anything except GET/HEAD
  if (req.method != "GET" && req.method != "HEAD") {
    return (pass);
  }

  # don't cache images or assets
  if (req.url ~ "\.(js|css|jpg|jpeg|png|gif|ico|tiff|tif|bmp|svg)$") {
    return (pass);
  }

  # fix up the request
  unset req.http.cookie;
  return (hash);
}

sub vcl_backend_response {
  set beresp.do_stream = false;

  # device detect
  if (bereq.http.X-UA-Device) {
    if (!beresp.http.Vary) { # no Vary at all
      set beresp.http.Vary = "X-UA-Device";
    } elseif (beresp.http.Vary !~ "X-UA-Device") { # add to existing Vary
      set beresp.http.Vary = beresp.http.Vary + ", X-UA-Device";
    }
  }

  # bypass cache for files > 5 MB
  if (std.integer(beresp.http.Content-Length, 0) > 5242880) {
    set beresp.uncacheable = true;
    set beresp.ttl = 120s;
    return (deliver);
  }

  # catch obvious reasons we can't cache
  if (beresp.http.Set-Cookie) {
    set beresp.ttl = 0s;
  }

  # avoid caching error responses (1m grace period)
  if (beresp.status >= 500) {
    set beresp.ttl = 1m;
    return (deliver);
  }

  # set times
  set beresp.ttl = 24h;
  set beresp.grace = 4h;
  return (deliver);
}

sub vcl_deliver {
  # device detect
  if ((req.http.X-UA-Device) && (resp.http.Vary)) {
    set resp.http.Vary = regsub(resp.http.Vary, "X-UA-Device", "User-Agent");
  }

  # remove junk headers
  unset resp.http.Server;
  unset resp.http.Via;
  unset resp.http.X-Powered-By;
  unset resp.http.X-Runtime;
  unset resp.http.X-Varnish;

  if (obj.hits > 0) {
    set resp.http.X-Cache = "HIT";
  } else {
    set resp.http.X-Cache = "MISS";
  }
}

这个 link 实际上完美地澄清并回答了我未能阐明的所有事情...... https://info.varnish-software.com/blog/adding-headers-gain-insight-vcl

答案是将你需要的所有数据位铲到vcl_recv中的reqheaders中,然后将它们复制到vcl_deliver中的响应中.

他陈述了以下内容,说明了为什么它不会被缓存:

Since the req object is not delivered to the client we need to copy the data from the req object to resp. We do this when we deliver it. If you do it in vcl_backend_response the headers will be stored in the cache and this might not be what you want.

@Tallboy 的回答拯救了我。总结一下你想要做的是:

# set the value in vcl_recv
sub vcl_recv {
   set req.http.X-NAVO-AY = "AYAYAYAYAYAYYYYY";
}
# copy the value from req to resp (because it is not done automatically)
sub vcl_deliver {
    set resp.http.X-NAVO-AY = req.http.X-NAVO-AY;
}