Varnish VCL - Avaliação de Expressões Regulares

3

Eu tenho lutado nos últimos dias com esse problema:

Basicamente, desejo enviar para o navegador do cliente um cookie com o formato foo[sha1oftheurl]=[randomvalue] se e somente se o cookie ainda não tiver sido definido.

por exemplo. Se um navegador do cliente solicitar "/page.html", a resposta HTTP será como: resp.http.Set-Cookie = "foo4c9ae249e9e061dd6e30893e03dc10a58cc40ee6=ABCD;"

então, se o mesmo pedido de cliente "/index.html", a resposta HTTP conterá um cabeçalho: resp.http.Set-Cookie = "foo14fe4559026d4c5b5eb530ee70300c52d99e70d7=QWERTY;"

No final, o navegador do cliente terá dois cookies: %código% foo4c9ae249e9e061dd6e30893e03dc10a58cc40ee6=ABCD

Agora, isso não é complicado por si só. O código a seguir faz isso:

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;
}

No entanto, esse código não leva em consideração o caso em que o cookie já existe. Eu preciso verificar se o Cookie não existe antes de gerar um valor aleatório. Então eu pensei sobre esse código:

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();
    }
}

O problema é que o Varnish não calcula a expressão Regular no tempo de execução. O que leva a esse erro quando tento compilar:

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

Poder-se-ia propor resolver o meu problema fazendo a correspondência na parte "mais" do cookie ou até mesmo "abtest [a-fA-F0-9] {40}":

if(!req.http.Cookie ~ "abtest[a-fA-F0-9]{40}")
{
    set req.http.random-value = random.get_rand();
}

Mas este código corresponde a qualquer cookie iniciado por 'abtest' e contendo uma string hexadecimal de 40 caracteres. O que significa que, se um cliente solicitar "/page.html" primeiro, "/index.html", a condição será avaliada como verdadeira mesmo que o cookie para o "/index.html" não tenha sido definido.

Eu encontrei no bug report phk ou alguém declarando que as expressões regulares de computação eram extremamente caras e é por isso que elas são avaliadas durante a compilação. Considerando isso, acredito que não há como conseguir o que eu quero do jeito que estou tentando.

Existe alguma maneira de resolver este problema, além de escrever um vmod?

Obrigado pela sua ajuda!

-Hugues

    
por liquidity 10.10.2012 / 23:44

2 respostas

1

Aqui está uma resposta que recebi da lista de discussão do Varnish:

TL; DR; Não é possível sem um vmod ou C inline.

Tudo o que você declarou é verdade. Sem inline C ou um vmod a única alternativa que eu conheço para conseguir isso seria para gerar dinamicamente as partes de sua configuração que depende do sha1 de um URL.

Por exemplo, você pode usar algo semelhante ao seguinte:

#!/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;

Modificado a seu gosto, obviamente, o que resulta nas seguintes saída:

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();
}

Isso tem o benefício de que tudo com exceção do aleatório número é calculado antes do tempo, mas tem a desvantagem de ter para usar neste caso, outro script para manter uma lista de arquivos acessíveis para gerar seu hash.

Honestamente, não tenho certeza se isso vai ajudar ou não, mas talvez lhe dê outras ideias para explorar.

~ Paul

    
por 12.10.2012 / 23:07
2

Existe um truque!

Alterar a condição

if(!req.http.Cookie ~ req.http.regex)

para:

if(!req.http.Cookie ~ {"" + req.http.regex + ""})

Isso muda para um CSTR.

    
por 05.09.2016 / 08:10

Tags