...
 
Commits (20)
......@@ -32,6 +32,19 @@ For development/debugging cycles, the ``configure`` option
off optimizations and function inlining, so that a debugger will step
through the code as expected.
Experience has shown that adding ``-ggdb3`` to ``CFLAGS`` is
beneficial if you need to examine the VMOD with the gdb debugger. The
shared object for a VMOD is loaded from a directory relative to the
Varnish home directory (by default ``/usr/local/var/$INSTANCE`` for
development builds). A debugger needs to locate the shared object from
that relative path to load its symbols, so the Varnish home directory
should be the current working directory when the debugger is run. For
example::
# To run gdb and examine a coredump
$ cd /usr/local/var/myinstance
$ gdb /usr/local/sbin/varnishd /path/to/coredump
By default, the VMOD is built with the stack protector enabled
(compile option ``-fstack-protector``), but it can be disabled with
the ``configure`` option ``--disable-stack-protector``.
This diff is collapsed.
AC_PREREQ([2.68])
AC_INIT([libvmod-re2], [1.5.2], [varnish-support@uplex.de], [vmod-re2])
AC_INIT([libvmod-re2], [trunk], [varnish-support@uplex.de], [vmod-re2])
AC_COPYRIGHT([Copyright 2016-2017 UPLEX - Nils Goroll Systemoptimierung])
AC_CONFIG_MACRO_DIR([m4])
AC_CONFIG_AUX_DIR([build-aux])
......@@ -39,7 +39,7 @@ if echo "$RE2_LIBS" | grep -- '-L' >/dev/null ; then
fi
fi
VARNISH_PREREQ([6.0.0])
VARNISH_PREREQ([6.0.0], [6.1.0])
VARNISH_VMODS([re2])
VMOD_TESTS="$(cd $srcdir/src && echo tests/*.vtc)"
......@@ -70,6 +70,28 @@ AC_FUNC_REALLOC
AC_FUNC_ERROR_AT_LINE
AC_CHECK_HEADER_STDBOOL
# Check if the Set::Match() method supports error reporting, to notify
# if a match failed due to the DFA hitting the max_mem
# limit. Available since RE2 commit ee52f03, or since version
# 2017-12-01.
AC_LANG(C++)
SAVE_CXXFLAGS="$CXXFLAGS"
CXXFLAGS+=" -std=c++11"
AC_MSG_CHECKING([for RE2::Set::Match() with ErrorInfo])
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <re2/set.h>]], [[
re2::RE2::Set s(re2::RE2::DefaultOptions, re2::RE2::UNANCHORED);
s.Match("", NULL, NULL)]])],
[AC_MSG_RESULT([yes])
AC_DEFINE([HAVE_SET_MATCH_ERRORINFO], [1],
[Define to 1 if RE2::Set::Match() has the ErrorInfo parameter])
],
[AC_MSG_RESULT([no])
AC_DEFINE([HAVE_SET_MATCH_ERRORINFO], [0],
[Define to 1 if RE2::Set::Match() has the ErrorInfo parameter])
])
CXXFLAGS="$SAVE_CXXFLAGS"
AC_LANG(C)
# --enable-stack-protector
AC_ARG_ENABLE(stack-protector,
AS_HELP_STRING([--enable-stack-protector],[enable stack protector (default is YES)]),
......
......@@ -27,6 +27,8 @@ libvmod_re2_la_LIBADD = @RE2_LIBS@
vmod_re2.c set.c re2.c: vmod_re2.h
vmod_re2.h: vcc_if.h
vmod_re2.lo: $(nodist_libvmod_re2_la_SOURCES)
vcc_if.h vmod_re2.rst vmod_re2.man.rst: vcc_if.c
......
This diff is collapsed.
# looks like -*- vcl -*-
varnishtest "cost() function"
varnish v1 -vcl {
import ${vmod_re2};
backend b { .host = "${bad_ip}"; }
sub vcl_init {
# Tests from re2 testing/re2_test.cc
new simple = re2.regex("simple regexp");
new medium = re2.regex("medium.*regexp");
new complex = re2.regex("complex.{1,128}regexp");
}
sub vcl_recv {
return(synth(200));
}
sub vcl_synth {
set resp.http.C-Simple = simple.cost();
set resp.http.C-Medium = medium.cost();
set resp.http.C-Complex = complex.cost();
set resp.http.C-Simple-F = re2.cost("simple regexp");
set resp.http.C-Medium-F = re2.cost("medium.*regexp");
set resp.http.C-Complex-F = re2.cost("complex.{1,128}regexp");
set resp.http.Fail-F = re2.cost("(");
}
} -start
client c1 {
txreq
rxresp
expect resp.status == 200
expect resp.http.C-Simple ~ {^\d+$}
expect resp.http.C-Medium ~ {^\d+$}
expect resp.http.C-Complex ~ {^\d+$}
expect resp.http.C-Simple > 0
expect resp.http.C-Medium > resp.http.C-Simple
expect resp.http.C-Complex > resp.http.C-Medium
expect resp.http.C-Simple-F == resp.http.C-Simple
expect resp.http.C-Medium-F == resp.http.C-Medium
expect resp.http.C-Complex-F == resp.http.C-Complex
expect resp.http.Fail-F == "-1"
} -run
logexpect l1 -v v1 -d 1 -g vxid -q "VCL_Error" {
expect 0 * Begin req
expect * = VCL_Error {^vmod re2 error: re2\.cost\("\("\): Cannot compile: }
expect * = End
} -run
# looks like -*- vcl -*-
varnishtest "quotemeta() function"
varnish v1 -vcl {
import ${vmod_re2};
backend be { .host = "${bad_ip}"; }
sub vcl_recv {
return(synth(200));
}
sub vcl_synth {
# Tests from re2 testing/re2_test.cc
set resp.http.q1 =
re2.match(re2.quotemeta("foo"), "foo");
set resp.http.q2 =
re2.match(re2.quotemeta("foo.bar"), "foo.bar");
set resp.http.q3 =
re2.match(re2.quotemeta("foo\.bar"), "foo\.bar");
set resp.http.q4 =
re2.match(re2.quotemeta("[1-9]"), "[1-9]");
set resp.http.q5 =
re2.match(re2.quotemeta("1.5-2.0?"), "1.5-2.0?");
set resp.http.q6 =
re2.match(re2.quotemeta("\d"), "\d");
set resp.http.q7 =
re2.match(re2.quotemeta("Who doesn't like ice cream?"),
"Who doesn't like ice cream?");
set resp.http.q8 =
re2.match(re2.quotemeta("((a|b)c?d*e+[f-h]i)"),
"((a|b)c?d*e+[f-h]i)");
set resp.http.q9 =
re2.match(re2.quotemeta("((?!)xxx).*yyy"),
"((?!)xxx).*yyy");
set resp.http.q10 =
re2.match(re2.quotemeta("(["), "([");
set resp.http.n1 =
re2.match(re2.quotemeta("..."), "bar");
set resp.http.n2 =
re2.match(re2.quotemeta("\."), ".");
set resp.http.n3 =
re2.match(re2.quotemeta("\."), "..");
set resp.http.n4 =
re2.match(re2.quotemeta("(a)"), "a");
set resp.http.n5 =
re2.match(re2.quotemeta("(a|b)"), "a");
set resp.http.n6 =
re2.match(re2.quotemeta("(a|b)"), "(a)");
set resp.http.n7 =
re2.match(re2.quotemeta("(a|b)"), "a|b");
set resp.http.n8 =
re2.match(re2.quotemeta("[0-9]"), "0");
set resp.http.n9 =
re2.match(re2.quotemeta("[0-9]"), "0-9");
set resp.http.n10 =
re2.match(re2.quotemeta("[0-9]"), "[9]");
set resp.http.latin1 =
re2.match(re2.quotemeta("3\xb2 = 9"), "3\xb2 = 9");
set resp.http.utf8-1 =
re2.match(re2.quotemeta("Plácido Domingo"),
"Plácido Domingo", utf8=true);
set resp.http.utf8-2 =
re2.match(re2.quotemeta("xyz"), "xyz", utf8=true);
set resp.http.utf8-3 =
re2.match(re2.quotemeta("\xc2\xb0"), "\xc2\xb0", utf8=true);
set resp.http.utf8-4 =
re2.match(re2.quotemeta("27\xc2\xb0 degrees"),
"27\xc2\xb0 degrees", utf8=true);
set resp.http.utf8-5 =
re2.match(re2.quotemeta("\xe2\x80\xb3"),
"\xe2\x80\xb3", utf8=true);
set resp.http.utf8-6 =
re2.match(re2.quotemeta("\xf0\x9d\x85\x9f"),
"\xf0\x9d\x85\x9f", utf8=true);
set resp.http.utf8-7 =
re2.match(re2.quotemeta("27\xc2\xb0"),
"27\xc2\xb0", utf8=true);
set resp.http.utf8-n =
re2.match(re2.quotemeta("27\xc2\xb0"),
"27\\xc2\\xb0", utf8=true);
# Example from re2.h
set resp.http.re2-h = re2.quotemeta("1.5-2.0?");
}
} -start
client c1 {
txreq
rxresp
expect resp.http.q1 == "true"
expect resp.http.q2 == "true"
expect resp.http.q3 == "true"
expect resp.http.q4 == "true"
expect resp.http.q5 == "true"
expect resp.http.q6 == "true"
expect resp.http.q7 == "true"
expect resp.http.q8 == "true"
expect resp.http.q9 == "true"
expect resp.http.q10 == "true"
expect resp.http.n1 == "false"
expect resp.http.n2 == "false"
expect resp.http.n3 == "false"
expect resp.http.n4 == "false"
expect resp.http.n5 == "false"
expect resp.http.n6 == "false"
expect resp.http.n7 == "false"
expect resp.http.n8 == "false"
expect resp.http.n9 == "false"
expect resp.http.n10 == "false"
expect resp.http.latin1 == "true"
expect resp.http.utf8-1 == "true"
expect resp.http.utf8-2 == "true"
expect resp.http.utf8-3 == "true"
expect resp.http.utf8-4 == "true"
expect resp.http.utf8-5 == "true"
expect resp.http.utf8-6 == "true"
expect resp.http.utf8-7 == "true"
expect resp.http.utf8-n == "false"
expect resp.http.re2-h == {1\.5\-2\.0\?}
} -run
# looks like -*- vcl -*-
varnishtest "set.extract() method"
varnish v1 -vcl {
import ${vmod_re2};
backend be { .host = "${bad_ip}"; }
sub vcl_init {
new s = re2.set();
s.add("(.*)@([^.]*)", save=true);
s.add(".*", save=true);
s.compile();
new n = re2.set();
n.add("(.*)@([^.]*)");
n.add(".*", save=true);
n.compile();
}
sub vcl_recv {
return(synth(200));
}
sub vcl_synth {
# cf. extract.vtc
set resp.http.uucp = s.extract("boris@kremvax.ru", "\2!\1",
n=1);
set resp.http.quote = s.extract("foo", "'\0'", n=2);
set resp.http.fail = s.extract("foo", "bar", "fallback", n=1);
set resp.http.undeffallback
= s.extract("foo", "bar", req.http.undef, n=1);
set resp.http.undeftext
= s.extract(req.http.undef, "x", "text undef", n=1);
set resp.http.undefrewrite
= s.extract("b", req.http.undef, "rewrite undef", n=1);
set resp.http.s-match = s.match("boris@kremvax.ru");
set resp.http.s-nmatches = s.nmatches();
set resp.http.s-extract
= s.extract("boris@kremvax.ru", "\2!\1");
set resp.http.s-first
= s.extract("boris@kremvax.ru", "\2!\1", select=FIRST);
set resp.http.s-last
= s.extract("boris@kremvax.ru", "\2!\1", select=LAST);
set resp.http.n-match = n.match("boris@kremvax.ru");
set resp.http.n-n = n.nmatches();
set resp.http.n-extract
= n.extract("boris@kremvax.ru", "\2!\1");
set resp.http.n-first
= n.extract("boris@kremvax.ru", "\2!\1", select=FIRST);
set resp.http.n-last
= n.extract("boris@kremvax.ru", "\2!\1", select=LAST);
}
} -start
client c1 {
txreq
rxresp
expect resp.http.uucp == "kremvax!boris"
expect resp.http.quote == "'foo'"
expect resp.http.fail == "fallback"
expect resp.http.undeffallback == "**EXTRACT METHOD FAILED**"
expect resp.http.undeftext == "text undef"
expect resp.http.undefrewrite == "rewrite undef"
expect resp.http.s-match == "true"
expect resp.http.s-nmatches == "2"
expect resp.http.s-extract == ""
expect resp.http.s-first == "kremvax!boris"
expect resp.http.s-last == "!"
expect resp.http.n-match == "true"
expect resp.http.n-n == "2"
expect resp.http.n-extract == ""
expect resp.http.n-first == ""
expect resp.http.n-last == "!"
} -run
logexpect l1 -v v1 -d 1 -g vxid -q "VCL_Error" {
expect 0 * Begin req
expect * = VCL_Error "^vmod re2 error: s_..extract..: fallback is undefined$"
expect * = VCL_Error "^vmod re2 error: s_..extract.text=<undefined>, fallback=.text undef..: text is undefined$"
expect * = VCL_Error "^vmod re2 error: s_..extract.text=.b., rewrite=<undefined>, fallback=.rewrite undef..: rewrite is undefined$"
expect * = VCL_Error "^vmod re2 error: s.extract.0.: 2 successful matches$"
expect * = VCL_Error "^vmod re2 error: n.extract.boris@kremvax.ru, .2!.1, 0, FIRST.: Pattern 1 was not saved$"
expect * = End
} -run
This diff is collapsed.
# looks like -*- vcl -*-
varnishtest "set.sub() method"
varnishtest "set.sub() method, and set.saved() for REGEX"
varnish v1 -vcl {
import ${vmod_re2};
......@@ -12,11 +12,6 @@ varnish v1 -vcl {
s.add("\w+", save=true);
s.add("b", save=true);
s.compile();
new n = re2.set();
n.add("b");
n.add("b+", save=true);
n.compile();
}
sub vcl_recv {
......@@ -24,6 +19,15 @@ varnish v1 -vcl {
}
sub vcl_synth {
set resp.http.s-saved-1 = s.saved(n=1);
set resp.http.s-saved-2 = s.saved(n=2);
set resp.http.s-saved-3 = s.saved(n=3);
set resp.http.s-saved-regex-1 = s.saved(REGEX, n=1);
set resp.http.s-saved-regex-2 = s.saved(REGEX, n=2);
set resp.http.s-saved-regex-3 = s.saved(REGEX, n=3);
set resp.http.s-saved-b4-match = s.saved();
set resp.http.s-saved-regex-b4-match = s.saved(REGEX);
set resp.http.s-sub-1
= s.sub("the quick brown fox jumps over the lazy dogs.",
"\2\1ay", n=1);
......@@ -38,12 +42,14 @@ varnish v1 -vcl {
"\2\1ay", select=FIRST);
set resp.http.s-ab = s.sub("ababababab", "bb", select=LAST);
set resp.http.n-sub-1 = n.sub("bbbbbb", "bb", n=1);
set resp.http.n-sub-2 = n.sub("bbbbbb", "bb", n=2);
set resp.http.n-match = n.match("bbbbbb");
set resp.http.n-n = n.nmatches();
set resp.http.n-first = n.sub("bbbbbb", "bb", select=FIRST);
set resp.http.n-last = n.sub("bbbbbb", "bb", select=LAST);
set resp.http.s-saved-match = s.saved();
set resp.http.s-saved-regex-match = s.saved(REGEX);
set resp.http.s-saved-match-first = s.saved(select=FIRST);
set resp.http.s-saved-match-last = s.saved(select=LAST);
set resp.http.s-saved-regex-match-first
= s.saved(REGEX, select=FIRST);
set resp.http.s-saved-regex-match-last
= s.saved(REGEX, select=LAST);
}
} -start
......@@ -51,6 +57,14 @@ client c1 {
txreq
rxresp
expect resp.status == 200
expect resp.http.s-saved-1 == "true"
expect resp.http.s-saved-2 == "true"
expect resp.http.s-saved-3 == "true"
expect resp.http.s-saved-regex-1 == "true"
expect resp.http.s-saved-regex-2 == "true"
expect resp.http.s-saved-regex-3 == "true"
expect resp.http.s-saved-b4-match == "false"
expect resp.http.s-saved-regex-b4-match == "false"
expect resp.http.s-sub-1 == "ethay quick brown fox jumps over the lazy dogs."
expect resp.http.s-sub-2 == "abcd-NOSPAM.efghi@google.com"
expect resp.http.s-sub-3 == "abbabababab"
......@@ -58,23 +72,105 @@ client c1 {
expect resp.http.s-n == "3"
expect resp.http.s-pangram == resp.http.s-sub-1
expect resp.http.s-ab == resp.http.s-sub-3
expect resp.http.n-sub-1 == ""
expect resp.http.n-sub-2 == "bb"
expect resp.http.n-match == "true"
expect resp.http.n-n == "2"
expect resp.http.n-first == resp.http.n-sub-1
expect resp.http.n-last == resp.http.n-sub-2
expect resp.http.s-saved-match == "false"
expect resp.http.s-saved-regex-match == "false"
expect resp.http.s-saved-match-first == "true"
expect resp.http.s-saved-match-last == "true"
expect resp.http.s-saved-regex-match-first == "true"
expect resp.http.s-saved-regex-match-last == "true"
} -run
logexpect l1 -v v1 -d 1 -g vxid -q "VCL_Error" {
expect 0 * Begin req
expect * = VCL_Error "^vmod re2 error: s.saved.. called without prior match$"
expect * = VCL_Error "^vmod re2 error: s.saved.. called without prior match$"
expect * = VCL_Error "^vmod re2 error: s.saved.0.: 3 successful matches$"
expect * = VCL_Error "^vmod re2 error: s.saved.0.: 3 successful matches$"
expect * = End
} -run
varnish v1 -vcl {
import ${vmod_re2};
backend be { .host = "${bad_ip}"; }
sub vcl_init {
new n = re2.set();
n.add("b");
n.add("b+", save=true);
n.compile();
}
sub vcl_recv {
return(synth(200));
}
sub vcl_synth {
set resp.http.n-saved-neg = n.saved(n=-1);
set resp.http.n-saved-0 = n.saved(n=0);
set resp.http.n-saved-1 = n.saved(n=1);
set resp.http.n-saved-2 = n.saved(n=2);
set resp.http.n-saved-regex-neg = n.saved(REGEX, n=-1);
set resp.http.n-saved-regex-0 = n.saved(REGEX, n=0);
set resp.http.n-saved-regex-1 = n.saved(REGEX, n=1);
set resp.http.n-saved-regex-2 = n.saved(REGEX, n=2);
set resp.http.n-sub-1 = n.sub("bbbbbb", "bb", n=1);
set resp.http.n-sub-2 = n.sub("bbbbbb", "bb", n=2);
set resp.http.n-match = n.match("bbbbbb");
set resp.http.n-n = n.nmatches();
set resp.http.n-first = n.sub("bbbbbb", "bb", select=FIRST);
set resp.http.n-last = n.sub("bbbbbb", "bb", select=LAST);
set resp.http.n-saved-match = n.saved();
set resp.http.n-saved-regex-match = n.saved(REGEX);
set resp.http.n-saved-match-first = n.saved(select=FIRST);
set resp.http.n-saved-match-last = n.saved(select=LAST);
set resp.http.n-saved-regex-match-first
= n.saved(REGEX, select=FIRST);
set resp.http.n-saved-regex-match-last
= n.saved(REGEX, select=LAST);
}
}
logexpect l1 -v v1 -d 0 -g vxid -q "VCL_Error" {
expect 0 * Begin req
expect * = VCL_Error "^vmod re2 error: n.sub.bbbbbb, bb, 1, UNIQUE.: Pattern 1 was not saved$"
expect * = VCL_Error "^vmod re2 error: n.sub.bbbbbb, bb, 0, FIRST.: Pattern 1 was not saved$"
expect * = End
} -start
client c1 {
txreq
rxresp
expect resp.status == 200
expect resp.http.n-saved-neg == "false"
expect resp.http.n-saved-0 == "false"
expect resp.http.n-saved-1 == "false"
expect resp.http.n-saved-2 == "true"
expect resp.http.n-saved-regex-neg == resp.http.n-saved-neg
expect resp.http.n-saved-regex-0 == resp.http.n-saved-0
expect resp.http.n-saved-regex-1 == resp.http.n-saved-1
expect resp.http.n-saved-regex-2 == resp.http.n-saved-2
expect resp.http.n-sub-1 == ""
expect resp.http.n-sub-2 == "bb"
expect resp.http.n-match == "true"
expect resp.http.n-n == "2"
expect resp.http.n-first == resp.http.n-sub-1
expect resp.http.n-last == resp.http.n-sub-2
expect resp.http.n-saved-match == "false"
expect resp.http.n-saved-regex-match == resp.http.n-saved-match
expect resp.http.n-saved-match-first == "false"
expect resp.http.n-saved-match-last == "true"
expect resp.http.n-saved-regex-match-first == resp.http.n-saved-match-first
expect resp.http.n-saved-regex-match-last == resp.http.n-saved-match-last
} -run
logexpect l1 -wait
# Tests for the off-by-one error discovered in vre2_rewrite().
varnish v1 -vcl {
import ${vmod_re2};
......
# looks like -*- vcl -*-
varnishtest "set.suball() method"
varnish v1 -vcl {
import ${vmod_re2};
backend be { .host = "${bad_ip}"; }
sub vcl_init {
new s = re2.set();
s.add("(qu|[b-df-hj-np-tv-z]*)([a-z]+)", save=true);
s.add("\w+", save=true);
s.add("^", save=true);
s.add("$", save=true);
s.add("b", save=true);
s.add("b+", save=true);
s.add("b*", save=true);
s.compile();
new n = re2.set();
n.add("b");
n.add("b+", save=true);
n.compile();
}
sub vcl_recv {
return(synth(200));
}
sub vcl_synth {
set resp.http.s-suball-1
= s.suball("the quick brown fox jumps over the lazy dogs.",
"\2\1ay", n=1);
set resp.http.s-suball-2 = s.suball("abcd.efghi@google.com",
"\0-NOSPAM", n=2);
set resp.http.s-suball-3 = s.suball("foo", "(START)", n=3);
set resp.http.s-suball-3-1 = s.suball("", "(START)", n=3);
set resp.http.s-suball-4 = s.suball("", "(END)", n=4);
set resp.http.s-suball-5-1 = s.suball("ababababab", "bb", n=5);
set resp.http.s-suball-5-2 = s.suball("bbbbbb", "bb", n=5);
set resp.http.s-suball-6 = s.suball("bbbbbb", "bb", n=6);
set resp.http.s-suball-7-1 = s.suball("bbbbbb", "bb", n=7);
set resp.http.s-suball-7-2 = s.suball("aaaaa", "bb", n=7);
set resp.http.s-match
= s.match("the quick brown fox jumps over the lazy dogs.");
set resp.http.s-n = s.nmatches();
set resp.http.s-pangram
= s.suball("the quick brown fox jumps over the lazy dogs.",
"\2\1ay", select=FIRST);
set resp.http.s-ab = s.suball("ababababab", "bb", select=LAST);
set resp.http.n-suball-1 = n.suball("bbbbbb", "bb", n=1);
set resp.http.n-suball-2 = n.suball("bbbbbb", "bb", n=2);
set resp.http.n-match = n.match("bbbbbb");
set resp.http.n-n = n.nmatches();
set resp.http.n-first = n.suball("bbbbbb", "bb", select=FIRST);
set resp.http.n-last = n.suball("bbbbbb", "bb", select=LAST);
}
} -start
client c1 {
txreq
rxresp
expect resp.status == 200
expect resp.http.s-suball-1 == "ethay ickquay ownbray oxfay umpsjay overay ethay azylay ogsday."
expect resp.http.s-suball-2 == "abcd-NOSPAM.efghi-NOSPAM@google-NOSPAM.com-NOSPAM"
expect resp.http.s-suball-3 == "(START)foo"
expect resp.http.s-suball-3-1 == "(START)"
expect resp.http.s-suball-4 == "(END)"
expect resp.http.s-suball-5-1 == "abbabbabbabbabb"
expect resp.http.s-suball-5-2 == "bbbbbbbbbbbb"
expect resp.http.s-suball-6 == "bb"
expect resp.http.s-suball-7-1 == "bb"
expect resp.http.s-suball-7-2 == "bbabbabbabbabbabb"
expect resp.http.s-match == "true"
expect resp.http.s-n == "7"
expect resp.http.s-pangram == resp.http.s-suball-1
expect resp.http.s-ab == resp.http.s-suball-7-2
expect resp.http.n-suball-1 == ""
expect resp.http.n-suball-2 == "bb"
expect resp.http.n-match == "true"
expect resp.http.n-n == "2"
expect resp.http.n-first == resp.http.n-suball-1
expect resp.http.n-last == resp.http.n-suball-2
} -run
logexpect l1 -v v1 -d 1 -g vxid -q "VCL_Error" {
expect 0 * Begin req
expect * = VCL_Error "^vmod re2 error: n.suball.bbbbbb, bb, 1, UNIQUE.: Pattern 1 was not saved$"
expect * = VCL_Error "^vmod re2 error: n.suball.bbbbbb, bb, 0, FIRST.: Pattern 1 was not saved$"
expect * = End
} -run
......@@ -51,7 +51,7 @@ static const void *match_failed = (void *) &c;
static size_t match_sz;
static const char * const rewrite_name[] = {
const char * const rewrite_name[] = {
[SUB] = "sub",
[SUBALL] = "suball",
[EXTRACT] = "extract",
......@@ -468,6 +468,29 @@ vmod_regex_extract(VRT_CTX, struct vmod_re2_regex *re, VCL_STRING text,
return rewrite_method(ctx, EXTRACT, re, text, rewrite, fallback);
}
static VCL_INT
cost(VRT_CTX, vre2 * const restrict vre2, const char * const restrict context)
{
int cost;
const char *err;
if ((err = vre2_cost(vre2, &cost)) != NULL) {
VERR(ctx, "%s.cost(): Cannot retrieve cost: %s", context,
err);
return (-1);
}
return cost;
}
VCL_INT
vmod_regex_cost(VRT_CTX, struct vmod_re2_regex *re)
{
CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
CHECK_OBJ_NOTNULL(re, VMOD_RE2_REGEX_MAGIC);
return cost(ctx, re->vre2, re->vcl_name);
}
/* Regex function interface */
#define ERR_PREFIX "re2.match(pattern=\"%.40s\", text=\"%.40s\"): "
......@@ -711,6 +734,59 @@ vmod_extract(VRT_CTX, VCL_STRING pattern, VCL_STRING text, VCL_STRING rewrite,
one_line);
}
#define ERR_PREFIX \
"re2.quotemeta(\"%.40s\", fallback=\"%.40s\"): "
VCL_STRING
vmod_quotemeta(VRT_CTX, VCL_STRING unquoted, VCL_STRING fallback)
{
size_t bytes, len;
char *ret;
const char *err;
CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
ret = WS_Front(ctx->ws);
bytes = WS_Reserve(ctx->ws, 0);
if (bytes == 0) {
VERR(ctx, ERR_PREFIX "workspace overflow", unquoted, fallback);
return fallback;
}
if ((err = vre2_quotemeta(unquoted, ret, bytes, &len)) != NULL) {
VERR(ctx, ERR_PREFIX "%s", unquoted, fallback, err);
WS_Release(ctx->ws, 0);
return fallback;
}
WS_Release(ctx->ws, len + 1);
return ret;
}
#undef ERR_PREFIX
VCL_INT
vmod_cost(VRT_CTX, VCL_STRING pattern, REGEX_OPTS)
{
vre2 *vre2 = NULL;
const char *err;
VCL_INT kost;
CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
if ((err = vre2_init(&vre2, pattern, utf8, posix_syntax, longest_match,
max_mem, literal, never_nl, dot_nl, never_capture,
case_sensitive, perl_classes, word_boundary,
one_line))
!= NULL) {
VERR(ctx, "re2.cost(\"%.40s\"): Cannot compile: %s", pattern,
err);
vre2_fini(&vre2);
return -1;
}
kost = cost(ctx, vre2, "re2");
vre2_fini(&vre2);
return kost;
}
VCL_STRING
vmod_version(const struct vrt_ctx *ctx __attribute__((unused)))
{
......
......@@ -60,3 +60,6 @@ struct vmod_re2_regex {
};
void errmsg(VRT_CTX, const char *fmt, ...);
/* Defined in vmod_re2.c */
extern const char * const rewrite_name[];
This diff is collapsed.
......@@ -99,6 +99,12 @@ vre2::extract(string *out, const char * const text, const char * const rewrite)
return RE2::Extract(text, *re_, rewrite, out);
}
inline int
vre2::size() const
{
return re_->ProgramSize();
}
const char *
vre2_init(vre2 **vre2p, const char *pattern, unsigned utf8,
unsigned posix_syntax, unsigned longest_match, long max_mem,
......@@ -218,6 +224,34 @@ vre2_rewrite(vre2 *vre2, const rewrite_e mode, const char * const text,
CATCHALL
}
const char *
vre2_quotemeta(const char * const unquoted, char * const dest,
const size_t bytes, size_t * const len)
{
try {
string result;
result = RE2::QuoteMeta(unquoted);
if (result.size() + 1 > bytes)
throw runtime_error("insufficient workspace");
*len = result.size();
result.copy(dest, *len);
dest[*len] = '\0';
return NULL;
}
CATCHALL
}
const char *
vre2_cost(vre2 *vre2, int *cost)
{
try {
*cost = vre2->size();
return NULL;
}
CATCHALL
}
const char *
vre2_fini(vre2 **vre2)
{
......
......@@ -36,6 +36,7 @@
#include <re2/re2.h>
using namespace re2;
using std::string;
class vre2 {
private:
......@@ -53,6 +54,7 @@ public:
bool global_replace(string *text, const char * const rewrite) const;
bool extract(string *out, const char * const text,
const char * const rewrite) const;
int size() const;
};
#else
typedef struct vre2 vre2;
......@@ -90,6 +92,10 @@ extern "C" {
const char * const rewrite,
char * const dest, const size_t bytes,
int * const match, size_t * const len);
const char *vre2_cost(vre2 *vre2, int *cost);
const char *vre2_quotemeta(const char * const unquoted,
char * const dest, const size_t bytes,
size_t * const len);
#ifdef __cplusplus
}
......
......@@ -27,6 +27,8 @@
*
*/
#include "config.h"
#include <algorithm>
#include "vre2set.h"
......@@ -70,9 +72,19 @@ vre2set::compile() const
}
inline bool
vre2set::match(const char* subject, vector<int>* m) const
vre2set::match(const char* subject, vector<int>* m, errorkind_e* err) const
{
#if HAVE_SET_MATCH_ERRORINFO
bool ret;
RE2::Set::ErrorInfo errinfo;
ret = set_->Match(subject, m, &errinfo);
*err = (errorkind_e) errinfo.kind;
return ret;
#else
*err = NOT_IMPLEMENTED;
return set_->Match(subject, m);
#endif
}
const char *
......@@ -148,13 +160,14 @@ vre2set_compile(vre2set *set)
const char *
vre2set_match(vre2set *set, const char * const subject, int * const match,
void *buf, const size_t buflen, size_t * const nmatches)
void *buf, const size_t buflen, size_t * const nmatches,
errorkind_e * const err)
{
try {
vector<int> m;
*nmatches = 0;
*match = set->match(subject, &m);
*match = set->match(subject, &m, err);
if (*match) {
if (m.size() * sizeof(int) > buflen)
return "insufficient space to copy match data";
......
......@@ -30,12 +30,21 @@
#ifndef _VRE2SET_H
#define _VRE2SET_H
typedef enum {
NO_ERROR = 0,
NOT_COMPILED,
OUT_OF_MEMORY,
INCONSISTENT,
NOT_IMPLEMENTED
} errorkind_e;
#ifdef __cplusplus
#include <re2/re2.h>
#include <re2/set.h>
using namespace re2;
using std::string;
class vre2set {
private:
......@@ -46,7 +55,8 @@ public:
virtual ~vre2set();
int add(const char* pattern, string* error) const;
bool compile() const;
bool match(const char* subject, std::vector<int>* m) const;
bool match(const char* subject, std::vector<int>* m,
errorkind_e* err) const;
};
#else
typedef struct vre2set vre2set;
......@@ -74,7 +84,8 @@ extern "C" {
const char *vre2set_compile(vre2set *set);
const char *vre2set_match(vre2set *set, const char *subject,
int * const match, void *buf,
const size_t buflen, size_t * const nmatches);
const size_t buflen, size_t * const nmatches,
errorkind_e * const err);
#ifdef __cplusplus
}
......