Commit 8be0403a authored by Geoff Simmons's avatar Geoff Simmons Committed by Dridi Boukelmoune

Add std.fnmatch()

Closes: #2737
parent bbccfcd6
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
......@@ -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}"
......
......@@ -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)`
......@@ -37,6 +37,7 @@
#include <string.h>
#include <syslog.h>
#include <sys/socket.h>
#include <fnmatch.h>
#include "cache/cache.h"
......@@ -304,3 +305,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);
}
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