Commit c70e410a authored by Geoff Simmons's avatar Geoff Simmons

Breaking changes to invoke VCL failure for "can't happen" errors.

Up to now we have emitted sentinel values for this class of errors,
a relic of the time before VCL failure was introduced, when the VMOD
was first developed. This may lead to fatal errors going unnoticed.
These are the sorts of errors that should fail fast in development
and testing, and never make it into production.

We were already using VCL failure for the integer() method, since
there is no sentinel integer.

VCL failure is now invoked for these errors:

- any regex compilation failure

- all out of workspace errors

- compile() or add() called in any VCL subroutine besides vcl_init

- match() called for a set that was not compiled

- failures reported by the RE2 lib for: match(), backref(),
  namedref(), the rewrite operations (sub, suball and extract),
  cost(), quotemeta()

- numeric index reference for a set object (n parameter) that is out
  of range (greater than the number of patterns in the set)

- functions and methods that require a previous successful match
  operation (with information stored in priv_task) when there was no
  prior match, or the previous macth failed.

- use of select=UNIQUE when more than one pattern in a set matched

- numeric (by index) or "associative" (after match) retrieval of an
  object for a set when no such object was saved in the constructor:
  strings, backends, regexen, etc

- any of the following are undefined (NULL): fallbacks; patterns for
  regex functions (which are compiled at runtime); the text and
  rewrite parameters for the rewrite operations; name parameter
  for namedrefs

- also if the named parameter for namedrefs is the empty string

- backref number out of range (greater than the number of backrefs)

- backref or namedref attempted without a prior match

- backref or namedref when never_capture=true in the contructor

- low-level failures reported by RE2 (eg cannot determine the number
  of backref groups). Most of these are possible, since the call
  returns an error status, but I have never seen them happen.

VTC tests have been revised for the new error handling, which changes
quite a bit in this commit.

While we're here, use backend None in the tests where appropriate.
Note that "bad_ip" backends are still necessary to test methods and
functions that retrieve backends (numeric or associative references
for sets).

