Commit f64a54e3 authored by Dridi Boukelmoune's avatar Dridi Boukelmoune

Import vmod_cookie from varnish/varnish-modules

This is in essence the same VMOD so migrating from one to the other
should result in no VCL change, except for the removal of DEPRECATED
cookie.filter_except().

The test suite was refreshed to take advantage of recent features and
mostly operates in vcl_synth as a result, not wasting cycles bringing
needless backends up.

The C code and VCC descriptor were also improved for better code style
compliance and documentation consistency, without changing the VMOD's
behavior. On the C side actual changes mostly consisted in adding missing
assertions but there were notable changes:

- usage of VRE_Free instead of free (with test case)
- WS_VSB_* facility usage for the one use case

There are other opportunities for improvement, like usage of more modern
facilities like VRT_fail, or a named Lck instead of a plain mutex.

Closes #3184
parent 746384b2
......@@ -853,6 +853,7 @@ AC_CONFIG_FILES([
lib/libvarnishapi/Makefile
lib/libvcc/Makefile
lib/libvgz/Makefile
lib/libvmod_cookie/Makefile
lib/libvmod_debug/Makefile
lib/libvmod_std/Makefile
lib/libvmod_directors/Makefile
......
......@@ -5,6 +5,7 @@ SUBDIRS = \
libvarnishapi \
libvcc \
libvgz \
libvmod_cookie \
libvmod_debug \
libvmod_std \
libvmod_directors \
......
libvmod_cookie_la_SOURCES = \
vmod_cookie.c
# Use vmodtool.py generated automake boilerplate
include $(srcdir)/automake_boilerplate.am
# Generated by vmodtool.py --boilerplate.
AM_LDFLAGS = $(AM_LT_LDFLAGS)
AM_CPPFLAGS = \
-I$(top_srcdir)/include \
-I$(top_srcdir)/bin/varnishd \
-I$(top_builddir)/include
vmoddir = $(pkglibdir)/vmods
vmodtool = $(top_srcdir)/lib/libvcc/vmodtool.py
vmodtoolargs ?= --strict --boilerplate
vmod_LTLIBRARIES = libvmod_cookie.la
libvmod_cookie_la_CFLAGS = \
@SAN_CFLAGS@
libvmod_cookie_la_LDFLAGS = \
-export-symbols-regex 'Vmod_cookie_Data' \
$(AM_LDFLAGS) \
$(VMOD_LDFLAGS) \
@SAN_LDFLAGS@
nodist_libvmod_cookie_la_SOURCES = vcc_if.c vcc_if.h
$(libvmod_cookie_la_OBJECTS): vcc_if.h
vcc_if.h vmod_cookie.rst vmod_cookie.man.rst: vcc_if.c
vcc_if.c: $(vmodtool) $(srcdir)/vmod.vcc
@PYTHON@ $(vmodtool) $(vmodtoolargs) $(srcdir)/vmod.vcc
EXTRA_DIST = vmod.vcc automake_boilerplate.am
CLEANFILES = $(builddir)/vcc_if.c $(builddir)/vcc_if.h \
$(builddir)/vmod_cookie.rst \
$(builddir)/vmod_cookie.man.rst
TESTS = \
tests/cookie_b00000.vtc \
tests/cookie_b00001.vtc \
tests/cookie_b00002.vtc \
tests/cookie_b00003.vtc \
tests/cookie_b00004.vtc \
tests/cookie_b00005.vtc \
tests/cookie_b00006.vtc \
tests/cookie_b00007.vtc \
tests/cookie_b00008.vtc \
tests/cookie_b00009.vtc \
tests/cookie_b00010.vtc \
tests/cookie_b00011.vtc \
tests/cookie_b00012.vtc \
tests/cookie_b00013.vtc \
tests/cookie_r00028.vtc \
tests/cookie_v00000.vtc
EXTRA_DIST += $(TESTS)
vtc-refresh-tests:
@PYTHON@ $(vmodtool) $(vmodtoolargs) $(srcdir)/vmod.vcc
@cd $(top_builddir) && ./config.status --file=$(subdir)/Makefile
include $(top_srcdir)/vtc.am
varnishtest "Test vmod_cookie"
varnish v1 -vcl {
import cookie;
backend be none;
sub vcl_recv { return (synth(200)); }
sub vcl_synth {
cookie.set("cookie1", "cookie1BAD");
cookie.set("cookie2", "cookie2value");
cookie.set("cookie3", "cookie3value");
cookie.set("cookie4", "cookie4value");
cookie.set("cookie1", "cookie1value"); # overrides cookie1
cookie.delete("cookie2");
set resp.http.X-foo = cookie.get_string();
}
} -start
client c1 {
txreq -url "/"
rxresp
expect resp.http.X-foo == "cookie1=cookie1value; cookie3=cookie3value; cookie4=cookie4value;"
} -run
varnishtest "Test cookie.clean()"
varnish v1 -vcl {
import cookie;
backend be none;
sub vcl_recv { return (synth(200)); }
sub vcl_synth {
cookie.clean();
cookie.set("cookie1", "cookie1BAD");
set resp.http.X-foo = cookie.get_string();
cookie.clean();
set resp.http.X-bar = cookie.get_string();
}
} -start
client c1 {
txreq
rxresp
expect resp.http.X-foo == "cookie1=cookie1BAD;"
expect resp.http.X-bar == ""
} -run
varnishtest "NULL/empty value checks"
varnish v1 -vcl {
import cookie;
backend be none;
sub vcl_recv { return (synth(200)); }
sub vcl_synth {
# nothing in here at this point.
set req.http.x-foo = cookie.get_string();
# XXX: We might want to revisit these assumptions since "="
# (empty name and empty value) is as correct as "name=value"
# for a cookie. See rfc6265 section 5.2 for reference.
# empty name
cookie.set("", "foo");
# empty value
cookie.set("cookie1", "");
# proper NULL
cookie.set(req.http.null, "foo");
# double delete
cookie.delete("cookie2");
cookie.delete("cookie2");
cookie.delete(req.http.null);
set resp.http.x-foo = cookie.get_string();
}
} -start
client c1 {
txreq -url "/"
rxresp
expect resp.http.x-foo == ""
} -run
varnishtest "Test cookie.keep()"
varnish v1 -vcl {
import cookie;
backend be none;
sub vcl_recv { return (synth(200)); }
sub vcl_synth {
cookie.set("biscuit", "standard");
cookie.set("bredela", "eggwhites");
cookie.set("chocolatechip", "verychippy");
cookie.set("empire", "jellytots");
cookie.keep("bredela,empire,baz");
set resp.http.X-foo = cookie.get_string();
# Test exotic admin-supplied filter strings.
cookie.parse("bredela=eggwhites; empire=jellytots;");
cookie.keep(",,,,bredela, ,empire,baz,");
set resp.http.X-bar = cookie.get_string();
cookie.keep(req.http.none);
set resp.http.X-baz = cookie.get_string();
}
} -start
client c1 {
txreq -url "/"
rxresp
expect resp.http.X-foo == "bredela=eggwhites; empire=jellytots;"
expect resp.http.X-bar == "bredela=eggwhites; empire=jellytots;"
expect resp.http.X-baz == ""
} -run
varnishtest "Test cookie.filter()"
varnish v1 -vcl {
import cookie;
backend be none;
sub vcl_recv { return (synth(200)); }
sub vcl_synth {
cookie.set("biscuit", "standard");
cookie.set("bredela", "eggwhites");
cookie.set("chocolatechip", "verychippy");
cookie.set("empire", "jellytots");
cookie.filter("bredela,empire,baz");
set resp.http.X-foo = cookie.get_string();
# Test exotic admin-supplied filter strings.
cookie.parse("bredela=eggwhites; empire=jellytots;");
cookie.filter(",,,,bredela, ,baz,");
set resp.http.X-bar = cookie.get_string();
cookie.parse("foo=bar; bar=baz;");
cookie.filter(req.http.none);
set resp.http.X-baz = cookie.get_string();
}
} -start
client c1 {
txreq -url "/"
rxresp
expect resp.http.X-foo == "biscuit=standard; chocolatechip=verychippy;"
expect resp.http.X-bar == "empire=jellytots;"
expect resp.http.X-baz == "foo=bar; bar=baz;"
} -run
varnishtest "Test missing cookie"
varnish v1 -vcl {
import cookie;
backend be none;
sub vcl_recv { return (synth(200)); }
sub vcl_synth {
cookie.set("cookie1", "cookie1value");
cookie.set("cookie2", "cookie2value");
set resp.http.X-foo = cookie.get("cookie2");
# Make sure we handle this gracefully.
set resp.http.X-bar = "" + cookie.get("non-existing");
}
} -start
client c1 {
txreq -url "/"
rxresp
expect resp.http.X-foo == "cookie2value"
expect resp.http.X-bar == ""
} -run
varnishtest "Test vmod_cookie concurrency"
barrier b1 cond 2
server s1 {
rxreq
barrier b1 sync
expect req.url == "/s1"
txresp
} -start
server s2 {
rxreq
expect req.url == "/s2"
txresp
} -start
varnish v1 -vcl+backend {
import cookie;
sub vcl_recv {
cookie.parse(req.http.cookie);
if (req.url == "/s1") {
set req.backend_hint = s1;
}
else {
set req.backend_hint = s2;
}
}
sub vcl_deliver {
set resp.http.x-val = cookie.get("a");
}
} -start
client c1 {
txreq -url "/s1" -hdr "Cookie: a=bar"
rxresp
expect resp.http.x-val == "bar"
} -start
client c2 {
barrier b1 sync
txreq -url "/s2" -hdr "Cookie: a=foo"
rxresp
expect resp.http.x-val == "foo"
} -run
client c1 -wait
varnishtest "Test cookie.isset()"
varnish v1 -vcl {
import cookie;
backend be none;
sub vcl_recv { return (synth(200)); }
sub vcl_synth {
cookie.set("cookie1", "cookie1value");
set resp.http.does = cookie.isset("cookie1");
set resp.http.does-not = cookie.isset("non-existent");
set resp.http.null = cookie.isset("");
set resp.http.null2 = cookie.isset(req.http.probably-null);
}
} -start
client c1 {
txreq -url "/"
rxresp
expect resp.http.does == "true"
expect resp.http.does-not == "false"
expect resp.http.null == "false"
expect resp.http.null2 == "false"
} -run
varnishtest "Test large cookies"
varnish v1 -cliok "param.set workspace_client 64k" -vcl {
import cookie;
backend be none;
sub vcl_recv { return (synth(200)); }
sub vcl_synth {
cookie.parse(req.http.cookie);
set resp.http.cookiestring = cookie.get_string();
set resp.http.cookie1 = cookie.isset("cookie1");
}
} -start
client c1 {
# Insanely long cookie name.
txreq -url "/" -hdr "Cookie: phohx8aingie6Ide7peephie5paip6ang4thooh4ooquai8ohvah7eiqueeki8ooth7viequ0Tha5thewiSheih5jaimaiTahr1wi8WooQuoe7loothieThahweeneichoo8cufeelu3tie5cei1iShiemiezoofox6ahcaevaihocheungai2aeghaichaingee0EiGie3Ees5ujaem5uquahpieFeelei7Ohngei1afaTooph4aiquum1aewaidatheshuh1fohhoor0hoo6aeTeiy9xougahf3jeapooshuhoob5deiwareingahth7ahf2fafeer8Oobiewai3rei8ofejohjeiye4die8Na7ze6eixajauCairoth0lek8vioyuom6eih0egho2aingoo7coh1at3niochu6osahthi0ue1Luchae1eifeupiuwaa0raidiewaijese4oozee4eihie5shaBaoreacooNg8uW9eru9kigh3Feesi8iex2pu7ohfaiBiezael6ifaujiek4nutae1aalohchoteiPuaM2chiefaicaopheKohsh6Ho1wiephieseef1daj3Pahfie2ooch8shaing5baXeiLiep9lahfe9uDaxeehielais2eix3iekiew8aiter9Foo8noo2hae7ohdie1iB7hoop3podeengooSothoojui4AhXu5Nain8ohqu8if1ue5iTheimei5oghie9sheiv4Hejah1veixahcaixie8ahyieT8Phay4daeTei1aRiemae6oicheef2miiNuoxeil1kae2nea1roh9Rei1keiwaT2eoJaiNgie0den6aideif3uechaishaec4cai2eozieb9aeN9sai9ahnielohdaeGh2kaeleiteitai0ietoo7eiCha0baiW7dai0im1jul5OWijaLo2ohh3kooxu2oFah3loob6feiw7pie9eighu8ik4chae0Athou2fah5ieQuuic0Mu1j=cookievalue"
rxresp
expect resp.http.cookiestring == "phohx8aingie6Ide7peephie5paip6ang4thooh4ooquai8ohvah7eiqueeki8ooth7viequ0Tha5thewiSheih5jaimaiTahr1wi8WooQuoe7loothieThahweeneichoo8cufeelu3tie5cei1iShiemiezoofox6ahcaevaihocheungai2aeghaichaingee0EiGie3Ees5ujaem5uquahpieFeelei7Ohngei1afaTooph4aiquum1aewaidatheshuh1fohhoor0hoo6aeTeiy9xougahf3jeapooshuhoob5deiwareingahth7ahf2fafeer8Oobiewai3rei8ofejohjeiye4die8Na7ze6eixajauCairoth0lek8vioyuom6eih0egho2aingoo7coh1at3niochu6osahthi0ue1Luchae1eifeupiuwaa0raidiewaijese4oozee4eihie5shaBaoreacooNg8uW9eru9kigh3Feesi8iex2pu7ohfaiBiezael6ifaujiek4nutae1aalohchoteiPuaM2chiefaicaopheKohsh6Ho1wiephieseef1daj3Pahfie2ooch8shaing5baXeiLiep9lahfe9uDaxeehielais2eix3iekiew8aiter9Foo8noo2hae7ohdie1iB7hoop3podeengooSothoojui4AhXu5Nain8ohqu8if1ue5iTheimei5oghie9sheiv4Hejah1veixahcaixie8ahyieT8Phay4daeTei1aRiemae6oicheef2miiNuoxeil1kae2nea1roh9Rei1keiwaT2eoJaiNgie0den6aideif3uechaishaec4cai2eozieb9aeN9sai9ahnielohdaeGh2kaeleiteitai0ietoo7eiCha0baiW7dai0im1jul5OWijaLo2ohh3kooxu2oFah3loob6feiw7pie9eighu8ik4chae0Athou2fah5ieQuuic0Mu1j=cookievalue;"
# Insane 6KB cookie value.
txreq -url "/" -hdr "Cookie: cookie1=foobarbazfoobarbazphohx8aingie6Ide7peephie5paip6ang4thooh4ooquai8ohvah7eiqueeki8ooth7viequ0Tha5thewiSheih5jaimaiTahr1wi8WooQuoe7loothieThahweeneichoo8cufeelu3tie5cei1iShiemiezoofox6ahcaevaihocheungai2aeghaichaingee0EiGie3Ees5ujaem5uquahpieFeelei7Ohngei1afaTooph4aiquum1aewaidatheshuh1fohhoor0hoo6aeTeiy9xougahf3jeapooshuhoob5deiwareingahth7ahf2fafeer8Oobiewai3rei8ofejohjeiye4die8Na7ze6eixajauCairoth0lek8vioyuom6eih0egho2aingoo7coh1at3niochu6osahthi0ue1Luchae1eifeupiuwaa0raidiewaijese4oozee4eihie5shaBaoreacooNg8uW9eru9kigh3Feesi8iex2pu7ohfaiBiezael6ifaujiek4nutae1aalohchoteiPuaM2chiefaicaopheKohsh6Ho1wiephieseef1daj3Pahfie2ooch8shaing5baXeiLiep9lahfe9uDaxeehielais2eix3iekiew8aiter9Foo8noo2hae7ohdie1iB7hoop3podeengooSothoojui4AhXu5Nain8ohqu8if1ue5iTheimei5oghie9sheiv4Hejah1veixahcaixie8ahyieT8Phay4daeTei1aRiemae6oicheef2miiNuoxeil1kae2nea1roh9Rei1keiwaT2eoJaiNgie0den6aideif3uechaishaec4cai2eozieb9aeN9sai9ahnielohdaeGh2kaeleiteitai0ietoo7eiCha0baiW7dai0im1jul5OWijaLo2ohh3kooxu2oFah3loob6feiw7pie9eighu8ik4chae0Athou2fah5ieQuuic0Mu1jphohx8aingie6Ide7peephie5paip6ang4thooh4ooquai8ohvah7eiqueeki8ooth7viequ0Tha5thewiSheih5jaimaiTahr1wi8WooQuoe7loothieThahweeneichoo8cufeelu3tie5cei1iShiemiezoofox6ahcaevaihocheungai2aeghaichaingee0EiGie3Ees5ujaem5uquahpieFeelei7Ohngei1afaTooph4aiquum1aewaidatheshuh1fohhoor0hoo6aeTeiy9xougahf3jeapooshuhoob5deiwareingahth7ahf2fafeer8Oobiewai3rei8ofejohjeiye4die8Na7ze6eixajauCairoth0lek8vioyuom6eih0egho2aingoo7coh1at3niochu6osahthi0ue1Luchae1eifeupiuwaa0raidiewaijese4oozee4eihie5shaBaoreacooNg8uW9eru9kigh3Feesi8iex2pu7ohfaiBiezael6ifaujiek4nutae1aalohchoteiPuaM2chiefaicaopheKohsh6Ho1wiephieseef1daj3Pahfie2ooch8shaing5baXeiLiep9lahfe9uDaxeehielais2eix3iekiew8aiter9Foo8noo2hae7ohdie1iB7hoop3podeengooSothoojui4AhXu5Nain8ohqu8if1ue5iTheimei5oghie9sheiv4Hejah1veixahcaixie8ahyieT8Phay4daeTei1aRiemae6oicheef2miiNuoxeil1kae2nea1roh9Rei1keiwaT2eoJaiNgie0den6aideif3uechaishaec4cai2eozieb9aeN9sai9ahnielohdaeGh2kaeleiteitai0ietoo7eiCha0baiW7dai0im1jul5OWijaLo2ohh3kooxu2oFah3loob6feiw7pie9eighu8ik4chae0Athou2fah5ieQuuic0Mu1jphohx8aingie6Ide7peephie5paip6ang4thooh4ooquai8ohvah7eiqueeki8ooth7viequ0Tha5thewiSheih5jaimaiTahr1wi8WooQuoe7loothieThahweeneichoo8cufeelu3tie5cei1iShiemiezoofox6ahcaevaihocheungai2aeghaichaingee0EiGie3Ees5ujaem5uquahpieFeelei7Ohngei1afaTooph4aiquum1aewaidatheshuh1fohhoor0hoo6aeTeiy9xougahf3jeapooshuhoob5deiwareingahth7ahf2fafeer8Oobiewai3rei8ofejohjeiye4die8Na7ze6eixajauCairoth0lek8vioyuom6eih0egho2aingoo7coh1at3niochu6osahthi0ue1Luchae1eifeupiuwaa0raidiewaijese4oozee4eihie5shaBaoreacooNg8uW9eru9kigh3Feesi8iex2pu7ohfaiBiezael6ifaujiek4nutae1aalohchoteiPuaM2chiefaicaopheKohsh6Ho1wiephieseef1daj3Pahfie2ooch8shaing5baXeiLiep9lahfe9uDaxeehielais2eix3iekiew8aiter9Foo8noo2hae7ohdie1iB7hoop3podeengooSothoojui4AhXu5Nain8ohqu8if1ue5iTheimei5oghie9sheiv4Hejah1veixahcaixie8ahyieT8Phay4daeTei1aRiemae6oicheef2miiNuoxeil1kae2nea1roh9Rei1keiwaT2eoJaiNgie0den6aideif3uechaishaec4cai2eozieb9aeN9sai9ahnielohdaeGh2kaeleiteitai0ietoo7eiCha0baiW7dai0im1jul5OWijaLo2ohh3kooxu2oFah3loob6feiw7pie9eighu8ik4chae0Athou2fah5ieQuuic0Mu1jphohx8aingie6Ide7peephie5paip6ang4thooh4ooquai8ohvah7eiqueeki8ooth7viequ0Tha5thewiSheih5jaimaiTahr1wi8WooQuoe7loothieThahweeneichoo8cufeelu3tie5cei1iShiemiezoofox6ahcaevaihocheungai2aeghaichaingee0EiGie3Ees5ujaem5uquahpieFeelei7Ohngei1afaTooph4aiquum1aewaidatheshuh1fohhoor0hoo6aeTeiy9xougahf3jeapooshuhoob5deiwareingahth7ahf2fafeer8Oobiewai3rei8ofejohjeiye4die8Na7ze6eixajauCairoth0lek8vioyuom6eih0egho2aingoo7coh1at3niochu6osahthi0ue1Luchae1eifeupiuwaa0raidiewaijese4oozee4eihie5shaBaoreacooNg8uW9eru9kigh3Feesi8iex2pu7ohfaiBiezael6ifaujiek4nutae1aalohchoteiPuaM2chiefaicaopheKohsh6Ho1wiephieseef1daj3Pahfie2ooch8shaing5baXeiLiep9lahfe9uDaxeehielais2eix3iekiew8aiter9Foo8noo2hae7ohdie1iB7hoop3podeengooSothoojui4AhXu5Nain8ohqu8if1ue5iTheimei5oghie9sheiv4Hejah1veixahcaixie8ahyieT8Phay4daeTei1aRiemae6oicheef2miiNuoxeil1kae2nea1roh9Rei1keiwaT2eoJaiNgie0den6aideif3uechaishaec4cai2eozieb9aeN9sai9ahnielohdaeGh2kaeleiteitai0ietoo7eiCha0baiW7dai0im1jul5OWijaLo2ohh3kooxu2oFah3loob6feiw7pie9eighu8ik4chae0Athou2fah5ieQuuic0Mu1jphohx8aingie6Ide7peephie5paip6ang4thooh4ooquai8ohvah7eiqueeki8ooth7viequ0Tha5thewiSheih5jaimaiTahr1wi8WooQuoe7loothieThahweeneichoo8cufeelu3tie5cei1iShiemiezoofox6ahcaevaihocheungai2aeghaichaingee0EiGie3Ees5ujaem5uquahpieFeelei7Ohngei1afaTooph4aiquum1aewaidatheshuh1fohhoor0hoo6aeTeiy9xougahf3jeapooshuhoob5deiwareingahth7ahf2fafeer8Oobiewai3rei8ofejohjeiye4die8Na7ze6eixajauCairoth0lek8vioyuom6eih0egho2aingoo7coh1at3niochu6osahthi0ue1Luchae1eifeupiuwaa0raidiewaijese4oozee4eihie5shaBaoreacooNg8uW9eru9kigh3Feesi8iex2pu7ohfaiBiezael6ifaujiek4nutae1aalohchoteiPuaM2chiefaicaopheKohsh6Ho1wiephieseef1daj3Pahfie2ooch8shaing5baXeiLiep9lahfe9uDaxeehielais2eix3iekiew8aiter9Foo8noo2hae7ohdie1iB7hoop3podeengooSothoojui4AhXu5Nain8ohqu8if1ue5iTheimei5oghie9sheiv4Hejah1veixahcaixie8ahyieT8Phay4daeTei1aRiemae6oicheef2miiNuoxeil1kae2nea1roh9Rei1keiwaT2eoJaiNgie0den6aideif3uechaishaec4cai2eozieb9aeN9sai9ahnielohdaeGh2kaeleiteitai0ietoo7eiCha0baiW7dai0im1jul5OWijaLo2ohh3kooxu2oFah3loob6feiw7pie9eighu8ik4chae0Athou2fah5ieQuuic0Mu1jphohx8aingie6Ide7peephie5paip6ang4thooh4ooquai8ohvah7eiqueeki8ooth7viequ0Tha5thewiSheih5jaimaiTahr1wi8WooQuoe7loothieThahweeneichoo8cufeelu3tie5cei1iShiemiezoofox6ahcaevaihocheungai2aeghaichaingee0EiGie3Ees5ujaem5uquahpieFeelei7Ohngei1afaTooph4aiquum1aewaidatheshuh1fohhoor0hoo6aeTeiy9xougahf3jeapooshuhoob5deiwareingahth7ahf2fafeer8Oobiewai3rei8ofejohjeiye4die8Na7ze6eixajauCairoth0lek8vioyuom6eih0egho2aingoo7coh1at3niochu6osahthi0ue1Luchae1eifeupiuwaa0raidiewaijese4oozee4eihie5shaBaoreacooNg8uW9eru9kigh3Feesi8iex2pu7ohfaiBiezael6ifaujiek4nutae1aalohchoteiPuaM2chiefaicaopheKohsh6Ho1wiephieseef1daj3Pahfie2ooch8shaing5baXeiLiep9lahfe9uDaxeehielais2eix3iekiew8aiter9Foo8noo2hae7ohdie1iB7hoop3podeengooSothoojui4AhXu5Nain8ohqu8if1ue5iTheimei5oghie9sheiv4Hejah1veixahcaixie8ahyieT8Phay4daeTei1aRiemae6oicheef2miiNuoxeil1kae2nea1roh9Rei1keiwaT2eoJaiNgie0den6aideif3uechaishaec4cai2eozieb9aeN9sai9ahnielohdaeGh2kaeleiteitai0ietoo7eiCha0baiW7dai0im1jul5OWijaLo2ohh3kooxu2oFah3loob6feiw7pie9eighu8ik4chae0Athou2fah5ieQuuic0Mu1j;"
rxresp
# We support long cookie values, should be fine.
expect resp.http.cookie1 == "true"
} -run
varnishtest "Test cookie parser"
varnish v1 -vcl {
import cookie;
backend be none;
sub vcl_recv { return (synth(200)); }
sub vcl_synth {
cookie.parse(req.http.cookie);
set resp.http.X-foo = cookie.get_string();
}
} -start
client c1 {
txreq -hdr "Cookie: csrftoken=0e0c3616e41a6bd561b72b7f5fc1128f; sessionid=a707505310ddf259bb290d3ca63fc560"
rxresp
expect resp.http.X-foo == "csrftoken=0e0c3616e41a6bd561b72b7f5fc1128f; sessionid=a707505310ddf259bb290d3ca63fc560;"
} -run
client c2 {
txreq -hdr "Cookie: __utmc=253898641; __utma=253898641.654622101.1372224466.1372224466.1372224466.1; __utmb=253898641.44.10.1372224466; __utmz=253898641.1372224466.1.1.utmcsr=google|utmccn=(organic)|utmcmd=organic|utmctr=index%2Bof%2Bccnp%2Bpdf"
rxresp
expect resp.http.X-foo == "__utmc=253898641; __utma=253898641.654622101.1372224466.1372224466.1372224466.1; __utmb=253898641.44.10.1372224466; __utmz=253898641.1372224466.1.1.utmcsr=google|utmccn=(organic)|utmcmd=organic|utmctr=index%2Bof%2Bccnp%2Bpdf;"
} -run
client c3 {
txreq -hdr "Cookie: "
rxresp
expect resp.http.X-foo == ""
} -run
# An empty cookie is a non-existing cookie for us.
client c4 {
txreq -hdr "Cookie: emptycookie="
rxresp
expect resp.http.X-foo == ""
} -run
# A single cookie should also work.
client c5 {
txreq -hdr "Cookie: cookie1=foobarbaz"
rxresp
expect resp.http.X-foo == "cookie1=foobarbaz;"
} -run
# Don't overflow the buffer with an edge case
client c6 {
txreq -hdr "Cookie: csrftoken=0e0c3616e41a6bd561b72b7f5fc1128a;=" -hdr "X-Not-Cookie: sessionid=a707505310ddf259bb290d3ca63fc561"
rxresp
expect resp.http.X-foo == "csrftoken=0e0c3616e41a6bd561b72b7f5fc1128a;"
} -run
varnishtest "Test rfc1123 string formatting function"
varnish v1 -vcl {
import cookie;
backend be none;
sub vcl_recv { return (synth(200)); }
sub vcl_synth {
set resp.http.x-date = cookie.format_rfc1123(now, 1d);
}
} -start
client c1 {
txreq -url "/"
rxresp
expect resp.http.date != <undef>
} -run
varnishtest "Test cookie.get_re()"
varnish v1 -vcl {
import cookie;
backend be none;
sub vcl_recv { return (synth(200)); }
sub vcl_synth {
cookie.set("biscuit", "standard");
cookie.set("chocolatechip", "verychippy");
cookie.set("empire", "jellytots");
set resp.http.X-first = cookie.get_re("DOES_NOT_EXIST");
set resp.http.X-second = cookie.get_re("biscuit");
set resp.http.X-third = cookie.get_re("DOES_NOT_EXIST_EITHER");
}
} -start
client c1 {
txreq -url "/"
rxresp
expect resp.http.X-first == ""
expect resp.http.X-second == "standard"
expect resp.http.X-third == ""
} -run
varnishtest "Test cookie.filter_re()"
varnish v1 -vcl {
import cookie;
backend be none;
sub vcl_recv { return (synth(200)); }
sub vcl_synth {
cookie.set("biscuit", "standard");
cookie.set("bredela", "eggwhites");
cookie.set("empire", "jellytots");
set resp.http.X-foo = cookie.get_string();
cookie.filter_re("^NOT-MATCHING-ANYTHING$");
set resp.http.X-bar = cookie.get_string();
cookie.filter_re("^bredela");
set resp.http.X-baz = cookie.get_string();
cookie.filter_re(".*");
set resp.http.X-qux = cookie.get_string();
}
} -start
client c1 {
txreq -url "/"
rxresp
expect resp.http.X-foo == resp.http.X-bar
expect resp.http.X-baz != resp.http.X-foo
expect resp.http.X-baz == "biscuit=standard; empire=jellytots;"
expect resp.http.X-qux == ""
} -run
varnishtest "Test cookie.keep_re()"
varnish v1 -vcl {
import cookie;
backend be none;
sub vcl_recv { return (synth(200)); }
sub vcl_synth {
cookie.parse("foo=bar; baz=qux;");
cookie.keep_re("NOTHING_MATCHES_SO_NOTHING_KEPT$");
set resp.http.X-empty = cookie.get_string();
cookie.parse("biscuit=standard; bredela=eggwhites; empire=jellytots;");
cookie.keep_re("^b");
set resp.http.X-bees = cookie.get_string();
}
} -start
client c1 {
txreq -url "/"
rxresp
expect resp.http.X-empty == ""
expect resp.http.X-bees == "biscuit=standard; bredela=eggwhites;"
} -run
varnishtest "Test issue https://github.com/varnish/varnish-modules/issues/28"
varnish v1 -vcl {
import cookie;
backend be none;
sub vcl_recv { return (synth(200)); }
sub vcl_synth {
cookie.parse(req.http.cookie);
set resp.http.X-foo = cookie.get_string();
}
} -start
client c1 {
txreq -hdr "Cookie: csrftoken=0e0c3616e41a6bd561b72b7f5fc1128f; sessionid=a707505310ddf259bb290d3ca63fc560"
rxresp
expect resp.http.X-foo == "csrftoken=0e0c3616e41a6bd561b72b7f5fc1128f; sessionid=a707505310ddf259bb290d3ca63fc560;"
txreq -hdr "Cookie: csrftoken=0e0c3616e41a6bd561b72b7f5fc1128f; sessionid=a707505310ddf259bb290d3ca63fc560"
rxresp
expect resp.http.X-foo == "csrftoken=0e0c3616e41a6bd561b72b7f5fc1128f; sessionid=a707505310ddf259bb290d3ca63fc560;"
txreq -hdr "Cookie: csrftoken=0e0c3616e41a6bd561b72b7f5fc1128f; sessionid=a707505310ddf259bb290d3ca63fc560"
rxresp
expect resp.http.X-foo == "csrftoken=0e0c3616e41a6bd561b72b7f5fc1128f; sessionid=a707505310ddf259bb290d3ca63fc560;"
txreq -hdr "Cookie: csrftoken=0e0c3616e41a6bd561b72b7f5fc1128f; sessionid=a707505310ddf259bb290d3ca63fc560"
rxresp
expect resp.http.X-foo == "csrftoken=0e0c3616e41a6bd561b72b7f5fc1128f; sessionid=a707505310ddf259bb290d3ca63fc560;"
txreq -hdr "Cookie: csrftoken=0e0c3616e41a6bd561b72b7f5fc1128f; sessionid=a707505310ddf259bb290d3ca63fc560"
rxresp
expect resp.http.X-foo == "csrftoken=0e0c3616e41a6bd561b72b7f5fc1128f; sessionid=a707505310ddf259bb290d3ca63fc560;"
txreq -hdr "Cookie: csrftoken=0e0c3616e41a6bd561b72b7f5fc1128f; sessionid=a707505310ddf259bb290d3ca63fc560"
rxresp
expect resp.http.X-foo == "csrftoken=0e0c3616e41a6bd561b72b7f5fc1128f; sessionid=a707505310ddf259bb290d3ca63fc560;"
txreq -hdr "Cookie: csrftoken=0e0c3616e41a6bd561b72b7f5fc1128f; sessionid=a707505310ddf259bb290d3ca63fc560"
rxresp
expect resp.http.X-foo == "csrftoken=0e0c3616e41a6bd561b72b7f5fc1128f; sessionid=a707505310ddf259bb290d3ca63fc560;"
txreq -hdr "Cookie: csrftoken=0e0c3616e41a6bd561b72b7f5fc1128f; sessionid=a707505310ddf259bb290d3ca63fc560"
rxresp
expect resp.http.X-foo == "csrftoken=0e0c3616e41a6bd561b72b7f5fc1128f; sessionid=a707505310ddf259bb290d3ca63fc560;"
txreq -hdr "Cookie: csrftoken=0e0c3616e41a6bd561b72b7f5fc1128f; sessionid=a707505310ddf259bb290d3ca63fc560"
rxresp
expect resp.http.X-foo == "csrftoken=0e0c3616e41a6bd561b72b7f5fc1128f; sessionid=a707505310ddf259bb290d3ca63fc560;"
txreq -hdr "Cookie: csrftoken=0e0c3616e41a6bd561b72b7f5fc1128f; sessionid=a707505310ddf259bb290d3ca63fc560"
rxresp
expect resp.http.X-foo == "csrftoken=0e0c3616e41a6bd561b72b7f5fc1128f; sessionid=a707505310ddf259bb290d3ca63fc560;"
}
client c2 {
txreq -hdr "Cookie: csrftoken=0e0c3616e41a6bd561b72b7f5fc1128f; sessionid=a707505310ddf259bb290d3ca63fc560"
rxresp
expect resp.http.X-foo == "csrftoken=0e0c3616e41a6bd561b72b7f5fc1128f; sessionid=a707505310ddf259bb290d3ca63fc560;"
txreq -hdr "Cookie: csrftoken=0e0c3616e41a6bd561b72b7f5fc1128f; sessionid=a707505310ddf259bb290d3ca63fc560"
rxresp
expect resp.http.X-foo == "csrftoken=0e0c3616e41a6bd561b72b7f5fc1128f; sessionid=a707505310ddf259bb290d3ca63fc560;"
txreq -hdr "Cookie: csrftoken=0e0c3616e41a6bd561b72b7f5fc1128f; sessionid=a707505310ddf259bb290d3ca63fc560"
rxresp
expect resp.http.X-foo == "csrftoken=0e0c3616e41a6bd561b72b7f5fc1128f; sessionid=a707505310ddf259bb290d3ca63fc560;"
txreq -hdr "Cookie: csrftoken=0e0c3616e41a6bd561b72b7f5fc1128f; sessionid=a707505310ddf259bb290d3ca63fc560"
rxresp
expect resp.http.X-foo == "csrftoken=0e0c3616e41a6bd561b72b7f5fc1128f; sessionid=a707505310ddf259bb290d3ca63fc560;"
txreq -hdr "Cookie: csrftoken=0e0c3616e41a6bd561b72b7f5fc1128f; sessionid=a707505310ddf259bb290d3ca63fc560"
rxresp
expect resp.http.X-foo == "csrftoken=0e0c3616e41a6bd561b72b7f5fc1128f; sessionid=a707505310ddf259bb290d3ca63fc560;"
txreq -hdr "Cookie: csrftoken=0e0c3616e41a6bd561b72b7f5fc1128f; sessionid=a707505310ddf259bb290d3ca63fc560"
rxresp
expect resp.http.X-foo == "csrftoken=0e0c3616e41a6bd561b72b7f5fc1128f; sessionid=a707505310ddf259bb290d3ca63fc560;"
txreq -hdr "Cookie: csrftoken=0e0c3616e41a6bd561b72b7f5fc1128f; sessionid=a707505310ddf259bb290d3ca63fc560"
rxresp
expect resp.http.X-foo == "csrftoken=0e0c3616e41a6bd561b72b7f5fc1128f; sessionid=a707505310ddf259bb290d3ca63fc560;"
txreq -hdr "Cookie: csrftoken=0e0c3616e41a6bd561b72b7f5fc1128f; sessionid=a707505310ddf259bb290d3ca63fc560"
rxresp
expect resp.http.X-foo == "csrftoken=0e0c3616e41a6bd561b72b7f5fc1128f; sessionid=a707505310ddf259bb290d3ca63fc560;"
txreq -hdr "Cookie: csrftoken=0e0c3616e41a6bd561b72b7f5fc1128f; sessionid=a707505310ddf259bb290d3ca63fc560"
rxresp
expect resp.http.X-foo == "csrftoken=0e0c3616e41a6bd561b72b7f5fc1128f; sessionid=a707505310ddf259bb290d3ca63fc560;"
txreq -hdr "Cookie: csrftoken=0e0c3616e41a6bd561b72b7f5fc1128f; sessionid=a707505310ddf259bb290d3ca63fc560"
rxresp
expect resp.http.X-foo == "csrftoken=0e0c3616e41a6bd561b72b7f5fc1128f; sessionid=a707505310ddf259bb290d3ca63fc560;"
}
client c1 -repeat 2 -run
client c2 -repeat 2 -run
$Module cookie 3 "Varnish Cookie Module"
DESCRIPTION
===========
Handle HTTP cookies easier in Varnish VCL.
Parses a cookie header into an internal data store, where per-cookie
get/set/delete functions are available. A filter_except() method removes all
but a set comma-separated list of cookies. A filter() method removes a comma-
separated list of cookies.
Regular expressions can be used for either selecting cookies, deleting matching
cookies and deleting non-matching cookie names.
A convenience function for formatting the Set-Cookie Expires date field
is also included.
The state loaded with cookie.parse() has a lifetime of the current request
or backend request context. To pass variables to the backend request, store
the contents as fake bereq headers.
Filtering example::
import cookie;
sub vcl_recv {
if (req.http.cookie) {
cookie.parse(req.http.cookie);
# Either delete the ones you want to get rid of:
cookie.delete("cookie2");
# or delete all but a few:
cookie.keep("SESSIONID,PHPSESSID");
# Store it back into req so it will be passed to the backend.
set req.http.cookie = cookie.get_string();
# If empty, unset so the builtin VCL can consider it for caching.
if (req.http.cookie == "") {
unset req.http.cookie;
}
}
}
$ABI strict
$Function VOID clean(PRIV_TASK)
Clean up previously parsed cookies. It is not necessary to run clean()
in normal operations.
Example::
sub vcl_recv {
cookie.clean();
}
$Function VOID delete(PRIV_TASK, STRING cookiename)
Delete ``cookiename`` from internal vmod storage if it exists.
Example::
sub vcl_recv {
cookie.parse("cookie1: value1; cookie2: value2;");
cookie.delete("cookie2");
# get_string() will now yield "cookie1: value1";
}
$Function VOID filter(PRIV_TASK, STRING filterstring)
Delete all cookies from internal vmod storage that are in the
comma-separated argument cookienames.
Example::
sub vcl_recv {
cookie.parse("cookie1: value1; cookie2: value2; cookie3: value3");
cookie.filter("cookie1,cookie2");
# get_string() will now yield
# "cookie3: value3";
}
$Function VOID filter_re(PRIV_TASK, PRIV_CALL, STRING expression)
Delete all cookies from internal vmod storage that matches the
regular expression ``expression``.
Example::
sub vcl_recv {
cookie.parse("cookie1: value1; cookie2: value2; cookie3: value3");
cookie.filter_re("^cookie[12]$");
# get_string() will now yield
# "cookie3: value3";
}
$Function VOID keep(PRIV_TASK, STRING filterstring)
Delete all cookies from internal vmod storage that is not in the
comma-separated argument cookienames.
Example::
sub vcl_recv {
cookie.parse("cookie1: value1; cookie2: value2; cookie3: value3");
cookie.keep("cookie1,cookie2");
# get_string() will now yield
# "cookie1: value1; cookie2: value2;";
}
$Function VOID keep_re(PRIV_TASK, PRIV_CALL, STRING expression)
Delete all cookies from internal vmod storage that does not match
expression ``expression``.
Example::
sub vcl_recv {
cookie.parse("cookie1: value1; cookie2: value2; cookie3: value3");
cookie.keep_re("^cookie1,cookie2");
# get_string() will now yield
# "cookie1: value1; cookie2: value2;";
}
$Function STRING format_rfc1123(TIME now, DURATION timedelta)
Get a RFC1123 formatted date string suitable for inclusion in a
Set-Cookie response header.
Care should be taken if the response has multiple Set-Cookie headers.
In that case the header vmod should be used.
Example::
sub vcl_deliver {
# Set a userid cookie on the client that lives for 5 minutes.
set resp.http.Set-Cookie = "userid=" + req.http.userid +
"; Expires=" + cookie.format_rfc1123(now, 5m) + "; httpOnly";
}
$Function STRING get(PRIV_TASK, STRING cookiename)
Get the value of ``cookiename``, as stored in internal vmod storage. If
``cookiename`` does not exist an empty string is returned.
Example::
import std;
sub vcl_recv {
cookie.parse("cookie1: value1; cookie2: value2;");
std.log("cookie1 value is: " + cookie.get("cookie1"));
}
$Function STRING get_re(PRIV_TASK, PRIV_CALL, STRING expression)
Get the value of the first cookie in internal vmod storage that matches
regular expression ``expression``. If nothing matches, an empty string
is returned.
Example::
import std;
sub vcl_recv {
cookie.parse("cookie1: value1; cookie2: value2;");
std.log("cookie1 value is: " + cookie.get_re("^cookie1$"));
}
$Function STRING get_string(PRIV_TASK)
Get a Cookie string value with all cookies in internal vmod storage. Does
not modify internal storage.
Example::
sub vcl_recv {
cookie.parse(req.http.cookie);
cookie.filter_except("SESSIONID,PHPSESSID");
set req.http.cookie = cookie.get_string();
}
$Function BOOL isset(PRIV_TASK, STRING cookiename)
Check if ``cookiename`` is set in the internal vmod storage.
Example::
import std;
sub vcl_recv {
cookie.parse("cookie1: value1; cookie2: value2;");
if (cookie.isset("cookie2")) {
std.log("cookie2 is set.");
}
}
$Function VOID parse(PRIV_TASK, STRING cookieheader)
Parse the cookie string in ``cookieheader``. If state already exists,
``clean()`` will be run first.
Example::
sub vcl_recv {
cookie.parse(req.http.Cookie);
}
$Function VOID set(PRIV_TASK, STRING cookiename, STRING value)
Set the internal vmod storage for ``cookiename`` to ``value``.
Example::
sub vcl_recv {
cookie.set("cookie1", "value1");
std.log("cookie1 value is: " + cookie.get("cookie1"));
}
/*-
* Copyright (c) 2012-2020 Varnish Software
*
* Author: Lasse Karstensen <lasse.karstensen@gmail.com>
* Author: Lasse Karstensen <lkarsten@varnish-software.com>
* Author: Dridi Boukelmoune <dridi.boukelmoune@gmail.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* Cookie VMOD that simplifies handling of the Cookie request header.
*/
#include "config.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <pthread.h>
#include <cache/cache.h>
#include <vsb.h>
#include <vre.h>
#include "vcc_if.h"
#define VRE_MAX_GROUPS 8
enum filter_action {
blacklist,
whitelist
};
static pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;
struct cookie {
unsigned magic;
#define VMOD_COOKIE_ENTRY_MAGIC 0x3BB41543
char *name;
char *value;
VTAILQ_ENTRY(cookie) list;
};
/* A structure to represent both whitelists and blacklists */
struct matchlist {
char *name;
VTAILQ_ENTRY(matchlist) list;
};
struct vmod_cookie {
unsigned magic;
#define VMOD_COOKIE_MAGIC 0x4EE5FB2E
VTAILQ_HEAD(, cookie) cookielist;
};
static void
cobj_free(void *p)
{
struct vmod_cookie *vcp;
CAST_OBJ_NOTNULL(vcp, p, VMOD_COOKIE_MAGIC);
FREE_OBJ(vcp);
}
static struct vmod_cookie *
cobj_get(struct vmod_priv *priv)
{
struct vmod_cookie *vcp;
if (priv->priv == NULL) {
ALLOC_OBJ(vcp, VMOD_COOKIE_MAGIC);
AN(vcp);
VTAILQ_INIT(&vcp->cookielist);
priv->priv = vcp;
priv->free = cobj_free;
} else
CAST_OBJ_NOTNULL(vcp, priv->priv, VMOD_COOKIE_MAGIC);
return (vcp);
}
VCL_VOID
vmod_parse(VRT_CTX, struct vmod_priv *priv, VCL_STRING cookieheader)
{
struct vmod_cookie *vcp = cobj_get(priv);
char *name, *value;
const char *p, *sep;
int i = 0;
if (cookieheader == NULL || *cookieheader == '\0') {
VSLb(ctx->vsl, SLT_Debug, "cookie: nothing to parse");
return;
}
/* If called twice during the same request, clean out old state. */
if (!VTAILQ_EMPTY(&vcp->cookielist))
vmod_clean(ctx, priv);
p = cookieheader;
while (*p != '\0') {
while (isspace(*p))
p++;
sep = strchr(p, '=');
if (sep == NULL)
break;
name = strndup(p, pdiff(p, sep));
p = sep + 1;
sep = p;
while (*sep != '\0' && *sep != ';')
sep++;
value = strndup(p, pdiff(p, sep));
vmod_set(ctx, priv, name, value);
free(name);
free(value);
i++;
if (*sep == '\0')
break;
p = sep + 1;
}
VSLb(ctx->vsl, SLT_Debug, "cookie: parsed %i cookies.", i);
}
static struct cookie *
find_cookie(struct vmod_cookie *vcp, VCL_STRING name)
{
struct cookie *cookie;
VTAILQ_FOREACH(cookie, &vcp->cookielist, list) {
CHECK_OBJ_NOTNULL(cookie, VMOD_COOKIE_ENTRY_MAGIC);
if (!strcmp(cookie->name, name))
break;
}
return (cookie);
}
VCL_VOID
vmod_set(VRT_CTX, struct vmod_priv *priv, VCL_STRING name, VCL_STRING value)
{
struct vmod_cookie *vcp = cobj_get(priv);
struct cookie *cookie;
char *p;
CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
CHECK_OBJ_NOTNULL(ctx->ws, WS_MAGIC);
/* Empty cookies should be ignored. */
if (name == NULL || *name == '\0')
return;
if (value == NULL || *value == '\0')
return;
cookie = find_cookie(vcp, name);
if (cookie != NULL) {
p = WS_Printf(ctx->ws, "%s", value);
if (p == NULL) {
VSLb(ctx->vsl, SLT_Error,
"cookie: Workspace overflow in set()");
} else
cookie->value = p;
return;
}
cookie = WS_Alloc(ctx->ws, sizeof *cookie);
if (cookie == NULL) {
VSLb(ctx->vsl, SLT_Error,
"cookie: unable to get storage for cookie");
return;
}
INIT_OBJ(cookie, VMOD_COOKIE_ENTRY_MAGIC);
cookie->name = WS_Printf(ctx->ws, "%s", name);
cookie->value = WS_Printf(ctx->ws, "%s", value);
if (cookie->name == NULL || cookie->value == NULL) {
VSLb(ctx->vsl, SLT_Error,
"cookie: unable to get storage for cookie");
return;
}
VTAILQ_INSERT_TAIL(&vcp->cookielist, cookie, list);
}
VCL_BOOL
vmod_isset(VRT_CTX, struct vmod_priv *priv, const char *name)
{
struct vmod_cookie *vcp = cobj_get(priv);
struct cookie *cookie;
CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
if (name == NULL || *name == '\0')
return (0);
cookie = find_cookie(vcp, name);
return (cookie ? 1 : 0);
}
VCL_STRING
vmod_get(VRT_CTX, struct vmod_priv *priv, VCL_STRING name)
{
struct vmod_cookie *vcp = cobj_get(priv);
struct cookie *cookie;
CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
if (name == NULL || *name == '\0')
return (NULL);
cookie = find_cookie(vcp, name);
return (cookie ? cookie->value : NULL);
}
static vre_t *
compile_re(VRT_CTX, VCL_STRING expression) {
vre_t *vre;
const char *error;
int erroroffset;
vre = VRE_compile(expression, 0, &error, &erroroffset);
if (vre == NULL) {
VSLb(ctx->vsl, SLT_Error,
"cookie: PCRE compile error at char %i: %s",
erroroffset, error);
}
return (vre);
}
static void
free_re(void *priv)
{
vre_t *vre;
AN(priv);
vre = priv;
VRE_free(&vre);
AZ(vre);
}
VCL_STRING
vmod_get_re(VRT_CTX, struct vmod_priv *priv, struct vmod_priv *priv_call,
VCL_STRING expression)
{
struct vmod_cookie *vcp = cobj_get(priv);
int i, ovector[VRE_MAX_GROUPS];
struct cookie *cookie = NULL;
struct cookie *current;
vre_t *vre = NULL;
CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
if (expression == NULL || *expression == '\0')
return (NULL);
if (priv_call->priv == NULL) {
AZ(pthread_mutex_lock(&mtx));
vre = compile_re(ctx, expression);
if (vre == NULL) {
AZ(pthread_mutex_unlock(&mtx));
return (NULL);
}
priv_call->priv = vre;
priv_call->free = free_re;
AZ(pthread_mutex_unlock(&mtx));
}
VTAILQ_FOREACH(current, &vcp->cookielist, list) {
CHECK_OBJ_NOTNULL(current, VMOD_COOKIE_ENTRY_MAGIC);
VSLb(ctx->vsl, SLT_Debug, "cookie: checking %s", current->name);
i = VRE_exec(vre, current->name, strlen(current->name), 0, 0,
ovector, VRE_MAX_GROUPS, NULL);
if (i < 0)
continue;
VSLb(ctx->vsl, SLT_Debug, "cookie: %s is a match for regex '%s'",
current->name, expression);
cookie = current;
break;
}
return (cookie ? cookie->value : NULL);
}
VCL_VOID
vmod_delete(VRT_CTX, struct vmod_priv *priv, VCL_STRING name)
{
struct vmod_cookie *vcp = cobj_get(priv);
struct cookie *cookie;
CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
if (name == NULL || *name == '\0')
return;
cookie = find_cookie(vcp, name);
if (cookie != NULL)
VTAILQ_REMOVE(&vcp->cookielist, cookie, list);
}
VCL_VOID
vmod_clean(VRT_CTX, struct vmod_priv *priv)
{
struct vmod_cookie *vcp = cobj_get(priv);
CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
AN(vcp);
VTAILQ_INIT(&vcp->cookielist);
}
static void
filter_cookies(struct vmod_priv *priv, VCL_STRING list_s,
enum filter_action mode)
{
struct cookie *cookieptr, *safeptr;
struct vmod_cookie *vcp = cobj_get(priv);
struct matchlist *mlentry, *mlsafe;
char const *p = list_s, *q;
int matched = 0;
VTAILQ_HEAD(, matchlist) matchlist_head;
VTAILQ_INIT(&matchlist_head);
/* Parse the supplied list. */
while (p && *p != '\0') {
while (isspace(*p))
p++;
if (*p == '\0')
break;
q = p;
while (*q != '\0' && *q != ',')
q++;
if (q == p) {
p++;
continue;
}
/* XXX: can we reserve/release lumps of txt instead of
* malloc/free?
*/
mlentry = malloc(sizeof *mlentry);
AN(mlentry);
mlentry->name = strndup(p, q - p);
AN(mlentry->name);
VTAILQ_INSERT_TAIL(&matchlist_head, mlentry, list);
p = q;
if (*p != '\0')
p++;
}
/* Filter existing cookies that either aren't in the whitelist or
* are in the blacklist (depending on the filter_action) */
VTAILQ_FOREACH_SAFE(cookieptr, &vcp->cookielist, list, safeptr) {
CHECK_OBJ_NOTNULL(cookieptr, VMOD_COOKIE_ENTRY_MAGIC);
matched = 0;
VTAILQ_FOREACH(mlentry, &matchlist_head, list) {
if (strcmp(cookieptr->name, mlentry->name) == 0) {
matched = 1;
break;
}
}
if (matched != mode)
VTAILQ_REMOVE(&vcp->cookielist, cookieptr, list);
}
VTAILQ_FOREACH_SAFE(mlentry, &matchlist_head, list, mlsafe) {
VTAILQ_REMOVE(&matchlist_head, mlentry, list);
free(mlentry->name);
free(mlentry);
}
}
VCL_VOID
vmod_keep(VRT_CTX, struct vmod_priv *priv, VCL_STRING whitelist_s)
{
CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
filter_cookies(priv, whitelist_s, whitelist);
}
VCL_VOID
vmod_filter(VRT_CTX, struct vmod_priv *priv, VCL_STRING blacklist_s)
{
CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
filter_cookies(priv, blacklist_s, blacklist);
}
static VCL_VOID
re_filter(VRT_CTX, struct vmod_priv *priv, struct vmod_priv *priv_call,
VCL_STRING expression, enum filter_action mode)
{
struct vmod_cookie *vcp = cobj_get(priv);
struct cookie *current, *safeptr;
int i, ovector[VRE_MAX_GROUPS];
vre_t *vre;
CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
if (priv_call->priv == NULL) {
AZ(pthread_mutex_lock(&mtx));
vre = compile_re(ctx, expression);
if (vre == NULL) {
AZ(pthread_mutex_unlock(&mtx));
return; // Not much else to do, error already logged.
}
priv_call->priv = vre;
priv_call->free = free_re;
AZ(pthread_mutex_unlock(&mtx));
}
VTAILQ_FOREACH_SAFE(current, &vcp->cookielist, list, safeptr) {
CHECK_OBJ_NOTNULL(current, VMOD_COOKIE_ENTRY_MAGIC);
i = VRE_exec(priv_call->priv, current->name,
strlen(current->name), 0, 0, ovector, VRE_MAX_GROUPS, NULL);
switch (mode) {
case blacklist:
if (i < 0)
continue;
VSLb(ctx->vsl, SLT_Debug,
"Removing matching cookie %s (value: %s)",
current->name, current->value);
VTAILQ_REMOVE(&vcp->cookielist, current, list);
break;
case whitelist:
if (i >= 0) {
VSLb(ctx->vsl, SLT_Debug,
"Cookie %s matches expression '%s'",
current->name, expression);
continue;
}
VSLb(ctx->vsl, SLT_Debug,
"Removing cookie %s (value: %s)",
current->name, current->value);
VTAILQ_REMOVE(&vcp->cookielist, current, list);
break;
default:
WRONG("invalid mode");
}
}
}
VCL_VOID
vmod_keep_re(VRT_CTX, struct vmod_priv *priv, struct vmod_priv *priv_call,
VCL_STRING expression)
{
CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
re_filter(ctx, priv, priv_call, expression, whitelist);
}
VCL_VOID
vmod_filter_re(VRT_CTX, struct vmod_priv *priv, struct vmod_priv *priv_call,
VCL_STRING expression)
{
CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
re_filter(ctx, priv, priv_call, expression, blacklist);
}
VCL_STRING
vmod_get_string(VRT_CTX, struct vmod_priv *priv)
{
struct cookie *curr;
struct vsb output[1];
char *res;
struct vmod_cookie *vcp = cobj_get(priv);
CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
CHECK_OBJ_NOTNULL(ctx->ws, WS_MAGIC);
WS_VSB_new(output, ctx->ws);
VTAILQ_FOREACH(curr, &vcp->cookielist, list) {
CHECK_OBJ_NOTNULL(curr, VMOD_COOKIE_ENTRY_MAGIC);
AN(curr->name);
AN(curr->value);
VSB_printf(output, "%s%s=%s;",
(curr == VTAILQ_FIRST(&vcp->cookielist)) ? "" : " ",
curr->name, curr->value);
}
res = WS_VSB_finish(output, ctx->ws, NULL);
if (res == NULL)
VSLb(ctx->vsl, SLT_Error, "cookie: Workspace overflow");
return (res);
}
VCL_STRING
vmod_format_rfc1123(VRT_CTX, VCL_TIME ts, VCL_DURATION duration)
{
CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
return (VRT_TIME_string(ctx, ts + duration));
}
......@@ -15,6 +15,7 @@ dist_man_MANS = \
varnishtest.1 \
vtc.7 \
varnishtop.1 \
vmod_cookie.3 \
vmod_directors.3 \
vmod_purge.3 \
vmod_std.3 \
......@@ -91,6 +92,9 @@ varnishhist.1: \
$(top_builddir)/doc/sphinx/include/varnishhist_synopsis.rst
$(BUILD_MAN) $(top_srcdir)/doc/sphinx/reference/varnishhist.rst $@
vmod_cookie.3: $(top_builddir)/lib/libvmod_cookie/vmod_cookie.man.rst
$(BUILD_MAN) $? $@
vmod_directors.3: $(top_builddir)/lib/libvmod_directors/vmod_directors.man.rst
$(BUILD_MAN) $? $@
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment