[master] 23386a454 Add std.fnmatch()
Geoff Simmons
geoff at uplex.de
Tue Aug 7 08:42:07 UTC 2018
commit 23386a454284a62781799f484684048be866608b
Author: Geoff Simmons <geoff at uplex.de>
Date: Fri Jul 27 17:26:16 2018 +0200
Add std.fnmatch()
Closes: #2737
diff --git a/bin/varnishtest/tests/m00050.vtc b/bin/varnishtest/tests/m00050.vtc
new file mode 100644
index 000000000..a6578a6bd
--- /dev/null
+++ b/bin/varnishtest/tests/m00050.vtc
@@ -0,0 +1,188 @@
+varnishtest "std.fnmatch()"
+
+varnish v1 -vcl {
+ import std;
+ backend b { .host = "${bad_ip}"; }
+
+ sub vcl_recv {
+ return (synth(200));
+ }
+
+ sub vcl_synth {
+ set resp.http.Match
+ = std.fnmatch(req.http.Pattern, req.http.Subject);
+ set resp.http.Match-Nopathname = std.fnmatch(req.http.Pattern,
+ req.http.Subject,
+ pathname=false);
+ set resp.http.Match-Noescape = std.fnmatch(req.http.Pattern,
+ req.http.Subject,
+ noescape=true);
+ set resp.http.Match-Period = std.fnmatch(req.http.Pattern,
+ req.http.Subject,
+ period=true);
+ }
+} -start
+
+client c1 {
+ txreq -hdr "Pattern: /foo/" -hdr "Subject: /foo/"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.Match == "true"
+ expect resp.http.Match-Nopathname == resp.http.Match
+ expect resp.http.Match-Noescape == resp.http.Match
+ expect resp.http.Match-Period == resp.http.Match
+
+ txreq -hdr "Pattern: /foo/" -hdr "Subject: /bar/"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.Match == "false"
+ expect resp.http.Match-Nopathname == resp.http.Match
+ expect resp.http.Match-Noescape == resp.http.Match
+ expect resp.http.Match-Period == resp.http.Match
+
+ txreq -hdr "Pattern: /foo/*" -hdr "Subject: /foo/bar"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.Match == "true"
+ expect resp.http.Match-Nopathname == resp.http.Match
+ expect resp.http.Match-Noescape == resp.http.Match
+ expect resp.http.Match-Period == resp.http.Match
+
+ txreq -hdr "Pattern: /foo/bar/*" -hdr "Subject: /foo/bar"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.Match == "false"
+ expect resp.http.Match-Nopathname == resp.http.Match
+ expect resp.http.Match-Noescape == resp.http.Match
+ expect resp.http.Match-Period == resp.http.Match
+
+ txreq -hdr "Pattern: /foo/?" -hdr "Subject: /foo/b"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.Match == "true"
+ expect resp.http.Match-Nopathname == resp.http.Match
+ expect resp.http.Match-Noescape == resp.http.Match
+ expect resp.http.Match-Period == resp.http.Match
+
+ txreq -hdr "Pattern: /foo/?" -hdr "Subject: /foo/bar"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.Match == "false"
+ expect resp.http.Match-Nopathname == resp.http.Match
+ expect resp.http.Match-Noescape == resp.http.Match
+ expect resp.http.Match-Period == resp.http.Match
+
+ txreq -hdr "Pattern: /foo/[a-z]" -hdr "Subject: /foo/b"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.Match == "true"
+ expect resp.http.Match-Nopathname == resp.http.Match
+ expect resp.http.Match-Noescape == resp.http.Match
+ expect resp.http.Match-Period == resp.http.Match
+
+ txreq -hdr "Pattern: /foo/[a-z]" -hdr "Subject: /foo/B"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.Match == "false"
+ expect resp.http.Match-Nopathname == resp.http.Match
+ expect resp.http.Match-Noescape == resp.http.Match
+ expect resp.http.Match-Period == resp.http.Match
+
+ txreq -hdr "Pattern: /foo/[!a-z]" -hdr "Subject: /foo/B"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.Match == "true"
+ expect resp.http.Match-Nopathname == resp.http.Match
+ expect resp.http.Match-Noescape == resp.http.Match
+ expect resp.http.Match-Period == resp.http.Match
+
+ txreq -hdr "Pattern: /foo/*/quux" -hdr "Subject: /foo/bar/baz/quux"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.Match == "false"
+ expect resp.http.Match-Nopathname == "true"
+ expect resp.http.Match-Noescape == resp.http.Match
+ expect resp.http.Match-Period == resp.http.Match
+
+ txreq -hdr "Pattern: /foo/?/bar" -hdr "Subject: /foo///bar"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.Match == "false"
+ expect resp.http.Match-Nopathname == "true"
+ expect resp.http.Match-Noescape == resp.http.Match
+ expect resp.http.Match-Period == resp.http.Match
+
+ txreq -hdr "Pattern: /foo/[a/b]/bar" -hdr "Subject: /foo///bar"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.Match == "false"
+ expect resp.http.Match-Nopathname == "true"
+ expect resp.http.Match-Noescape == resp.http.Match
+ expect resp.http.Match-Period == resp.http.Match
+
+ txreq -hdr {Pattern: \\foo} -hdr {Subject: \foo}
+ rxresp
+ expect resp.status == 200
+ expect resp.http.Match == "true"
+ expect resp.http.Match-Nopathname == resp.http.Match
+ expect resp.http.Match-Noescape == "false"
+ expect resp.http.Match-Period == resp.http.Match
+
+ txreq -hdr "Pattern: *foo" -hdr "Subject: .foo"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.Match == "true"
+ expect resp.http.Match-Nopathname == resp.http.Match
+ expect resp.http.Match-Noescape == resp.http.Match
+ expect resp.http.Match-Period == "false"
+
+ txreq -hdr "Pattern: /*foo" -hdr "Subject: /.foo"
+ rxresp
+ expect resp.status == 200
+ expect resp.http.Match == "true"
+ expect resp.http.Match-Nopathname == resp.http.Match
+ expect resp.http.Match-Noescape == resp.http.Match
+ expect resp.http.Match-Period == "false"
+} -run
+
+server s1 {
+ rxreq
+ txresp
+} -start
+
+varnish v1 -vcl+backend {
+ import std;
+
+ sub vcl_deliver {
+ set resp.http.Match
+ = std.fnmatch(req.http.Pattern, req.http.Subject);
+ }
+}
+
+client c1 {
+ txreq -hdr "Pattern: /foo/"
+ rxresp
+ expect resp.status == 503
+ expect resp.reason == "VCL failed"
+} -run
+
+logexpect l1 -v v1 -d 1 -g vxid -q "VCL_Error" {
+ expect 0 * Begin
+ expect * = VCL_Error {^std\.fnmatch\(\): subject is NULL$}
+ expect * = End
+} -run
+
+logexpect l1 -v v1 -d 0 -g vxid -q "VCL_Error" {
+ expect 0 * Begin
+ expect * = VCL_Error {^std\.fnmatch\(\): pattern is NULL$}
+ expect * = End
+} -start
+
+client c1 {
+ txreq -hdr "Subject: /foo/"
+ rxresp
+ expect resp.status == 503
+ expect resp.reason == "VCL failed"
+} -run
+
+logexpect l1 -wait
diff --git a/configure.ac b/configure.ac
index 6e6734c3e..3a45477b3 100644
--- a/configure.ac
+++ b/configure.ac
@@ -208,6 +208,7 @@ AC_CHECK_HEADERS([sys/vfs.h])
AC_CHECK_HEADERS([endian.h])
AC_CHECK_HEADERS([pthread_np.h], [], [], [#include <pthread.h>])
AC_CHECK_HEADERS([priv.h])
+AC_CHECK_HEADERS([fnmatch.h], [], [AC_MSG_ERROR([fnmatch.h is required])])
# Checks for library functions.
_VARNISH_CHECK_EXPLICIT_BZERO
@@ -218,6 +219,7 @@ AC_CHECK_FUNCS([closefrom])
AC_CHECK_FUNCS([sigaltstack])
AC_CHECK_FUNCS([getpeereid])
AC_CHECK_FUNCS([getpeerucred])
+AC_CHECK_FUNCS([fnmatch], [], [AC_MSG_ERROR([fnmatch(3) is required])])
save_LIBS="${LIBS}"
LIBS="${PTHREAD_LIBS}"
diff --git a/lib/libvmod_std/vmod.vcc b/lib/libvmod_std/vmod.vcc
index c8305ae9d..c46d24156 100644
--- a/lib/libvmod_std/vmod.vcc
+++ b/lib/libvmod_std/vmod.vcc
@@ -361,8 +361,70 @@ $Function BOOL syntax(REAL)
Description
Returns the true if VCL version is at least REAL.
+$Function BOOL fnmatch(STRING pattern, STRING subject, BOOL pathname=1,
+ BOOL noescape=0, BOOL period=0)
+
+Description
+ Shell-style pattern matching; returns `true` if *subject*
+ matches *pattern*, where *pattern* may contain wildcard
+ characters such as \* or ?.
+
+ The match is executed by the implementation of `fnmatch(3)` on
+ your system. The rules for pattern matching on most systems
+ include the following:
+
+ * \* matches any sequence of characters
+
+ * ? matches a single character
+
+ * a bracket expresion such as [abc] or [!0-9] is interpreted
+ as a character class according to the rules of basic regular
+ expressions (*not* PCRE regexen), except that ! is used for
+ character class negation instead of ^.
+
+ If *pathname* is `true`, then the forward slash character / is
+ only matched literally, and never matches \*, ? or a bracket
+ expression. Otherwise, / may match one of those patterns. By
+ default, *pathname* is `true`.
+
+ If *noescape* is `true`, then the backslash character \\ is
+ matched as an ordinary character. Otherwise, \\ is an escape
+ character, and matches the character that follows it in the
+ `pattern`. For example, \\\\ matches \\ when *noescape* is
+ `true`, and \\\\ when `false`. By default, *noescape* is
+ `false`.
+
+ If *period* is `true`, then a leading period character . only
+ matches literally, and never matches \*, ? or a bracket
+ expression. A period is leading if it is the first character
+ in `subject`; if *pathname* is also `true`, then a period that
+ immediately follows a / is also leading (as in "/."). By
+ default, *period* is `false`.
+
+ `fnmatch()` invokes VCL failure and returns `false` if either
+ of *pattern* or *subject* is NULL -- for example, if an unset
+ header is specified.
+
+Examples
+ | # Matches URLs such as /foo/bar and /foo/bar/baz
+ | if (std.fnmatch("/foo/\*", req.url)) { ... }
+ |
+ | # Matches /foo/bar/quux, but not /foo/bar/baz/quux
+ | if (std.fnmatch("/foo/\*/quux", req.url)) { ... }
+ |
+ | # Matches /foo/bar/quux and /foo/bar/baz/quux
+ | if (std.fnmatch("/foo/\*/quux", req.url, pathname=false)) { ... }
+ |
+ | # Matches /foo/bar, /foo/car and /foo/far
+ | if (std.fnmatch("/foo/?ar", req.url)) { ... }
+ |
+ | # Matches /foo/ followed by a non-digit
+ | if (std.fnmatch("/foo/[!0-9]", req.url)) { ... }
+
+
SEE ALSO
========
* :ref:`varnishd(1)`
* :ref:`vsl(7)`
+* `fnmatch(3)`
diff --git a/lib/libvmod_std/vmod_std.c b/lib/libvmod_std/vmod_std.c
index 4be62ae7e..03a39453e 100644
--- a/lib/libvmod_std/vmod_std.c
+++ b/lib/libvmod_std/vmod_std.c
@@ -37,6 +37,7 @@
#include <string.h>
#include <syslog.h>
#include <sys/socket.h>
+#include <fnmatch.h>
#include "cache/cache.h"
@@ -302,3 +303,28 @@ vmod_syntax(VRT_CTX, VCL_REAL r)
*/
return (round(r * 10) <= ctx->syntax);
}
+
+VCL_BOOL v_matchproto_(td_std_fnmatch)
+vmod_fnmatch(VRT_CTX, VCL_STRING pattern, VCL_STRING subject,
+ VCL_BOOL pathname, VCL_BOOL noescape, VCL_BOOL period)
+{
+ int flags = 0;
+
+ CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
+ if (pattern == NULL) {
+ VRT_fail(ctx, "std.fnmatch(): pattern is NULL");
+ return (0);
+ }
+ if (subject == NULL) {
+ VRT_fail(ctx, "std.fnmatch(): subject is NULL");
+ return (0);
+ }
+
+ if (pathname)
+ flags |= FNM_PATHNAME;
+ if (noescape)
+ flags |= FNM_NOESCAPE;
+ if (period)
+ flags |= FNM_PERIOD;
+ return (fnmatch(pattern, subject, flags) != FNM_NOMATCH);
+}
More information about the varnish-commit
mailing list