Commit fc3a7a02 authored by Geoff Simmons's avatar Geoff Simmons

Initial commit, passes the first tests for set matches.

parents
((nil . ((indent-tabs-mode . t)))
(c-mode . ((c-file-style . "BSD"))))
Makefile
Makefile.in
.deps/
.libs/
*.o
*.lo
*.la
*~
*.[1-9]
.dirstamp
/aclocal.m4
/autom4te.cache/
/compile
/config.guess
/config.h
/config.h.in
/config.log
/config.status
/config.sub
/configure
/depcomp
/install-sh
/libtool
/ltmain.sh
/missing
/stamp-h1
/m4/
/src/vcc_if.c
/src/vcc_if.h
/src/vmod_*rst
CONTRIBUTING
============
To contribute code or documentation, submit a pull request at the
`source repository website
<https://code.uplex.de/uplex-varnish/libvmod-selector>`_.
If you have a problem or discover a bug, you can post an `issue
<https://code.uplex.de/uplex-varnish/libvmod-selector/issues>`_ at
the website. You can also write to <varnish-support@uplex.de>.
For developers
--------------
The VMOD source code is in C, and compilation has been tested with gcc
and clang. The code MUST always compile successfully with both of
them.
The build specifies C99 conformance for C sources (``-std=c99``). All
compiler warnings are turned on, and all warnings are considered
errors (``-Werror -Wall -Wextra``). The code MUST always build
without warnings or errors under these constraints.
By default, ``CFLAGS`` is set to ``-g -O2``, so that symbols are
included in the shared library, and optimization is at level
``O2``. To change or disable these options, set ``CFLAGS`` explicitly
before calling ``configure`` (it may be set to the empty string).
For development/debugging cycles, the ``configure`` option
``--enable-debugging`` is recommended (off by default). This will turn
off optimizations and function inlining, so that a debugger will step
through the code as expected.
By default, the VMOD is built with the stack protector enabled
(compile option ``-fstack-protector``), but it can be disabled with
the ``configure`` option ``--disable-stack-protector``.
See LICENSE for details.
INSTALLATION
============
The VMOD is built against a Varnish installation, and the autotools
use ``pkg-config(1)`` to locate the necessary header files and other
resources. This sequence will install the VMOD::
> ./autogen.sh # for builds from the git repo
> ./configure
> make
> make check # to run unit tests in src/tests/*.vtc
> make distcheck # run check and prepare a distribution tarball
> sudo make install
See `CONTRIBUTING.rst <CONTRIBUTING.rst>`_ for notes about building
from source.
If you have installed Varnish in non-standard directories, call
``autogen.sh`` and ``configure`` with the ``PKG_CONFIG_PATH``
environment variable set to include the paths where the ``.pc`` file
can be located for ``varnishapi``. For example, when varnishd
configure was called with ``--prefix=$PREFIX``, use::
> PKG_CONFIG_PATH=${PREFIX}/lib/pkgconfig
> export PKG_CONFIG_PATH
By default, the vmod ``configure`` script installs the vmod in
the same directory as Varnish, determined via ``pkg-config(1)``. The
vmod installation directory can be overridden by passing the
``VMOD_DIR`` variable to ``configure``.
Other files such as the man-page are installed in the locations
determined by ``configure``, which inherits its default ``--prefix``
setting from Varnish.
Copyright (c) 2018 UPLEX Nils Goroll Systemoptimierung
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.
ACLOCAL_AMFLAGS = -I m4 -I ${VARNISHAPI_DATAROOTDIR}/aclocal
SUBDIRS = src
DISTCHECK_CONFIGURE_FLAGS = \
VMOD_DIR='$${libdir}/varnish/vmods'
EXTRA_DIST = README.rst LICENSE COPYING CONTRIBUTING.rst INSTALL.rst
doc_DATA = README.rst LICENSE COPYING CONTRIBUTING.rst INSTALL.rst
README.rst: src/vmod_selector.rst
cp src/vmod_selector.rst README.rst
src/vmod_selector.rst:
make -C src vmod_selector.rst
%.1 %.2 %.3 %.4 %.5 %.6 %.7 %.8 %.9:
if HAVE_RST2MAN
${RST2MAN} $< $@
else
@echo "========================================"
@echo "You need rst2man installed to make dist"
@echo "========================================"
@false
endif
..
.. NB: This file is machine generated, DO NOT EDIT!
..
.. Edit vmod.vcc and run make instead
..
.. role:: ref(emphasis)
.. _vmod_selector(3):
=============
vmod_selector
=============
---------------------------------------------------------------------------------------
Varnish Module for matching strings associated with backends, regexen and other strings
---------------------------------------------------------------------------------------
:Manual section: 3
CONTENTS
========
* :ref:`obj_set`
* :ref:`func_set.add`
* :ref:`func_set.debug`
* :ref:`func_set.match`
* :ref:`func_version`
SYNOPSIS
========
import selector;
new <obj> = selector.set([BOOL case_sensitive])
VOID <obj>.add(STRING [, STRING string] [,STRING regex] [, BACKEND backend])
BOOL <obj>.match(STRING)
INT <obj>.nmatches()
BOOL <obj>.matched(INT)
INT <obj>.which([ENUM select])
STRING <obj>.string([INT n,] [ENUM select])
BACKEND <obj>.backend([INT n,] [ENUM select])
STRING <obj>.sub(STRING text, STRING rewrite [, INT n] [, ENUM select])
# VMOD version
STRING selector.version()
DESCRIPTION
===========
Varnish Module (VMOD) for ...
.. _obj_set:
new xset = set(BOOL case_sensitive=1)
-------------------------------------
Create a set object ...
Example::
sub vcl_init {
new ...
}
.. _func_set.add:
set.add(...)
------------
::
VOID xset.add(
STRING,
STRING string=0,
STRING regex=0,
BACKEND backend=0
)
Add the given string to the set. XXX ...
.. _func_set.match:
BOOL xset.match(STRING)
-----------------------
Returns ``true`` if and only if ...
Example::
if (myset.match(req.http.Host)) {
call do_on_match;
}
.. _func_set.debug:
STRING xset.debug()
-------------------
Intentionally not documented.
.. _func_version:
STRING version()
----------------
Return the version string for this VMOD.
Example::
std.log("Using VMOD selector version: " + selector.version());
REQUIREMENTS
============
The VMOD requires Varnish version XXX. See the source repository for
versions of the VMOD that are compatible with other Varnish versions.
INSTALLATION
============
See `INSTALL.rst <INSTALL.rst>`_ in the source repository.
LIMITATIONS
===========
XXX ...
AUTHOR
======
* Geoffrey Simmons <geoff@uplex.de>
UPLEX Nils Goroll Systemoptimierung
SEE ALSO
========
* varnishd(1)
* vcl(7)
* VMOD source repository: https://code.uplex.de/uplex-varnish/libvmod-selector
COPYRIGHT
=========
::
Copyright (c) 2018 UPLEX Nils Goroll Systemoptimierung
All rights reserved
Author: Geoffrey Simmons <geoffrey.simmons@uplex.de>
See LICENSE
#!/bin/sh
warn() {
echo "WARNING: $@" 1>&2
}
case `uname -s` in
Darwin)
LIBTOOLIZE=glibtoolize
;;
FreeBSD)
LIBTOOLIZE=libtoolize
;;
Linux)
LIBTOOLIZE=libtoolize
;;
SunOS)
LIBTOOLIZE=libtoolize
;;
*)
warn "unrecognized platform:" `uname -s`
LIBTOOLIZE=libtoolize
esac
automake_version=`automake --version | tr ' ' '\n' | egrep '^[0-9]\.[0-9a-z.-]+'`
if [ -z "$automake_version" ] ; then
warn "unable to determine automake version"
else
case $automake_version in
0.*|1.[0-8]|1.[0-8][.-]*)
warn "automake ($automake_version) detected; 1.9 or newer recommended"
;;
*)
;;
esac
fi
# check for varnishapi.m4 in custom paths
dataroot=$(pkg-config --variable=datarootdir varnishapi 2>/dev/null)
if [ -z "$dataroot" ] ; then
cat >&2 <<'EOF'
Package varnishapi was not found in the pkg-config search path.
Perhaps you should add the directory containing `varnishapi.pc'
to the PKG_CONFIG_PATH environment variable
EOF
exit 1
fi
set -ex
aclocal -I m4 -I ${dataroot}/aclocal
$LIBTOOLIZE --copy --force
autoheader
automake --add-missing --copy --foreign
autoconf
AC_PREREQ(2.68)
AC_COPYRIGHT([Copyright (c) 2018 UPLEX - Nils Goroll Systemoptimierung])
AC_INIT([libvmod-selector], [trunk], [varnish-support@uplex.de], [vmod-selector])
AC_CONFIG_MACRO_DIR([m4])
AC_CONFIG_SRCDIR(src/vmod_selector.vcc)
AM_CONFIG_HEADER(config.h)
AC_CANONICAL_SYSTEM
AC_LANG(C)
AM_INIT_AUTOMAKE([1.12 -Wall -Werror foreign parallel-tests])
AM_SILENT_RULES([yes])
AM_PROG_AR
LT_PREREQ([2.2.6])
LT_INIT([dlopen disable-static])
AC_PROG_CC
AC_PROG_CC_STDC
if test "x$ac_cv_prog_cc_c99" = xno; then
AC_MSG_ERROR([Could not find a C99 compatible compiler])
fi
AC_PROG_CPP
AX_PTHREAD(,[AC_MSG_ERROR([Could not configure pthreads support])])
LIBS="$PTHREAD_LIBS $LIBS"
CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
CC="$PTHREAD_CC"
AC_ARG_WITH([rst2man],
AS_HELP_STRING(
[--with-rst2man=PATH],
[Location of rst2man (auto)]),
[RST2MAN="$withval"],
AC_CHECK_PROGS(RST2MAN, [rst2man rst2man.py], []))
AM_CONDITIONAL(HAVE_RST2MAN, [test "x$RST2MAN" != "xno"])
m4_ifndef([VARNISH_PREREQ], AC_MSG_ERROR([Need varnish.m4 -- see README.rst]))
VARNISH_PREREQ([6.0.0])
VARNISH_VMODS([selector])
VMOD_TESTS="$(cd $srcdir/src && echo tests/*.vtc)"
AC_SUBST(VMOD_TESTS)
PKG_CHECK_VAR([LIBVARNISHAPI_LIBDIR], [varnishapi], [libdir])
AC_SUBST([VARNISH_LIBRARY_PATH],
[$LIBVARNISHAPI_LIBDIR:$LIBVARNISHAPI_LIBDIR/varnish])
# Checks for C sources
AC_CHECK_FUNCS([strdup])
# --enable-stack-protector
AC_ARG_ENABLE(stack-protector,
AS_HELP_STRING([--enable-stack-protector],[enable stack protector (default is YES)]),
[],
[enable_stack_protector=yes])
if test "x$enable_stack_protector" != "xno"; then
AX_CHECK_COMPILE_FLAG([-fstack-protector],
AX_CHECK_LINK_FLAG([-fstack-protector],
[CFLAGS="${CFLAGS} -fstack-protector"], [], []),
[], [])
fi
# --enable-debugging
AC_ARG_ENABLE(debugging,
AS_HELP_STRING([--enable-debugging],[enable debugging (default is NO)]),
[],
[enable_debugging=no])
# AC_PROG_CC sets CFLAGS to '-g -O2' unless already set, so there's no
# need to add -g. Disable or change by explicitly setting CFLAGS. If
# this option is enabled, then -Og or -O0 becomes the last
# optimization option, and hence takes precedence.
if test "x$enable_debugging" != "xno"; then
CFLAGS="${CFLAGS} -fno-inline"
AX_CHECK_COMPILE_FLAG([-Og],
[CFLAGS="${CFLAGS} -O0 -Og"],
[CFLAGS="${CFLAGS} -O0"],
[])
fi
AC_CONFIG_FILES([
Makefile
src/Makefile
])
AC_OUTPUT
AUTOMAKE_OPTIONS = subdir-objects
AM_CFLAGS = $(VARNISHAPI_CFLAGS) -Wall -Werror -Wextra -std=c99
AM_LDFLAGS = $(VARNISHAPI_LIBS) $(VMOD_LDFLAGS) -ldl
vmod_LTLIBRARIES = libvmod_selector.la
libvmod_selector_la_SOURCES = \
vmod_selector.c \
patricia.h \
patricia.c
nodist_libvmod_selector_la_SOURCES = \
vcc_if.c \
vcc_if.h
dist_man_MANS = vmod_selector.3
vmod_selector.c patricia.c: patricia.h
vmod_selector.lo: $(nodist_libvmod_selector_la_SOURCES)
vcc_if.h vmod_selector.rst vmod_selector.man.rst: vcc_if.c
vcc_if.c: vmod_selector.vcc
$(AM_V_VMODTOOL) $(PYTHON) $(VMODTOOL) -o vcc_if $(srcdir)/vmod_selector.vcc
vmod_selector.3: vmod_selector.man.rst
$(AM_V_GEN) $(RST2MAN) vmod_selector.man.rst vmod_selector.3
AM_TESTS_ENVIRONMENT = \
PATH="$(VMOD_TEST_PATH)" \
LD_LIBRARY_PATH="$(VARNISH_LIBRARY_PATH)"
TEST_EXTENSIONS = .vtc
VTC_LOG_COMPILER = varnishtest -v
AM_VTC_LOG_FLAGS = -Dvmod_selector="$(VMOD_SELECTOR)"
# To test an individual VTC test named test.vtc:
# $ cd src/
# $ make check TESTS=tests/test.vtc
TESTS = @VMOD_TESTS@
EXTRA_DIST = \
vmod_selector.vcc \
$(VMOD_TESTS)
CLEANFILES = \
$(builddir)/vcc_if.c \
$(builddir)/vcc_if.h \
$(builddir)/vmod_selector.rst \
$(builddir)/vmod_selector.man.rst \
$(builddir)/vmod_selector.3
/*-
* Copyright (c) 2018 UPLEX Nils Goroll Systemoptimierung
* All rights reserved
*
* Author: Geoffrey Simmons <geoffrey.simmons@uplex.de>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
* Inspired in part by Varnish hash_critbit.c and tarsnap's Patricia
* implementation.
*/
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include "vdef.h"
#include "vas.h"
#include "miniobj.h"
#include "patricia.h"
struct pt_y {
unsigned magic;
#define PT_Y_MAGIC 0xfa564d14
struct pt_y *leaf[2];
unsigned idx;
unsigned short off;
unsigned short len;
unsigned char bitmask;
};
static unsigned char pt_bittbl[256] = { 0 };
void
PT_Init(void)
{
unsigned char x;
unsigned y;
AZ(pt_bittbl[0x34 ^ 0x34]);
y = 0;
for (x = 0; x < 8; x++)
for (; y < (1U << x); y++)
pt_bittbl[y] = 8 - x;
/* Quick asserts for sanity check */
assert(pt_bittbl[0x34 ^ 0x34] == 8);
AZ(pt_bittbl[0xaa ^ 0x55]);
assert(pt_bittbl[0x01 ^ 0x22] == 2);
assert(pt_bittbl[0x10 ^ 0x0b] == 3);
}
int
PT_Inited(void)
{
return (pt_bittbl[0x34 ^ 0x34] != 0);
}
static struct pt_y *
y_alloc(unsigned idx, unsigned short off, size_t len)
{
struct pt_y *y;
if (len > USHRT_MAX) {
errno = ERANGE;
return (NULL);
}
errno = 0;
ALLOC_OBJ(y, PT_Y_MAGIC);
if (y == NULL)
return (NULL);
y->idx = idx;
y->off = off;
y->len = (unsigned short) len;
AZ(y->leaf[0]);
AZ(y->leaf[1]);
AZ(y->bitmask);
return (y);
}
static inline struct pt_y *
y_leaf_alloc(unsigned idx, unsigned char *c, unsigned char *b)
{
return y_alloc(idx, (unsigned short)(uintptr_t)(c - b),
strlen((char *)c));
}
static struct pt_y *
y_dup(struct pt_y *y0, unsigned short len)
{
struct pt_y *y;
assert(len < y0->len);
errno = 0;
ALLOC_OBJ(y, PT_Y_MAGIC);
if (y == NULL)
return (NULL);
y->leaf[0] = y0->leaf[0];
y->leaf[1] = y0->leaf[1];
y->bitmask = y0->bitmask;
y->idx = y0->idx;
y->off = y0->off + len;
y->len = y0->len - len;
return (y);
}
int
PT_Insert(struct pt_y * * restrict root, unsigned idx,
char * const restrict * const restrict strings)
{
struct pt_y *y;
unsigned char *c, *b;
AN(root);
CHECK_OBJ_ORNULL(*root, PT_Y_MAGIC);
AN(strings);
AN(strings[idx]);
if (*root == NULL) {
*root = y_alloc(idx, 0, strlen(strings[idx]));
if (*root == NULL)
return (-1);
return (0);
}
y = *root;
b = (unsigned char *)strings[idx];
c = b;
errno = 0;
for (;;) {
unsigned short i;
unsigned char *s;
unsigned char bit;
struct pt_y *y_old, *y_new;
CHECK_OBJ_NOTNULL(y, PT_Y_MAGIC);
s = (unsigned char *)(strings[y->idx] + y->off);
for (i = 0; *c != '\0' && i < y->len && s[i] == *c; i++)
c++;
if (s[i] == '\0' && *c == '\0') {
/*
* The string to be inserted is already in the
* trie.
*/
assert(i == y->len);
errno = EINVAL;
return (-1);
}
if (i == y->len) {
/*
* The string to be inserted has a prefix that is
* already in the tree.
*/
bit = (y->bitmask & *c) != 0;
assert(bit < 2);
if (y->leaf[bit] != NULL) {
y = y->leaf[bit];
continue;
}
y_new = y_leaf_alloc(idx, c, b);
if (y_new == NULL)
return (-1);
y->leaf[bit] = y_new;
return (0);
}
/* Split the current node. */
y_new = y_leaf_alloc(idx, c, b);
if (y_new == NULL)
return (-1);
y_old = y_dup(y, i);
if (y_old == NULL) {
FREE_OBJ(y_new);
return (-1);
}
y->len = i;
y->bitmask = 0x80 >> pt_bittbl[s[i] ^ *c];
assert((*c & y->bitmask) != (s[i] & y->bitmask));
bit = (*c & y->bitmask) != 0;
assert(bit < 2);
y->leaf[bit] = y_new;
y->leaf[1-bit] = y_old;
return (0);
}
}
unsigned
PT_Lookup(const struct pt_y * const restrict root,
char * const restrict * const restrict strings,
const char * const restrict subject)
{
const struct pt_y *y;
const unsigned char *c = (unsigned char *) subject;
AN(strings);
AN(subject);
for (y = root; y != NULL;) {
const unsigned char *s;
unsigned char b;
unsigned short i;
CHECK_OBJ_NOTNULL(y, PT_Y_MAGIC);
s = (unsigned char *)strings[y->idx] + y->off;
AN(s);
for (i = 0; *c != '\0' && i < y->len && s[i] == *c; i++)
c++;
if (s[i] == '\0' && *c == '\0')
return y->idx;
if (i == y->len) {
b = (y->bitmask & *c) != 0;
assert(b < 2);
y = y->leaf[b];
continue;
}
return (UINT_MAX);
}
return (UINT_MAX);
}
void
PT_Free(struct pt_y *y)
{
if (y == NULL)
return;
CHECK_OBJ(y, PT_Y_MAGIC);
PT_Free(y->leaf[0]);
PT_Free(y->leaf[1]);
free(y->leaf[0]);
free(y->leaf[1]);
FREE_OBJ(y);
}
static void
pt_print_tree(struct pt_y *y, struct vsb *sb, char **strings)
{
CHECK_OBJ_NOTNULL(y, PT_Y_MAGIC);
CHECK_OBJ_NOTNULL(sb, VSB_MAGIC);
VSB_printf(sb, "node = %p\n", y);
VSB_printf(sb, "leaf[0] = %p\n", y->leaf[0]);
VSB_printf(sb, "leaf[1] = %p\n", y->leaf[1]);
VSB_printf(sb, "idx = %u\n", y->idx);
if (strings[y->idx] != NULL)
VSB_printf(sb, "strings[idx] = %s\n", strings[y->idx]);
VSB_printf(sb, "off = %u\n", y->off);
VSB_printf(sb, "len = %u\n", y->len);
VSB_printf(sb, "bitmask = 0x%02x\n\n", y->bitmask);
if (y->leaf[0] != NULL)
pt_print_tree(y->leaf[0], sb, strings);
if (y->leaf[1] != NULL)
pt_print_tree(y->leaf[1], sb, strings);
}
struct vsb *
PT_Dump(struct pt_y *root, char **strings)
{
struct vsb *sb = VSB_new_auto();
VSB_printf(sb, "root = %p\n\n", root);
if (root != NULL) {
AN(strings);
pt_print_tree(root, sb, strings);
}
VSB_finish(sb);
return (sb);
}
/*-
* Copyright (c) 2018 UPLEX Nils Goroll Systemoptimierung
* All rights reserved
*
* Author: Geoffrey Simmons <geoffrey.simmons@uplex.de>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <errno.h>
#include <limits.h>
#include <unistd.h>
#include "vsb.h"
struct pt_y;
#if 0
struct t_stats {
unsigned magic;
#define T_STATS_MAGIC 0xf92e6603
double d_mean;
unsigned d_max;
unsigned n_nodes;
unsigned n_leaves;
}
#endif
void PT_Init(void);
int PT_Inited(void);
int PT_Insert(struct pt_y * * restrict root, unsigned idx,
char * const restrict * const restrict strings);
unsigned PT_Lookup(const struct pt_y * const restrict root,
char * const restrict * const restrict strings,
const char * const restrict subject);
void PT_Free(struct pt_y *y);
struct vsb * PT_Dump(struct pt_y *root, char **strings);
# looks like -*- vcl -*-
varnishtest "vcl.use and .discard, and version string"
server s1 {} -start
varnish v1 -vcl+backend {
import ${vmod_selector};
} -start
varnish v1 -vcl+backend {}
varnish v1 -cli "vcl.list"
varnish v1 -cli "vcl.use vcl1"
varnish v1 -cli "vcl.use vcl2"
varnish v1 -cli "vcl.use vcl1"
varnish v1 -cli "vcl.show vcl1"
varnish v1 -cli "vcl.use vcl2"
varnish v1 -cli "vcl.discard vcl1"
varnish v1 -cli "vcl.list"
varnish v1 -vcl {
import ${vmod_selector};
backend b { .host = "${bad_ip}"; }
sub vcl_recv {
return(synth(200));
}
sub vcl_synth {
set resp.http.x-version = selector.version();
if (!resp.http.x-version) {
set resp.status = 500;
}
return(deliver);
}
}
client c1 {
txreq -url "/"
rxresp
expect resp.status == 200
expect resp.http.x-version ~ "^.+$"
} -run
# looks like -*- vcl -*-
varnishtest "match() method"
varnish v1 -vcl {
import ${vmod_selector};
import std;
backend b { .host = "${bad_ip}"; }
sub vcl_init {
new t = selector.set();
t.add("foo");
t.add("bar");
t.add("baz");
t.add("quux");
t.add("foobar");
}
sub vcl_recv {
return (synth(200));
}
sub vcl_synth {
std.timestamp("BeforeMatches");
set resp.http.Match-Foo = t.match("foo");
set resp.http.Match-Bar = t.match("bar");
set resp.http.Match-Baz = t.match("baz");
set resp.http.Match-Quux = t.match("quux");
set resp.http.Match-Foobar = t.match("foobar");
std.timestamp("AfterMatches");
std.timestamp("BeforeMatches");
set resp.http.Match-Oof = t.match("oof");
set resp.http.Match-Rab = t.match("rab");
set resp.http.Match-Zab = t.match("zab");
set resp.http.Match-Xuuq = t.match("xuuq");
set resp.http.Match-Raboof = t.match("raboof");
std.timestamp("AfterMatches");
set resp.body = t.debug();
return (deliver);
}
} -start
client c1 {
txreq
rxresp
expect resp.http.Match-Foo == "true"
expect resp.http.Match-Bar == "true"
expect resp.http.Match-Baz == "true"
expect resp.http.Match-Quux == "true"
expect resp.http.Match-Foobar == "true"
expect resp.http.Match-Oof == "false"
expect resp.http.Match-Rab == "false"
expect resp.http.Match-Zab == "false"
expect resp.http.Match-Xuuq == "false"
expect resp.http.Match-Raboof == "false"
} -run
varnish v1 -errvcl {vmod selector failure: t.add(): "foo" added more than once} {
import ${vmod_selector};
backend b { .host = "${bad_ip}"; }
sub vcl_init {
new t = selector.set();
t.add("foo");
t.add("foo");
}
}
/*-
* Copyright (c) 2018 UPLEX Nils Goroll Systemoptimierung
* All rights reserved
*
* Author: Geoffrey Simmons <geoffrey.simmons@uplex.de>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/* for strdup() */
#define _POSIX_C_SOURCE 200809L
#include "config.h"
#include <stdlib.h>
#include <string.h>
#include "cache/cache.h"
#include "vcl.h"
#include "vre.h"
#include "cache/cache_director.h"
#include "patricia.h"
#define VFAIL(ctx, fmt, ...) \
VRT_fail((ctx), "vmod selector failure: " fmt, __VA_ARGS__)
#define VERR(ctx, fmt, ...) \
VSLb((ctx)->vsl, SLT_VCL_Error, "vmod selector error: " fmt, \
__VA_ARGS__)
struct entry {
unsigned magic;
#define VMOD_SELECTOR_ENTRY_MAGIC 0x733dbe63
char *string;
VCL_BACKEND backend;
vre_t *re;
};
struct vmod_selector_set {
unsigned magic;
#define VMOD_SELECTOR_SET_MAGIC 0x838979ef
struct entry **table;
char **members;
struct pt_y *origo;
char *vcl_name;
unsigned nmembers;
unsigned nentries;
VCL_BOOL case_sensitive;
};
/* Event function */
int
event(VRT_CTX, struct vmod_priv *priv, enum vcl_event_e e)
{
(void) ctx;
(void) priv;
if (e == VCL_EVENT_LOAD && !PT_Inited())
PT_Init();
return 0;
}
/* Object regex */
VCL_VOID
vmod_set__init(VRT_CTX, struct vmod_selector_set **setp, const char *vcl_name,
VCL_BOOL case_sensitive)
{
struct vmod_selector_set *set;
CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
AN(setp);
AZ(*setp);
AN(vcl_name);
ALLOC_OBJ(set, VMOD_SELECTOR_SET_MAGIC);
AN(set);
*setp = set;
set->vcl_name = strdup(vcl_name);
AN(set->vcl_name);
set->case_sensitive = case_sensitive;
AZ(set->table);
AZ(set->members);
AZ(set->origo);
AZ(set->nentries);
}
VCL_VOID
vmod_set__fini(struct vmod_selector_set **setp)
{
struct vmod_selector_set *set;
if (setp == NULL || *setp == NULL)
return;
CHECK_OBJ(*setp, VMOD_SELECTOR_SET_MAGIC);
set = *setp;
*setp = NULL;
PT_Free(set->origo);
for (unsigned i = 0; i < set->nmembers; i++)
free(set->members[i]);
for (unsigned i = 0; i < set->nentries; i++) {
struct entry *entry = set->table[i];
CHECK_OBJ_NOTNULL(entry, VMOD_SELECTOR_ENTRY_MAGIC);
free(entry->string);
if (entry->re != NULL)
VRE_free(&entry->re);
FREE_OBJ(entry);
}
free(set->members);
free(set->table);
free(set->vcl_name);
FREE_OBJ(set);
}
VCL_VOID
vmod_set_add(VRT_CTX, struct vmod_selector_set *set, VCL_STRING member,
VCL_STRING string, VCL_STRING regex, VCL_BACKEND backend)
{
struct entry *entry;
unsigned n;
vre_t *re = NULL;
const char *error;
int erroffset;
CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
CHECK_OBJ_NOTNULL(set, VMOD_SELECTOR_SET_MAGIC);
CHECK_OBJ_ORNULL(backend, DIRECTOR_MAGIC);
if ((ctx->method & VCL_MET_INIT) == 0) {
VFAIL(ctx, "%s.add() may only be called in vcl_init",
set->vcl_name);
return;
}
if (member == NULL) {
VFAIL(ctx, "%s.add(): string to be added is NULL",
set->vcl_name);
return;
}
set->nmembers++;
n = set->nmembers;
set->members = realloc(set->members, n * sizeof(VCL_STRING));
AN(set->members);
set->members[n - 1] = strdup(member);
AN(set->members[n - 1]);
errno = 0;
if (PT_Insert(&set->origo, n - 1, set->members) != 0) {
if (errno == EINVAL)
VFAIL(ctx, "%s.add(): \"%s\" added more than once",
set->vcl_name, member);
else
VFAIL(ctx, "%s.add(\"%s\") failed: %s", set->vcl_name,
member, strerror(errno));
return;
}
if (regex != NULL) {
/* XXX expose VRE options */
re = VRE_compile(regex, 0, &error, &erroffset);
if (re == NULL) {
VFAIL(ctx, "%s.add(): cannot compile regular expression"
" '%s': %s at offset %d", set->vcl_name, regex,
error, erroffset);
return;
}
}
if (string == NULL && re == NULL && backend == NULL)
return;
set->nentries = n;
set->table = realloc(set->table, n * sizeof(struct entry *));
AN(set->table);
ALLOC_OBJ(entry, VMOD_SELECTOR_ENTRY_MAGIC);
AN(entry);
if (string != NULL)
entry->string = strdup(string);
if (re != NULL)
entry->re = re;
if (backend != NULL)
entry->backend = backend;
set->table[n - 1] = entry;
}
VCL_BOOL
vmod_set_match(VRT_CTX, struct vmod_selector_set *set, VCL_STRING subject)
{
CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
CHECK_OBJ_NOTNULL(set, VMOD_SELECTOR_SET_MAGIC);
if (subject == NULL) {
VERR(ctx, "%s.match(): subject string is NULL", set->vcl_name);
return (0);
}
if (set->nmembers == 0) {
VERR(ctx, "%s.match(): no entries were added", set->vcl_name);
return (0);
}
AN(set->origo);
AN(set->members);
return (PT_Lookup(set->origo, set->members, subject) != UINT_MAX);
}
VCL_STRING
vmod_set_debug(VRT_CTX, struct vmod_selector_set *set)
{
struct vsb *sb;
VCL_STRING output;
CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
CHECK_OBJ_NOTNULL(set, VMOD_SELECTOR_SET_MAGIC);
sb = PT_Dump(set->origo, set->members);
CHECK_OBJ_NOTNULL(sb, VSB_MAGIC);
output = WS_Copy(ctx->ws, VSB_data(sb), -1);
VSB_destroy(&sb);
if (output == NULL)
VFAIL(ctx, "%s.debug(): out of workspace", set->vcl_name);
return output;
}
VCL_STRING
vmod_version(VRT_CTX)
{
(void) ctx;
return VERSION;
}
#-
# Copyright (c) 2018 UPLEX Nils Goroll Systemoptimierung
# All rights reserved
#
# Author: Geoffrey Simmons <geoffrey.simmons@uplex.de>
#
# See LICENSE
#
$Module selector 3 Varnish Module for matching strings associated with backends, regexen and other strings
$ABI vrt
$Synopsis manual
SYNOPSIS
========
import selector;
new <obj> = selector.set([BOOL case_sensitive])
VOID <obj>.add(STRING [, STRING string] [,STRING regex] [, BACKEND backend])
BOOL <obj>.match(STRING)
INT <obj>.nmatches()
BOOL <obj>.matched(INT)
INT <obj>.which([ENUM select])
STRING <obj>.string([INT n,] [ENUM select])
BACKEND <obj>.backend([INT n,] [ENUM select])
STRING <obj>.sub(STRING text, STRING rewrite [, INT n] [, ENUM select])
# VMOD version
STRING selector.version()
DESCRIPTION
===========
Varnish Module (VMOD) for ...
$Object set(BOOL case_sensitive=1)
Create a set object ...
Example::
sub vcl_init {
new ...
}
$Method VOID .add(STRING, STRING string=0, STRING regex=0, BACKEND backend=0)
Add the given string to the set. XXX ...
$Method BOOL .match(STRING)
Returns ``true`` if and only if ...
Example::
if (myset.match(req.http.Host)) {
call do_on_match;
}
$Method STRING .debug()
Intentionally not documented.
$Function STRING version()
Return the version string for this VMOD.
Example::
std.log("Using VMOD selector version: " + selector.version());
REQUIREMENTS
============
The VMOD requires Varnish version XXX. See the source repository for
versions of the VMOD that are compatible with other Varnish versions.
INSTALLATION
============
See `INSTALL.rst <INSTALL.rst>`_ in the source repository.
LIMITATIONS
===========
XXX ...
AUTHOR
======
* Geoffrey Simmons <geoff@uplex.de>
UPLEX Nils Goroll Systemoptimierung
SEE ALSO
========
* varnishd(1)
* vcl(7)
* VMOD source repository: https://code.uplex.de/uplex-varnish/libvmod-selector
$Event event
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