Commit c7e8f60a authored by Geoff Simmons's avatar Geoff Simmons

Add the integer parameter to set.add(), and the set.integer() method.

Also update set.saved(), to enable checking if an integer was saved.
parent b8a93178
......@@ -49,7 +49,8 @@ SYNOPSIS
# set object interface
new OBJECT = re2.set([ENUM anchor] [, <regex options>])
VOID <obj>.add(STRING [, STRING string] [, BACKEND backend])
VOID <obj>.add(STRING [, BOOL save] [, BOOL never_capture] [, STRING string]
[, BACKEND backend] [, INT integer])
VOID <obj>.compile()
BOOL <obj>.match(STRING)
INT <obj>.nmatches()
......@@ -57,13 +58,14 @@ SYNOPSIS
INT <obj>.which([ENUM select])
STRING <obj>.string([INT n,] [ENUM select])
BACKEND <obj>.backend([INT n,] [ENUM select])
INT <obj>.integer([INT n] [, ENUM select])
STRING <obj>.sub(STRING text, STRING rewrite [, INT n]
[, ENUM select])
STRING <obj>.suball(STRING text, STRING rewrite [, INT n]
[, ENUM select])
STRING <obj>.extract(STRING text, STRING rewrite [, INT n]
[, ENUM select])
BOOL <obj>.saved([ENUM {REGEX, STR, BE} which] [, INT n]
BOOL <obj>.saved([ENUM {REGEX, STR, BE, INT} which] [, INT n]
[, ENUM select])
# utility function
......@@ -967,30 +969,30 @@ Example::
.. _vmod_re2.set.add:
VOID xset.add(STRING, STRING string, BACKEND backend, BOOL save, BOOL never_capture)
------------------------------------------------------------------------------------
VOID xset.add(STRING, [STRING string], [BACKEND backend], [BOOL save], [BOOL never_capture], [INT integer])
-----------------------------------------------------------------------------------------------------------
::
VOID xset.add(
STRING,
STRING string=0,
BACKEND backend=0,
BOOL save=0,
BOOL never_capture=0
[STRING string],
[BACKEND backend],
[BOOL save],
[BOOL never_capture],
[INT integer]
)
Add the given pattern to the set. If the pattern is invalid,
``.add()`` fails, and the VCL will fail to load, with an error message
describing the problem.
If values for the ``string`` and/or ``backend`` parameters are
provided, then these values can be retrieved with the ``.string()``
and ``.backend()`` methods, respectively, as described below. This
makes it possible to associate a string or a backend with the added
pattern after it matches successfully. ``string`` and ``backend``
default to NULL; that is; by default the pattern is not associated
with any such value.
If values for the ``string``, ``backend`` and/or ``integer``
parameters are provided, then these values can be retrieved with the
``.string()``, ``.backend()`` and ``.integer()`` methods,
respectively, as described below. This makes it possible to associate
data with the added pattern after it matches successfully. By default
the pattern is not associated with any such value.
If ``save`` is true, then the given pattern is compiled and saved as a
``regex`` object, just as if the ``regex`` constructor described above
......@@ -1421,6 +1423,80 @@ Example::
}
}
.. _vmod_re2.set.integer:
INT xset.integer(INT n, ENUM select)
------------------------------------
::
INT xset.integer(
INT n=0,
ENUM {FIRST, LAST, UNIQUE} select=UNIQUE
)
Returns the integer associated with the `nth` pattern added to the
set, or with the pattern in the set that matched in the most recent
call to ``.match()`` in the same task scope.
The rules for selecting a pattern from the set and its associated
integer based on ``n`` and ``select`` are the same as described above
for ``.string()``.
``.integer()`` invokes VCL failure under the same error conditions
described for ``.string()`` above -- ``n`` and ``select`` are invalid,
or no integer was associated with the selected pattern with the
``.add()`` method.
Note that VCL failure differs from the failure mode for ``.string()``
and ``.backend()``, since there is no distinguished "error" value that
could be returned as the INT. VCL failure has the same effect as if
``return(fail)`` were called from a VCL subroutine; usually, control
directs immediately to ``vcl_synth``, with the response status set to
503, and the response reason set to "VCL failed".
You can avoid that, for example, by testing if ``.nmatches()==1``
after calling ``.match()``, if you need to ensure that calling
``.integer(select=UNIQUE)`` will not fail.
Example::
# Generate redirect responses based on the Host header. In the
# example, subdomains are removed in the new Location, and the
# associated integer is used to set the redirect status code.
sub vcl_init {
# No more than one pattern can match the same string. So it
# is safe to call .integer() with default select=UNIQUE in
# vcl_recv below (no risk of VCL failure).
new redir = re2.set(anchor=both);
redir.add("www\.[^.]+\.foo\.com", integer=301, string="www.foo.com");
redir.add("www\.[^.]+\.bar\.com", integer=302, string="www.bar.com");
redir.add("www\.[^.]+\.baz\.com", integer=303, string="www.baz.com");
redir.add("www\.[^.]+\.quux\.com", integer=307, string="www.quux.com");
redir.compile();
}
sub vcl_recv {
if (redir.match(req.http.Host)) {
# Construct a Location header that will be used in the
# synthetic redirect response.
set req.http.Location = "http://" + redir.string() + req.url;
# Set the response status from the associated integer.
return( synth(redir.integer()) );
}
}
sub vcl_synth {
if (resp.status >= 301 && resp.status <= 307) {
# We come here from the synth return for the redirect
# response. The status code was set from .integer().
set resp.http.Location = req.http.Location;
return(deliver);
}
}
.. _vmod_re2.set.sub:
STRING xset.sub(STRING text, STRING rewrite, STRING fallback, INT n, ENUM select)
......@@ -1618,7 +1694,7 @@ BOOL xset.saved(ENUM which, INT n, ENUM select)
::
BOOL xset.saved(
ENUM {REGEX, STR, BE} which=REGEX,
ENUM {REGEX, STR, BE, INT} which=REGEX,
INT n=0,
ENUM {FIRST, LAST, UNIQUE} select=UNIQUE
)
......@@ -1628,12 +1704,21 @@ Returns true if and only if an object of the type indicated by
added to the set, or for the pattern indicated by ``select`` after the
most recent ``.match()`` call.
In other words, ``.saved()`` returns true for ``which=REGEX`` if the
individual regex was saved with ``.add(save=true)`` for the indicated
pattern; for ``which=STR`` if a string was stored with the ``string``
parameter in ``.add()``, and for ``which=BE`` if a backend was stored
with the ``.backend()`` attribute. The default value of ``which`` is
``REGEX``.
In other words, ``.saved()`` returns true:
* for ``which=REGEX`` if the individual regex was saved with
``.add(save=true)`` for the indicated pattern
* for ``which=STR`` if a string was stored with the ``string``
parameter in ``.add()``
* for ``which=BE`` if a backend was stored with the ``backend``
attribute.
* for ``which=INT`` if an integer was stored with the ``integer``
attribute.
The default value of ``which`` is ``REGEX``.
The pattern in the set is identified by ``n`` and ``select`` according
to the rules given above. ``.saved()`` fails, returning false with a
......@@ -1731,7 +1816,7 @@ branch. See the source repository for versions of the VMOD that are
compatible with other Varnish versions.
It requires the RE2 library, and has been tested against RE2 versions
since 2015-06-01 (through 2019-04-01 at the time of writing).
since 2015-06-01 (through 2019-08-01 at the time of writing).
If the VMOD is built against versions of RE2 since 2017-12-01, it uses
a version of the set match operation that reports out-of-memory
......@@ -1781,10 +1866,17 @@ SEE ALSO
========
* varnishd(1)
* vcl(7)
* VMOD source repository: https://code.uplex.de/uplex-varnish/libvmod-re2
* Gitlab mirror: https://gitlab.com/uplex/varnish/libvmod-re2
* RE2 git repo: https://github.com/google/re2
* RE2 syntax: https://github.com/google/re2/wiki/Syntax
* "Implementing Regular Expressions": https://swtch.com/~rsc/regexp/
* Series of articles motivating the design of RE2, with discussion
......
......@@ -35,9 +35,13 @@
#define INIT(ctx) (((ctx)->method & VCL_MET_INIT) != 0)
#define STR_ADDED 0
#define BE_ADDED 1
#define RE_ADDED 2
enum bitmap_e {
STRING = 0,
BACKEND,
REGEX,
INTEGER,
__MAX_BITMAP,
};
#define NELEMS(a) (sizeof(a) / sizeof((a)[0]))
......@@ -59,11 +63,12 @@ struct vmod_re2_set {
unsigned magic;
#define VMOD_RE2_SET_MAGIC 0xf6d7b15a
vre2set *set;
struct vbitmap *added[3];
struct vbitmap *added[__MAX_BITMAP];
char *vcl_name;
char **string;
VCL_BACKEND *backend;
struct vmod_re2_regex **regex;
VCL_INT *integer;
struct set_options opts;
unsigned compiled;
int npatterns;
......@@ -181,10 +186,10 @@ vmod_set__fini(struct vmod_re2_set **setp)
*setp = NULL;
vre2set_fini(&set->set);
for (int i = 0; i < set->npatterns; i++) {
if (vbit_test(set->added[STR_ADDED], i)
if (vbit_test(set->added[STRING], i)
&& set->string[i] != NULL)
free(set->string[i]);
if (vbit_test(set->added[RE_ADDED], i)
if (vbit_test(set->added[REGEX], i)
&& set->regex[i] != NULL)
vmod_regex__fini(&set->regex[i]);
}
......@@ -198,12 +203,11 @@ vmod_set__fini(struct vmod_re2_set **setp)
#define ERR_PREFIX "%s.add(\"%.40s\"): "
VCL_VOID
vmod_set_add(VRT_CTX, struct vmod_re2_set *set, VCL_STRING pattern,
VCL_STRING string, VCL_BACKEND backend, VCL_BOOL save,
VCL_BOOL never_capture)
vmod_set_add(VRT_CTX, struct vmod_re2_set *set, struct VARGS(set_add) *args)
{
const char *err;
int n;
VCL_STRING pattern = args->arg1;
CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
CHECK_OBJ_NOTNULL(set, VMOD_RE2_SET_MAGIC);
......@@ -226,31 +230,42 @@ vmod_set_add(VRT_CTX, struct vmod_re2_set *set, VCL_STRING pattern,
return;
}
if (string != NULL) {
if (args->valid_string && args->string != NULL) {
if ((set->string = realloc(set->string,
(n + 1) * (sizeof(char *))))
== NULL) {
VFAILNOMEM(ctx, ERR_PREFIX "adding string %s",
set->vcl_name, pattern, string);
set->vcl_name, pattern, args->string);
return;
}
set->string[n] = strdup(string);
set->string[n] = strdup(args->string);
AN(set->string[n]);
vbit_set(set->added[STR_ADDED], n);
vbit_set(set->added[STRING], n);
}
if (backend != NULL) {
if (args->valid_backend && args->backend != NULL) {
if ((set->backend = realloc(set->backend,
(n + 1) * (sizeof(VCL_BACKEND))))
== NULL) {
VFAILNOMEM(ctx, ERR_PREFIX "adding backend %s",
set->vcl_name, pattern,
VRT_BACKEND_string(backend));
VRT_BACKEND_string(args->backend));
return;
}
set->backend[n] = args->backend;
vbit_set(set->added[BACKEND], n);
}
if (args->valid_integer) {
if ((set->integer = realloc(set->integer,
(n + 1) * (sizeof(VCL_INT))))
== NULL) {
VFAILNOMEM(ctx, ERR_PREFIX "adding integer %jd",
set->vcl_name, pattern, args->integer);
return;
}
set->backend[n] = backend;
vbit_set(set->added[BE_ADDED], n);
set->integer[n] = args->integer;
vbit_set(set->added[INTEGER], n);
}
if (save) {
if (args->valid_save && args->save) {
struct vmod_re2_regex *re = NULL;
char *vcl_name;
size_t namelen;
......@@ -264,7 +279,7 @@ vmod_set_add(VRT_CTX, struct vmod_re2_set *set, VCL_STRING pattern,
set->opts.posix_syntax,
set->opts.longest_match, set->opts.max_mem,
set->opts.literal, set->opts.never_nl,
set->opts.dot_nl, never_capture,
set->opts.dot_nl, args->never_capture,
set->opts.case_sensitive,
set->opts.perl_classes,
set->opts.word_boundary, set->opts.one_line);
......@@ -282,7 +297,7 @@ vmod_set_add(VRT_CTX, struct vmod_re2_set *set, VCL_STRING pattern,
return;
}
set->regex[n] = re;
vbit_set(set->added[RE_ADDED], n);
vbit_set(set->added[REGEX], n);
}
set->npatterns++;
}
......@@ -541,7 +556,7 @@ rewritef(VRT_CTX, struct vmod_re2_set * const restrict set,
idx = get_match_idx(ctx, set, n, selects, rewrite_name[type]);
if (idx < 0)
return NULL;
if (!vbit_test(set->added[RE_ADDED], idx)) {
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,
......@@ -592,7 +607,7 @@ vmod_set_string(VRT_CTX, struct vmod_re2_set *set, VCL_INT n, VCL_ENUM selects)
idx = get_match_idx(ctx, set, n, selects, "string");
if (idx < 0)
return NULL;
if (!vbit_test(set->added[STR_ADDED], idx)) {
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);
......@@ -617,7 +632,7 @@ vmod_set_backend(VRT_CTX, struct vmod_re2_set *set, VCL_INT n, VCL_ENUM selects)
idx = get_match_idx(ctx, set, n, selects, "backend");
if (idx < 0)
return NULL;
if (!vbit_test(set->added[BE_ADDED], idx)) {
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);
......@@ -626,6 +641,35 @@ vmod_set_backend(VRT_CTX, struct vmod_re2_set *set, VCL_INT n, VCL_ENUM selects)
return set->backend[idx];
}
VCL_INT
vmod_set_integer(VRT_CTX, struct vmod_re2_set *set, VCL_INT n, VCL_ENUM selects)
{
int idx;
CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
CHECK_OBJ_NOTNULL(set, VMOD_RE2_SET_MAGIC);
if (set->integer == NULL) {
VERR(ctx, "%s.integer(%jd): No integers were set for %s",
set->vcl_name, n, set->vcl_name);
*ctx->handling = VCL_RET_FAIL;
return (0);
}
idx = get_match_idx(ctx, set, n, selects, "integer");
if (idx < 0) {
*ctx->handling = VCL_RET_FAIL;
return (0);
}
if (!vbit_test(set->added[INTEGER], idx)) {
AN(selects);
VERR(ctx, "%s.integer(%jd, %s): Integer %jd was not added",
set->vcl_name, n, selects, idx + 1);
*ctx->handling = VCL_RET_FAIL;
return (0);
}
return set->integer[idx];
}
VCL_BOOL
vmod_set_saved(VRT_CTX, struct vmod_re2_set *set, VCL_ENUM whichs, VCL_INT n,
VCL_ENUM selects)
......@@ -639,11 +683,13 @@ vmod_set_saved(VRT_CTX, struct vmod_re2_set *set, VCL_ENUM whichs, VCL_INT n,
if (idx < 0)
return 0;
if (whichs == VENUM(REGEX))
return vbit_test(set->added[RE_ADDED], idx);
return vbit_test(set->added[REGEX], idx);
if (whichs == VENUM(BE))
return vbit_test(set->added[BE_ADDED], idx);
return vbit_test(set->added[BACKEND], idx);
if (whichs == VENUM(STR))
return vbit_test(set->added[STR_ADDED], idx);
return vbit_test(set->added[STRING], idx);
if (whichs == VENUM(INT))
return vbit_test(set->added[INTEGER], idx);
WRONG("illegal which ENUM");
return 0;
}
......@@ -435,3 +435,178 @@ client c1 {
} -run
logexpect l1 -wait
varnish v1 -vcl {
import ${vmod_re2};
backend b { .host = "${bad_ip}"; }
sub vcl_init {
new i = re2.set();
i.add("foo", integer=47);
i.add("bar", integer=-11);
i.compile();
}
sub vcl_recv {
return(synth(200));
}
sub vcl_synth {
set resp.http.i-integer-1 = i.integer(1);
set resp.http.i-integer-2 = i.integer(2);
set resp.http.i-int-saved-1 = i.saved(INT, 1);
set resp.http.i-int-saved-2 = i.saved(INT, 2);
set resp.http.i-saved-before-match = i.saved(INT);
set resp.http.i-foo-match = i.match("foo");
set resp.http.i-foo-n = i.nmatches();
set resp.http.i-foo-integer = i.integer();
set resp.http.i-foo-saved = i.saved(INT);
set resp.http.i-bar-match = i.match("bar");
set resp.http.i-bar-n = i.nmatches();
set resp.http.i-bar-integer = i.integer();
set resp.http.i-bar-saved = i.saved(INT);
set resp.http.i-bar-integer-0 = i.integer(0);
set resp.http.i-bar-integer-1 = i.integer(-1);
set resp.http.i-bar-saved-0 = i.saved(INT, 0);
set resp.http.i-bar-saved-1 = i.saved(INT, -1);
set resp.http.i-fail-match = i.match("fail");
set resp.http.i-fail-n = i.nmatches();
set resp.http.i-fail-saved = i.saved(INT);
set resp.http.i-many-match = i.match("foobar");
set resp.http.i-many-n = i.nmatches();
set resp.http.i-many-first = i.integer(select=FIRST);
set resp.http.i-many-last = i.integer(select=LAST);
set resp.http.i-many-saved = i.saved(INT);
set resp.http.i-many-saved-first = i.saved(INT, select=FIRST);
set resp.http.i-many-saved-last = i.saved(INT, select=LAST);
}
}
logexpect l1 -v v1 -d 0 -g vxid -q "VCL_Error" {
expect 0 * Begin req
expect * = VCL_Error {^vmod re2 error: i\.saved\(\) called without prior match$}
expect * = VCL_Error {^vmod re2 error: i\.saved\(0\): previous match was unsuccessful$}
expect * = VCL_Error {^vmod re2 error: i\.saved\(0\): 2 successful matches$}
expect * = End
} -start
client c1 {
txreq
rxresp
expect resp.status == 200
expect resp.http.i-integer-1 == 47
expect resp.http.i-integer-2 == -11
expect resp.http.i-int-saved-1 == "true"
expect resp.http.i-int-saved-2 == "true"
expect resp.http.i-saved-before-match == "false"
expect resp.http.i-foo-match == "true"
expect resp.http.i-foo-n == 1
expect resp.http.i-foo-integer == 47
expect resp.http.i-foo-saved == "true"
expect resp.http.i-bar-match == "true"
expect resp.http.i-bar-n == 1
expect resp.http.i-bar-integer == -11
expect resp.http.i-bar-saved == "true"
expect resp.http.i-bar-integer-0 == resp.http.i-bar-integer
expect resp.http.i-bar-integer-1 == resp.http.i-bar-integer
expect resp.http.i-bar-saved-0 == "true"
expect resp.http.i-bar-saved-1 == "true"
expect resp.http.i-fail-match == "false"
expect resp.http.i-fail-n == 0
expect resp.http.i-many-match == "true"
expect resp.http.i-many-n == 2
expect resp.http.i-many-first == 47
expect resp.http.i-many-last == -11
expect resp.http.i-many-saved == "false"
expect resp.http.i-many-saved-first == "true"
expect resp.http.i-many-saved-last == "true"
} -run
logexpect l1 -wait
varnish v1 -vcl {
import ${vmod_re2};
backend b { .host = "${bad_ip}"; }
sub vcl_init {
new i = re2.set();
i.add("foo", integer=47);
i.add("bar", integer=-11);
i.compile();
}
sub vcl_recv {
if (req.url == "/1") {
set req.http.i-before-match = i.integer();
}
if (req.url == "/2") {
set req.http.i-fail-match = i.match("fail");
set req.http.i-fail-integer = i.integer();
}
if (req.url == "/3") {
set req.http.i-many-match = i.match("foobar");
set req.http.i-many-integer = i.integer();
}
if (req.url == "/4") {
set req.http.i-outofrange = i.integer(3);
}
return(synth(200));
}
}
logexpect l1 -v v1 -d 0 -g vxid -q "VCL_Error" {
expect 0 * Begin req
expect * = VCL_Error {^vmod re2 error: i\.integer\(\) called without prior match$}
expect 0 = ReqHeader "i-before-match: 0"
expect * = End
expect 0 * Begin req
expect * = VCL_Error {^vmod re2 error: i\.integer\(0\): previous match was unsuccessful$}
expect 0 = ReqHeader "i-fail-integer: 0"
expect * = End
expect 0 * Begin req
expect * = VCL_Error {^vmod re2 error: i\.integer\(0\): 2 successful matches$}
expect 0 = ReqHeader "i-many-integer: 0"
expect * = End
expect 0 * Begin req
expect * = VCL_Error {^vmod re2 error: i\.integer\(3\): set has 2 patterns$}
expect 0 = ReqHeader "i-outofrange: 0"
expect * = End
} -start
client c1 {
txreq -url /1
rxresp
expect resp.status == 503
expect resp.reason == "VCL failed"
expect_close
} -run
client c1 {
txreq -url /2
rxresp
expect resp.status == 503
expect resp.reason == "VCL failed"
expect_close
} -run
client c1 {
txreq -url /3
rxresp
expect resp.status == 503
expect resp.reason == "VCL failed"
expect_close
} -run
client c1 {
txreq -url /4
rxresp
expect resp.status == 503
expect resp.reason == "VCL failed"
expect_close
} -run
logexpect l1 -wait
......@@ -45,7 +45,8 @@ SYNOPSIS
# set object interface
new OBJECT = re2.set([ENUM anchor] [, <regex options>])
VOID <obj>.add(STRING [, STRING string] [, BACKEND backend])
VOID <obj>.add(STRING [, BOOL save] [, BOOL never_capture] [, STRING string]
[, BACKEND backend] [, INT integer])
VOID <obj>.compile()
BOOL <obj>.match(STRING)
INT <obj>.nmatches()
......@@ -53,13 +54,14 @@ SYNOPSIS
INT <obj>.which([ENUM select])
STRING <obj>.string([INT n,] [ENUM select])
BACKEND <obj>.backend([INT n,] [ENUM select])
INT <obj>.integer([INT n] [, ENUM select])
STRING <obj>.sub(STRING text, STRING rewrite [, INT n]
[, ENUM select])
STRING <obj>.suball(STRING text, STRING rewrite [, INT n]
[, ENUM select])
STRING <obj>.extract(STRING text, STRING rewrite [, INT n]
[, ENUM select])
BOOL <obj>.saved([ENUM {REGEX, STR, BE} which] [, INT n]
BOOL <obj>.saved([ENUM {REGEX, STR, BE, INT} which] [, INT n]
[, ENUM select])
# utility function
......@@ -766,20 +768,19 @@ Example::
perl_classes=true);
}
$Method VOID .add(STRING, STRING string=0, BACKEND backend=0, BOOL save=0,
BOOL never_capture=0)
$Method VOID .add(STRING, [STRING string], [BACKEND backend], [BOOL save],
[BOOL never_capture], [INT integer])
Add the given pattern to the set. If the pattern is invalid,
``.add()`` fails, and the VCL will fail to load, with an error message
describing the problem.
If values for the ``string`` and/or ``backend`` parameters are
provided, then these values can be retrieved with the ``.string()``
and ``.backend()`` methods, respectively, as described below. This
makes it possible to associate a string or a backend with the added
pattern after it matches successfully. ``string`` and ``backend``
default to NULL; that is; by default the pattern is not associated
with any such value.
If values for the ``string``, ``backend`` and/or ``integer``
parameters are provided, then these values can be retrieved with the
``.string()``, ``.backend()`` and ``.integer()`` methods,
respectively, as described below. This makes it possible to associate
data with the added pattern after it matches successfully. By default
the pattern is not associated with any such value.
If ``save`` is true, then the given pattern is compiled and saved as a
``regex`` object, just as if the ``regex`` constructor described above
......@@ -1175,6 +1176,70 @@ Example::
}
}
$Method INT .integer(INT n=0, ENUM {FIRST, LAST, UNIQUE} select=UNIQUE)
Returns the integer associated with the `nth` pattern added to the
set, or with the pattern in the set that matched in the most recent
call to ``.match()`` in the same task scope.
The rules for selecting a pattern from the set and its associated
integer based on ``n`` and ``select`` are the same as described above
for ``.string()``.
``.integer()`` invokes VCL failure under the same error conditions
described for ``.string()`` above -- ``n`` and ``select`` are invalid,
or no integer was associated with the selected pattern with the
``.add()`` method.
Note that VCL failure differs from the failure mode for ``.string()``
and ``.backend()``, since there is no distinguished "error" value that
could be returned as the INT. VCL failure has the same effect as if
``return(fail)`` were called from a VCL subroutine; usually, control
directs immediately to ``vcl_synth``, with the response status set to
503, and the response reason set to "VCL failed".
You can avoid that, for example, by testing if ``.nmatches()==1``
after calling ``.match()``, if you need to ensure that calling
``.integer(select=UNIQUE)`` will not fail.
Example::
# Generate redirect responses based on the Host header. In the
# example, subdomains are removed in the new Location, and the
# associated integer is used to set the redirect status code.
sub vcl_init {
# No more than one pattern can match the same string. So it
# is safe to call .integer() with default select=UNIQUE in
# vcl_recv below (no risk of VCL failure).
new redir = re2.set(anchor=both);
redir.add("www\.[^.]+\.foo\.com", integer=301, string="www.foo.com");
redir.add("www\.[^.]+\.bar\.com", integer=302, string="www.bar.com");
redir.add("www\.[^.]+\.baz\.com", integer=303, string="www.baz.com");
redir.add("www\.[^.]+\.quux\.com", integer=307, string="www.quux.com");
redir.compile();
}
sub vcl_recv {
if (redir.match(req.http.Host)) {
# Construct a Location header that will be used in the
# synthetic redirect response.
set req.http.Location = "http://" + redir.string() + req.url;
# Set the response status from the associated integer.
return( synth(redir.integer()) );
}
}
sub vcl_synth {
if (resp.status >= 301 && resp.status <= 307) {
# We come here from the synth return for the redirect
# response. The status code was set from .integer().
set resp.http.Location = req.http.Location;
return(deliver);
}
}
$Method STRING .sub(STRING text, STRING rewrite,
STRING fallback="**SUB METHOD FAILED**",
INT n=0, ENUM {FIRST, LAST, UNIQUE} select=UNIQUE)
......@@ -1331,7 +1396,7 @@ Example::
}
}
$Method BOOL .saved(ENUM {REGEX, STR, BE} which=REGEX, INT n=0,
$Method BOOL .saved(ENUM {REGEX, STR, BE, INT} which=REGEX, INT n=0,
ENUM {FIRST, LAST, UNIQUE} select=UNIQUE)
Returns true if and only if an object of the type indicated by
......@@ -1339,12 +1404,21 @@ Returns true if and only if an object of the type indicated by
added to the set, or for the pattern indicated by ``select`` after the
most recent ``.match()`` call.
In other words, ``.saved()`` returns true for ``which=REGEX`` if the
individual regex was saved with ``.add(save=true)`` for the indicated
pattern; for ``which=STR`` if a string was stored with the ``string``
parameter in ``.add()``, and for ``which=BE`` if a backend was stored
with the ``.backend()`` attribute. The default value of ``which`` is
``REGEX``.
In other words, ``.saved()`` returns true:
* for ``which=REGEX`` if the individual regex was saved with
``.add(save=true)`` for the indicated pattern
* for ``which=STR`` if a string was stored with the ``string``
parameter in ``.add()``
* for ``which=BE`` if a backend was stored with the ``backend``
attribute.
* for ``which=INT`` if an integer was stored with the ``integer``
attribute.
The default value of ``which`` is ``REGEX``.
The pattern in the set is identified by ``n`` and ``select`` according
to the rules given above. ``.saved()`` fails, returning false with a
......@@ -1430,7 +1504,7 @@ branch. See the source repository for versions of the VMOD that are
compatible with other Varnish versions.
It requires the RE2 library, and has been tested against RE2 versions
since 2015-06-01 (through 2019-04-01 at the time of writing).
since 2015-06-01 (through 2019-08-01 at the time of writing).
If the VMOD is built against versions of RE2 since 2017-12-01, it uses
a version of the set match operation that reports out-of-memory
......@@ -1480,10 +1554,17 @@ SEE ALSO
========
* varnishd(1)
* vcl(7)
* VMOD source repository: https://code.uplex.de/uplex-varnish/libvmod-re2
* Gitlab mirror: https://gitlab.com/uplex/varnish/libvmod-re2
* RE2 git repo: https://github.com/google/re2
* RE2 syntax: https://github.com/google/re2/wiki/Syntax
* "Implementing Regular Expressions": https://swtch.com/~rsc/regexp/
* Series of articles motivating the design of RE2, with discussion
......
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