![Varnish VCL - 正则表达式评估](https://linux22.com/image/598271/Varnish%20VCL%20-%20%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F%E8%AF%84%E4%BC%B0.png)
我这几天一直在努力解决这个问题:
foo[sha1oftheurl]=[randomvalue]
基本上,当且仅当尚未设置 cookie 时,我才想向客户端浏览器发送形式为 的 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]
## e.g 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 在运行时不计算正则表达式。这导致我在尝试编译时出现此错误:
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
有人可以建议通过匹配 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 个字符的十六进制字符串的 cookie。这意味着,如果客户端首先请求“/page.html”,然后请求“/index.html”,则即使“/index.html”的 cookie 尚未设置,条件也将评估为 true。
我在错误报告中发现 phk 或其他人指出计算正则表达式非常昂贵,这就是为什么在编译期间对它们进行评估的原因。考虑到这一点,我相信没有办法像我一直尝试的那样实现我想要的东西。
除了编写 vmod 之外,还有其他方法可以解决这个问题吗?
感谢您的帮助!
—休格斯
答案1
有一个窍门!
更改条件
if(!req.http.Cookie ~ req.http.regex)
到:
if(!req.http.Cookie ~ {"" + req.http.regex + ""})
这会将其切换到 CSTR。
答案2
以下是我从 Varnish 邮件列表得到的答案:
TL;DR; 没有 vmod 或内联 C 就不可能。
您所说的一切都是真的。如果没有内联 C 或 vmod,我知道的唯一实现这一点的替代方法是动态生成依赖于 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();
}
这样做的好处是除了随机数之外的所有内容都可以提前计算出来,但缺点是在这种情况下必须使用另一个脚本来维护可访问文件列表以生成它们的哈希值。
老实说,我不确定这是否有帮助,但也许它会给你提供其他值得探索的想法。
~保罗