Commit 04b3e9e0 authored by Geoff Simmons's avatar Geoff Simmons

Add the .check_call() method.

parent 7edfb217
......@@ -1533,6 +1533,15 @@ Example:
XXX ...
#### BOOL xset.check\_call(INT n, ENUM select)
BOOL xset.check_call(
INT n=0,
ENUM {FIRST, LAST, UNIQUE} select=UNIQUE
)
XXX ...
#### BOOL xset.saved(ENUM which, INT n, ENUM select)
BOOL xset.saved(
......
......@@ -782,6 +782,81 @@ vmod_set_subroutine(VRT_CTX, struct VPFX(re2_set) *set, VCL_INT n,
return set->sub[idx];
}
VCL_BOOL
vmod_set_check_call(VRT_CTX, struct VPFX(re2_set) *set, VCL_INT n,
VCL_ENUM selects)
{
struct task_set_match *task;
int idx = 0;
VCL_STRING err;
CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
CHECK_OBJ_NOTNULL(set, VMOD_RE2_SET_MAGIC);
if (set->sub == NULL) {
VNOTICE(ctx,
"%s.check_call(%jd): No subroutines were set for %s",
set->vcl_name, (intmax_t)n, set->vcl_name);
return (0);
}
/*
* XXX: considerable DRY with get_match_idx().
* get_match_idx() invokes VRT_fail(), but we need to log
* SLT_Notice here, and the two alternatives are hard to
* disentangle within get_match_idx(). An alternative would be to
* pass in a flag to get_match_idx() that chooses between the two,
* and then we have to add the flag everywhere else. Consider
* that if we add anything else that needs SLT_Notice.
*/
if (n > set->npatterns) {
VNOTICE(ctx, "%s.check_call(%jd): set has %d patterns",
set->vcl_name, (intmax_t)n, set->npatterns);
return (0);
}
if (n <= 0) {
if ((task = get_task_data(ctx, set)) == NULL) {
VNOTICE(ctx,
"%s.check_call() called without prior match",
set->vcl_name);
return (0);
}
if (task->nmatches == 0) {
VNOTICE(ctx, "%s.check_call(%jd): previous match was "
"unsuccessful", set->vcl_name, (intmax_t)n);
return (0);
}
if (task->nmatches > 1) {
if (selects == VENUM(UNIQUE)) {
VNOTICE(ctx, "%s.check_call(%jd): %ld "
"successful matches", set->vcl_name,
(intmax_t)n, task->nmatches);
return (0);
}
if (selects == VENUM(LAST))
idx = task->nmatches - 1;
else
assert(selects == VENUM(FIRST));
}
WS_Assert_Allocated(ctx->ws, task->matches,
task->nmatches * sizeof(int));
idx = task->matches[idx];
}
if (!vbit_test(set->added[SUBROUTINE], idx)) {
AN(selects);
VNOTICE(ctx,
"%s.check_call(%jd, %s): subroutine %d was not added",
set->vcl_name, n, selects, idx + 1);
return (0);
}
if ((err = VRT_check_call(ctx, set->sub[idx])) != NULL) {
VNOTICE(ctx, "%s.check_call(): %s", set->vcl_name, err);
return (0);
}
return (1);
}
VCL_BOOL
vmod_set_saved(VRT_CTX, struct vmod_re2_set *set, VCL_ENUM whichs, VCL_INT n,
VCL_ENUM selects)
......
......@@ -35,12 +35,22 @@ varnish v1 -vcl+backend {
}
sub vcl_deliver {
set resp.http.Check-1 = s.check_call(1);
set resp.http.Check-2 = s.check_call(2);
set resp.http.X = "N";
call s.subroutine(1);
call s.subroutine(2);
set resp.http.s-saved-1 = s.saved(SUB, 1);
set resp.http.s-saved-2 = s.saved(SUB, 2);
set resp.http.s-check-before-match = s.check_call();
set resp.http.s-check-unique-before-match =
s.check_call(select=UNIQUE);
set resp.http.s-check-first-before-match =
s.check_call(select=FIRST);
set resp.http.s-check-last-before-match =
s.check_call(select=LAST);
if (req.http.Test == "b4match") {
if (req.http.Call == "subroutine") {
call s.subroutine();
......@@ -53,12 +63,20 @@ varnish v1 -vcl+backend {
set resp.http.s-foo-match = s.match("foo");
set resp.http.s-foo-n = s.nmatches();
set resp.http.Foo-Check = s.check_call();
set resp.http.Foo-Check-Unique = s.check_call(select=UNIQUE);
set resp.http.Foo-Check-First = s.check_call(select=FIRST);
set resp.http.Foo-Check-Last = s.check_call(select=LAST);
set resp.http.X = "F";
call s.subroutine();
set resp.http.s-foo-saved = s.saved(SUB);
set resp.http.s-bar-match = s.match("bar");
set resp.http.s-bar-n = s.nmatches();
set resp.http.Bar-Check = s.check_call();
set resp.http.Bar-Check-Unique = s.check_call(select=UNIQUE);
set resp.http.Bar-Check-First = s.check_call(select=FIRST);
set resp.http.Bar-Check-Last = s.check_call(select=LAST);
set resp.http.X = "B";
call s.subroutine();
set resp.http.s-bar-saved = s.saved(SUB);
......@@ -66,9 +84,15 @@ varnish v1 -vcl+backend {
call s.subroutine(-1);
set resp.http.s-bar-saved-0 = s.saved(SUB, 0);
set resp.http.s-bar-saved-1 = s.saved(SUB, -1);
set resp.http.Bar-Check-0 = s.check_call(0);
set resp.http.Bar-Check-1 = s.check_call(-1);
set resp.http.s-fail-match = s.match("fail");
set resp.http.s-fail-n = s.nmatches();
set resp.http.Fail-Check = s.check_call();
set resp.http.Fail-Check-Unique = s.check_call(select=UNIQUE);
set resp.http.Fail-Check-First = s.check_call(select=FIRST);
set resp.http.Fail-Check-Last = s.check_call(select=LAST);
if (req.http.Test == "failmatch") {
set resp.http.X = "X";
if (req.http.Call == "subroutine") {
......@@ -82,6 +106,10 @@ varnish v1 -vcl+backend {
set resp.http.s-many-match = s.match("foobar");
set resp.http.s-many-n = s.nmatches();
set resp.http.X = "2";
set resp.http.Many-Check = s.check_call();
set resp.http.Many-Check-Unique = s.check_call(select=UNIQUE);
set resp.http.Many-Check-First = s.check_call(select=FIRST);
set resp.http.Many-Check-Last = s.check_call(select=LAST);
call s.subroutine(select=FIRST);
call s.subroutine(select=LAST);
set resp.http.s-many-saved-first = s.saved(SUB, select=FIRST);
......@@ -95,6 +123,7 @@ varnish v1 -vcl+backend {
set resp.http.s-many-saved = s.saved(SUB);
}
}
set resp.http.Range-Check = s.check_call(3);
if (req.http.Test == "range") {
set resp.http.X = "3";
call s.subroutine(3);
......@@ -109,22 +138,74 @@ client c1 {
expect resp.status == 200
expect resp.http.Foo == "NF2"
expect resp.http.Bar == "NBBB2"
expect resp.http.Check-1 == "true"
expect resp.http.Check-2 == "true"
expect resp.http.s-saved-1 == "true"
expect resp.http.s-saved-2 == "true"
expect resp.http.s-check-before-match == "false"
expect resp.http.s-check-unique-before-match == "false"
expect resp.http.s-check-first-before-match == "false"
expect resp.http.s-check-last-before-match == "false"
expect resp.http.s-foo-match == "true"
expect resp.http.s-foo-n == 1
expect resp.http.Foo-Check == "true"
expect resp.http.Foo-Check-Unique == "true"
expect resp.http.Foo-Check-First == "true"
expect resp.http.Foo-Check-Last == "true"
expect resp.http.s-foo-saved == "true"
expect resp.http.s-bar-match == "true"
expect resp.http.s-bar-n == 1
expect resp.http.Bar-Check == "true"
expect resp.http.Bar-Check-Unique == "true"
expect resp.http.Bar-Check-First == "true"
expect resp.http.Bar-Check-Last == "true"
expect resp.http.s-bar-saved == "true"
expect resp.http.s-bar-saved-0 == "true"
expect resp.http.s-bar-saved-1 == "true"
expect resp.http.Bar-Check-0 == "true"
expect resp.http.Bar-Check-1 == "true"
expect resp.http.s-fail-match == "false"
expect resp.http.s-fail-n == 0
expect resp.http.Fail-Check == "false"
expect resp.http.Fail-Check-Unique == "false"
expect resp.http.Fail-Check-First == "false"
expect resp.http.Fail-Check-Last == "false"
expect resp.http.s-many-match == "true"
expect resp.http.s-many-n == 2
expect resp.http.Many-Check == "false"
expect resp.http.Many-Check-Unique == "false"
expect resp.http.Many-Check-First == "true"
expect resp.http.Many-Check-Last == "true"
expect resp.http.s-many-saved-first == "true"
expect resp.http.s-many-saved-last == "true"
expect resp.http.Range-Check == "false"
} -run
logexpect l1 -v v1 -d 1 -g vxid -q "Notice" {
expect 0 * Begin req
expect * = Notice {^vmod_re2: s\.check_call\(\) called without prior match$}
expect 0 = RespHeader {^s-check-before-match: false$}
expect * = Notice {^vmod_re2: s\.check_call\(\) called without prior match$}
expect 0 = RespHeader {^s-check-unique-before-match: false$}
expect * = Notice {^vmod_re2: s\.check_call\(\) called without prior match$}
expect 0 = RespHeader {^s-check-first-before-match: false$}
expect * = Notice {^vmod_re2: s\.check_call\(\) called without prior match$}
expect 0 = RespHeader {^s-check-last-before-match: false$}
expect * = Notice {^vmod_re2: s\.check_call\(0\): previous match was unsuccessful$}
expect 0 = RespHeader {^Fail-Check: false$}
expect * = Notice {^vmod_re2: s\.check_call\(0\): previous match was unsuccessful$}
expect 0 = RespHeader {^Fail-Check-Unique: false$}
expect * = Notice {^vmod_re2: s\.check_call\(0\): previous match was unsuccessful$}
expect 0 = RespHeader {^Fail-Check-First: false$}
expect * = Notice {^vmod_re2: s\.check_call\(0\): previous match was unsuccessful$}
expect 0 = RespHeader {^Fail-Check-Last: false$}
expect * = Notice {^vmod_re2: s\.check_call\(0\): 2 successful matches$}
expect 0 = RespHeader {^Many-Check: false$}
expect * = Notice {^vmod_re2: s\.check_call\(0\): 2 successful matches$}
expect 0 = RespHeader {^Many-Check-Unique: false$}
expect * = Notice {^vmod_re2: s\.check_call\(3\): set has 2 patterns$}
expect 0 = RespHeader {^Range-Check: false$}
expect * = End
} -run
client c1 {
......@@ -246,3 +327,216 @@ logexpect l1 -v v1 -d 1 -g vxid -q "VCL_Error" {
expect 0 = VCL_return fail
expect * = End
} -run
varnish v1 -vcl {
import ${vmod_re2};
backend b None;
sub foo {
set req.http.Foo = "called";
}
sub vcl_init {
new s = re2.set();
s.add("foo", sub=foo);
s.add("bar");
}
sub vcl_recv {
if (s.match(req.http.Word)) {
set req.http.Check = s.check_call();
call s.subroutine();
}
return (synth(200));
}
sub vcl_synth {
set resp.http.Check = req.http.Check;
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 {^s\.subroutine\(0, UNIQUE\): subroutine 2 was not added$}
expect * = VCL_return fail
expect * = End
} -start
logexpect l2 -v v1 -d 0 -g vxid -q "Notice" {
expect 0 * Begin req
expect * = Notice {^vmod_re2: s\.check_call\(0, UNIQUE\): subroutine 2 was not added$}
expect 0 = ReqHeader {^Check: false$}
expect * = End
} -start
client c1 {
txreq -hdr "Word: foo"
rxresp
expect resp.status == 200
expect resp.http.Check == "true"
expect resp.http.Foo == "called"
txreq -hdr "Word: bar"
rxresp
expect resp.status == 503
expect resp.reason == "VCL failed"
expect resp.http.Check == ""
expect resp.http.Foo == ""
expect_close
} -run
logexpect l1 -wait
logexpect l2 -wait
varnish v1 -vcl {
import ${vmod_re2};
backend b None;
sub vcl_init {
new s = re2.set();
s.add("foo");
}
sub vcl_recv {
set req.http.Check = s.check_call(1);
call s.subroutine(1);
return (synth(200));
}
sub vcl_synth {
set resp.http.Check = req.http.Check;
return (deliver);
}
}
logexpect l1 -v v1 -d 0 -g vxid -q "VCL_Error" {
expect 0 * Begin req
expect * = VCL_Error {^s\.subroutine\(1\): No subroutines were set for s$}
expect * = VCL_return fail
expect * = End
} -start
logexpect l2 -v v1 -d 0 -g vxid -q "Notice" {
expect 0 * Begin req
expect * = Notice {^vmod_re2: s\.check_call\(1\): No subroutines were set for s$}
expect 0 = ReqHeader {^Check: false$}
expect * = End
} -start
client c1 {
txreq
rxresp
expect resp.status == 503
expect resp.reason == "VCL failed"
expect resp.http.Check == ""
expect_close
} -run
logexpect l1 -wait
logexpect l2 -wait
varnish v1 -errvcl {Symbol not found: 'foo'} {
import ${vmod_re2};
backend b None;
sub vcl_init {
new s = re2.set();
s.add("foo", sub=foo);
}
}
varnish v1 -vcl {
import ${vmod_re2};
backend b None;
sub foo {
set bereq.http.Foo = "called";
}
sub vcl_init {
new s = re2.set();
s.add("foo", sub=foo);
}
sub vcl_recv {
set req.http.Check = s.check_call(1);
call s.subroutine(1);
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
logexpect l2 -v v1 -d 0 -g vxid -q "Notice" {
expect 0 * Begin req
expect * = Notice {^vmod_re2: s\.check_call\(\): Dynamic call to "sub foo\{\}" not allowed from here$}
expect 0 = ReqHeader {^Check: false$}
expect * = End
} -start
client c1 {
txreq
rxresp
expect resp.status == 503
expect resp.reason == "VCL failed"
expect_close
} -run
logexpect l1 -wait
logexpect l2 -wait
varnish v1 -vcl {
import ${vmod_re2};
backend b None;
sub foo {
call bar;
}
sub vcl_init {
new s = re2.set();
s.add("foo", sub=foo);
}
sub bar {
set req.http.Check = s.check_call(1);
call s.subroutine(1);
}
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
logexpect l2 -v v1 -d 0 -g vxid -q "Notice" {
expect 0 * Begin req
expect * = Notice {^vmod_re2: s\.check_call\(\): Recursive dynamic call to "sub foo\{\}"$}
expect 0 = ReqHeader {^Check: false$}
expect * = End
} -start
client c1 {
txreq
rxresp
expect resp.status == 503
expect resp.reason == "VCL failed"
expect_close
} -run
logexpect l1 -wait
logexpect l2 -wait
......@@ -47,6 +47,9 @@
#define VERRNOMEM(ctx, fmt, ...) \
VFAIL((ctx), fmt ", out of space", __VA_ARGS__)
#define VNOTICE(ctx, fmt, ...) \
VSLb((ctx)->vsl, SLT_Notice, "vmod_re2: " fmt, __VA_ARGS__)
struct vmod_re2_regex {
unsigned magic;
#define VMOD_RE2_REGEX_MAGIC 0x5c3f6f24
......
......@@ -1401,6 +1401,10 @@ $Method SUB .subroutine(INT n=0, ENUM {FIRST, LAST, UNIQUE} select=UNIQUE)
XXX ...
$Method BOOL .check_call(INT n=0, ENUM {FIRST, LAST, UNIQUE} select=UNIQUE)
XXX ...
$Method BOOL .saved(ENUM {REGEX, STR, BE, INT, SUB} which=REGEX, INT n=0,
ENUM {FIRST, LAST, UNIQUE} select=UNIQUE)
......
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