Error reporting is now done with VCL_fail(), so we get rid of the
errmsg() function, and with it the re2.c source (since that was all
there was in re2.c).
parent 07cbe881
......@@ -1638,12 +1638,7 @@ Example:
black.hdr_filter(resp, false);
}
#### STRING quotemeta(STRING, STRING fallback)
STRING quotemeta(
STRING,
STRING fallback="**QUOTEMETA FUNCTION FAILED**"
)
#### STRING quotemeta(STRING)
Returns a copy of the argument string with all regex metacharacters
escaped via backslash. When the returned string is used as a regular
......
......@@ -10,7 +10,6 @@ libvmod_re2_la_SOURCES = \
vmod_re2.h \
vmod_re2.c \
set.c \
re2.c \
vre2/vre2.h \
vre2/vre2.cpp \
vre2/vre2set.h \
......@@ -25,7 +24,7 @@ MAINTAINERCLEANFILES = $(dist_man_MANS)
libvmod_re2_la_LIBADD = @RE2_LIBS@
vmod_re2.c set.c re2.c: vmod_re2.h
vmod_re2.c set.c: vmod_re2.h
vmod_re2.h: vcc_if.h
......
/*-
* Copyright (c) 2017 UPLEX Nils Goroll Systemoptimierung
* All rights reserved
*
* Author: Geoffrey Simmons <geoffrey.simmons@uplex.de>
*
* 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.
*/
#include "vmod_re2.h"
void
errmsg(VRT_CTX, const char *fmt, ...)
{
va_list args;
AZ(ctx->method & VCL_MET_TASK_H);
va_start(args, fmt);
if (ctx->vsl)
VSLbv(ctx->vsl, SLT_VCL_Error, fmt, args);
else
VSLv(SLT_VCL_Error, 0, fmt, args);
va_end(args);
}
......@@ -213,7 +213,7 @@ vmod_set_add(VRT_CTX, struct vmod_re2_set *set, struct VARGS(set_add) *args)
if (pattern == NULL)
pattern = "";
if (!INIT(ctx)) {
VERR(ctx, ERR_PREFIX ".add() may only be called in vcl_init",
VFAIL(ctx, ERR_PREFIX ".add() may only be called in vcl_init",
set->vcl_name, pattern);
return;
}
......@@ -233,7 +233,7 @@ vmod_set_add(VRT_CTX, struct vmod_re2_set *set, struct VARGS(set_add) *args)
if ((set->string = realloc(set->string,
(n + 1) * (sizeof(char *))))
== NULL) {
VFAILNOMEM(ctx, ERR_PREFIX "adding string %s",
VERRNOMEM(ctx, ERR_PREFIX "adding string %s",
set->vcl_name, pattern, args->string);
return;
}
......@@ -245,7 +245,7 @@ vmod_set_add(VRT_CTX, struct vmod_re2_set *set, struct VARGS(set_add) *args)
if ((set->backend = realloc(set->backend,
(n + 1) * (sizeof(VCL_BACKEND))))
== NULL) {
VFAILNOMEM(ctx, ERR_PREFIX "adding backend %s",
VERRNOMEM(ctx, ERR_PREFIX "adding backend %s",
set->vcl_name, pattern,
VRT_BACKEND_string(args->backend));
return;
......@@ -257,7 +257,7 @@ vmod_set_add(VRT_CTX, struct vmod_re2_set *set, struct VARGS(set_add) *args)
if ((set->integer = realloc(set->integer,
(n + 1) * (sizeof(VCL_INT))))
== NULL) {
VFAILNOMEM(ctx, ERR_PREFIX "adding integer %jd",
VERRNOMEM(ctx, ERR_PREFIX "adding integer %jd",
set->vcl_name, pattern, args->integer);
return;
}
......@@ -313,8 +313,8 @@ vmod_set_compile(VRT_CTX, struct vmod_re2_set *set)
CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
CHECK_OBJ_NOTNULL(set, VMOD_RE2_SET_MAGIC);
if (!INIT(ctx)) {
VERR(ctx, ERR_PREFIX ".compile() may only be called in "
"vcl_init", set->vcl_name);
VFAIL(ctx, ERR_PREFIX ".compile() may only be called in "
"vcl_init", set->vcl_name);
return;
}
if (set->npatterns == 0) {
......@@ -324,12 +324,12 @@ vmod_set_compile(VRT_CTX, struct vmod_re2_set *set)
if (set->compiled) {
VFAIL(ctx, ERR_PREFIX "%s has already been compiled",
set->vcl_name, set->vcl_name);
set->vcl_name, set->vcl_name);
return;
}
if ((err = vre2set_compile(set->set)) != NULL) {
VFAIL(ctx, ERR_PREFIX "failed, possibly insufficient memory",
set->vcl_name);
set->vcl_name);
return;
}
set->compiled = 1;
......@@ -356,14 +356,15 @@ vmod_set_match(VRT_CTX, struct vmod_re2_set *set, VCL_STRING subject)
subject = "";
if (!set->compiled) {
VERR(ctx, ERR_PREFIX "%s was not compiled", set->vcl_name,
subject, set->vcl_name);
VFAIL(ctx, ERR_PREFIX "%s was not compiled", set->vcl_name,
subject, set->vcl_name);
return 0;
}
priv = VRT_priv_task(ctx, set);
if (priv == NULL) {
ERR(ctx, "No priv_task - workspace overflow?");
VFAIL(ctx, ERR_PREFIX "No priv_task - workspace overflow?",
set->vcl_name, subject);
return 0;
}
if (priv->priv == NULL) {
......@@ -386,7 +387,7 @@ vmod_set_match(VRT_CTX, struct vmod_re2_set *set, VCL_STRING subject)
buf = WS_Reservation(ctx->ws);
if ((err = vre2set_match(set->set, subject, &match, buf, buflen,
&task->nmatches, &errkind)) != NULL) {
VERR(ctx, ERR_PREFIX "%s", set->vcl_name, subject, err);
VFAIL(ctx, ERR_PREFIX "%s", set->vcl_name, subject, err);
WS_Release(ctx->ws, 0);
return 0;
}
......@@ -402,9 +403,9 @@ vmod_set_match(VRT_CTX, struct vmod_re2_set *set, VCL_STRING subject)
case NOT_IMPLEMENTED:
break;
case OUT_OF_MEMORY:
VERR(ctx, ERR_PREFIX "RE2 lib indicates out-of-memory "
"during match, consider increasing max_mem",
set->vcl_name, subject);
VFAIL(ctx, ERR_PREFIX "RE2 lib indicates out-of-memory "
"during match, consider increasing max_mem",
set->vcl_name, subject);
break;
case NOT_COMPILED:
case INCONSISTENT:
......@@ -441,14 +442,14 @@ vmod_set_matched(VRT_CTX, struct vmod_re2_set *set, VCL_INT n)
CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
CHECK_OBJ_NOTNULL(set, VMOD_RE2_SET_MAGIC);
if (n < 1 || n > set->npatterns) {
VERR(ctx, "n=%d out of range in %s.matched() (%d patterns)", n,
set->vcl_name, set->npatterns);
VFAIL(ctx, "n=%jd out of range in %s.matched() (%d patterns)",
(intmax_t)n, set->vcl_name, set->npatterns);
return 0;
}
if ((task = get_task_data(ctx, set)) == NULL) {
VERR(ctx, "%s.matched(%d) called without prior match",
set->vcl_name, n);
VFAIL(ctx, "%s.matched(%jd) called without prior match",
set->vcl_name, (intmax_t)n);
return 0;
}
......@@ -479,7 +480,7 @@ vmod_set_nmatches(VRT_CTX, struct vmod_re2_set *set)
CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
CHECK_OBJ_NOTNULL(set, VMOD_RE2_SET_MAGIC);
if ((task = get_task_data(ctx, set)) == NULL) {
VERR(ctx, "%s.nmatches() called without prior match",
VFAIL(ctx, "%s.nmatches() called without prior match",
set->vcl_name);
return 0;
}
......@@ -494,28 +495,29 @@ get_match_idx(VRT_CTX, struct vmod_re2_set *set, VCL_INT n, VCL_ENUM selects,
int idx = 0;
if (n > set->npatterns) {
VERR(ctx, "%s.%s(%lld): set has %d patterns", set->vcl_name,
method, n, set->npatterns);
VFAIL(ctx, "%s.%s(%jd): set has %d patterns", set->vcl_name,
method, (intmax_t)n, set->npatterns);
return -1;
}
if (n > 0)
return n - 1;
if ((task = get_task_data(ctx, set)) == NULL) {
VERR(ctx, "%s.%s() called without prior match", set->vcl_name,
method);
VFAIL(ctx, "%s.%s() called without prior match", set->vcl_name,
method);
return -1;
}
if (task->nmatches == 0) {
VERR(ctx, "%s.%s(%lld): previous match was unsuccessful",
set->vcl_name, method, n);
VFAIL(ctx, "%s.%s(%jd): previous match was unsuccessful",
set->vcl_name, method, (intmax_t)n);
return -1;
}
if (task->nmatches > 1) {
if (selects == VENUM(UNIQUE)) {
VERR(ctx, "%s.%s(%lld): %d successful matches",
set->vcl_name, method, n, task->nmatches);
VFAIL(ctx, "%s.%s(%jd): %ld successful matches",
set->vcl_name, method, (intmax_t)n,
task->nmatches);
return -1;
}
if (selects == VENUM(LAST))
......@@ -547,8 +549,9 @@ rewritef(VRT_CTX, struct vmod_re2_set * const restrict set,
CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
CHECK_OBJ_NOTNULL(set, VMOD_RE2_SET_MAGIC);
if (set->regex == NULL) {
VERR(ctx, "%s.%s(%lld): No regexen were saved for %s",
set->vcl_name, rewrite_name[type], n, set->vcl_name);
VFAIL(ctx, "%s.%s(%jd): No regexen were saved for %s",
set->vcl_name, rewrite_name[type], (intmax_t)n,
set->vcl_name);
return NULL;
}
......@@ -557,9 +560,9 @@ rewritef(VRT_CTX, struct vmod_re2_set * const restrict set,
return NULL;
if (!vbit_test(set->added[REGEX], idx)) {
AN(selects);
VERR(ctx, "%s.%s(%s, %s, %lld, %s): Pattern %d was not saved",
set->vcl_name, rewrite_name[type], text, rewrite, n,
selects, idx + 1);
VFAIL(ctx, "%s.%s(%s, %s, %jd, %s): Pattern %d was not saved",
set->vcl_name, rewrite_name[type], text, rewrite,
(intmax_t)n, selects, idx + 1);
return NULL;
}
return (regex_rewrite[type])(ctx, set->regex[idx], text, rewrite,
......@@ -598,8 +601,8 @@ vmod_set_string(VRT_CTX, struct vmod_re2_set *set, VCL_INT n, VCL_ENUM selects)
CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
CHECK_OBJ_NOTNULL(set, VMOD_RE2_SET_MAGIC);
if (set->string == NULL) {
VERR(ctx, "%s.string(%lld): No strings were set for %s",
set->vcl_name, n, set->vcl_name);
VFAIL(ctx, "%s.string(%jd): No strings were set for %s",
set->vcl_name, (intmax_t)n, set->vcl_name);
return NULL;
}
......@@ -608,8 +611,8 @@ vmod_set_string(VRT_CTX, struct vmod_re2_set *set, VCL_INT n, VCL_ENUM selects)
return NULL;
if (!vbit_test(set->added[STRING], idx)) {
AN(selects);
VERR(ctx, "%s.string(%lld, %s): String %lld was not added",
set->vcl_name, n, selects, idx + 1);
VFAIL(ctx, "%s.string(%jd, %s): String %d was not added",
set->vcl_name, (intmax_t)n, selects, idx + 1);
return NULL;
}
return set->string[idx];
......@@ -623,8 +626,8 @@ vmod_set_backend(VRT_CTX, struct vmod_re2_set *set, VCL_INT n, VCL_ENUM selects)
CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
CHECK_OBJ_NOTNULL(set, VMOD_RE2_SET_MAGIC);
if (set->backend == NULL) {
VERR(ctx, "%s.backend(%lld): No backends were set for %s",
set->vcl_name, n, set->vcl_name);
VFAIL(ctx, "%s.backend(%jd): No backends were set for %s",
set->vcl_name, (intmax_t)n, set->vcl_name);
return NULL;
}
......@@ -633,8 +636,8 @@ vmod_set_backend(VRT_CTX, struct vmod_re2_set *set, VCL_INT n, VCL_ENUM selects)
return NULL;
if (!vbit_test(set->added[BACKEND], idx)) {
AN(selects);
VERR(ctx, "%s.backend(%lld, %s): Backend %lld was not added",
set->vcl_name, n, selects, idx + 1);
VFAIL(ctx, "%s.backend(%jd, %s): Backend %d was not added",
set->vcl_name, (intmax_t)n, selects, idx + 1);
return NULL;
}
return set->backend[idx];
......@@ -649,20 +652,17 @@ vmod_set_integer(VRT_CTX, struct vmod_re2_set *set, VCL_INT n, VCL_ENUM selects)
CHECK_OBJ_NOTNULL(set, VMOD_RE2_SET_MAGIC);
if (set->integer == NULL) {
VRT_fail(ctx,
"%s.integer(%jd): No integers were set for %s",
set->vcl_name, n, set->vcl_name);
"%s.integer(%jd): No integers were set for %s",
set->vcl_name, (intmax_t)n, set->vcl_name);
return (0);
}
idx = get_match_idx(ctx, set, n, selects, "integer");
if (idx < 0) {
VRT_fail(ctx, "See previous VCL_Error");
if (idx < 0)
return (0);
}
if (!vbit_test(set->added[INTEGER], idx)) {
AN(selects);
VRT_fail(ctx,
"%s.integer(%jd, %s): Integer %d was not added",
VRT_fail(ctx, "%s.integer(%jd, %s): integer %d was not added",
set->vcl_name, n, selects, idx + 1);
return (0);
}
......@@ -732,8 +732,8 @@ vmod_set_hdr_filter(VRT_CTX, struct VPFX(re2_set) *set, VCL_HTTP hp,
if ((err = vre2set_matchonly(set->set, hdr, len, &match,
&errkind))
!= NULL) {
VERR(ctx, "%s.hdr_filter(%.*s): %s", set->vcl_name, len,
hdr, err);
VFAIL(ctx, "%s.hdr_filter(%.*s): %s", set->vcl_name,
len, hdr, err);
goto loop;
}
......@@ -742,9 +742,9 @@ vmod_set_hdr_filter(VRT_CTX, struct VPFX(re2_set) *set, VCL_HTTP hp,
case NOT_IMPLEMENTED:
break;
case OUT_OF_MEMORY:
VERR(ctx, "%s.hdr_filter(%.*s): RE2 lib indicates "
"out-of-memory during match, consider increasing "
"max_mem", set->vcl_name, len, hdr);
VFAIL(ctx, "%s.hdr_filter(%.*s): RE2 lib indicates "
"out-of-memory during match, consider increasing "
"max_mem", set->vcl_name, len, hdr);
goto loop;
case NOT_COMPILED:
case INCONSISTENT:
......
......@@ -23,7 +23,7 @@ varnish v1 -cli "vcl.list"
varnish v1 -vcl {
import ${vmod_re2};
backend b { .host = "${bad_ip}"; }
backend b None;
sub vcl_recv {
return(synth(200));
......
......@@ -10,6 +10,7 @@ server s1 {
varnish v1 -vcl+backend {
import ${vmod_re2};
import std;
sub vcl_init {
new frobnitz = re2.regex("(frob)(nitz)");
......@@ -18,34 +19,34 @@ varnish v1 -vcl+backend {
new never = re2.regex("(bar)(baz)", never_capture=true);
}
sub vcl_recv {
return(pass);
}
sub vcl_deliver {
# Call to backref() before match()
set resp.http.nomatch = barbaz.backref(0, "fallback");
if (req.http.Test == "nomatch") {
# Call to backref() before match()
set resp.http.nomatch = barbaz.backref(0);
}
/* does not match */
if (frobnitz.match(resp.http.foo)) {
set resp.http.frob = "nitz";
if (req.http.Test == "matchfail") {
/* does not match */
if (frobnitz.match(resp.http.foo)) {
set resp.http.frob = "nitz";
}
/* ... so all backrefs fail */
set resp.http.frobref
= frobnitz.backref(std.integer(req.http.Ref), "f");
}
/* ... so all backrefs fail */
set resp.http.frob0 = frobnitz.backref(0, "fallback0");
set resp.http.frob1 = frobnitz.backref(1, "fallback1");
set resp.http.frob2 = frobnitz.backref(2, "fallback2");
set resp.http.frob3 = frobnitz.backref(3, "fallback3");
/* match succeeds */
if (barbaz.match(resp.http.foo)) {
set resp.http.foo0 = barbaz.backref(0, "error0");
set resp.http.foo1 = barbaz.backref(1, "error1");
set resp.http.foo2 = barbaz.backref(2, "error2");
# Even if a match succeeded, backref() fails if
# the fallback is undefined.
set resp.http.undeffallback
= barbaz.backref(0, resp.http.undef);
set resp.http.foo0 = barbaz.backref(0);
set resp.http.foo1 = barbaz.backref(1);
set resp.http.foo2 = barbaz.backref(2);
if (req.http.Test == "fallbackundef") {
# Even if a match succeeded, backref() fails if
# the fallback is undefined.
set resp.http.undeffallback
= barbaz.backref(0, resp.http.undef);
}
}
/* match fails */
if (barbaz.match(resp.http.barf)) {
......@@ -54,9 +55,10 @@ varnish v1 -vcl+backend {
/* ... so all backrefs fail, including the backrefs
* from the previous match
*/
set resp.http.barf0 = barbaz.backref(0, "fallback0");
set resp.http.barf1 = barbaz.backref(1, "fallback1");
set resp.http.barf2 = barbaz.backref(2, "fallback2");
if (req.http.Test == "prevbackref") {
set resp.http.barf
= barbaz.backref(std.integer(req.http.Ref), "FAIL");
}
if (azbc.match("abc")) {
set resp.http.abc1 = azbc.backref(1, "error1");
......@@ -69,16 +71,14 @@ varnish v1 -vcl+backend {
if (never.match(resp.http.foo)) {
set resp.http.never = "match";
}
/*
* backrefs always fail, including backref 0, when
* never_capture=true
*/
set resp.http.never0 = never.backref(0, "fallback0");
set resp.http.never1 = never.backref(1, "fallback1");
set resp.http.never2 = never.backref(2, "fallback2");
/* Fallback default */
set resp.http.nofallback = never.backref(0);
if (req.http.Test == "never") {
/*
* backrefs always fail, including backref 0, when
* never_capture=true
*/
set resp.http.neverRef
= never.backref(std.integer(req.http.Ref));
}
}
} -start
......@@ -86,57 +86,181 @@ client c1 {
txreq
rxresp
expect resp.status == 200
expect resp.http.nomatch == "fallback"
expect resp.http.frob == <undef>
expect resp.http.frob0 == "fallback0"
expect resp.http.frob1 == "fallback1"
expect resp.http.frob2 == "fallback2"
expect resp.http.frob3 == "fallback3"
expect resp.http.foo0 == "barbaz"
expect resp.http.foo1 == "bar"
expect resp.http.foo2 == "baz"
expect resp.http.undeffallback ~ "^..BACKREF (METHOD|FUNCTION) FAILED..$"
expect resp.http.puke == <undef>
expect resp.http.barf0 == "fallback0"
expect resp.http.barf1 == "fallback1"
expect resp.http.barf2 == "fallback2"
expect resp.http.abc == <undef>
expect resp.http.abc1 == "a"
expect resp.http.abc2 == "none"
expect resp.http.abc3 == "bc"
expect resp.http.never == "match"
expect resp.http.never0 == "fallback0"
expect resp.http.never1 == "fallback1"
expect resp.http.never2 == "fallback2"
expect resp.http.nofallback ~ "^..BACKREF (METHOD|FUNCTION) FAILED..$"
txreq -hdr "Test: matchfail" -hdr "Ref: 0"
rxresp
expect resp.status == 200
expect resp.http.frob == <undef>
expect resp.http.frobref == "f"
txreq -hdr "Test: matchfail" -hdr "Ref: 1"
rxresp
expect resp.status == 200
expect resp.http.frob == <undef>
expect resp.http.frobref == "f"
txreq -hdr "Test: matchfail" -hdr "Ref: 2"
rxresp
expect resp.status == 200
expect resp.http.frob == <undef>
expect resp.http.frobref == "f"
# Out of range causes VCL failure.
txreq -hdr "Test: matchfail" -hdr "Ref: 3"
rxresp
expect resp.status == 503
expect resp.reason == "VCL failed"
expect resp.http.frobref == <undef>
expect_close
} -run
logexpect l1 -v v1 -d 1 -g vxid -q "VCL_Error" {
expect 0 * Begin req
# get the "out of range" error message even when the backref
# would have also failed due to failing prior match
expect * = VCL_Error {^vmod re2 failure: frobnitz\.backref\(ref=3, fallback="f"\): backref out of range \(max 2\)$}
expect 0 = RespHeader {^frobref: $}
expect 0 = VCL_return fail
expect * = End
} -run
logexpect l1 -v v1 -d 0 -g vxid -q "VCL_Error" {
expect 0 * Begin req
# due to calling barbaz.backref() before .match()
expect * = VCL_Error "^vmod re2 error: barbaz.backref.ref=0, fallback=.fallback..: backref called without prior match$"
expect * = ReqHeader {^Test: nomatch$}
expect * = VCL_Error {^vmod re2 failure: barbaz\.backref\(ref=0, fallback="[^"]+"\): backref called without prior match$}
# "
expect 0 = RespHeader {^nomatch: $}
expect 0 = VCL_return fail
expect * = End
} -start
client c2 {
txreq -hdr "Test: nomatch"
rxresp
expect resp.status == 503
expect resp.reason == "VCL failed"
expect resp.http.nomatch == <undef>
expect_close
} -run
# get the "out of range" error message even when the backref
# would have also failed due to failing prior match
expect * = VCL_Error "^vmod re2 error: frobnitz.backref.ref=3, fallback=.fallback3..: backref out of range .max 2.$"
logexpect l1 -wait
expect * = VCL_Error "^vmod re2 error: barbaz.backref.ref=0, fallback=.<undefined>..: fallback is undefined$"
logexpect l1 -v v1 -d 0 -g vxid -q "VCL_Error" {
expect 0 * Begin req
expect * = ReqHeader {^Test: fallbackundef$}
expect * = RespHeader {^foo0: barbaz$}
expect 0 = RespHeader {^foo1: bar$}
expect 0 = RespHeader {^foo2: baz$}
expect 0 = VCL_Error {^vmod re2 failure: barbaz\.backref\(ref=0, fallback="<undefined>"\): fallback is undefined$}
expect 0 = RespHeader {^undeffallback: $}
expect 0 = VCL_return fail
expect * = End
} -start
expect * = VCL_Error "^vmod re2 error: never.backref.ref=0, fallback=.fallback0..: never_capture is true for object never$"
expect * = VCL_Error "^vmod re2 error: never.backref.ref=1, fallback=.fallback1..: never_capture is true for object never$"
expect * = VCL_Error "^vmod re2 error: never.backref.ref=2, fallback=.fallback2..: never_capture is true for object never$"
client c3 {
txreq -hdr "Test: fallbackundef"
rxresp
expect resp.status == 503
expect resp.reason == "VCL failed"
expect resp.http.foo0 == <undef>
expect resp.http.foo1 == <undef>
expect resp.http.foo2 == <undef>
expect resp.http.undeffallback == <undef>
expect_close
} -run
logexpect l1 -wait
# Backref fails due to the previous match failing do not cause VCL
# failure.
client c4 {
txreq -hdr "Test: prevbackref" -hdr "Ref: 0"
rxresp
expect resp.status == 200
expect resp.http.puke == <undef>
expect resp.http.barf == "FAIL"
txreq -hdr "Test: prevbackref" -hdr "Ref: 1"
rxresp
expect resp.status == 200
expect resp.http.puke == <undef>
expect resp.http.barf == "FAIL"
txreq -hdr "Test: prevbackref" -hdr "Ref: 2"
rxresp
expect resp.status == 200
expect resp.http.puke == <undef>
expect resp.http.barf == "FAIL"
} -run
logexpect l1 -v v1 -d 0 -g vxid -q "VCL_Error" {
expect 0 * Begin req
expect * = ReqHeader {^Test: never$}
expect * = VCL_Error {^vmod re2 failure: never\.backref\(ref=0, fallback="\*\*BACKREF METHOD FAILED\*\*"\): never_capture is true for object never$}
expect 0 = RespHeader {^neverRef: $}
expect 0 = VCL_return fail
expect * = End
expect 0 * Begin req
expect * = ReqHeader {^Test: never$}
expect * = VCL_Error {^vmod re2 failure: never\.backref\(ref=1, fallback="\*\*BACKREF METHOD FAILED\*\*"\): never_capture is true for object never$}
expect 0 = RespHeader {^neverRef: $}
expect 0 = VCL_return fail
expect * = End
expect 0 * Begin req
expect * = ReqHeader {^Test: never$}
expect * = VCL_Error {^vmod re2 failure: never\.backref\(ref=2, fallback="\*\*BACKREF METHOD FAILED\*\*"\): never_capture is true for object never$}
expect 0 = RespHeader {^neverRef: $}
expect 0 = VCL_return fail
expect * = End
} -start
client c5 {
txreq -hdr "Test: never" -hdr "Ref: 0"
rxresp
expect resp.status == 503
expect resp.reason == "VCL failed"
expect resp.http.neverRef == <undef>
expect_close
} -run
# The same tests with named refs
client c6 {
txreq -hdr "Test: never" -hdr "Ref: 1"
rxresp
expect resp.status == 503
expect resp.reason == "VCL failed"
expect resp.http.neverRef == <undef>
expect_close
} -run
server s1 -wait
server s1 -start
client c7 {
txreq -hdr "Test: never" -hdr "Ref: 2"
rxresp
expect resp.status == 503
expect resp.reason == "VCL failed"
expect resp.http.neverRef == <undef>
expect_close
} -run
logexpect l1 -wait
# # The same tests with named refs
varnish v1 -vcl+backend {
import ${vmod_re2};
import std;
sub vcl_init {
new frobnitz = re2.regex("(?P<frob>frob)(?P<nitz>nitz)");
......@@ -146,39 +270,44 @@ varnish v1 -vcl+backend {
never_capture=true);
}
sub vcl_recv {
return(pass);
}
sub vcl_deliver {
# Call to namedref() before match()
set resp.http.nomatch = barbaz.namedref("bar", "fallback");
if (req.http.Test == "nomatch") {
# Call to namedref() before match()
set resp.http.nomatch = barbaz.namedref("bar");
}
if (frobnitz.match(resp.http.foo)) {
set resp.http.frob = "nitz";
if (req.http.Test == "matchfail") {
if (frobnitz.match(resp.http.foo)) {
set resp.http.frob = "nitz";
}
set resp.http.frobref
= frobnitz.namedref(req.http.Ref, "f");
}
set resp.http.frob1 = frobnitz.namedref("frob", "fallback1");
set resp.http.frob2 = frobnitz.namedref("nitz", "fallback2");
if (barbaz.match(resp.http.foo)) {
set resp.http.foo1 = barbaz.namedref("bar", "error1");
set resp.http.foo2 = barbaz.namedref("baz", "error2");
set resp.http.undeffallback
= barbaz.namedref("bar", resp.http.undef);
set resp.http.foo1 = barbaz.namedref("bar");
set resp.http.foo2 = barbaz.namedref("baz");
if (req.http.Test == "fallbackundef") {
set resp.http.undeffallback
= barbaz.namedref("bar", resp.http.undef);
}
# Fail if the name is empty or undefined
set resp.http.emptyname
= barbaz.namedref("", "name empty");
set resp.http.undefname
= barbaz.namedref(req.http.undef,
"name undefined");
if (req.http.Test == "emptyname") {
set resp.http.emptyname = barbaz.namedref("");
}
if (req.http.Test == "emptyname") {
set resp.http.undefname
= barbaz.namedref(req.http.undef);
}
}
if (barbaz.match(resp.http.barf)) {
set resp.http.puke = "match";
}
set resp.http.barf1 = barbaz.namedref("bar", "fallback1");
set resp.http.barf2 = barbaz.namedref("baz", "fallback2");
if (req.http.Test == "prevbackref") {
set resp.http.barf = barbaz.namedref(req.http.Ref, "f");
}
if (azbc.match("abc")) {
set resp.http.abc1 = azbc.namedref("a", "error1");
......@@ -191,88 +320,166 @@ varnish v1 -vcl+backend {
if (never.match(resp.http.foo)) {
set resp.http.never = "match";
}
set resp.http.never1 = never.namedref("bar", "fallback1");