3

I have been struggling for the past few days with this problem:

Basically, I want to send to a client browser a cookie of the form foo[sha1oftheurl]=[randomvalue] if and only if the cookie has not already been set.

e.g. If a client browser requests "/page.html", the HTTP response will be like: resp.http.Set-Cookie = "foo4c9ae249e9e061dd6e30893e03dc10a58cc40ee6=ABCD;"

then, if the same client request "/index.html", the HTTP response will contain a header: resp.http.Set-Cookie = "foo14fe4559026d4c5b5eb530ee70300c52d99e70d7=QWERTY;"

In the end, the client browser will have 2 cookies: foo4c9ae249e9e061dd6e30893e03dc10a58cc40ee6=ABCD foo14fe4559026d4c5b5eb530ee70300c52d99e70d7=QWERTY

Now, that, is not complicated in itself. The following code does it:

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

However, this code does not take into account the case where the Cookie already exists. I need to check that the Cookie does not exists before generating a random value. So I thought about this code:

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

The problem is that Varnish does not compute Regular expression at run time. Which leads to this error when I try to compile:

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

One could propose to solve my problem by matching on the "abtest" part of the cookie or even "abtest[a-fA-F0-9]{40}":

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

But this code matches any cookie starting by 'abtest' and containing an hexadecimal string of 40 characters. Which means that if a client requests "/page.html" first, then "/index.html", the condition will evaluate to true even if the cookie for the "/index.html" has not been set.

I found in bug report phk or someone else stating that computing regular expressions was extremely expensive which is why they are evaluated during compilation. Considering this, I believe that there is no way of achieving what I want the way I've been trying to.

Is there any way of solving this problem, other than writting a vmod?

Thanks for your help!

-Hugues

2
  • 1
    How many different urls you will have? I'm guessing that with all this cookies you don't want to use Varnish as cache? Maybe this is a job for backend?
    – RJS
    Oct 10, 2012 at 22:15
  • The number of URL will be variable. This post is actually part of a larger problem involving A/B Testing. Varnish gets a request, check if the there's an AB Test for the current page, if yes, checks if the request has an ABTest[sha1]=[abtestvalue] cookie corresponding to this page, if yes, varnish serves the corresponding cached version of the page, if no, varnish (as opposed to the backend) creates the cookie and serves the corresponding cached version of the page.Varnish generating the cookie avoids requesting the backend each time a client comes with no cookie set.
    – liquidity
    Oct 10, 2012 at 23:03

2 Answers 2

5

There is a trick!

Change the condition

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

to:

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

This switches it to a CSTR.

2
  • I don't think this actually works. {" .. }" is a long string, so it takes everything as a string. Jun 28, 2018 at 2:02
  • Looks cool, syntactically correct, but unfortunately wrong. Value of req.http.regex variable will be ignored the actual regex here will be "req.http.regex" taken as literal. Jan 9 at 9:58
1

Here is an answer I got from the Varnish mailing list:

TL;DR; Not possible without a vmod or inline C.

Everything you stated is true. Without inline C or a vmod the only alternative I know of to accomplish this would be to dynamically generate the parts of your config that depend on the sha1 of a url.

For instance, you could use something similar to the following:

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

Modified to your liking obviously, which results in the following 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();
}

This has the benefit that everything with exception to the random number is computed ahead of time, but has the drawback of having to use in this case, another script to maintain a list of accessible files to generate their hash.

Honestly not sure if this is going to help or not, but maybe it'll give you other ideas to explore.

~Paul

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .