Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
L
libvmod-re2
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
uplex-varnish
libvmod-re2
Commits
c7e8f60a
Commit
c7e8f60a
authored
Aug 23, 2019
by
Geoff Simmons
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
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
Changes
4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
462 additions
and
68 deletions
+462
-68
README.rst
README.rst
+115
-23
set.c
src/set.c
+72
-26
set_retrieve.vtc
src/tests/set_retrieve.vtc
+175
-0
vmod_re2.vcc
src/vmod_re2.vcc
+100
-19
No files found.
README.rst
View file @
c7e8f60a
...
...
@@ -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-0
4
-01 at the time of writing).
since 2015-06-01 (through 2019-0
8
-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
...
...
src/set.c
View file @
c7e8f60a
...
...
@@ -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
[
STR
ING
],
i
)
&&
set
->
string
[
i
]
!=
NULL
)
free
(
set
->
string
[
i
]);
if
(
vbit_test
(
set
->
added
[
RE
_ADDED
],
i
)
if
(
vbit_test
(
set
->
added
[
RE
GEX
],
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
[
STR
ING
],
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
[
RE
GEX
],
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
[
RE
GEX
],
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
[
STR
ING
],
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
[
B
E_ADDE
D
],
idx
))
{
if
(
!
vbit_test
(
set
->
added
[
B
ACKEN
D
],
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
[
RE
GEX
],
idx
);
if
(
whichs
==
VENUM
(
BE
))
return
vbit_test
(
set
->
added
[
B
E_ADDE
D
],
idx
);
return
vbit_test
(
set
->
added
[
B
ACKEN
D
],
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
;
}
src/tests/set_retrieve.vtc
View file @
c7e8f60a
...
...
@@ -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
src/vmod_re2.vcc
View file @
c7e8f60a
...
...
@@ -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-0
4
-01 at the time of writing).
since 2015-06-01 (through 2019-0
8
-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)