Последние несколько дней я боролся с этой проблемой:
По сути, я хочу отправить клиентскому браузеру файл cookie в форме foo[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]
## 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
Можно предложить решить мою проблему, сопоставив «abtest» часть файла cookie или даже «abtest [a-fA-F0-9] {40}»:
if(!req.http.Cookie ~ "abtest[a-fA-F0-9]{40}")
{
set req.http.random-value = random.get_rand();
}
Но этот код соответствует любому cookie, начинающемуся с abtest и содержащему шестнадцатеричную строку из 40 символов. Это означает, что если клиент сначала запрашивает «/page.html», а затем «/index.html», условие будет оцениваться как истинное, даже если файл cookie для «/index.html» не был установлен.
Я нашел в отчете об ошибке phk или у кого-то еще, что вычисление регулярных выражений было чрезвычайно дорогостоящим, поэтому они оцениваются во время компиляции. Учитывая это, я считаю, что нет способа достичь того, чего я хочу, так, как я пытался.
Есть ли способ решить эту проблему, кроме написания vmod?
Спасибо за вашу помощь!
-Hugues
Есть уловка!
Измените условие
if(!req.http.Cookie ~ req.http.regex)
кому:
if(!req.http.Cookie ~ {"" + req.http.regex + ""})
Это переключает его на CSTR.
Вот ответ, который я получил из списка рассылки Varnish:
TL; DR; Невозможно без vmod или встроенного C.
Все, что вы сказали, правда. Без встроенного C или vmod единственной известной мне альтернативой для этого было бы динамическое создание частей вашей конфигурации, которые зависят от sha1 URL-адреса.
Например, вы можете использовать что-то похожее на следующее:
#!/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();
}
Это имеет то преимущество, что все, кроме случайного числа, вычисляется заранее, но имеет недостаток, заключающийся в необходимости использования в этом случае другого скрипта для поддержки списка доступных файлов для генерации их хэшей.
Честно говоря, не уверен, поможет это или нет, но, возможно, это даст вам другие идеи для изучения.
~ Пол