过去几天我一直在为这个问题苦苦挣扎:
基本上,我想发送给客户浏览器一个cookie的formsfoo[sha1oftheurl]=[randomvalue]当且仅当cookie尚未设置。
例如,如果客户端浏览器请求“/page.html”,则HTTP响应将如下所示: resp.http.Set-Cookie = "foo4c9ae249e9e061dd6e30893e03dc10a58cc40ee6=ABCD;"
那么,如果相同的客户端请求“/index.html”,HTTP响应将包含一个标头: resp.http.Set-Cookie = "foo14fe4559026d4c5b5eb530ee70300c52d99e70d7=QWERTY;"
最后,客户端浏览器将有2个cookie: foo4c9ae249e9e061dd6e30893e03dc10a58cc40ee6=ABCD foo14fe4559026d4c5b5eb530ee70300c52d99e70d7=QWERTY
现在,这本身并不复杂。 下面的代码是这样的:
import digest; import random; ##This vmod does not exist, it's just for the example. sub vcl_recv() { ## We compute the sha1 of the requested URL and store it in req.http.Url-Sha1 set req.http.Url-Sha1 = digest.hash_sha1(req.url); set req.http.random-value = random.get_rand(); } sub vcl_deliver() { ## We create a cookie on the client browser by creating a "Set-Cookie" header ## In our case the cookie we create is of the form foo[sha1]=[randomvalue] ## eg for a URL "/page.html" the cookie will be foo4c9ae249e9e061dd6e30893e03dc10a58cc40ee6=[randomvalue] set resp.http.Set-Cookie = {""} + resp.http.Set-Cookie + "foo"+req.http.Url-Sha1+"="+req.http.random-value; }
但是,这个代码没有考虑到Cookie已经存在的情况。 在生成一个随机值之前,我需要检查Cookie是否存在。 所以我想到了这个代码:
import digest; import random; sub vcl_recv() { ## We compute the sha1 of the requested URL and store it in req.http.Url-Sha1 set req.http.Url-Sha1 = digest.hash_sha1(req.url); set req.http.random-value = random.get_rand(); set req.http.regex = "abtest"+req.http.Url-Sha1; if(!req.http.Cookie ~ req.http.regex) { set req.http.random-value = random.get_rand(); } }
问题是Varnish在运行时不计算正则expression式。 导致这个错误,当我尝试编译:
Message from VCC-compiler: Expected CSTR got 'req.http.regex' (program line 940), at ('input' Line 42 Pos 31) if(req.http.Cookie !~ req.http.regex) { ------------------------------##############--- Running VCC-compiler failed, exit 1 VCL compilation failed
有人可能会build议通过匹配cookie的“abtest”部分甚至“abtest [a-fA-F0-9] {40}”来解决我的问题:
if(!req.http.Cookie ~ "abtest[a-fA-F0-9]{40}") { set req.http.random-value = random.get_rand(); }
但是这个代码匹配以'abtest'开始并且包含40个字符的hexstring的任何cookie。 这意味着,如果客户端首先请求“/page.html”,然后是“/index.html”,则即使未设置“/index.html”的cookie,条件也将评估为真。
我发现在bug报告phk或其他人声称计算正则expression式是非常昂贵的,这就是为什么他们在编译过程中进行评估。 考虑到这一点,我相信没有办法以我想要的方式达到我想要的。
有没有办法解决这个问题,除了写一个vmod?
谢谢你的帮助!
-Hugues
有一个窍门!
改变条件
if(!req.http.Cookie ~ req.http.regex)
至:
if(!req.http.Cookie ~ {"" + req.http.regex + ""})
这将其切换到CSTR。
这是我从Varnish邮件列表中得到的答案:
TL; DR; 没有vmod或内联C是不可能的
你所说的一切都是真的。 没有内联的C或者一个vmod,我知道完成这个的唯一方法就是dynamic地生成你的configuration文件,这个configuration文件依赖于一个url的sha1。
例如,你可以使用类似于以下的东西:
#!/usr/bin/perl -w use strict; use Digest::SHA qw(sha1_hex); my @files = qw!/index.html /homepage.html!; my $output; my $seen = 0; foreach(@files){ if($seen++){ $output .= "else"; } $output .= "if(req.url == \"$_\"){\n"; $output .= " set resp.http.Set-Cookie = \"foo" . sha1_hex($_) . "=\" + random.get_rand();\n"; $output .= "}\n" } print $output;
根据自己的喜好进行修改,结果如下:
if(req.url == "/index.html"){ set resp.http.Set-Cookie = "foo14fe4559026d4c5b5eb530ee70300c52d99e70d7=" + random.get_rand(); } elseif(req.url == "/homepage.html"){ set resp.http.Set-Cookie = "foo6593125bb8fade312b1081d4ee1998f316aa4081=" + random.get_rand(); }
这样做的好处是,除了随机数之外的所有内容都是提前计算的,但是在这种情况下必须使用另一个脚本来维护可访问文件的列表来生成它们的散列。
老实说,不知道这是否会有所帮助,但也许它会给你其他的想法来探索。
〜保罗