Commit d6d30486 authored by Geoff Simmons's avatar Geoff Simmons

Add the .subroutine() method.

parent 5cea4e6d
......@@ -438,8 +438,8 @@ Examples::
.. _xset.add():
VOID xset.add(STRING, [STRING string], [STRING regex], [BACKEND backend], [INT integer], [BOOL bool])
-----------------------------------------------------------------------------------------------------
VOID xset.add(STRING, [STRING string], [STRING regex], [BACKEND backend], [INT integer], [BOOL bool], [SUB sub])
----------------------------------------------------------------------------------------------------------------
::
......@@ -449,7 +449,8 @@ VOID xset.add(STRING, [STRING string], [STRING regex], [BACKEND backend], [INT i
[STRING regex],
[BACKEND backend],
[INT integer],
[BOOL bool]
[BOOL bool],
[SUB sub]
)
Add the given string to the set. As indicated above, elements added to
......@@ -1056,6 +1057,21 @@ Example::
# /foo/bar/1/2/* is rewritten as /foo/bar/2/1/*
# /foo/bar/baz/1/2/* is rewritten as /foo/bar/baz/2/1/*
.. _xset.subroutine():
SUB xset.subroutine(INT n, STRING element, ENUM select)
-------------------------------------------------------
::
SUB xset.subroutine(
INT n=0,
STRING element=0,
ENUM {UNIQUE, EXACT, FIRST, LAST, SHORTEST, LONGEST} select=UNIQUE
)
XXX ...
.. _selector.version():
STRING version()
......
......@@ -273,3 +273,21 @@ vmod_set_bool(VRT_CTX, struct VPFX(selector_set) *set, VCL_INT n,
return (set->table[idx]->bool);
}
VCL_SUB
vmod_set_subroutine(VRT_CTX, struct VPFX(selector_set) *set, VCL_INT n,
VCL_STRING element, VCL_ENUM selects)
{
unsigned idx;
CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
CHECK_OBJ_NOTNULL(set, VMOD_SELECTOR_SET_MAGIC);
idx = get_idx(ctx, n, set, "subroutine", element, selects);
if (idx == UINT_MAX)
return (NULL);
if (!check_added(ctx, set, idx, SUB, "subroutine", "subroutine"))
return (NULL);
return (set->table[idx]->sub);
}
# looks like -*- vcl -*-
varnishtest "call() method"
varnish v1 -vcl {
import ${vmod_selector};
backend b None;
sub foo {
if (!resp.http.Foo) {
set resp.http.Foo = resp.http.X;
}
else {
set resp.http.Foo = resp.http.Foo + resp.http.X;
}
}
sub bar {
if (!resp.http.Bar) {
set resp.http.Bar = resp.http.X;
}
else {
set resp.http.Bar = resp.http.Bar + resp.http.X;
}
}
sub baz {
if (!resp.http.Baz) {
set resp.http.Baz = resp.http.X;
}
else {
set resp.http.Baz = resp.http.Baz + resp.http.X;
}
}
sub quux {
if (!resp.http.Quux) {
set resp.http.Quux = resp.http.X;
}
else {
set resp.http.Quux = resp.http.Quux + resp.http.X;
}
}
sub foobar {
if (!resp.http.Foobar) {
set resp.http.Foobar = resp.http.X;
}
else {
set resp.http.Foobar = resp.http.Foobar + resp.http.X;
}
}
sub vcl_init {
new s = selector.set();
s.add("foo", sub=foo);
s.add("bar", sub=bar);
s.add("baz", sub=baz);
s.add("quux", sub=quux);
s.add("foobar", sub=foobar);
}
sub vcl_recv {
return (synth(200));
}
sub vcl_synth {
set resp.http.X = "N";
call s.subroutine(1);
call s.subroutine(2);
call s.subroutine(3);
call s.subroutine(4);
call s.subroutine(5);
set resp.http.X = "S";
if (s.match(req.http.Word)) {
call s.subroutine();
call s.subroutine(select=UNIQUE);
call s.subroutine(select=EXACT);
call s.subroutine(select=FIRST);
call s.subroutine(select=LAST);
call s.subroutine(select=SHORTEST);
call s.subroutine(select=LONGEST);
}
set resp.http.X = "E";
call s.subroutine(element="foo");
call s.subroutine(element="bar");
call s.subroutine(element="baz");
call s.subroutine(element="quux");
call s.subroutine(element="foobar");
if (req.http.Element) {
call s.subroutine(element=req.http.Element);
}
return (deliver);
}
} -start
client c1 {
txreq
rxresp
expect resp.status == 200
expect resp.http.Foo == "NE"
expect resp.http.Bar == "NE"
expect resp.http.Baz == "NE"
expect resp.http.Quux == "NE"
expect resp.http.Foobar == "NE"
txreq -hdr "Word: foo"
rxresp
expect resp.status == 200
expect resp.http.Foo == "NSSSSSSSE"
expect resp.http.Bar == "NE"
expect resp.http.Baz == "NE"
expect resp.http.Quux == "NE"
expect resp.http.Foobar == "NE"
txreq -hdr "Word: bar"
rxresp
expect resp.status == 200
expect resp.http.Foo == "NE"
expect resp.http.Bar == "NSSSSSSSE"
expect resp.http.Baz == "NE"
expect resp.http.Quux == "NE"
expect resp.http.Foobar == "NE"
txreq -hdr "Word: baz"
rxresp
expect resp.status == 200
expect resp.http.Foo == "NE"
expect resp.http.Bar == "NE"
expect resp.http.Baz == "NSSSSSSSE"
expect resp.http.Quux == "NE"
expect resp.http.Foobar == "NE"
txreq -hdr "Word: quux"
rxresp
expect resp.status == 200
expect resp.http.Foo == "NE"
expect resp.http.Bar == "NE"
expect resp.http.Baz == "NE"
expect resp.http.Quux == "NSSSSSSSE"
expect resp.http.Foobar == "NE"
txreq -hdr "Word: foobar"
rxresp
expect resp.status == 200
expect resp.http.Foo == "NE"
expect resp.http.Bar == "NE"
expect resp.http.Baz == "NE"
expect resp.http.Quux == "NE"
expect resp.http.Foobar == "NSSSSSSSE"
} -run
client c1 {
txreq -hdr "Element: oof"
expect_close
} -run
logexpect l1 -v v1 -d 1 -g vxid -q "Notice" {
expect 0 * Begin req
expect * = Notice {^vmod_selector: s\.match\(\): subject string is NULL$}
expect * = End
} -run
logexpect l1 -v v1 -d 1 -g vxid -q "VCL_Error" {
expect 0 * Begin req
expect * = VCL_Error {^vmod selector failure: s\.subroutine\(element="oof"\): no such element$}
expect * = VCL_return fail
expect * = End
} -run
varnish v1 -vcl {
import ${vmod_selector};
import std;
backend b None;
sub foo {
set resp.http.Called = "foo";
}
sub bar {
set resp.http.Called = "bar";
}
sub baz {
set resp.http.Called = "baz";
}
sub quux {
set resp.http.Called = "quux";
}
sub foobar {
set resp.http.Called = "foobar";
}
sub vcl_init {
new s = selector.set();
s.add("foo", sub=foo);
s.add("bar", sub=bar);
s.add("baz", sub=baz);
s.add("quux", sub=quux);
s.add("foobar", sub=foobar);
}
sub vcl_recv {
call s.subroutine(std.integer(req.http.Int));
return (synth(200));
}
}
logexpect l1 -v v1 -d 0 -g vxid -q "VCL_Error" {
expect 0 * Begin req
expect * = VCL_Error {^vmod selector failure: s\.subroutine\(\) called without prior match$}
expect * = VCL_return fail
expect * = End
expect 0 * Begin req
expect * = VCL_Error {^vmod selector failure: s\.subroutine\(\) called without prior match$}
expect * = VCL_return fail
expect * = End
expect 0 * Begin req
expect * = VCL_Error {^vmod selector failure: s\.subroutine\(6\): set has 5 elements$}
expect * = VCL_return fail
expect * = End
} -start
client c1 {
txreq -hdr "Int: -1"
rxresp
expect resp.status == 503
expect resp.reason == "VCL failed"
expect resp.http.Called == <undef>
expect_close
} -run
client c1 {
txreq -hdr "Int: 0"
rxresp
expect resp.status == 503
expect resp.reason == "VCL failed"
expect resp.http.Called == <undef>
expect_close
} -run
client c1 {
txreq -hdr "Int: 6"
rxresp
expect resp.status == 503
expect resp.reason == "VCL failed"
expect resp.http.Called == <undef>
expect_close
} -run
logexpect l1 -wait
varnish v1 -vcl {
import ${vmod_selector};
backend b None;
sub foo {
if (!resp.http.Foo) {
set resp.http.Foo = "X";
}
else {
set resp.http.Foo = resp.http.Foo + "X";
}
}
sub notfoo {
set resp.http.Called = "notfoo";
}
sub vcl_init {
new s = selector.set();
s.add("foobarbazquux", sub=notfoo);
s.add("foobarbaz", sub=notfoo);
s.add("foobar", sub=notfoo);
s.add("foo", sub=foo);
}
sub vcl_recv {
return (synth(200));
}
sub vcl_synth {
if (s.hasprefix(req.http.Word)) {
call s.subroutine();
call s.subroutine(select=UNIQUE);
call s.subroutine(select=EXACT);
call s.subroutine(select=FIRST);
call s.subroutine(select=LAST);
call s.subroutine(select=SHORTEST);
call s.subroutine(select=LONGEST);
}
if (req.http.Element) {
call s.subroutine(element=req.http.Element);
call s.subroutine(select=UNIQUE);
call s.subroutine(select=EXACT);
call s.subroutine(select=FIRST);
call s.subroutine(select=LAST);
call s.subroutine(select=SHORTEST);
call s.subroutine(select=LONGEST);
}
return (deliver);
}
}
client c1 {
txreq -hdr "Word: foo"
rxresp
expect resp.status == 200
expect resp.http.Foo == "XXXXXXX"
expect resp.http.Called == <undef>
txreq -hdr "Element: foo"
rxresp
expect resp.status == 200
expect resp.http.Foo == "XXXXXXX"
expect resp.http.Called == <undef>
} -run
varnish v1 -vcl {
import ${vmod_selector};
backend b None;
sub foo {
if (!resp.http.Foo) {
set resp.http.Foo = resp.http.X;
}
else {
set resp.http.Foo = resp.http.Foo + resp.http.X;
}
}
sub foobar {
if (!resp.http.Foobar) {
set resp.http.Foobar = resp.http.X;
}
else {
set resp.http.Foobar = resp.http.Foobar + resp.http.X;
}
}
sub foobarbaz {
if (!resp.http.Foobarbaz) {
set resp.http.Foobarbaz = resp.http.X;
}
else {
set resp.http.Foobarbaz
= resp.http.Foobarbaz + resp.http.X;
}
}
sub foobarbazquux {
if (!resp.http.Foobarbazquux) {
set resp.http.Foobarbazquux = resp.http.X;
}
else {
set resp.http.Foobarbazquux
= resp.http.Foobarbazquux + resp.http.X;
}
}
sub vcl_init {
new s = selector.set();
s.add("foobarbazquux", sub=foobarbazquux);
s.add("foobarbaz", sub=foobarbaz);
s.add("foobar", sub=foobar);
s.add("foo", sub=foo);
}
sub vcl_recv {
return (synth(200));
}
sub vcl_synth {
if (s.hasprefix(req.http.Word)) {
set resp.http.X = "E";
call s.subroutine(select=EXACT);
set resp.http.X = "F";
call s.subroutine(select=FIRST);
set resp.http.X = "La";
call s.subroutine(select=LAST);
set resp.http.X = "S";
call s.subroutine(select=SHORTEST);
set resp.http.X = "Lo";
call s.subroutine(select=LONGEST);
}
return (deliver);
}
}
client c1 {
txreq -hdr "Word: foobar"
rxresp
expect resp.status == 200
expect resp.http.Foo == "LaS"
expect resp.http.Foobar == "EFLo"
expect resp.http.Foobarbaz == <undef>
expect resp.http.Foobarbazquux == <undef>
txreq -hdr "Word: foobarbaz"
rxresp
expect resp.status == 200
expect resp.http.Foo == "LaS"
expect resp.http.Foobar == <undef>
expect resp.http.Foobarbaz == "EFLo"
expect resp.http.Foobarbazquux == <undef>
txreq -hdr "Word: foobarbazquux"
rxresp
expect resp.status == 200
expect resp.http.Foo == "LaS"
expect resp.http.Foobar == <undef>
expect resp.http.Foobarbaz == <undef>
expect resp.http.Foobarbazquux == "EFLo"
} -run
varnish v1 -vcl {
import ${vmod_selector};
backend b None;
sub foo {
set req.http.Foo = "called";
}
sub foobar {
set req.http.Foobar = "called";
}
sub foobarbaz {
set req.http.Foobarbaz = "called";
}
sub foobarbazquux {
set req.http.Foobarbazquux = "called";
}
sub vcl_init {
new s = selector.set();
s.add("foobarbazquux", sub=foobarbazquux);
s.add("foobarbaz", sub=foobarbaz);
s.add("foobar", sub=foobar);
s.add("foo", sub=foo);
}
sub vcl_recv {
if (s.hasprefix(req.http.Word)) {
call s.subroutine(select=EXACT);
call s.subroutine(select=UNIQUE);
}
return (synth(200));
}
}
logexpect l1 -v v1 -d 0 -g vxid -q "VCL_Error" {
expect * * Begin req
expect * = ReqHeader "Foobar: called"
expect * = VCL_Error {^vmod selector failure: s\.subroutine\(select=UNIQUE\): 2 elements were matched$}
expect * = VCL_return fail
expect * = End
expect 0 * Begin req
expect * = VCL_Error {^vmod selector failure: s\.subroutine\(select=EXACT\): no element matched exactly$}
expect * = VCL_return fail
expect * = End
} -start
client c1 {
txreq -hdr "Word: foobar"
rxresp
expect resp.status == 503
expect resp.reason == "VCL failed"
expect_close
} -run
client c1 {
txreq -hdr "Word: foobarb"
rxresp
expect resp.status == 503
expect resp.reason == "VCL failed"
expect_close
} -run
logexpect l1 -wait
varnish v1 -vcl {
import ${vmod_selector};
backend b None;
sub foo {
set req.http.Foo = "called";
}
sub vcl_init {
new s = selector.set();
s.add("foo", sub=foo);
s.add("bar");
}
sub vcl_recv {
if (s.match(req.http.Word)) {
call s.subroutine();
}
return (synth(200));
}
sub vcl_synth {
set resp.http.Foo = req.http.Foo;
return (deliver);
}
}
logexpect l1 -v v1 -d 0 -g vxid -q "VCL_Error" {
expect 0 * Begin req
expect * = VCL_Error {^vmod selector failure: s\.subroutine\(\): subroutine not added for element 2$}
expect * = VCL_return fail
expect * = End
} -start
client c1 {
txreq -hdr "Word: foo"
rxresp
expect resp.status == 200
expect resp.http.Foo == "called"
txreq -hdr "Word: bar"
rxresp
expect resp.status == 503
expect resp.reason == "VCL failed"
expect_close
} -run
logexpect l1 -wait
varnish v1 -errvcl {Symbol not found: 'foo'} {
import ${vmod_selector};
backend b None;
sub vcl_init {
new s = selector.set();
s.add("foo", sub=foo);
}
}
varnish v1 -vcl {
import ${vmod_selector};
backend b None;
sub foo {
set bereq.http.Foo = "called";
}
sub vcl_init {
new s = selector.set();
s.add("foo", sub=foo);
}
sub vcl_recv {
call s.subroutine(element="foo");
return (synth(200));
}
}
logexpect l1 -v v1 -d 0 -g vxid -q "VCL_Error" {
expect 0 * Begin req
expect * = VCL_Error {^call to "sub foo\{\}" not allowed from here$}
expect * = VCL_return fail
expect * = End
} -start
client c1 {
txreq
rxresp
expect resp.status == 503
expect resp.reason == "VCL failed"
expect_close
} -run
logexpect l1 -wait
varnish v1 -vcl {
import ${vmod_selector};
backend b None;
sub foo {
call bar;
}
sub vcl_init {
new s = selector.set();
s.add("foo", sub=foo);
}
sub bar {
call s.subroutine(element="foo");
}
sub vcl_recv {
call foo;
return (synth(200));
}
}
logexpect l1 -v v1 -d 0 -g vxid -q "VCL_Error" {
expect 0 * Begin req
expect * = VCL_Error {^Recursive call to "sub foo\{\}"$}
expect * = VCL_return fail
expect * = End
} -start
client c1 {
txreq
rxresp
expect resp.status == 503
expect resp.reason == "VCL failed"
expect_close
} -run
logexpect l1 -wait
......@@ -506,7 +506,7 @@ vmod_set_add(VRT_CTX, struct vmod_selector_set *set,
}
if (!args->valid_string && re == NULL && !args->valid_backend
&& !args->valid_integer && !args->valid_bool)
&& !args->valid_integer && !args->valid_bool && !args->valid_sub)
return;
set->table = realloc(set->table, n * sizeof(struct entry *));
......@@ -534,6 +534,10 @@ vmod_set_add(VRT_CTX, struct vmod_selector_set *set,
entry->bool = args->bool;
set_added(set, n - 1, BOOLEAN);
}
if (args->valid_sub) {
entry->sub = args->sub;
set_added(set, n - 1, SUB);
}
set->table[n - 1] = entry;
}
......
......@@ -48,11 +48,12 @@
struct entry {
unsigned magic;
#define VMOD_SELECTOR_ENTRY_MAGIC 0x733dbe63
VCL_BOOL bool;
char *string;
VCL_BACKEND backend;
VCL_SUB sub;
vre_t *re;
VCL_INT integer;
VCL_BOOL bool;
};
enum bitmap_e {
......@@ -61,6 +62,7 @@ enum bitmap_e {
REGEX,
INTEGER,
BOOLEAN,
SUB,
__MAX_BITMAP,
};
......
......@@ -423,7 +423,7 @@ Examples::
}
$Method VOID .add(STRING, [STRING string], [STRING regex], [BACKEND backend],
[INT integer], [BOOL bool])
[INT integer], [BOOL bool], [SUB sub])
Add the given string to the set. As indicated above, elements added to
the set are implicitly numbered in the order in which they are added
......@@ -931,6 +931,12 @@ Example::
# /foo/bar/1/2/* is rewritten as /foo/bar/2/1/*
# /foo/bar/baz/1/2/* is rewritten as /foo/bar/baz/2/1/*
$Method SUB .subroutine(INT n=0, STRING element=0,
ENUM {UNIQUE, EXACT, FIRST, LAST, SHORTEST, LONGEST}
select=UNIQUE)
XXX ...
$Function STRING version()
Return the version string for this VMOD.
......
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