Commit 2b6b31ac 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 0582ef38
...@@ -57,7 +57,8 @@ SYNOPSIS ...@@ -57,7 +57,8 @@ SYNOPSIS
# set object interface # set object interface
new OBJECT = re2.set([ENUM anchor] [, <regex options>]) 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() VOID <obj>.compile()
BOOL <obj>.match(STRING) BOOL <obj>.match(STRING)
INT <obj>.nmatches() INT <obj>.nmatches()
...@@ -65,13 +66,14 @@ SYNOPSIS ...@@ -65,13 +66,14 @@ SYNOPSIS
INT <obj>.which([ENUM select]) INT <obj>.which([ENUM select])
STRING <obj>.string([INT n,] [ENUM select]) STRING <obj>.string([INT n,] [ENUM select])
BACKEND <obj>.backend([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] STRING <obj>.sub(STRING text, STRING rewrite [, INT n]
[, ENUM select]) [, ENUM select])
STRING <obj>.suball(STRING text, STRING rewrite [, INT n] STRING <obj>.suball(STRING text, STRING rewrite [, INT n]
[, ENUM select]) [, ENUM select])
STRING <obj>.extract(STRING text, STRING rewrite [, INT n] STRING <obj>.extract(STRING text, STRING rewrite [, INT n]
[, ENUM select]) [, 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]) [, ENUM select])
# utility function # utility function
...@@ -1000,23 +1002,23 @@ set.add(...) ...@@ -1000,23 +1002,23 @@ set.add(...)
VOID xset.add( VOID xset.add(
STRING, STRING,
STRING string=0, [STRING string],
BACKEND backend=0, [BACKEND backend],
BOOL save=0, [BOOL save],
BOOL never_capture=0 [BOOL never_capture],
[INT integer]
) )
Add the given pattern to the set. If the pattern is invalid, 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 ``.add()`` fails, and the VCL will fail to load, with an error message
describing the problem. describing the problem.
If values for the ``string`` and/or ``backend`` parameters are If values for the ``string``, ``backend`` and/or ``integer``
provided, then these values can be retrieved with the ``.string()`` parameters are provided, then these values can be retrieved with the
and ``.backend()`` methods, respectively, as described below. This ``.string()``, ``.backend()`` and ``.integer()`` methods,
makes it possible to associate a string or a backend with the added respectively, as described below. This makes it possible to associate
pattern after it matches successfully. ``string`` and ``backend`` data with the added pattern after it matches successfully. By default
default to NULL; that is; by default the pattern is not associated the pattern is not associated with any such value.
with any such value.
If ``save`` is true, then the given pattern is compiled and saved as a If ``save`` is true, then the given pattern is compiled and saved as a
``regex`` object, just as if the ``regex`` constructor described above ``regex`` object, just as if the ``regex`` constructor described above
...@@ -1455,6 +1457,81 @@ Example:: ...@@ -1455,6 +1457,81 @@ Example::
} }
.. _func_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);
}
}
.. _func_set.sub: .. _func_set.sub:
set.sub(...) set.sub(...)
...@@ -1655,7 +1732,7 @@ BOOL xset.saved(ENUM which, INT n, ENUM select) ...@@ -1655,7 +1732,7 @@ BOOL xset.saved(ENUM which, INT n, ENUM select)
:: ::
BOOL xset.saved( BOOL xset.saved(
ENUM {REGEX, STR, BE} which=REGEX, ENUM {REGEX, STR, BE, INT} which=REGEX,
INT n=0, INT n=0,
ENUM {FIRST, LAST, UNIQUE} select=UNIQUE ENUM {FIRST, LAST, UNIQUE} select=UNIQUE
) )
...@@ -1665,12 +1742,21 @@ Returns true if and only if an object of the type indicated by ...@@ -1665,12 +1742,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 added to the set, or for the pattern indicated by ``select`` after the
most recent ``.match()`` call. most recent ``.match()`` call.
In other words, ``.saved()`` returns true for ``which=REGEX`` if the In other words, ``.saved()`` returns true:
individual regex was saved with ``.add(save=true)`` for the indicated
pattern; for ``which=STR`` if a string was stored with the ``string`` * for ``which=REGEX`` if the individual regex was saved with
parameter in ``.add()``, and for ``which=BE`` if a backend was stored ``.add(save=true)`` for the indicated pattern
with the ``.backend()`` attribute. The default value of ``which`` is
``REGEX``. * 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 The pattern in the set is identified by ``n`` and ``select`` according
to the rules given above. ``.saved()`` fails, returning false with a to the rules given above. ``.saved()`` fails, returning false with a
...@@ -1823,10 +1909,17 @@ SEE ALSO ...@@ -1823,10 +1909,17 @@ SEE ALSO
======== ========
* varnishd(1) * varnishd(1)
* vcl(7) * vcl(7)
* VMOD source repository: https://code.uplex.de/uplex-varnish/libvmod-re2 * 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 git repo: https://github.com/google/re2
* RE2 syntax: https://github.com/google/re2/wiki/Syntax * RE2 syntax: https://github.com/google/re2/wiki/Syntax
* "Implementing Regular Expressions": https://swtch.com/~rsc/regexp/ * "Implementing Regular Expressions": https://swtch.com/~rsc/regexp/
* Series of articles motivating the design of RE2, with discussion * Series of articles motivating the design of RE2, with discussion
......
...@@ -35,9 +35,13 @@ ...@@ -35,9 +35,13 @@
#define INIT(ctx) (((ctx)->method & VCL_MET_INIT) != 0) #define INIT(ctx) (((ctx)->method & VCL_MET_INIT) != 0)
#define STR_ADDED 0 enum bitmap_e {
#define BE_ADDED 1 STRING = 0,
#define RE_ADDED 2 BACKEND,
REGEX,
INTEGER,
__MAX_BITMAP,
};
#define NELEMS(a) (sizeof(a) / sizeof((a)[0])) #define NELEMS(a) (sizeof(a) / sizeof((a)[0]))
...@@ -59,11 +63,12 @@ struct vmod_re2_set { ...@@ -59,11 +63,12 @@ struct vmod_re2_set {
unsigned magic; unsigned magic;
#define VMOD_RE2_SET_MAGIC 0xf6d7b15a #define VMOD_RE2_SET_MAGIC 0xf6d7b15a
vre2set *set; vre2set *set;
struct vbitmap *added[3]; struct vbitmap *added[__MAX_BITMAP];
char *vcl_name; char *vcl_name;
char **string; char **string;
VCL_BACKEND *backend; VCL_BACKEND *backend;
struct vmod_re2_regex **regex; struct vmod_re2_regex **regex;
VCL_INT *integer;
struct set_options opts; struct set_options opts;
unsigned compiled; unsigned compiled;
int npatterns; int npatterns;
...@@ -181,10 +186,10 @@ vmod_set__fini(struct vmod_re2_set **setp) ...@@ -181,10 +186,10 @@ vmod_set__fini(struct vmod_re2_set **setp)
*setp = NULL; *setp = NULL;
vre2set_fini(&set->set); vre2set_fini(&set->set);
for (int i = 0; i < set->npatterns; i++) { 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) && set->string[i] != NULL)
free(set->string[i]); free(set->string[i]);
if (vbit_test(set->added[RE_ADDED], i) if (vbit_test(set->added[REGEX], i)
&& set->regex[i] != NULL) && set->regex[i] != NULL)
vmod_regex__fini(&set->regex[i]); vmod_regex__fini(&set->regex[i]);
} }
...@@ -198,12 +203,11 @@ vmod_set__fini(struct vmod_re2_set **setp) ...@@ -198,12 +203,11 @@ vmod_set__fini(struct vmod_re2_set **setp)
#define ERR_PREFIX "%s.add(\"%.40s\"): " #define ERR_PREFIX "%s.add(\"%.40s\"): "
VCL_VOID VCL_VOID
vmod_set_add(VRT_CTX, struct vmod_re2_set *set, VCL_STRING pattern, vmod_set_add(VRT_CTX, struct vmod_re2_set *set, struct vmod_set_add_arg *args)
VCL_STRING string, VCL_BACKEND backend, VCL_BOOL save,
VCL_BOOL never_capture)
{ {
const char *err; const char *err;
int n; int n;
VCL_STRING pattern = args->arg1;
CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC); CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
CHECK_OBJ_NOTNULL(set, VMOD_RE2_SET_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, ...@@ -226,31 +230,42 @@ vmod_set_add(VRT_CTX, struct vmod_re2_set *set, VCL_STRING pattern,
return; return;
} }
if (string != NULL) { if (args->valid_string && args->string != NULL) {
if ((set->string = realloc(set->string, if ((set->string = realloc(set->string,
(n + 1) * (sizeof(char *)))) (n + 1) * (sizeof(char *))))
== NULL) { == NULL) {
VFAILNOMEM(ctx, ERR_PREFIX "adding string %s", VFAILNOMEM(ctx, ERR_PREFIX "adding string %s",
set->vcl_name, pattern, string); set->vcl_name, pattern, args->string);
return; return;
} }
set->string[n] = strdup(string); set->string[n] = strdup(args->string);
AN(set->string[n]); 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, if ((set->backend = realloc(set->backend,
(n + 1) * (sizeof(VCL_BACKEND)))) (n + 1) * (sizeof(VCL_BACKEND))))
== NULL) { == NULL) {
VFAILNOMEM(ctx, ERR_PREFIX "adding backend %s", VFAILNOMEM(ctx, ERR_PREFIX "adding backend %s",
set->vcl_name, pattern, 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; return;
} }
set->backend[n] = backend; set->integer[n] = args->integer;
vbit_set(set->added[BE_ADDED], n); vbit_set(set->added[INTEGER], n);
} }
if (save) { if (args->valid_save && args->save) {
struct vmod_re2_regex *re = NULL; struct vmod_re2_regex *re = NULL;
char *vcl_name; char *vcl_name;
size_t namelen; size_t namelen;
...@@ -264,7 +279,7 @@ vmod_set_add(VRT_CTX, struct vmod_re2_set *set, VCL_STRING pattern, ...@@ -264,7 +279,7 @@ vmod_set_add(VRT_CTX, struct vmod_re2_set *set, VCL_STRING pattern,
set->opts.posix_syntax, set->opts.posix_syntax,
set->opts.longest_match, set->opts.max_mem, set->opts.longest_match, set->opts.max_mem,
set->opts.literal, set->opts.never_nl, 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.case_sensitive,
set->opts.perl_classes, set->opts.perl_classes,
set->opts.word_boundary, set->opts.one_line); 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, ...@@ -282,7 +297,7 @@ vmod_set_add(VRT_CTX, struct vmod_re2_set *set, VCL_STRING pattern,
return; return;
} }
set->regex[n] = re; set->regex[n] = re;
vbit_set(set->added[RE_ADDED], n); vbit_set(set->added[REGEX], n);
} }
set->npatterns++; set->npatterns++;
} }
...@@ -541,7 +556,7 @@ rewritef(VRT_CTX, struct vmod_re2_set * const restrict set, ...@@ -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]); idx = get_match_idx(ctx, set, n, selects, rewrite_name[type]);
if (idx < 0) if (idx < 0)
return NULL; return NULL;
if (!vbit_test(set->added[RE_ADDED], idx)) { if (!vbit_test(set->added[REGEX], idx)) {
AN(selects); AN(selects);
VERR(ctx, "%s.%s(%s, %s, %lld, %s): Pattern %d was not saved", VERR(ctx, "%s.%s(%s, %s, %lld, %s): Pattern %d was not saved",
set->vcl_name, rewrite_name[type], text, rewrite, n, 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) ...@@ -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"); idx = get_match_idx(ctx, set, n, selects, "string");
if (idx < 0) if (idx < 0)
return NULL; return NULL;
if (!vbit_test(set->added[STR_ADDED], idx)) { if (!vbit_test(set->added[STRING], idx)) {
AN(selects); AN(selects);
VERR(ctx, "%s.string(%lld, %s): String %lld was not added", VERR(ctx, "%s.string(%lld, %s): String %lld was not added",
set->vcl_name, n, selects, idx + 1); 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) ...@@ -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"); idx = get_match_idx(ctx, set, n, selects, "backend");
if (idx < 0) if (idx < 0)
return NULL; return NULL;
if (!vbit_test(set->added[BE_ADDED], idx)) { if (!vbit_test(set->added[BACKEND], idx)) {
AN(selects); AN(selects);
VERR(ctx, "%s.backend(%lld, %s): Backend %lld was not added", VERR(ctx, "%s.backend(%lld, %s): Backend %lld was not added",
set->vcl_name, n, selects, idx + 1); 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) ...@@ -626,6 +641,35 @@ vmod_set_backend(VRT_CTX, struct vmod_re2_set *set, VCL_INT n, VCL_ENUM selects)
return set->backend[idx]; 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 VCL_BOOL
vmod_set_saved(VRT_CTX, struct vmod_re2_set *set, VCL_ENUM whichs, VCL_INT n, vmod_set_saved(VRT_CTX, struct vmod_re2_set *set, VCL_ENUM whichs, VCL_INT n,
VCL_ENUM selects) VCL_ENUM selects)
...@@ -639,11 +683,13 @@ vmod_set_saved(VRT_CTX, struct vmod_re2_set *set, VCL_ENUM whichs, VCL_INT n, ...@@ -639,11 +683,13 @@ vmod_set_saved(VRT_CTX, struct vmod_re2_set *set, VCL_ENUM whichs, VCL_INT n,
if (idx < 0) if (idx < 0)
return 0; return 0;
if (whichs == vmod_enum_REGEX) if (whichs == vmod_enum_REGEX)
return vbit_test(set->added[RE_ADDED], idx); return vbit_test(set->added[REGEX], idx);
if (whichs == vmod_enum_BE) if (whichs == vmod_enum_BE)
return vbit_test(set->added[BE_ADDED], idx); return vbit_test(set->added[BACKEND], idx);
if (whichs == vmod_enum_STR) if (whichs == vmod_enum_STR)
return vbit_test(set->added[STR_ADDED], idx); return vbit_test(set->added[STRING], idx);
if (whichs == vmod_enum_INT)
return vbit_test(set->added[INTEGER], idx);
WRONG("illegal which ENUM"); WRONG("illegal which ENUM");
return 0; return 0;
} }
...@@ -435,3 +435,178 @@ client c1 { ...@@ -435,3 +435,178 @@ client c1 {
} -run } -run
logexpect l1 -wait 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
...@@ -44,7 +44,8 @@ SYNOPSIS ...@@ -44,7 +44,8 @@ SYNOPSIS
# set object interface # set object interface
new OBJECT = re2.set([ENUM anchor] [, <regex options>]) 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() VOID <obj>.compile()
BOOL <obj>.match(STRING) BOOL <obj>.match(STRING)
INT <obj>.nmatches() INT <obj>.nmatches()
...@@ -52,13 +53,14 @@ SYNOPSIS ...@@ -52,13 +53,14 @@ SYNOPSIS
INT <obj>.which([ENUM select]) INT <obj>.which([ENUM select])
STRING <obj>.string([INT n,] [ENUM select]) STRING <obj>.string([INT n,] [ENUM select])
BACKEND <obj>.backend([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] STRING <obj>.sub(STRING text, STRING rewrite [, INT n]
[, ENUM select]) [, ENUM select])
STRING <obj>.suball(STRING text, STRING rewrite [, INT n] STRING <obj>.suball(STRING text, STRING rewrite [, INT n]
[, ENUM select]) [, ENUM select])
STRING <obj>.extract(STRING text, STRING rewrite [, INT n] STRING <obj>.extract(STRING text, STRING rewrite [, INT n]
[, ENUM select]) [, 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]) [, ENUM select])
# utility function # utility function
...@@ -765,20 +767,19 @@ Example:: ...@@ -765,20 +767,19 @@ Example::
perl_classes=true); perl_classes=true);
} }
$Method VOID .add(STRING, STRING string=0, BACKEND backend=0, BOOL save=0, $Method VOID .add(STRING, [STRING string], [BACKEND backend], [BOOL save],
BOOL never_capture=0) [BOOL never_capture], [INT integer])
Add the given pattern to the set. If the pattern is invalid, 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 ``.add()`` fails, and the VCL will fail to load, with an error message
describing the problem. describing the problem.
If values for the ``string`` and/or ``backend`` parameters are If values for the ``string``, ``backend`` and/or ``integer``
provided, then these values can be retrieved with the ``.string()`` parameters are provided, then these values can be retrieved with the
and ``.backend()`` methods, respectively, as described below. This ``.string()``, ``.backend()`` and ``.integer()`` methods,
makes it possible to associate a string or a backend with the added respectively, as described below. This makes it possible to associate
pattern after it matches successfully. ``string`` and ``backend`` data with the added pattern after it matches successfully. By default
default to NULL; that is; by default the pattern is not associated the pattern is not associated with any such value.
with any such value.
If ``save`` is true, then the given pattern is compiled and saved as a If ``save`` is true, then the given pattern is compiled and saved as a
``regex`` object, just as if the ``regex`` constructor described above ``regex`` object, just as if the ``regex`` constructor described above
...@@ -1174,6 +1175,70 @@ Example:: ...@@ -1174,6 +1175,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, $Method STRING .sub(STRING text, STRING rewrite,
STRING fallback="**SUB METHOD FAILED**", STRING fallback="**SUB METHOD FAILED**",
INT n=0, ENUM {FIRST, LAST, UNIQUE} select=UNIQUE) INT n=0, ENUM {FIRST, LAST, UNIQUE} select=UNIQUE)
...@@ -1330,7 +1395,7 @@ Example:: ...@@ -1330,7 +1395,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) ENUM {FIRST, LAST, UNIQUE} select=UNIQUE)
Returns true if and only if an object of the type indicated by Returns true if and only if an object of the type indicated by
...@@ -1338,12 +1403,21 @@ Returns true if and only if an object of the type indicated by ...@@ -1338,12 +1403,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 added to the set, or for the pattern indicated by ``select`` after the
most recent ``.match()`` call. most recent ``.match()`` call.
In other words, ``.saved()`` returns true for ``which=REGEX`` if the In other words, ``.saved()`` returns true:
individual regex was saved with ``.add(save=true)`` for the indicated
pattern; for ``which=STR`` if a string was stored with the ``string`` * for ``which=REGEX`` if the individual regex was saved with
parameter in ``.add()``, and for ``which=BE`` if a backend was stored ``.add(save=true)`` for the indicated pattern
with the ``.backend()`` attribute. The default value of ``which`` is
``REGEX``. * 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 The pattern in the set is identified by ``n`` and ``select`` according
to the rules given above. ``.saved()`` fails, returning false with a to the rules given above. ``.saved()`` fails, returning false with a
...@@ -1479,10 +1553,17 @@ SEE ALSO ...@@ -1479,10 +1553,17 @@ SEE ALSO
======== ========
* varnishd(1) * varnishd(1)
* vcl(7) * vcl(7)
* VMOD source repository: https://code.uplex.de/uplex-varnish/libvmod-re2 * 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 git repo: https://github.com/google/re2
* RE2 syntax: https://github.com/google/re2/wiki/Syntax * RE2 syntax: https://github.com/google/re2/wiki/Syntax
* "Implementing Regular Expressions": https://swtch.com/~rsc/regexp/ * "Implementing Regular Expressions": https://swtch.com/~rsc/regexp/
* Series of articles motivating the design of RE2, with discussion * 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