commit d3a3c3648f9cd22a1afa1408573ab1aa8d43c06a Author: James Antill Date: Mon Aug 8 14:10:17 2022 -0400 Import rpm: c63cb00e38af6eccab04ab91a3f3364413fbcb43 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e2d6450 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +SOURCES/varnish-modules-0.15.0.tar.gz diff --git a/gating.yaml b/gating.yaml new file mode 100644 index 0000000..2e4e68f --- /dev/null +++ b/gating.yaml @@ -0,0 +1,9 @@ +--- !Policy +product_versions: + - rhel-9 +decision_context: osci_compose_gate +rules: + - !PassingTestCaseRule {test_case_name: baseos-ci.brew-build.tier1.functional} + - !PassingTestCaseRule {test_case_name: baseos-ci.brew-build.tier2.functional} + - !PassingTestCaseRule {test_case_name: baseos-ci.brew-build.tier3.functional} + - !PassingTestCaseRule {test_case_name: baseos-ci.brew-build.acceptance-tier.functional} diff --git a/sources b/sources new file mode 100644 index 0000000..0d731d6 --- /dev/null +++ b/sources @@ -0,0 +1 @@ +SHA1 (varnish-modules-0.15.0.tar.gz) = 6c8c86fa749cbade9627f71ac9b51ec016f7e006 diff --git a/varnish-0.15.0.add.bootstrap.patch b/varnish-0.15.0.add.bootstrap.patch new file mode 100644 index 0000000..ad233c7 --- /dev/null +++ b/varnish-0.15.0.add.bootstrap.patch @@ -0,0 +1,44 @@ +--- /dev/null 2019-09-18 14:09:23.308277474 +0200 ++++ bootstrap 2019-09-24 09:55:24.218322744 +0200 +@@ -0,0 +1,41 @@ ++#!/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 ++ ++# 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 ++mkdir -p m4 ++aclocal -I m4 -I ${dataroot}/aclocal ++$LIBTOOLIZE --copy --force ++autoheader ++automake --add-missing --copy --foreign ++autoconf diff --git a/varnish-modules-0.15.0-vmod-tcp-int.patch b/varnish-modules-0.15.0-vmod-tcp-int.patch new file mode 100644 index 0000000..bc31cf4 --- /dev/null +++ b/varnish-modules-0.15.0-vmod-tcp-int.patch @@ -0,0 +1,13 @@ +diff --git a/src/vmod_tcp.c b/src/vmod_tcp.c +index fee4bd3..e1c5dfa 100644 +--- a/src/vmod_tcp.c ++++ b/src/vmod_tcp.c +@@ -174,7 +174,7 @@ vmod_set_socket_pace(VRT_CTX, VCL_INT rate) + sizeof(pacerate)) != 0) + VSLb(ctx->vsl, SLT_VCL_Error, "set_socket_pace(): Error setting pace rate."); + else +- VSLb(ctx->vsl, SLT_VCL_Log, "vmod-tcp: Socket paced to %lu KB/s.", rate); ++ VSLb(ctx->vsl, SLT_VCL_Log, "vmod-tcp: Socket paced to %d KB/s.", pacerate); + + # ifndef NDEBUG + int retval; diff --git a/varnish-modules-0.15.0.add.bootstrap.patch b/varnish-modules-0.15.0.add.bootstrap.patch new file mode 100644 index 0000000..c2106ba --- /dev/null +++ b/varnish-modules-0.15.0.add.bootstrap.patch @@ -0,0 +1,44 @@ +--- /dev/null 2019-09-28 14:48:27.937320545 +0200 ++++ bootstrap 2019-09-29 00:35:05.782099759 +0200 +@@ -0,0 +1,41 @@ ++#!/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 ++ ++# 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 ++mkdir -p m4 ++aclocal -I m4 -I ${dataroot}/aclocal ++$LIBTOOLIZE --copy --force ++autoheader ++automake --add-missing --copy --foreign ++autoconf diff --git a/varnish-modules-0.15.0.nigoroll_to_fe1a981_compatible_with_varnish-6.3.0.patch b/varnish-modules-0.15.0.nigoroll_to_fe1a981_compatible_with_varnish-6.3.0.patch new file mode 100644 index 0000000..e0932c2 --- /dev/null +++ b/varnish-modules-0.15.0.nigoroll_to_fe1a981_compatible_with_varnish-6.3.0.patch @@ -0,0 +1,3143 @@ +diff --git a/README.rst b/README.rst +index 7b757ed..709d82f 100644 +--- a/README.rst ++++ b/README.rst +@@ -1,3 +1,10 @@ ++Status of this repository ++========================= ++ ++This repository is based upon ++https://github.com/varnish/varnish-modules with necessary adjustments ++for varnish-cache master. It is being maintained by https://uplex.de/ ++ + Varnish module collection by Varnish Software + ============================================= + +@@ -49,14 +56,13 @@ If you are using the distro provided packages:: + + Then proceed to the configure and build:: + +- ./bootstrap # If running from git. + ./configure + make + make check # optional + sudo make install + + +-The resulting loadable modules (``libvmod_foo*.so`` files) will be installed to ++The resulting loadable modules (``libvmod_*.so`` files) will be installed to + the Varnish module directory. (default `/usr/lib/varnish/vmods/`) + + +@@ -86,6 +92,18 @@ The source git tree lives on Github: https://github.com/varnish/varnish-modules + All source code is placed in the master git branch. Pull requests and issue + reporting are appreciated. + ++Unlike building from releases, you need to first bootstrap the build system ++when you work from git:: ++ ++ ./bootstrap ++ ./configure ++ make ++ make check # recommended ++ ++If the ``configure`` step succeeds but the ``make`` step fails, check for ++warnings in the ``./configure`` output or the ``config.log`` file. You may be ++missing bootstrap dependencies not required by release archives. ++ + Porting + ------- + +diff --git a/configure.ac b/configure.ac +index 294ece3..df55b41 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -7,7 +7,7 @@ AC_CONFIG_AUX_DIR([build-aux]) + AC_CONFIG_SRCDIR(src/vmod_cookie.vcc) + AC_CONFIG_HEADERS(config.h) + +-AM_INIT_AUTOMAKE([1.11 foreign]) ++AM_INIT_AUTOMAKE([1.12.2 foreign]) + AM_SILENT_RULES([yes]) + AM_EXTRA_RECURSIVE_TARGETS([rst-docs]) + +diff --git a/docs/vmod_cookie.rst b/docs/vmod_cookie.rst +index d7a3666..892fe40 100644 +--- a/docs/vmod_cookie.rst ++++ b/docs/vmod_cookie.rst +@@ -4,25 +4,42 @@ + .. Edit vmod.vcc and run make instead + .. + +-.. role:: ref(emphasis) + +-.. _vmod_cookie(3): +- +-=========== +-vmod_cookie +-=========== ++:tocdepth: 1 + +---------------------- +-Varnish Cookie Module +---------------------- ++.. _vmod_cookie(3): + +-:Manual section: 3 ++=================================== ++VMOD cookie - Varnish Cookie Module ++=================================== + + SYNOPSIS + ======== + +-import cookie [from "path"] ; +- ++.. parsed-literal:: ++ ++ import cookie [from "path"] ++ ++ :ref:`vmod_cookie.clean` ++ ++ :ref:`vmod_cookie.delete` ++ ++ :ref:`vmod_cookie.filter` ++ ++ :ref:`vmod_cookie.filter_except` ++ ++ :ref:`vmod_cookie.format_rfc1123` ++ ++ :ref:`vmod_cookie.get` ++ ++ :ref:`vmod_cookie.get_string` ++ ++ :ref:`vmod_cookie.isset` ++ ++ :ref:`vmod_cookie.parse` ++ ++ :ref:`vmod_cookie.set` ++ + DESCRIPTION + =========== + +@@ -34,8 +51,8 @@ but a set comma-separated list of cookies. A filter() method removes a comma- + separated list of cookies. + + A convenience function for formatting the Set-Cookie Expires date field +-is also included. To actually manipulate the Set-Cookie response headers, +-vmod-header should be used instead though. ++is also included. If there are multiple Set-Cookie headers vmod-header ++should be used. + + The state loaded with cookie.parse() has a lifetime of the current request + or backend request context. To pass variables to the backend request, store +@@ -71,28 +88,11 @@ Filtering example:: + + .. vcl-end + +-CONTENTS +-======== +- +-* :ref:`func_clean` +-* :ref:`func_delete` +-* :ref:`func_filter` +-* :ref:`func_filter_except` +-* :ref:`func_format_rfc1123` +-* :ref:`func_get` +-* :ref:`func_get_string` +-* :ref:`func_isset` +-* :ref:`func_parse` +-* :ref:`func_set` + +-.. _func_clean: ++.. _vmod_cookie.clean: + +-clean +------ +- +-:: +- +- VOID clean(PRIV_TASK) ++VOID clean() ++------------ + + Description + Clean up previously parsed cookies. It is not necessary to run clean() +@@ -104,14 +104,10 @@ Example + cookie.clean(); + } + +-.. _func_delete: +- +-delete +------- +- +-:: ++.. _vmod_cookie.delete: + +- VOID delete(PRIV_TASK, STRING cookiename) ++VOID delete(STRING cookiename) ++------------------------------ + + Description + Delete `cookiename` from internal vmod storage if it exists. +@@ -125,14 +121,10 @@ Example + // get_string() will now yield "cookie1: value1"; + } + +-.. _func_filter: ++.. _vmod_cookie.filter: + +-filter +------- +- +-:: +- +- VOID filter(PRIV_TASK, STRING filterstring) ++VOID filter(STRING filterstring) ++-------------------------------- + + Description + Delete all cookies from internal vmod storage that are in the +@@ -148,14 +140,10 @@ Example + // "cookie3: value3"; + } + +-.. _func_filter_except: +- +-filter_except +-------------- +- +-:: ++.. _vmod_cookie.filter_except: + +- VOID filter_except(PRIV_TASK, STRING filterstring) ++VOID filter_except(STRING filterstring) ++--------------------------------------- + + Description + Delete all cookies from internal vmod storage that is not in the +@@ -170,14 +158,10 @@ Example + // "cookie1: value1; cookie2: value2;"; + } + +-.. _func_format_rfc1123: ++.. _vmod_cookie.format_rfc1123: + +-format_rfc1123 +--------------- +- +-:: +- +- STRING format_rfc1123(TIME now, DURATION timedelta) ++STRING format_rfc1123(TIME now, DURATION timedelta) ++--------------------------------------------------- + + Description + Get a RFC1123 formatted date string suitable for inclusion in a +@@ -193,14 +177,10 @@ Example + set resp.http.Set-Cookie = "userid=" + req.http.userid + "; Expires=" + cookie.format_rfc1123(now, 5m) + "; httpOnly"; + } + +-.. _func_get: +- +-get +---- +- +-:: ++.. _vmod_cookie.get: + +- STRING get(PRIV_TASK, STRING cookiename) ++STRING get(STRING cookiename) ++----------------------------- + + Description + Get the value of `cookiename`, as stored in internal vmod storage. If `cookiename` does not exist an empty string is returned. +@@ -213,14 +193,10 @@ Example + std.log("cookie1 value is: " + cookie.get("cookie1")); + } + +-.. _func_get_string: ++.. _vmod_cookie.get_string: + +-get_string +----------- +- +-:: +- +- STRING get_string(PRIV_TASK) ++STRING get_string() ++------------------- + + Description + Get a Cookie string value with all cookies in internal vmod storage. Does +@@ -234,14 +210,10 @@ Example + set req.http.cookie = cookie.get_string(); + } + +-.. _func_isset: +- +-isset +------ +- +-:: ++.. _vmod_cookie.isset: + +- BOOL isset(PRIV_TASK, STRING cookiename) ++BOOL isset(STRING cookiename) ++----------------------------- + + Description + Check if `cookiename` is set in the internal vmod storage. +@@ -257,14 +229,10 @@ Example + } + } + +-.. _func_parse: ++.. _vmod_cookie.parse: + +-parse +------ +- +-:: +- +- VOID parse(PRIV_TASK, STRING cookieheader) ++VOID parse(STRING cookieheader) ++------------------------------- + + Description + Parse the cookie string in `cookieheader`. If state already exists, clean() will be run first. +@@ -275,16 +243,10 @@ Example + cookie.parse(req.http.Cookie); + } + ++.. _vmod_cookie.set: + +- +-.. _func_set: +- +-set +---- +- +-:: +- +- VOID set(PRIV_TASK, STRING cookiename, STRING value) ++VOID set(STRING cookiename, STRING value) ++----------------------------------------- + + Description + Set the internal vmod storage for `cookiename` to `value`. +@@ -296,5 +258,3 @@ Example + cookie.set("cookie1", "value1"); + std.log("cookie1 value is: " + cookie.get("cookie1")); + } +- +- +diff --git a/docs/vmod_header.rst b/docs/vmod_header.rst +index 50f64db..1789727 100644 +--- a/docs/vmod_header.rst ++++ b/docs/vmod_header.rst +@@ -4,25 +4,30 @@ + .. Edit vmod.vcc and run make instead + .. + +-.. role:: ref(emphasis) + +-.. _vmod_header(3): ++:tocdepth: 1 + +-=========== +-vmod_header +-=========== +- +------------------------ +-Header VMOD for Varnish +------------------------ ++.. _vmod_header(3): + +-:Manual section: 3 ++===================================== ++VMOD header - Header VMOD for Varnish ++===================================== + + SYNOPSIS + ======== + +-import header [from "path"] ; +- ++.. parsed-literal:: ++ ++ import header [from "path"] ++ ++ :ref:`vmod_header.append` ++ ++ :ref:`vmod_header.copy` ++ ++ :ref:`vmod_header.get` ++ ++ :ref:`vmod_header.remove` ++ + DESCRIPTION + =========== + +@@ -51,22 +56,10 @@ Example:: + .. vcl-end + + +-CONTENTS +-======== +- +-* :ref:`func_append` +-* :ref:`func_copy` +-* :ref:`func_get` +-* :ref:`func_remove` +- +-.. _func_append: ++.. _vmod_header.append: + +-append +------- +- +-:: +- +- VOID append(HEADER, STRING) ++VOID append(HEADER, STRING) ++--------------------------- + + Description + Append an extra occurrence to an existing header. +@@ -74,14 +67,10 @@ Example + :: + header.append(beresp.http.Set-Cookie, "foo=bar") + +-.. _func_copy: +- +-copy +----- ++.. _vmod_header.copy: + +-:: +- +- VOID copy(HEADER, HEADER) ++VOID copy(HEADER, HEADER) ++------------------------- + + Description + Copy all source headers to a new header. +@@ -89,14 +78,10 @@ Example + :: + header.copy(beresp.http.set-cookie, beresp.http.x-old-cookie); + +-.. _func_get: +- +-get +---- ++.. _vmod_header.get: + +-:: +- +- STRING get(PRIV_CALL, HEADER header, STRING regex) ++STRING get(HEADER header, STRING regex) ++--------------------------------------- + + Description + Fetches the value of the first `header` that matches the given +@@ -105,17 +90,13 @@ Example + :: + set beresp.http.xusr = header.get(beresp.http.set-cookie,"user="); + +-.. _func_remove: +- +-remove +------- ++.. _vmod_header.remove: + +-:: +- +- VOID remove(PRIV_CALL, HEADER header, STRING regex) ++VOID remove(HEADER header, STRING regex) ++---------------------------------------- + + Description +- Remove all occurences of `header` that matches `regex`. ++ Remove all occurrences of `header` that matches `regex`. + Example + :: + header.remove(beresp.http.set-cookie,"^(?!(funcookie=))"); +@@ -135,4 +116,3 @@ BUGS + + You can't use dynamic regular expressions, which also holds true for normal + regular expressions in regsub(). +- +diff --git a/docs/vmod_saintmode.rst b/docs/vmod_saintmode.rst +index ceaaf44..801cc92 100644 +--- a/docs/vmod_saintmode.rst ++++ b/docs/vmod_saintmode.rst +@@ -4,26 +4,34 @@ + .. Edit vmod.vcc and run make instead + .. + +-.. role:: ref(emphasis) + +-.. _vmod_saintmode(3): +- +-============== +-vmod_saintmode +-============== ++:tocdepth: 1 + +---------------------------- +-Saint mode backend director +---------------------------- ++.. _vmod_saintmode(3): + +-:Manual section: 3 ++============================================ ++VMOD saintmode - Saint mode backend director ++============================================ + + SYNOPSIS + ======== + +-import saintmode [from "path"] ; +- +- ++.. parsed-literal:: ++ ++ import saintmode [from "path"] ++ ++ :ref:`vmod_saintmode.blacklist` ++ ++ :ref:`vmod_saintmode.status` ++ ++ :ref:`vmod_saintmode.saintmode` ++ ++ :ref:`vmod_saintmode.saintmode.backend` ++ ++ :ref:`vmod_saintmode.saintmode.blacklist_count` ++ ++ :ref:`vmod_saintmode.saintmode.is_healthy` ++ + DESCRIPTION + =========== + +@@ -95,23 +103,10 @@ Example:: + .. vcl-end + + +-CONTENTS +-======== +- +-* :ref:`func_blacklist` +-* :ref:`obj_saintmode` +-* :ref:`func_saintmode.backend` +-* :ref:`func_saintmode.blacklist_count` +-* :ref:`func_status` +- +-.. _func_blacklist: +- +-blacklist +---------- +- +-:: ++.. _vmod_saintmode.blacklist: + +- VOID blacklist(PRIV_VCL, DURATION expires) ++VOID blacklist(DURATION expires) ++-------------------------------- + + Marks the backend as sick for a specific object. Used in vcl_backend_response. + Corresponds to the use of ``beresp.saintmode`` in Varnish 3.0. Only available +@@ -126,15 +121,10 @@ Example:: + } + } + ++.. _vmod_saintmode.status: + +-.. _func_status: +- +-status +------- +- +-:: +- +- STRING status(PRIV_VCL) ++STRING status() ++--------------- + + Returns a JSON formatted status string suitable for use in vcl_synth. + +@@ -164,38 +154,33 @@ Example JSON output: + ] + } + ++.. _vmod_saintmode.saintmode: + +-.. _obj_saintmode: +- +-saintmode +---------- ++new xsaintmode = saintmode.saintmode(BACKEND backend, INT threshold) ++-------------------------------------------------------------------- + + :: + +- new OBJ = saintmode(PRIV_VCL, BACKEND backend, INT threshold) ++ new xsaintmode = saintmode.saintmode( ++ BACKEND backend, ++ INT threshold ++ ) + + Constructs a saintmode director object. The ``threshold`` argument sets + the saintmode threshold, which is the maximum number of items that can be + blacklisted before the whole backend is regarded as sick. Corresponds with the + ``saintmode_threshold`` parameter of Varnish 3.0. + +-Setting threshold to 0 disables saintmode, not the threshold. +- + Example:: + + sub vcl_init { + new sm = saintmode.saintmode(b, 10); + } + ++.. _vmod_saintmode.saintmode.backend: + +-.. _func_saintmode.backend: +- +-saintmode.backend +------------------ +- +-:: +- +- BACKEND saintmode.backend() ++BACKEND xsaintmode.backend() ++---------------------------- + + Used for assigning the backend from the saintmode object. + +@@ -205,15 +190,10 @@ Example:: + set bereq.backend = sm.backend(); + } + ++.. _vmod_saintmode.saintmode.blacklist_count: + +-.. _func_saintmode.blacklist_count: +- +-saintmode.blacklist_count +-------------------------- +- +-:: +- +- INT saintmode.blacklist_count() ++INT xsaintmode.blacklist_count() ++-------------------------------- + + Returns the number of objects currently blacklisted for a saintmode + director object. +@@ -226,3 +206,11 @@ Example: + set resp.http.troublecount = sm.blacklist_count(); + } + ++.. _vmod_saintmode.saintmode.is_healthy: ++ ++BOOL xsaintmode.is_healthy() ++---------------------------- ++ ++Checks if the object is currently blacklisted for a saintmode director object. ++If there are no valid objects available (called from vcl_hit or vcl_recv), ++the function will fall back to the backend's health function. +diff --git a/docs/vmod_tcp.rst b/docs/vmod_tcp.rst +index b37c863..5015443 100644 +--- a/docs/vmod_tcp.rst ++++ b/docs/vmod_tcp.rst +@@ -4,25 +4,30 @@ + .. Edit vmod.vcc and run make instead + .. + +-.. role:: ref(emphasis) + +-.. _vmod_tcp(3): +- +-======== +-vmod_tcp +-======== ++:tocdepth: 1 + +--------- +-TCP vmod +--------- ++.. _vmod_tcp(3): + +-:Manual section: 3 ++=================== ++VMOD tcp - TCP vmod ++=================== + + SYNOPSIS + ======== + +-import tcp [from "path"] ; +- ++.. parsed-literal:: ++ ++ import tcp [from "path"] ++ ++ :ref:`vmod_tcp.congestion_algorithm` ++ ++ :ref:`vmod_tcp.dump_info` ++ ++ :ref:`vmod_tcp.get_estimated_rtt` ++ ++ :ref:`vmod_tcp.set_socket_pace` ++ + DESCRIPTION + =========== + +@@ -59,22 +64,10 @@ Example:: + .. vcl-end + + +-CONTENTS +-======== +- +-* :ref:`func_congestion_algorithm` +-* :ref:`func_dump_info` +-* :ref:`func_get_estimated_rtt` +-* :ref:`func_set_socket_pace` +- +-.. _func_congestion_algorithm: ++.. _vmod_tcp.congestion_algorithm: + +-congestion_algorithm +--------------------- +- +-:: +- +- INT congestion_algorithm(STRING algorithm) ++INT congestion_algorithm(STRING algorithm) ++------------------------------------------ + + Set the client socket congestion control algorithm to S. Returns 0 on success, and -1 on error. + +@@ -84,15 +77,10 @@ Example:: + set req.http.x-tcp = tcp.congestion_algorithm("cubic"); + } + ++.. _vmod_tcp.dump_info: + +-.. _func_dump_info: +- +-dump_info +---------- +- +-:: +- +- VOID dump_info() ++VOID dump_info() ++---------------- + + Write the contents of the TCP_INFO data structure into varnishlog. + +@@ -107,17 +95,10 @@ Example varnishlog output:: + - VCL_Log tcpi2: pmtu=1500 rtt=152000 rttvar=76000 snd_cwnd=10 advmss=1448 reordering=3 + - VCL_Log getsockopt() returned: cubic + ++.. _vmod_tcp.get_estimated_rtt: + +- +- +-.. _func_get_estimated_rtt: +- +-get_estimated_rtt +------------------ +- +-:: +- +- REAL get_estimated_rtt() ++REAL get_estimated_rtt() ++------------------------ + + Get the estimated round-trip-time for the client socket. Unit: milliseconds. + +@@ -127,15 +108,10 @@ Example:: + std.log("client is far away."); + } + ++.. _vmod_tcp.set_socket_pace: + +-.. _func_set_socket_pace: +- +-set_socket_pace +---------------- +- +-:: +- +- VOID set_socket_pace(INT) ++VOID set_socket_pace(INT) ++------------------------- + + Set socket pacing on client-side TCP connection to PACE KB/s. Network interface + used must be using a supported scheduler. (fq) +@@ -143,5 +119,3 @@ used must be using a supported scheduler. (fq) + Example:: + + tcp.set_socket_pace(1000); +- +- +diff --git a/docs/vmod_var.rst b/docs/vmod_var.rst +index d5f7a2d..fe7dcce 100644 +--- a/docs/vmod_var.rst ++++ b/docs/vmod_var.rst +@@ -4,26 +4,56 @@ + .. Edit vmod.vcc and run make instead + .. + +-.. role:: ref(emphasis) + +-.. _vmod_var(3): ++:tocdepth: 1 + +-======== +-vmod_var +-======== +- +--------------------------------- +-Variable support for Varnish VCL +--------------------------------- ++.. _vmod_var(3): + +-:Manual section: 3 ++=========================================== ++VMOD var - Variable support for Varnish VCL ++=========================================== + + SYNOPSIS + ======== + +-import var [from "path"] ; +- +- ++.. parsed-literal:: ++ ++ import var [from "path"] ++ ++ :ref:`vmod_var.set` ++ ++ :ref:`vmod_var.get` ++ ++ :ref:`vmod_var.global_set` ++ ++ :ref:`vmod_var.global_get` ++ ++ :ref:`vmod_var.set_int` ++ ++ :ref:`vmod_var.get_int` ++ ++ :ref:`vmod_var.set_string` ++ ++ :ref:`vmod_var.get_string` ++ ++ :ref:`vmod_var.set_real` ++ ++ :ref:`vmod_var.get_real` ++ ++ :ref:`vmod_var.set_duration` ++ ++ :ref:`vmod_var.get_duration` ++ ++ :ref:`vmod_var.set_ip` ++ ++ :ref:`vmod_var.get_ip` ++ ++ :ref:`vmod_var.set_backend` ++ ++ :ref:`vmod_var.get_backend` ++ ++ :ref:`vmod_var.clear` ++ + This VMOD implements basic variable support in VCL. + + It supports strings, integers and real numbers. There are methods to get and +@@ -82,183 +112,122 @@ Example:: + .. vcl-end + + +-CONTENTS +-======== +- +-* :ref:`func_clear` +-* :ref:`func_get` +-* :ref:`func_get_duration` +-* :ref:`func_get_int` +-* :ref:`func_get_ip` +-* :ref:`func_get_real` +-* :ref:`func_get_string` +-* :ref:`func_global_get` +-* :ref:`func_global_set` +-* :ref:`func_set` +-* :ref:`func_set_duration` +-* :ref:`func_set_int` +-* :ref:`func_set_ip` +-* :ref:`func_set_real` +-* :ref:`func_set_string` +- +-.. _func_set: ++.. _vmod_var.set: + +-set +---- +- +-:: +- +- VOID set(PRIV_TASK, STRING key, STRING value) ++VOID set(STRING key, STRING value) ++---------------------------------- + + Set `key` to `value`. + +-.. _func_get: +- +-get +---- ++.. _vmod_var.get: + +-:: +- +- STRING get(PRIV_TASK, STRING) ++STRING get(STRING) ++------------------ + + Get `key` with data type STRING. If stored `key` is not a STRING an empty string is returned. + +-.. _func_global_set: +- +-global_set +----------- ++.. _vmod_var.global_set: + +-:: ++VOID global_set(STRING, STRING) ++------------------------------- + +- VOID global_set(STRING, STRING) + +-.. _func_global_get: + +-global_get +----------- ++.. _vmod_var.global_get: + +-:: ++STRING global_get(STRING) ++------------------------- + +- STRING global_get(STRING) + +-.. _func_set_int: + +-set_int +-------- ++.. _vmod_var.set_int: + +-:: +- +- VOID set_int(PRIV_TASK, STRING key, INT value) ++VOID set_int(STRING key, INT value) ++----------------------------------- + + Set `key` to `value`. + +-.. _func_get_int: +- +-get_int +-------- +- +-:: ++.. _vmod_var.get_int: + +- INT get_int(PRIV_TASK, STRING key) ++INT get_int(STRING key) ++----------------------- + + Get `key` with data type INT. If stored `key` is not an INT zero will be returned. + +-.. _func_set_string: +- +-set_string +----------- +- +-:: ++.. _vmod_var.set_string: + +- VOID set_string(PRIV_TASK, STRING key, STRING value) ++VOID set_string(STRING key, STRING value) ++----------------------------------------- + + Identical to set(). + +-.. _func_get_string: ++.. _vmod_var.get_string: + +-get_string +----------- +- +-:: +- +- STRING get_string(PRIV_TASK, STRING key) ++STRING get_string(STRING key) ++----------------------------- + + Identical to get(). + +-.. _func_set_real: +- +-set_real +--------- ++.. _vmod_var.set_real: + +-:: +- +- VOID set_real(PRIV_TASK, STRING key, REAL value) ++VOID set_real(STRING key, REAL value) ++------------------------------------- + + Set `key` to `value`. + +-.. _func_get_real: +- +-get_real +--------- ++.. _vmod_var.get_real: + +-:: +- +- REAL get_real(PRIV_TASK, STRING key) ++REAL get_real(STRING key) ++------------------------- + + Get `key` with data type REAL. If stored `key` is not a REAL zero will be returned. + +-.. _func_set_duration: +- +-set_duration +------------- ++.. _vmod_var.set_duration: + +-:: +- +- VOID set_duration(PRIV_TASK, STRING key, DURATION value) ++VOID set_duration(STRING key, DURATION value) ++--------------------------------------------- + + Set `key` to `value`. + +-.. _func_get_duration: ++.. _vmod_var.get_duration: + +-get_duration +------------- +- +-:: +- +- DURATION get_duration(PRIV_TASK, STRING key) ++DURATION get_duration(STRING key) ++--------------------------------- + + Get `key` with data type DURATION. If stored `key` is not a DURATION zero will be returned. + +-.. _func_set_ip: ++.. _vmod_var.set_ip: + +-set_ip +------- ++VOID set_ip(STRING key, IP value) ++--------------------------------- + +-:: ++Set `key` to `value`. + +- VOID set_ip(PRIV_TASK, STRING key, IP value) ++.. _vmod_var.get_ip: + +-Set `key` to `value`. ++IP get_ip(STRING key) ++--------------------- + +-.. _func_get_ip: ++Get `key` with data type IP. If stored `key` is not an IP null will be returned. + +-get_ip +------- ++.. _vmod_var.set_backend: + +-:: ++VOID set_backend(STRING key, BACKEND value) ++------------------------------------------- + +- IP get_ip(PRIV_TASK, STRING key) ++Set `key` to `value`. + +-Get `key` with data type IP. If stored `key` is not an IP null will be returned. ++.. _vmod_var.get_backend: + +-.. _func_clear: ++BACKEND get_backend(STRING key) ++------------------------------- + +-clear +------ ++Get `key` with data type BACKEND. If stored `key` is not a BACKEND, ++null will be returned. + +-:: ++.. _vmod_var.clear: + +- VOID clear(PRIV_TASK) ++VOID clear() ++------------ + + Clear all non-global variables. +- +diff --git a/docs/vmod_vsthrottle.rst b/docs/vmod_vsthrottle.rst +index 2593224..403aa7f 100644 +--- a/docs/vmod_vsthrottle.rst ++++ b/docs/vmod_vsthrottle.rst +@@ -4,25 +4,30 @@ + .. Edit vmod.vcc and run make instead + .. + +-.. role:: ref(emphasis) + +-.. _vmod_vsthrottle(3): +- +-=============== +-vmod_vsthrottle +-=============== ++:tocdepth: 1 + +---------------- +-Throttling VMOD +---------------- ++.. _vmod_vsthrottle(3): + +-:Manual section: 3 ++================================= ++VMOD vsthrottle - Throttling VMOD ++================================= + + SYNOPSIS + ======== + +-import vsthrottle [from "path"] ; +- ++.. parsed-literal:: ++ ++ import vsthrottle [from "path"] ++ ++ :ref:`vmod_vsthrottle.is_denied` ++ ++ :ref:`vmod_vsthrottle.return_token` ++ ++ :ref:`vmod_vsthrottle.remaining` ++ ++ :ref:`vmod_vsthrottle.blocked` ++ + DESCRIPTION + =========== + +@@ -37,6 +42,12 @@ The request rate is specified as the number of requests permitted over + a period. To keep things simple, this is passed as two separate + parameters, 'limit' and 'period'. + ++If an optional duration 'block' is specified, then access is denied ++altogether for that period of time after the rate limit is ++reached. This is a way to entirely turn away a particularly ++troublesome source of traffic for a while, rather than let them back ++in as soon as the rate slips back under the threshold. ++ + This VMOD implements a `token bucket algorithm`_. State associated + with the token bucket for each key is stored in-memory using BSD's + red-black tree implementation. +@@ -58,8 +69,9 @@ Example:: + sub vcl_recv { + # Varnish will set client.identity for you based on client IP. + +- if (vsthrottle.is_denied(client.identity, 15, 10s)) { +- # Client has exceeded 15 reqs per 10s ++ if (vsthrottle.is_denied(client.identity, 15, 10s, 30s)) { ++ # Client has exceeded 15 reqs per 10s. ++ # When this happens, block altogether for the next 30s. + return (synth(429, "Too Many Requests")); + } + +@@ -78,33 +90,37 @@ Example:: + + .. vcl-end + +-CONTENTS +-======== +- +-* :ref:`func_is_denied` +-* :ref:`func_remaining` + +-.. _func_is_denied: ++.. _vmod_vsthrottle.is_denied: + +-is_denied +---------- ++BOOL is_denied(STRING key, INT limit, DURATION period, DURATION block) ++---------------------------------------------------------------------- + + :: + +- BOOL is_denied(STRING key, INT limit, DURATION period) ++ BOOL is_denied( ++ STRING key, ++ INT limit, ++ DURATION period, ++ DURATION block=0 ++ ) + + Arguments: + + - key: A unique identifier to define what is being throttled - more examples below + - limit: How many requests in the specified period + - period: The time period ++ - block: a period to deny all access after hitting the threshold. Default is 0s + + Description + Can be used to rate limit the traffic for a specific key to a +- maximum of 'limit' requests per 'period' time. A token bucket +- is uniquely identified by the triplet of its key, limit and +- period, so using the same key multiple places with different +- rules will create multiple token buckets. ++ maximum of 'limit' requests per 'period' time. If 'block' is > 0s, ++ (0s by default), then always deny for 'key' for that length of time ++ after hitting the threshold. ++ ++ Note: A token bucket is uniquely identified by the 4-tuple of its ++ key, limit, period and block, so using the same key multiple places ++ with different rules will create multiple token buckets. + + Example + :: +@@ -118,20 +134,69 @@ Example + # ... + } + ++.. _vmod_vsthrottle.return_token: ++ ++VOID return_token(STRING key, INT limit, DURATION period, DURATION block) ++------------------------------------------------------------------------- ++ ++:: ++ ++ VOID return_token( ++ STRING key, ++ INT limit, ++ DURATION period, ++ DURATION block=0 ++ ) + +-.. _func_remaining: ++Arguments: ++ - Same arguments as is_denied() ++ ++Description ++ Increment (by one) the number of tokens in the specified bucket. is_denied() ++ decrements the bucket by one token and return_token() adds it back. ++ Using these two, you can effectively make a token bucket act like a limit on ++ concurrent requests instead of requests / time. ++ ++ Note: This function doesn't enforce anything, it merely credits a token to ++ appropriate bucket. ++ ++ Warning: If streaming is enabled (beresp.do_stream = true) as it is by ++ default now, vcl_deliver() is called *before* the response is sent ++ to the client (who may download it slowly). Thus you may credit the token ++ back too early if you use return_token() in vcl_backend_response(). ++ ++Example ++ :: + +-remaining +---------- ++ sub vcl_recv { ++ if (vsthrottle.is_denied(client.identity, 20, 20s)) { ++ # Client has more than 20 concurrent requests ++ return (synth(429, "Too Many Requests In Flight")); ++ } ++ ++ # ... ++ } ++ ++ sub vcl_deliver { ++ vsthrottle.return_token(client.identity, 10, 10s); ++ } ++ ++.. _vmod_vsthrottle.remaining: ++ ++INT remaining(STRING key, INT limit, DURATION period, DURATION block) ++--------------------------------------------------------------------- + + :: + +- INT remaining(STRING key, INT limit, DURATION period) ++ INT remaining( ++ STRING key, ++ INT limit, ++ DURATION period, ++ DURATION block=0 ++ ) + + Arguments: +- - key: A unique identifier to define what is being throttled +- - limit: How many requests in the specified period +- - period: The time period ++ - Same arguments as is_denied() + + Description + +@@ -147,3 +212,36 @@ Example + set resp.http.X-RateLimit-Remaining = vsthrottle.remaining(client.identity, 15, 10s); + } + ++.. _vmod_vsthrottle.blocked: ++ ++DURATION blocked(STRING key, INT limit, DURATION period, DURATION block) ++------------------------------------------------------------------------ ++ ++:: ++ ++ DURATION blocked( ++ STRING key, ++ INT limit, ++ DURATION period, ++ DURATION block ++ ) ++ ++Arguments: ++ - Same arguments as is_denied() ++ ++Description ++ ++ If the token bucket identified by the four parameters has been ++ blocked by use of the 'block' parameter in 'is_denied()', then ++ return the time remaining in the block. If it is not blocked, ++ return 0s. This can be used to inform clients how long they ++ will be locked out. ++ ++ ++Example ++ :: ++ ++ sub vcl_deliver { ++ set resp.http.Retry-After ++ = vsthrottle.blocked(client.identity, 15, 10s, 30s); ++ } +diff --git a/docs/vmod_xkey.rst b/docs/vmod_xkey.rst +index 40c22c2..86da183 100644 +--- a/docs/vmod_xkey.rst ++++ b/docs/vmod_xkey.rst +@@ -4,25 +4,26 @@ + .. Edit vmod.vcc and run make instead + .. + +-.. role:: ref(emphasis) + +-.. _vmod_xkey(3): +- +-========= +-vmod_xkey +-========= ++:tocdepth: 1 + +----------------------------------------- +-Surrogate keys support for Varnish Cache +----------------------------------------- ++.. _vmod_xkey(3): + +-:Manual section: 3 ++==================================================== ++VMOD xkey - Surrogate keys support for Varnish Cache ++==================================================== + + SYNOPSIS + ======== + +-import xkey [from "path"] ; ++.. parsed-literal:: + ++ import xkey [from "path"] ++ ++ :ref:`vmod_xkey.purge` ++ ++ :ref:`vmod_xkey.softpurge` ++ + DESCRIPTION + =========== + +@@ -39,13 +40,13 @@ Similarly with an e-commerce site, where various SKUs are often + referenced on a page. + + Hash keys are specified in the ``xkey`` response header. Multiple keys +-can be specified per header line with a space +-separator. Alternatively, they can be specified in multiple ``xkey`` ++can be specified per header line with spaces and/or commas as ++separators. Alternatively, they can be specified in multiple ``xkey`` + response headers. + + Preferably the secondary hash keys are set from the backend +-application, but can also be set from VCL in ``vcl_backend_response`` +-as in the above example. ++application, but the header can also be set from VCL in ++``vcl_backend_response``. + + .. vcl-start + +@@ -65,10 +66,14 @@ VCL example:: + if (client.ip !~ purgers) { + return (synth(403, "Forbidden")); + } +- set req.http.n-gone = xkey.purge(req.http.xkey-purge); +- # or: set req.http.n-gone = xkey.softpurge(req.http.xkey-purge) +- +- return (synth(200, "Invalidated "+req.http.n-gone+" objects")); ++ if (req.http.xkey) { ++ set req.http.n-gone = xkey.purge(req.http.xkey); ++ # or: set req.http.n-gone = xkey.softpurge(req.http.xkey) ++ ++ return (synth(200, "Invalidated "+req.http.n-gone+" objects")); ++ } else { ++ return (purge); ++ } + } + } + +@@ -88,9 +93,6 @@ header for a certain page might look like this:: + xkey: 166412 + xkey: 234323 + +-Alternatively you may instead use a single header with space separated +-values like ``xkey: 8155054 166412 234323``. +- + This requires a bit of VCL to be in place. The VCL can be found above. + + Then, in order to keep the web in sync with the database, a trigger is +@@ -98,17 +100,10 @@ set up in the database. When an SKU is updated this will trigger an + HTTP request towards the Varnish server, clearing out every object + with the matching xkey header:: + +- PURGE / HTTP/1.1 ++ GET / HTTP/1.1 + Host: www.example.com + xkey-purge: 166412 + +-Several ``xkey-purge`` headers are also supported like in the response +-example above, and you may also here use a single header with space +-seperated values like ``xkey-purge: 166412 234323``. +- +-Unlike `xkey` header for responses, purge header is fully configurable +-by means of adjusting the name of the header in the VCL example above. +- + Note the xkey-purge header. It is probably a good idea to protect + this with an ACL so random people from the Internet cannot purge your + cache. +@@ -122,20 +117,11 @@ Varnish will find the objects and clear them out, responding with:: + + The objects are now cleared. + +-CONTENTS +-======== +- +-* :ref:`func_purge` +-* :ref:`func_softpurge` +- +-.. _func_purge: + +-purge +------ ++.. _vmod_xkey.purge: + +-:: +- +- INT purge(STRING keys) ++INT purge(STRING keys) ++---------------------- + + Description + Purges all objects hashed on any key found in the ``keys`` argument. +@@ -143,15 +129,10 @@ Description + + The ``keys`` may contain a list of space-separated ids. + ++.. _vmod_xkey.softpurge: + +-.. _func_softpurge: +- +-softpurge +---------- +- +-:: +- +- INT softpurge(STRING keys) ++INT softpurge(STRING keys) ++-------------------------- + + Description + Performs a "soft purge" for all objects hashed on any key found in the +@@ -160,5 +141,3 @@ Description + A softpurge differs from a regular purge in that it resets an + object's TTL but keeps it available for grace mode and conditional + requests for the remainder of its configured grace and keep time. +- +- +diff --git a/src/Makefile.am b/src/Makefile.am +index 36ca267..6a4b48a 100644 +--- a/src/Makefile.am ++++ b/src/Makefile.am +@@ -143,6 +143,7 @@ VMOD_TESTS = \ + tests/vsthrottle/test01.vtc \ + tests/vsthrottle/test02.vtc \ + tests/vsthrottle/test03.vtc \ ++ tests/vsthrottle/test04.vtc \ + tests/xkey/test01.vtc \ + tests/xkey/test02.vtc \ + tests/xkey/test03.vtc \ +@@ -150,7 +151,8 @@ VMOD_TESTS = \ + tests/xkey/test05.vtc \ + tests/xkey/test06.vtc \ + tests/xkey/test07.vtc \ +- tests/xkey/test08.vtc ++ tests/xkey/test08.vtc \ ++ tests/xkey/test09.vtc + + SOFTPURGE_TESTS = \ + tests/softpurge/01-load.vtc \ +diff --git a/src/tests/saintmode/test02.vtc b/src/tests/saintmode/test02.vtc +index aa97734..bd8b537 100644 +--- a/src/tests/saintmode/test02.vtc ++++ b/src/tests/saintmode/test02.vtc +@@ -51,6 +51,8 @@ varnish v1 -vcl+backend { + + } -start + ++varnish v1 -cliok "backend.list" ++ + client c1 { + txreq -url "/a" + rxresp +diff --git a/src/tests/tcp/02-congestion.vtc b/src/tests/tcp/02-congestion.vtc +index 2972f6d..e0c09a6 100644 +--- a/src/tests/tcp/02-congestion.vtc ++++ b/src/tests/tcp/02-congestion.vtc +@@ -20,9 +20,12 @@ varnish v1 -vcl+backend { + client c1 { + txreq -url "/" + rxresp +- expect resp.http.a == "0" +- expect resp.http.b == "-1" +- expect resp.http.c == "0" ++ # XXX: it would be better to skip the test altogether when we ++ # don't have struct tcp_info available on the system. ++ # See https://github.com/varnish/varnish-modules/issues/115 ++ expect resp.http.a <= 0 ++ expect resp.http.b == -1 ++ expect resp.http.c <= 0 + } + + client c1 -run +diff --git a/src/tests/vsthrottle/test04.vtc b/src/tests/vsthrottle/test04.vtc +new file mode 100644 +index 0000000..bef9ecf +--- /dev/null ++++ b/src/tests/vsthrottle/test04.vtc +@@ -0,0 +1,40 @@ ++varnishtest "Test vsthrottle return_token()" ++ ++server s1 { ++ rxreq ++ txresp ++} -start ++ ++varnish v1 -vcl+backend { ++ import vsthrottle from "${vmod_builddir}/.libs/libvmod_vsthrottle.so"; ++ ++ sub vcl_deliver { ++ set resp.http.foo-count0 = vsthrottle.remaining("foo", 1, 1s); ++ if (!vsthrottle.is_denied("foo", 1, 1s)) { ++ set resp.http.f1 = "OK"; ++ } ++ set resp.http.foo-count1 = vsthrottle.remaining("foo", 1, 1s); ++ ++ if (!vsthrottle.is_denied("foo", 1, 1s)) { ++ set resp.http.f2 = "OK"; ++ } ++ set resp.http.foo-count2 = vsthrottle.remaining("foo", 1, 1s); ++ ++ vsthrottle.return_token("foo", 1, 1s); ++ ++ set resp.http.foo-count3 = vsthrottle.remaining("foo", 1, 1s); ++ } ++} -start ++ ++client c1 { ++ txreq -url "/" ++ rxresp ++ expect resp.http.foo-count0 == "1" ++ expect resp.http.f1 == "OK" ++ expect resp.http.foo-count1 == "0" ++ expect resp.http.f2 == ++ expect resp.http.foo-count2 == "0" ++ expect resp.http.foo-count3 == "1" ++} ++ ++client c1 -run +diff --git a/src/tests/xkey/test09.vtc b/src/tests/xkey/test09.vtc +new file mode 100644 +index 0000000..c010c37 +--- /dev/null ++++ b/src/tests/xkey/test09.vtc +@@ -0,0 +1,66 @@ ++varnishtest "Test xkey vmod multiple keys, comma separated" ++# https://github.com/varnish/varnish-modules/issues/102 ++ ++server s1 { ++ rxreq ++ txresp -hdr "xkey: asdf, fdsa" ++ ++ rxreq ++ txresp -hdr "xkey: qwer, rewq" ++} -start ++ ++varnish v1 -vcl+backend { ++ import xkey from "${vmod_builddir}/.libs/libvmod_xkey.so"; ++ ++ sub vcl_recv { ++ if (req.http.xkey-purge) { ++ if (xkey.purge(req.http.xkey-purge) != 0) { ++ return (synth(200, "Purged")); ++ } else { ++ return (synth(404, "No key")); ++ } ++ } ++ } ++ ++ sub vcl_backend_response { ++ set beresp.ttl = 60s; ++ set beresp.grace = 0s; ++ set beresp.keep = 0s; ++ } ++ ++ sub vcl_synth { ++ set resp.http.reason = resp.reason; ++ } ++} -start ++ ++client c1 { ++ txreq ++ rxresp ++ txreq -url /2 ++ rxresp ++} -run ++ ++varnish v1 -expect n_object == 2 ++ ++client c1 { ++ # Test the purge using the asdf key, so we check that we didn't include the comma ++ txreq -hdr "xkey-purge: asdf" ++ rxresp ++ expect resp.status == 200 ++ expect resp.http.reason == "Purged" ++} -run ++ ++delay 1 ++ ++varnish v1 -expect n_object == 1 ++ ++client c1 { ++ txreq -hdr "xkey-purge: rewq" ++ rxresp ++ expect resp.status == 200 ++ expect resp.http.reason == "Purged" ++} -run ++ ++delay 1 ++ ++varnish v1 -expect n_object == 0 +diff --git a/src/vmod_bodyaccess.c b/src/vmod_bodyaccess.c +index d1f324f..a8a8951 100644 +--- a/src/vmod_bodyaccess.c ++++ b/src/vmod_bodyaccess.c +@@ -114,6 +114,26 @@ bodyaccess_log_cb(struct req *req, void *priv, void *ptr, size_t len) + + return (bodyaccess_log(priv, ptr, len)); + } ++#elif defined(HAVE_OBJITERATE_F) && defined(OBJ_ITER_FLUSH) ++static int ++bodyaccess_bcat_cb(void *priv, unsigned flush, const void *ptr, ssize_t len) ++{ ++ ++ AN(priv); ++ ++ (void)flush; ++ return (VSB_bcat(priv, ptr, len)); ++} ++ ++static int ++bodyaccess_log_cb(void *priv, unsigned flush, const void *ptr, ssize_t len) ++{ ++ ++ AN(priv); ++ ++ (void)flush; ++ return (bodyaccess_log(priv, ptr, len)); ++} + #elif defined(HAVE_OBJITERATE_F) + static int + bodyaccess_bcat_cb(void *priv, int flush, const void *ptr, ssize_t len) +@@ -187,6 +207,8 @@ vmod_hash_req_body(VRT_CTX) + VCL_INT + vmod_len_req_body(VRT_CTX) + { ++ uint64_t u; ++ + CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC); + CHECK_OBJ_NOTNULL(ctx->req, REQ_MAGIC); + +@@ -202,7 +224,10 @@ vmod_len_req_body(VRT_CTX) + return (-1); + } + +- return (ctx->req->req_bodybytes); ++ AZ(ObjGetU64(ctx->req->wrk, ctx->req->body_oc, OA_LEN, &u)); ++ AZ(u > INT64_MAX); ++ ++ return ((VCL_INT)u); + } + + VCL_INT +diff --git a/src/vmod_bodyaccess.vcc b/src/vmod_bodyaccess.vcc +index d5cdf61..55666fc 100644 +--- a/src/vmod_bodyaccess.vcc ++++ b/src/vmod_bodyaccess.vcc +@@ -1,4 +1,4 @@ +-$Module bodyaccess 3 Varnish Module for request body access ++$Module bodyaccess 3 "Varnish Module for request body access" + + DESCRIPTION + =========== +diff --git a/src/vmod_cookie.vcc b/src/vmod_cookie.vcc +index edd383a..0f49bad 100644 +--- a/src/vmod_cookie.vcc ++++ b/src/vmod_cookie.vcc +@@ -1,4 +1,4 @@ +-$Module cookie 3 Varnish Cookie Module ++$Module cookie 3 "Varnish Cookie Module" + DESCRIPTION + =========== + +diff --git a/src/vmod_header.c b/src/vmod_header.c +index 798afdb..ea6b5d5 100644 +--- a/src/vmod_header.c ++++ b/src/vmod_header.c +@@ -193,7 +193,7 @@ header_http_cphdr(VRT_CTX, const struct http *hp, const char *hdr, + * vmod entrypoint. Sets up the header mutex. + */ + int +-event_function(VRT_CTX, struct vmod_priv *priv, enum vcl_event_e e) ++vmod_event_function(VRT_CTX, struct vmod_priv *priv, enum vcl_event_e e) + { + (void)ctx; + (void)priv; +diff --git a/src/vmod_header.vcc b/src/vmod_header.vcc +index 5df8dc6..4a5b27c 100644 +--- a/src/vmod_header.vcc ++++ b/src/vmod_header.vcc +@@ -1,4 +1,4 @@ +-$Module header 3 Header VMOD for Varnish ++$Module header 3 "Header VMOD for Varnish" + DESCRIPTION + =========== + +@@ -57,7 +57,7 @@ Example + $Function VOID remove(PRIV_CALL, HEADER header, STRING regex) + + Description +- Remove all occurences of `header` that matches `regex`. ++ Remove all occurrences of `header` that matches `regex`. + Example + :: + header.remove(beresp.http.set-cookie,"^(?!(funcookie=))"); +diff --git a/src/vmod_saintmode.c b/src/vmod_saintmode.c +index 26191cc..b0e4ac6 100644 +--- a/src/vmod_saintmode.c ++++ b/src/vmod_saintmode.c +@@ -35,14 +35,16 @@ + + #include "vmod_config.h" + +-#include "cache/cache_director.h" +-#include "cache/cache_backend.h" +- + #include "vsb.h" + #include "vtim.h" + + #include "vcc_saintmode_if.h" + ++static VCL_BOOL v_matchproto_(vdi_healthy_f) ++healthy(VRT_CTX, VCL_BACKEND, VCL_TIME *); ++static VCL_BACKEND v_matchproto_(vdi_resolve_f) ++resolve(VRT_CTX, VCL_BACKEND); ++ + struct trouble { + unsigned magic; + #define TROUBLE_MAGIC 0x4211ab21 +@@ -54,7 +56,7 @@ struct trouble { + struct vmod_saintmode_saintmode { + unsigned magic; + #define VMOD_SAINTMODE_MAGIC 0xa03756e4 +- struct director sdir[1]; ++ const struct director *sdir; + const struct director *be; + pthread_mutex_t mtx; + unsigned threshold; +@@ -63,6 +65,14 @@ struct vmod_saintmode_saintmode { + VTAILQ_HEAD(, trouble) troublelist; + }; + ++static const struct vdi_methods vmod_saintmode_methods[1] = {{ ++ .magic = VDI_METHODS_MAGIC, ++ .type = "saintmode", ++ .healthy = healthy, ++ .resolve = resolve ++}}; ++ ++ + struct saintmode_objs { + unsigned magic; + #define SAINTMODE_OBJS_MAGIC 0x9aa7beec +@@ -253,23 +263,26 @@ is_digest_healthy(const struct director *dir, + } + + /* All adapted from PHK's saintmode implementation in Varnish 3.0 */ +-static unsigned +-healthy(const struct director *dir, const struct busyobj *bo, double *changed) ++static VCL_BOOL v_matchproto_(vdi_healthy_f) ++healthy(VRT_CTX, VCL_BACKEND dir, VCL_TIME *changed) + { + struct vmod_saintmode_saintmode *sm; ++ const struct busyobj *bo; + unsigned retval; + const uint8_t* digest; + double t_prev; + struct vsl_log* vsl; + ++ CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC); + CHECK_OBJ_NOTNULL(dir, DIRECTOR_MAGIC); + CAST_OBJ_NOTNULL(sm, dir->priv, VMOD_SAINTMODE_MAGIC); + CHECK_OBJ_NOTNULL(sm->be, DIRECTOR_MAGIC); ++ bo = ctx->bo; + CHECK_OBJ_ORNULL(bo, BUSYOBJ_MAGIC); + + /* Saintmode is disabled, or list is empty */ + if (sm->threshold == 0 || sm->n_trouble == 0) +- return (sm->be->healthy(sm->be, bo, changed)); ++ return (VRT_Healthy(ctx, sm->be, changed)); + + if (!bo) { + digest = NULL; +@@ -282,7 +295,7 @@ healthy(const struct director *dir, const struct busyobj *bo, double *changed) + } + + retval = is_digest_healthy(dir, digest, t_prev, vsl); +- return (retval ? sm->be->healthy(sm->be, bo, changed) : 0); ++ return (retval ? VRT_Healthy(ctx, sm->be, changed) : 0); + } + + VCL_BOOL +@@ -303,19 +316,17 @@ vmod_saintmode_is_healthy(VRT_CTX, struct vmod_saintmode_saintmode *sm) + return is_digest_healthy(sm->sdir, digest, + ctx->req->t_prev, ctx->req->vsl); + } else +- return healthy(sm->sdir, ctx->bo, NULL); ++ return healthy(ctx, sm->sdir, NULL); + } + +-static const struct director * +-resolve(const struct director *dir, struct worker *wrk, struct busyobj *bo) { ++static VCL_BACKEND v_matchproto_(vdi_resolve_f) ++resolve(VRT_CTX, VCL_BACKEND dir) { + struct vmod_saintmode_saintmode *sm; +- double changed = 0.0; + + CHECK_OBJ_NOTNULL(dir, DIRECTOR_MAGIC); + CAST_OBJ_NOTNULL(sm, dir->priv, VMOD_SAINTMODE_MAGIC); +- (void)wrk; + +- if (!healthy(dir, bo, &changed)) ++ if (!healthy(ctx, dir, NULL)) + return (NULL); + + return (sm->be); +@@ -342,15 +353,7 @@ vmod_saintmode__init(VRT_CTX, struct vmod_saintmode_saintmode **smp, + sm->be = be; + VTAILQ_INIT(&sm->troublelist); + +- sm->sdir->magic = DIRECTOR_MAGIC; +- sm->sdir->resolve = resolve; +- sm->sdir->healthy = healthy; +-#ifdef HAVE_DIRECTOR_ADMIN_HEALTH +- sm->sdir->admin_health = VDI_AH_HEALTHY; +-#endif +- REPLACE(sm->sdir->vcl_name, vcl_name); +- sm->sdir->name = "saintmode"; +- sm->sdir->priv = sm; ++ sm->sdir = VRT_AddDirector(ctx, vmod_saintmode_methods, sm, "%s", vcl_name); + + if (!priv->priv) { + ALLOC_OBJ(sm_objs, SAINTMODE_OBJS_MAGIC); +@@ -380,7 +383,7 @@ vmod_saintmode__fini(struct vmod_saintmode_saintmode **smp) { + FREE_OBJ(tr); + } + +- free(sm->sdir->vcl_name); ++ VRT_DelDirector(&sm->sdir); + AZ(pthread_mutex_destroy(&sm->mtx)); + + /* We can no longer refer to the sm_objs after this +diff --git a/src/vmod_saintmode.vcc b/src/vmod_saintmode.vcc +index 4315e43..27f8ba0 100644 +--- a/src/vmod_saintmode.vcc ++++ b/src/vmod_saintmode.vcc +@@ -1,4 +1,4 @@ +-$Module saintmode 3 Saint mode backend director ++$Module saintmode 3 "Saint mode backend director" + + DESCRIPTION + =========== +diff --git a/src/vmod_softpurge.c b/src/vmod_softpurge.c +index 9273de5..0e46c25 100644 +--- a/src/vmod_softpurge.c ++++ b/src/vmod_softpurge.c +@@ -52,8 +52,12 @@ vmod_softpurge(VRT_CTX) + CHECK_OBJ_NOTNULL(ctx->req->wrk, WORKER_MAGIC); + oh = ctx->req->objcore->objhead; + CHECK_OBJ_NOTNULL(oh, OBJHEAD_MAGIC); +- spc = WS_Reserve(ctx->ws, 0); +- assert(spc >= sizeof *ocp); ++ spc = WS_ReserveAll(ctx->ws); ++ if (spc < sizeof *ocp) { ++ WS_Release(ctx->ws, 0); ++ VRT_fail(ctx, "insufficient workspace"); ++ return; ++ } + + nobj = 0; + ocp = (void*)ctx->ws->f; +diff --git a/src/vmod_softpurge.vcc b/src/vmod_softpurge.vcc +index 20252dd..aaf9a1c 100644 +--- a/src/vmod_softpurge.vcc ++++ b/src/vmod_softpurge.vcc +@@ -1,4 +1,4 @@ +-$Module softpurge Soft purge vmod ++$Module softpurge 3 "Soft purge vmod" + DESCRIPTION + =========== + +diff --git a/src/vmod_tcp.c b/src/vmod_tcp.c +index fee4bd3..570be45 100644 +--- a/src/vmod_tcp.c ++++ b/src/vmod_tcp.c +@@ -174,7 +174,7 @@ vmod_set_socket_pace(VRT_CTX, VCL_INT rate) + sizeof(pacerate)) != 0) + VSLb(ctx->vsl, SLT_VCL_Error, "set_socket_pace(): Error setting pace rate."); + else +- VSLb(ctx->vsl, SLT_VCL_Log, "vmod-tcp: Socket paced to %lu KB/s.", rate); ++ VSLb(ctx->vsl, SLT_VCL_Log, "vmod-tcp: Socket paced to %jd KB/s.", (intmax_t)rate); + + # ifndef NDEBUG + int retval; +diff --git a/src/vmod_tcp.vcc b/src/vmod_tcp.vcc +index 8659770..fddf7c6 100644 +--- a/src/vmod_tcp.vcc ++++ b/src/vmod_tcp.vcc +@@ -1,4 +1,4 @@ +-$Module tcp 3 TCP vmod ++$Module tcp 3 "TCP vmod" + DESCRIPTION + =========== + +diff --git a/src/vmod_var.vcc b/src/vmod_var.vcc +index 719b182..7af5cf1 100644 +--- a/src/vmod_var.vcc ++++ b/src/vmod_var.vcc +@@ -1,4 +1,4 @@ +-$Module var 3 Variable support for Varnish VCL ++$Module var 3 "Variable support for Varnish VCL" + + This VMOD implements basic variable support in VCL. + +diff --git a/src/vmod_vsthrottle.c b/src/vmod_vsthrottle.c +index ff1719c..e088860 100644 +--- a/src/vmod_vsthrottle.c ++++ b/src/vmod_vsthrottle.c +@@ -58,7 +58,7 @@ struct tbucket { + double block; + long tokens; + long capacity; +- VRB_ENTRY(tbucket) tree; ++ VRBT_ENTRY(tbucket) tree; + }; + + static int +@@ -67,9 +67,9 @@ keycmp(const struct tbucket *b1, const struct tbucket *b2) + return (memcmp(b1->digest, b2->digest, sizeof b1->digest)); + } + +-VRB_HEAD(tbtree, tbucket); +-VRB_PROTOTYPE_STATIC(tbtree, tbucket, tree, keycmp); +-VRB_GENERATE_STATIC(tbtree, tbucket, tree, keycmp); ++VRBT_HEAD(tbtree, tbucket); ++VRBT_PROTOTYPE_STATIC(tbtree, tbucket, tree, keycmp); ++VRBT_GENERATE_STATIC(tbtree, tbucket, tree, keycmp); + + /* To lessen potential mutex contention, we partition the buckets into + N_PART partitions. */ +@@ -119,12 +119,12 @@ get_bucket(const unsigned char *digest, long limit, double period, double now) + + INIT_OBJ(&k, TBUCKET_MAGIC); + memcpy(&k.digest, digest, sizeof k.digest); +- b = VRB_FIND(tbtree, &v->buckets, &k); ++ b = VRBT_FIND(tbtree, &v->buckets, &k); + if (b) { + CHECK_OBJ_NOTNULL(b, TBUCKET_MAGIC); + } else { + b = tb_alloc(digest, limit, period, now); +- AZ(VRB_INSERT(tbtree, &v->buckets, b)); ++ AZ(VRBT_INSERT(tbtree, &v->buckets, b)); + } + return (b); + } +@@ -205,6 +205,32 @@ vmod_is_denied(VRT_CTX, VCL_STRING key, VCL_INT limit, VCL_DURATION period, + return (ret); + } + ++VCL_VOID ++vmod_return_token(VRT_CTX, VCL_STRING key, VCL_INT limit, VCL_DURATION period, ++ VCL_DURATION block) ++{ ++ struct tbucket *b; ++ double now; ++ ++ struct vsthrottle *v; ++ unsigned char digest[SHA256_LEN]; ++ unsigned part; ++ ++ (void)ctx; ++ ++ if (!key) ++ return; ++ do_digest(digest, key, limit, period, block); ++ ++ part = digest[0] & N_PART_MASK; ++ v = &vsthrottle[part]; ++ AZ(pthread_mutex_lock(&v->mtx)); ++ now = VTIM_mono(); ++ b = get_bucket(digest, limit, period, now); ++ b->tokens++; ++ AZ(pthread_mutex_unlock(&v->mtx)); ++} ++ + /* Clean up expired entries. */ + static void + run_gc(double now, unsigned part) +@@ -213,11 +239,11 @@ run_gc(double now, unsigned part) + struct tbtree *buckets = &vsthrottle[part].buckets; + + /* XXX: Assert mtx is held ... */ +- VRB_FOREACH_SAFE(x, tbtree, buckets, y) { ++ VRBT_FOREACH_SAFE(x, tbtree, buckets, y) { + CHECK_OBJ_NOTNULL(x, TBUCKET_MAGIC); + if (now - x->last_used > x->period) { +- VRB_REMOVE(tbtree, buckets, x); +- free(x); ++ VRBT_REMOVE(tbtree, buckets, x); ++ FREE_OBJ(x); + } + } + } +@@ -291,9 +317,9 @@ fini(void *priv) + + for (p = 0; p < N_PART; ++p ) { + struct vsthrottle *v = &vsthrottle[p]; +- VRB_FOREACH_SAFE(x, tbtree, &v->buckets, y) { ++ VRBT_FOREACH_SAFE(x, tbtree, &v->buckets, y) { + CHECK_OBJ_NOTNULL(x, TBUCKET_MAGIC); +- VRB_REMOVE(tbtree, &v->buckets, x); ++ VRBT_REMOVE(tbtree, &v->buckets, x); + free(x); + } + } +@@ -302,7 +328,7 @@ fini(void *priv) + } + + int +-event_function(VRT_CTX, struct vmod_priv *priv, enum vcl_event_e e) ++vmod_event_function(VRT_CTX, struct vmod_priv *priv, enum vcl_event_e e) + { + if (e != VCL_EVENT_LOAD) + return (0); +@@ -318,7 +344,7 @@ event_function(VRT_CTX, struct vmod_priv *priv, enum vcl_event_e e) + struct vsthrottle *v = &vsthrottle[p]; + v->magic = VSTHROTTLE_MAGIC; + AZ(pthread_mutex_init(&v->mtx, NULL)); +- VRB_INIT(&v->buckets); ++ VRBT_INIT(&v->buckets); + } + } + n_init++; +diff --git a/src/vmod_vsthrottle.vcc b/src/vmod_vsthrottle.vcc +index 1f10080..72a8839 100644 +--- a/src/vmod_vsthrottle.vcc ++++ b/src/vmod_vsthrottle.vcc +@@ -1,4 +1,4 @@ +-$Module vsthrottle 3 Throttling VMOD ++$Module vsthrottle 3 "Throttling VMOD" + DESCRIPTION + =========== + +@@ -71,16 +71,17 @@ Arguments: + - key: A unique identifier to define what is being throttled - more examples below + - limit: How many requests in the specified period + - period: The time period +- - block: a period to deny all access after meeting the threshold ++ - block: a period to deny all access after hitting the threshold. Default is 0s + + Description + Can be used to rate limit the traffic for a specific key to a + maximum of 'limit' requests per 'period' time. If 'block' is > 0s, + (0s by default), then always deny for 'key' for that length of time +- after hitting the threshold. A token bucket is uniquely identified +- by the 4-tuple of its key, limit, period and block, so using the +- same key multiple places with different rules will create multiple +- token buckets. ++ after hitting the threshold. ++ ++ Note: A token bucket is uniquely identified by the 4-tuple of its ++ key, limit, period and block, so using the same key multiple places ++ with different rules will create multiple token buckets. + + Example + :: +@@ -95,14 +96,48 @@ Example + } + + ++$Function VOID return_token(STRING key, INT limit, DURATION period, ++ DURATION block=0) ++ ++Arguments: ++ - Same arguments as is_denied() ++ ++Description ++ Increment (by one) the number of tokens in the specified bucket. is_denied() ++ decrements the bucket by one token and return_token() adds it back. ++ Using these two, you can effectively make a token bucket act like a limit on ++ concurrent requests instead of requests / time. ++ ++ Note: This function doesn't enforce anything, it merely credits a token to ++ appropriate bucket. ++ ++ Warning: If streaming is enabled (beresp.do_stream = true) as it is by ++ default now, vcl_deliver() is called *before* the response is sent ++ to the client (who may download it slowly). Thus you may credit the token ++ back too early if you use return_token() in vcl_backend_response(). ++ ++Example ++ :: ++ ++ sub vcl_recv { ++ if (vsthrottle.is_denied(client.identity, 20, 20s)) { ++ # Client has more than 20 concurrent requests ++ return (synth(429, "Too Many Requests In Flight")); ++ } ++ ++ # ... ++ } ++ ++ sub vcl_deliver { ++ vsthrottle.return_token(client.identity, 10, 10s); ++ } ++ ++ + $Function INT remaining(STRING key, INT limit, DURATION period, + DURATION block=0) + + Arguments: +- - key: A unique identifier to define what is being throttled +- - limit: How many requests in the specified period +- - period: The time period +- - block: duration to block, defaults to 0s ++ - Same arguments as is_denied() + + Description + +@@ -122,10 +157,7 @@ $Function DURATION blocked(STRING key, INT limit, DURATION period, + DURATION block) + + Arguments: +- - key: A unique identifier to define what is being throttled +- - limit: How many requests in the specified period +- - period: The time period +- - block: duration to block ++ - Same arguments as is_denied() + + Description + +diff --git a/src/vmod_xkey.c b/src/vmod_xkey.c +index 3cf8069..ff825fd 100644 +--- a/src/vmod_xkey.c ++++ b/src/vmod_xkey.c +@@ -51,23 +51,23 @@ uintptr_t xkey_cb_handle; + + struct xkey_hashkey { + char digest[DIGEST_LEN]; +- VRB_ENTRY(xkey_hashkey) entry; ++ VRBT_ENTRY(xkey_hashkey) entry; + }; + static int xkey_hashcmp(const struct xkey_hashkey *k1, + const struct xkey_hashkey *k2); +-VRB_HEAD(xkey_hashtree, xkey_hashkey); +-static struct xkey_hashtree xkey_hashtree = VRB_INITIALIZER(&xkey_hashtree); +-VRB_PROTOTYPE(xkey_hashtree, xkey_hashkey, entry, xkey_hashcmp); ++VRBT_HEAD(xkey_hashtree, xkey_hashkey); ++static struct xkey_hashtree xkey_hashtree = VRBT_INITIALIZER(&xkey_hashtree); ++VRBT_PROTOTYPE(xkey_hashtree, xkey_hashkey, entry, xkey_hashcmp); + + struct xkey_ptrkey { + uintptr_t ptr; +- VRB_ENTRY(xkey_ptrkey) entry; ++ VRBT_ENTRY(xkey_ptrkey) entry; + }; + static int xkey_ptrcmp(const struct xkey_ptrkey *k1, + const struct xkey_ptrkey *k2); +-VRB_HEAD(xkey_octree, xkey_ptrkey); +-static struct xkey_octree xkey_octree = VRB_INITIALIZER(&xkey_octree); +-VRB_PROTOTYPE(xkey_octree, xkey_ptrkey, entry, xkey_ptrcmp) ++VRBT_HEAD(xkey_octree, xkey_ptrkey); ++static struct xkey_octree xkey_octree = VRBT_INITIALIZER(&xkey_octree); ++VRBT_PROTOTYPE(xkey_octree, xkey_ptrkey, entry, xkey_ptrcmp) + + struct xkey_hashhead; + struct xkey_ochead; +@@ -114,8 +114,8 @@ static struct { + + /*******************/ + +-VRB_GENERATE(xkey_hashtree, xkey_hashkey, entry, xkey_hashcmp); +-VRB_GENERATE(xkey_octree, xkey_ptrkey, entry, xkey_ptrcmp); ++VRBT_GENERATE(xkey_hashtree, xkey_hashkey, entry, xkey_hashcmp); ++VRBT_GENERATE(xkey_octree, xkey_ptrkey, entry, xkey_ptrcmp); + + static int + xkey_hashcmp(const struct xkey_hashkey *k1, const struct xkey_hashkey *k2) +@@ -246,7 +246,7 @@ xkey_hashtree_lookup(const unsigned char *digest, unsigned len) + AN(digest); + assert(len == sizeof(key.digest)); + memcpy(&key.digest, digest, len); +- pkey = VRB_FIND(xkey_hashtree, &xkey_hashtree, &key); ++ pkey = VRBT_FIND(xkey_hashtree, &xkey_hashtree, &key); + if (pkey != NULL) + CAST_OBJ_NOTNULL(head, (void *)pkey, XKEY_HASHHEAD_MAGIC); + return (head); +@@ -262,7 +262,7 @@ xkey_hashtree_insert(const unsigned char *digest, unsigned len) + head = xkey_hashhead_new(); + assert(len == sizeof(head->key.digest)); + memcpy(&head->key.digest, digest, len); +- key = VRB_INSERT(xkey_hashtree, &xkey_hashtree, &head->key); ++ key = VRBT_INSERT(xkey_hashtree, &xkey_hashtree, &head->key); + if (key != NULL) { + xkey_hashhead_delete(&head); + CAST_OBJ_NOTNULL(head, (void *)key, XKEY_HASHHEAD_MAGIC); +@@ -278,7 +278,7 @@ xkey_octree_lookup(uintptr_t ptr) + + AN(ptr); + key.ptr = ptr; +- pkey = VRB_FIND(xkey_octree, &xkey_octree, &key); ++ pkey = VRBT_FIND(xkey_octree, &xkey_octree, &key); + if (pkey) + CAST_OBJ_NOTNULL(head, (void *)pkey, XKEY_OCHEAD_MAGIC); + return (head); +@@ -293,7 +293,7 @@ xkey_octree_insert(uintptr_t ptr) + AN(ptr); + head = xkey_ochead_new(); + head->key.ptr = ptr; +- key = VRB_INSERT(xkey_octree, &xkey_octree, &head->key); ++ key = VRBT_INSERT(xkey_octree, &xkey_octree, &head->key); + if (key != NULL) { + xkey_ochead_delete(&head); + CAST_OBJ_NOTNULL(head, (void *)key, XKEY_OCHEAD_MAGIC); +@@ -342,7 +342,7 @@ xkey_remove(struct xkey_ochead **pochead) + oc->hashhead = NULL; + VTAILQ_REMOVE(&hashhead->ocs, oc, list_hashhead); + if (VTAILQ_EMPTY(&hashhead->ocs)) { +- VRB_REMOVE(xkey_hashtree, &xkey_hashtree, ++ VRBT_REMOVE(xkey_hashtree, &xkey_hashtree, + &hashhead->key); + xkey_hashhead_delete(&hashhead); + } +@@ -351,7 +351,7 @@ xkey_remove(struct xkey_ochead **pochead) + xkey_oc_delete(&oc); + } + AN(VTAILQ_EMPTY(&ochead->ocs)); +- VRB_REMOVE(xkey_octree, &xkey_octree, &ochead->key); ++ VRBT_REMOVE(xkey_octree, &xkey_octree, &ochead->key); + xkey_ochead_delete(&ochead); + } + +@@ -364,19 +364,19 @@ xkey_cleanup(void) + struct xkey_ochead *ochead; + struct xkey_oc *oc; + +- VRB_FOREACH(hashkey, xkey_hashtree, &xkey_hashtree) { ++ VRBT_FOREACH(hashkey, xkey_hashtree, &xkey_hashtree) { + CAST_OBJ_NOTNULL(hashhead, (void *)hashkey, + XKEY_HASHHEAD_MAGIC); + VTAILQ_CONCAT(&xkey_pool.ocs, &hashhead->ocs, list_ochead); + VTAILQ_INSERT_HEAD(&xkey_pool.hashheads, hashhead, list); + } +- VRB_INIT(&xkey_hashtree); ++ VRBT_INIT(&xkey_hashtree); + +- VRB_FOREACH(ockey, xkey_octree, &xkey_octree) { ++ VRBT_FOREACH(ockey, xkey_octree, &xkey_octree) { + CAST_OBJ_NOTNULL(ochead, (void *)ockey, XKEY_OCHEAD_MAGIC); + VTAILQ_INSERT_HEAD(&xkey_pool.ocheads, ochead, list); + } +- VRB_INIT(&xkey_octree); ++ VRBT_INIT(&xkey_octree); + + while (!VTAILQ_EMPTY(&xkey_pool.hashheads)) { + hashhead = VTAILQ_FIRST(&xkey_pool.hashheads); +@@ -410,11 +410,11 @@ xkey_tok(const char **b, const char **e) + t = *b; + AN(t); + +- while (isblank(*t)) ++ while (*t == ',' || isblank(*t)) + t++; + *b = t; + +- while (*t != '\0' && !isblank(*t)) ++ while (*t != '\0' && *t != ',' && !isblank(*t)) + t++; + *e = t; + return (*b < *e); +diff --git a/src/vmod_xkey.vcc b/src/vmod_xkey.vcc +index 637d4e8..3e6553c 100644 +--- a/src/vmod_xkey.vcc ++++ b/src/vmod_xkey.vcc +@@ -1,4 +1,4 @@ +-$Module xkey 3 Surrogate keys support for Varnish Cache ++$Module xkey 3 "Surrogate keys support for Varnish Cache" + DESCRIPTION + =========== + +@@ -15,8 +15,8 @@ Similarly with an e-commerce site, where various SKUs are often + referenced on a page. + + Hash keys are specified in the ``xkey`` response header. Multiple keys +-can be specified per header line with a space +-separator. Alternatively, they can be specified in multiple ``xkey`` ++can be specified per header line with spaces and/or commas as ++separators. Alternatively, they can be specified in multiple ``xkey`` + response headers. + + Preferably the secondary hash keys are set from the backend +@@ -93,7 +93,7 @@ Varnish will find the objects and clear them out, responding with:: + The objects are now cleared. + + $ABI strict +-$Event vmod_event ++$Event event + $Function INT purge(STRING keys) + + Description +diff --git a/src/vtree.h b/src/vtree.h +deleted file mode 100644 +index b88d9d2..0000000 +--- a/src/vtree.h ++++ /dev/null +@@ -1,770 +0,0 @@ +-/* $NetBSD: tree.h,v 1.8 2004/03/28 19:38:30 provos Exp $ */ +-/* $OpenBSD: tree.h,v 1.7 2002/10/17 21:51:54 art Exp $ */ +-/* $FreeBSD: release/9.0.0/sys/sys/tree.h 189204 2009-03-01 04:57:23Z bms $ */ +- +-/*- +- * Copyright 2002 Niels Provos +- * 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 ``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 THE AUTHOR 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. +- */ +- +-#ifndef _VTREE_H_ +-#define _VTREE_H_ +- +-#ifndef __unused +-#define __unused __attribute__((__unused__)) +-#endif +- +-/* +- * This file defines data structures for different types of trees: +- * splay trees and red-black trees. +- * +- * A splay tree is a self-organizing data structure. Every operation +- * on the tree causes a splay to happen. The splay moves the requested +- * node to the root of the tree and partly rebalances it. +- * +- * This has the benefit that request locality causes faster lookups as +- * the requested nodes move to the top of the tree. On the other hand, +- * every lookup causes memory writes. +- * +- * The Balance Theorem bounds the total access time for m operations +- * and n inserts on an initially empty tree as O((m + n)lg n). The +- * amortized cost for a sequence of m accesses to a splay tree is O(lg n); +- * +- * A red-black tree is a binary search tree with the node color as an +- * extra attribute. It fulfills a set of conditions: +- * - every search path from the root to a leaf consists of the +- * same number of black nodes, +- * - each red node (except for the root) has a black parent, +- * - each leaf node is black. +- * +- * Every operation on a red-black tree is bounded as O(lg n). +- * The maximum height of a red-black tree is 2lg (n+1). +- */ +- +-#define VSPLAY_HEAD(name, type) \ +-struct name { \ +- struct type *sph_root; /* root of the tree */ \ +-} +- +-#define VSPLAY_INITIALIZER(root) \ +- { NULL } +- +-#define VSPLAY_INIT(root) do { \ +- (root)->sph_root = NULL; \ +-} while (/*CONSTCOND*/ 0) +- +-#define VSPLAY_ENTRY(type) \ +-struct { \ +- struct type *spe_left; /* left element */ \ +- struct type *spe_right; /* right element */ \ +-} +- +-#define VSPLAY_LEFT(elm, field) (elm)->field.spe_left +-#define VSPLAY_RIGHT(elm, field) (elm)->field.spe_right +-#define VSPLAY_ROOT(head) (head)->sph_root +-#define VSPLAY_EMPTY(head) (VSPLAY_ROOT(head) == NULL) +- +-/* VSPLAY_ROTATE_{LEFT,RIGHT} expect that tmp hold VSPLAY_{RIGHT,LEFT} */ +-#define VSPLAY_ROTATE_RIGHT(head, tmp, field) do { \ +- VSPLAY_LEFT((head)->sph_root, field) = VSPLAY_RIGHT(tmp, field);\ +- VSPLAY_RIGHT(tmp, field) = (head)->sph_root; \ +- (head)->sph_root = tmp; \ +-} while (/*CONSTCOND*/ 0) +- +-#define VSPLAY_ROTATE_LEFT(head, tmp, field) do { \ +- VSPLAY_RIGHT((head)->sph_root, field) = VSPLAY_LEFT(tmp, field);\ +- VSPLAY_LEFT(tmp, field) = (head)->sph_root; \ +- (head)->sph_root = tmp; \ +-} while (/*CONSTCOND*/ 0) +- +-#define VSPLAY_LINKLEFT(head, tmp, field) do { \ +- VSPLAY_LEFT(tmp, field) = (head)->sph_root; \ +- tmp = (head)->sph_root; \ +- (head)->sph_root = VSPLAY_LEFT((head)->sph_root, field); \ +-} while (/*CONSTCOND*/ 0) +- +-#define VSPLAY_LINKRIGHT(head, tmp, field) do { \ +- VSPLAY_RIGHT(tmp, field) = (head)->sph_root; \ +- tmp = (head)->sph_root; \ +- (head)->sph_root = VSPLAY_RIGHT((head)->sph_root, field); \ +-} while (/*CONSTCOND*/ 0) +- +-#define VSPLAY_ASSEMBLE(head, node, left, right, field) do { \ +- VSPLAY_RIGHT(left, field) = VSPLAY_LEFT((head)->sph_root, field);\ +- VSPLAY_LEFT(right, field) = VSPLAY_RIGHT((head)->sph_root, field);\ +- VSPLAY_LEFT((head)->sph_root, field) = VSPLAY_RIGHT(node, field);\ +- VSPLAY_RIGHT((head)->sph_root, field) = VSPLAY_LEFT(node, field);\ +-} while (/*CONSTCOND*/ 0) +- +-/* Generates prototypes and inline functions */ +- +-#define VSPLAY_PROTOTYPE(name, type, field, cmp) \ +-void name##_VSPLAY(struct name *, struct type *); \ +-void name##_VSPLAY_MINMAX(struct name *, int); \ +-struct type *name##_VSPLAY_INSERT(struct name *, struct type *); \ +-struct type *name##_VSPLAY_REMOVE(struct name *, struct type *); \ +- \ +-/* Finds the node with the same key as elm */ \ +-static __inline struct type * \ +-name##_VSPLAY_FIND(struct name *head, struct type *elm) \ +-{ \ +- if (VSPLAY_EMPTY(head)) \ +- return(NULL); \ +- name##_VSPLAY(head, elm); \ +- if ((cmp)(elm, (head)->sph_root) == 0) \ +- return (head->sph_root); \ +- return (NULL); \ +-} \ +- \ +-static __inline struct type * \ +-name##_VSPLAY_NEXT(struct name *head, struct type *elm) \ +-{ \ +- name##_VSPLAY(head, elm); \ +- if (VSPLAY_RIGHT(elm, field) != NULL) { \ +- elm = VSPLAY_RIGHT(elm, field); \ +- while (VSPLAY_LEFT(elm, field) != NULL) { \ +- elm = VSPLAY_LEFT(elm, field); \ +- } \ +- } else \ +- elm = NULL; \ +- return (elm); \ +-} \ +- \ +-static __inline struct type * \ +-name##_VSPLAY_MIN_MAX(struct name *head, int val) \ +-{ \ +- name##_VSPLAY_MINMAX(head, val); \ +- return (VSPLAY_ROOT(head)); \ +-} +- +-/* Main splay operation. +- * Moves node close to the key of elm to top +- */ +-#define VSPLAY_GENERATE(name, type, field, cmp) \ +-struct type * \ +-name##_VSPLAY_INSERT(struct name *head, struct type *elm) \ +-{ \ +- if (VSPLAY_EMPTY(head)) { \ +- VSPLAY_LEFT(elm, field) = VSPLAY_RIGHT(elm, field) = NULL; \ +- } else { \ +- int __comp; \ +- name##_VSPLAY(head, elm); \ +- __comp = (cmp)(elm, (head)->sph_root); \ +- if(__comp < 0) { \ +- VSPLAY_LEFT(elm, field) = VSPLAY_LEFT((head)->sph_root, field);\ +- VSPLAY_RIGHT(elm, field) = (head)->sph_root; \ +- VSPLAY_LEFT((head)->sph_root, field) = NULL; \ +- } else if (__comp > 0) { \ +- VSPLAY_RIGHT(elm, field) = VSPLAY_RIGHT((head)->sph_root, field);\ +- VSPLAY_LEFT(elm, field) = (head)->sph_root; \ +- VSPLAY_RIGHT((head)->sph_root, field) = NULL; \ +- } else \ +- return ((head)->sph_root); \ +- } \ +- (head)->sph_root = (elm); \ +- return (NULL); \ +-} \ +- \ +-struct type * \ +-name##_VSPLAY_REMOVE(struct name *head, struct type *elm) \ +-{ \ +- struct type *__tmp; \ +- if (VSPLAY_EMPTY(head)) \ +- return (NULL); \ +- name##_VSPLAY(head, elm); \ +- if ((cmp)(elm, (head)->sph_root) == 0) { \ +- if (VSPLAY_LEFT((head)->sph_root, field) == NULL) { \ +- (head)->sph_root = VSPLAY_RIGHT((head)->sph_root, field);\ +- } else { \ +- __tmp = VSPLAY_RIGHT((head)->sph_root, field); \ +- (head)->sph_root = VSPLAY_LEFT((head)->sph_root, field);\ +- name##_VSPLAY(head, elm); \ +- VSPLAY_RIGHT((head)->sph_root, field) = __tmp; \ +- } \ +- return (elm); \ +- } \ +- return (NULL); \ +-} \ +- \ +-void \ +-name##_VSPLAY(struct name *head, struct type *elm) \ +-{ \ +- struct type __node, *__left, *__right, *__tmp; \ +- int __comp; \ +-\ +- VSPLAY_LEFT(&__node, field) = VSPLAY_RIGHT(&__node, field) = NULL;\ +- __left = __right = &__node; \ +-\ +- while ((__comp = (cmp)(elm, (head)->sph_root)) != 0) { \ +- if (__comp < 0) { \ +- __tmp = VSPLAY_LEFT((head)->sph_root, field); \ +- if (__tmp == NULL) \ +- break; \ +- if ((cmp)(elm, __tmp) < 0){ \ +- VSPLAY_ROTATE_RIGHT(head, __tmp, field);\ +- if (VSPLAY_LEFT((head)->sph_root, field) == NULL)\ +- break; \ +- } \ +- VSPLAY_LINKLEFT(head, __right, field); \ +- } else if (__comp > 0) { \ +- __tmp = VSPLAY_RIGHT((head)->sph_root, field); \ +- if (__tmp == NULL) \ +- break; \ +- if ((cmp)(elm, __tmp) > 0){ \ +- VSPLAY_ROTATE_LEFT(head, __tmp, field); \ +- if (VSPLAY_RIGHT((head)->sph_root, field) == NULL)\ +- break; \ +- } \ +- VSPLAY_LINKRIGHT(head, __left, field); \ +- } \ +- } \ +- VSPLAY_ASSEMBLE(head, &__node, __left, __right, field); \ +-} \ +- \ +-/* Splay with either the minimum or the maximum element \ +- * Used to find minimum or maximum element in tree. \ +- */ \ +-void name##_VSPLAY_MINMAX(struct name *head, int __comp) \ +-{ \ +- struct type __node, *__left, *__right, *__tmp; \ +-\ +- VSPLAY_LEFT(&__node, field) = VSPLAY_RIGHT(&__node, field) = NULL;\ +- __left = __right = &__node; \ +-\ +- while (1) { \ +- if (__comp < 0) { \ +- __tmp = VSPLAY_LEFT((head)->sph_root, field); \ +- if (__tmp == NULL) \ +- break; \ +- if (__comp < 0){ \ +- VSPLAY_ROTATE_RIGHT(head, __tmp, field);\ +- if (VSPLAY_LEFT((head)->sph_root, field) == NULL)\ +- break; \ +- } \ +- VSPLAY_LINKLEFT(head, __right, field); \ +- } else if (__comp > 0) { \ +- __tmp = VSPLAY_RIGHT((head)->sph_root, field); \ +- if (__tmp == NULL) \ +- break; \ +- if (__comp > 0) { \ +- VSPLAY_ROTATE_LEFT(head, __tmp, field); \ +- if (VSPLAY_RIGHT((head)->sph_root, field) == NULL)\ +- break; \ +- } \ +- VSPLAY_LINKRIGHT(head, __left, field); \ +- } \ +- } \ +- VSPLAY_ASSEMBLE(head, &__node, __left, __right, field); \ +-} +- +-#define VSPLAY_NEGINF -1 +-#define VSPLAY_INF 1 +- +-#define VSPLAY_INSERT(name, x, y) name##_VSPLAY_INSERT(x, y) +-#define VSPLAY_REMOVE(name, x, y) name##_VSPLAY_REMOVE(x, y) +-#define VSPLAY_FIND(name, x, y) name##_VSPLAY_FIND(x, y) +-#define VSPLAY_NEXT(name, x, y) name##_VSPLAY_NEXT(x, y) +-#define VSPLAY_MIN(name, x) (VSPLAY_EMPTY(x) ? NULL \ +- : name##_VSPLAY_MIN_MAX(x, VSPLAY_NEGINF)) +-#define VSPLAY_MAX(name, x) (VSPLAY_EMPTY(x) ? NULL \ +- : name##_VSPLAY_MIN_MAX(x, VSPLAY_INF)) +- +-#define VSPLAY_FOREACH(x, name, head) \ +- for ((x) = VSPLAY_MIN(name, head); \ +- (x) != NULL; \ +- (x) = VSPLAY_NEXT(name, head, x)) +- +-/* Macros that define a red-black tree */ +-#define VRB_HEAD(name, type) \ +-struct name { \ +- struct type *rbh_root; /* root of the tree */ \ +-} +- +-#define VRB_INITIALIZER(root) \ +- { NULL } +- +-#define VRB_INIT(root) do { \ +- (root)->rbh_root = NULL; \ +-} while (/*CONSTCOND*/ 0) +- +-#define VRB_BLACK 0 +-#define VRB_RED 1 +-#define VRB_ENTRY(type) \ +-struct { \ +- struct type *rbe_left; /* left element */ \ +- struct type *rbe_right; /* right element */ \ +- struct type *rbe_parent; /* parent element */ \ +- int rbe_color; /* node color */ \ +-} +- +-#define VRB_LEFT(elm, field) (elm)->field.rbe_left +-#define VRB_RIGHT(elm, field) (elm)->field.rbe_right +-#define VRB_PARENT(elm, field) (elm)->field.rbe_parent +-#define VRB_COLOR(elm, field) (elm)->field.rbe_color +-#define VRB_ROOT(head) (head)->rbh_root +-#define VRB_EMPTY(head) (VRB_ROOT(head) == NULL) +- +-#define VRB_SET(elm, parent, field) do { \ +- VRB_PARENT(elm, field) = parent; \ +- VRB_LEFT(elm, field) = VRB_RIGHT(elm, field) = NULL; \ +- VRB_COLOR(elm, field) = VRB_RED; \ +-} while (/*CONSTCOND*/ 0) +- +-#define VRB_SET_BLACKRED(black, red, field) do { \ +- VRB_COLOR(black, field) = VRB_BLACK; \ +- VRB_COLOR(red, field) = VRB_RED; \ +-} while (/*CONSTCOND*/ 0) +- +-#ifndef VRB_AUGMENT +-#define VRB_AUGMENT(x) do {} while (0) +-#endif +- +-#define VRB_ROTATE_LEFT(head, elm, tmp, field) do { \ +- (tmp) = VRB_RIGHT(elm, field); \ +- if ((VRB_RIGHT(elm, field) = VRB_LEFT(tmp, field)) != NULL) { \ +- VRB_PARENT(VRB_LEFT(tmp, field), field) = (elm); \ +- } \ +- VRB_AUGMENT(elm); \ +- if ((VRB_PARENT(tmp, field) = VRB_PARENT(elm, field)) != NULL) {\ +- if ((elm) == VRB_LEFT(VRB_PARENT(elm, field), field)) \ +- VRB_LEFT(VRB_PARENT(elm, field), field) = (tmp);\ +- else \ +- VRB_RIGHT(VRB_PARENT(elm, field), field) = (tmp);\ +- } else \ +- (head)->rbh_root = (tmp); \ +- VRB_LEFT(tmp, field) = (elm); \ +- VRB_PARENT(elm, field) = (tmp); \ +- VRB_AUGMENT(tmp); \ +- if ((VRB_PARENT(tmp, field))) \ +- VRB_AUGMENT(VRB_PARENT(tmp, field)); \ +-} while (/*CONSTCOND*/ 0) +- +-#define VRB_ROTATE_RIGHT(head, elm, tmp, field) do { \ +- (tmp) = VRB_LEFT(elm, field); \ +- if ((VRB_LEFT(elm, field) = VRB_RIGHT(tmp, field)) != NULL) { \ +- VRB_PARENT(VRB_RIGHT(tmp, field), field) = (elm); \ +- } \ +- VRB_AUGMENT(elm); \ +- if ((VRB_PARENT(tmp, field) = VRB_PARENT(elm, field)) != NULL) {\ +- if ((elm) == VRB_LEFT(VRB_PARENT(elm, field), field)) \ +- VRB_LEFT(VRB_PARENT(elm, field), field) = (tmp);\ +- else \ +- VRB_RIGHT(VRB_PARENT(elm, field), field) = (tmp);\ +- } else \ +- (head)->rbh_root = (tmp); \ +- VRB_RIGHT(tmp, field) = (elm); \ +- VRB_PARENT(elm, field) = (tmp); \ +- VRB_AUGMENT(tmp); \ +- if ((VRB_PARENT(tmp, field))) \ +- VRB_AUGMENT(VRB_PARENT(tmp, field)); \ +-} while (/*CONSTCOND*/ 0) +- +-/* Generates prototypes and inline functions */ +-#define VRB_PROTOTYPE(name, type, field, cmp) \ +- VRB_PROTOTYPE_INTERNAL(name, type, field, cmp,) +-#define VRB_PROTOTYPE_STATIC(name, type, field, cmp) \ +- VRB_PROTOTYPE_INTERNAL(name, type, field, cmp, __unused static) +-#define VRB_PROTOTYPE_INTERNAL(name, type, field, cmp, attr) \ +-/*lint -esym(528, name##_VRB_*) */ \ +-attr void name##_VRB_INSERT_COLOR(struct name *, struct type *); \ +-attr void name##_VRB_REMOVE_COLOR(struct name *, struct type *, struct type *);\ +-attr struct type *name##_VRB_REMOVE(struct name *, struct type *); \ +-attr struct type *name##_VRB_INSERT(struct name *, struct type *); \ +-attr struct type *name##_VRB_FIND(const struct name *, const struct type *); \ +-attr struct type *name##_VRB_NFIND(const struct name *, const struct type *); \ +-attr struct type *name##_VRB_NEXT(struct type *); \ +-attr struct type *name##_VRB_PREV(struct type *); \ +-attr struct type *name##_VRB_MINMAX(const struct name *, int); \ +- \ +- +-/* Main rb operation. +- * Moves node close to the key of elm to top +- */ +-#define VRB_GENERATE(name, type, field, cmp) \ +- VRB_GENERATE_INTERNAL(name, type, field, cmp,) +-#define VRB_GENERATE_STATIC(name, type, field, cmp) \ +- VRB_GENERATE_INTERNAL(name, type, field, cmp, __unused static) +-#define VRB_GENERATE_INTERNAL(name, type, field, cmp, attr) \ +-attr void \ +-name##_VRB_INSERT_COLOR(struct name *head, struct type *elm) \ +-{ \ +- struct type *parent, *gparent, *tmp; \ +- while ((parent = VRB_PARENT(elm, field)) != NULL && \ +- VRB_COLOR(parent, field) == VRB_RED) { \ +- gparent = VRB_PARENT(parent, field); \ +- if (parent == VRB_LEFT(gparent, field)) { \ +- tmp = VRB_RIGHT(gparent, field); \ +- if (tmp && VRB_COLOR(tmp, field) == VRB_RED) { \ +- VRB_COLOR(tmp, field) = VRB_BLACK; \ +- VRB_SET_BLACKRED(parent, gparent, field);\ +- elm = gparent; \ +- continue; \ +- } \ +- if (VRB_RIGHT(parent, field) == elm) { \ +- VRB_ROTATE_LEFT(head, parent, tmp, field);\ +- tmp = parent; \ +- parent = elm; \ +- elm = tmp; \ +- } \ +- VRB_SET_BLACKRED(parent, gparent, field); \ +- VRB_ROTATE_RIGHT(head, gparent, tmp, field); \ +- } else { \ +- tmp = VRB_LEFT(gparent, field); \ +- if (tmp && VRB_COLOR(tmp, field) == VRB_RED) { \ +- VRB_COLOR(tmp, field) = VRB_BLACK; \ +- VRB_SET_BLACKRED(parent, gparent, field);\ +- elm = gparent; \ +- continue; \ +- } \ +- if (VRB_LEFT(parent, field) == elm) { \ +- VRB_ROTATE_RIGHT(head, parent, tmp, field);\ +- tmp = parent; \ +- parent = elm; \ +- elm = tmp; \ +- } \ +- VRB_SET_BLACKRED(parent, gparent, field); \ +- VRB_ROTATE_LEFT(head, gparent, tmp, field); \ +- } \ +- } \ +- VRB_COLOR(head->rbh_root, field) = VRB_BLACK; \ +-} \ +- \ +-attr void \ +-name##_VRB_REMOVE_COLOR(struct name *head, struct type *parent, struct type *elm) \ +-{ \ +- struct type *tmp; \ +- while ((elm == NULL || VRB_COLOR(elm, field) == VRB_BLACK) && \ +- elm != VRB_ROOT(head)) { \ +- AN(parent); \ +- if (VRB_LEFT(parent, field) == elm) { \ +- tmp = VRB_RIGHT(parent, field); \ +- if (VRB_COLOR(tmp, field) == VRB_RED) { \ +- VRB_SET_BLACKRED(tmp, parent, field); \ +- VRB_ROTATE_LEFT(head, parent, tmp, field);\ +- tmp = VRB_RIGHT(parent, field); \ +- } \ +- if ((VRB_LEFT(tmp, field) == NULL || \ +- VRB_COLOR(VRB_LEFT(tmp, field), field) == VRB_BLACK) &&\ +- (VRB_RIGHT(tmp, field) == NULL || \ +- VRB_COLOR(VRB_RIGHT(tmp, field), field) == VRB_BLACK)) {\ +- VRB_COLOR(tmp, field) = VRB_RED; \ +- elm = parent; \ +- parent = VRB_PARENT(elm, field); \ +- } else { \ +- if (VRB_RIGHT(tmp, field) == NULL || \ +- VRB_COLOR(VRB_RIGHT(tmp, field), field) == VRB_BLACK) {\ +- struct type *oleft; \ +- if ((oleft = VRB_LEFT(tmp, field)) \ +- != NULL) \ +- VRB_COLOR(oleft, field) = VRB_BLACK;\ +- VRB_COLOR(tmp, field) = VRB_RED;\ +- VRB_ROTATE_RIGHT(head, tmp, oleft, field);\ +- tmp = VRB_RIGHT(parent, field); \ +- } \ +- VRB_COLOR(tmp, field) = VRB_COLOR(parent, field);\ +- VRB_COLOR(parent, field) = VRB_BLACK; \ +- if (VRB_RIGHT(tmp, field)) \ +- VRB_COLOR(VRB_RIGHT(tmp, field), field) = VRB_BLACK;\ +- VRB_ROTATE_LEFT(head, parent, tmp, field);\ +- elm = VRB_ROOT(head); \ +- break; \ +- } \ +- } else { \ +- tmp = VRB_LEFT(parent, field); \ +- if (VRB_COLOR(tmp, field) == VRB_RED) { \ +- VRB_SET_BLACKRED(tmp, parent, field); \ +- VRB_ROTATE_RIGHT(head, parent, tmp, field);\ +- tmp = VRB_LEFT(parent, field); \ +- } \ +- if ((VRB_LEFT(tmp, field) == NULL || \ +- VRB_COLOR(VRB_LEFT(tmp, field), field) == VRB_BLACK) &&\ +- (VRB_RIGHT(tmp, field) == NULL || \ +- VRB_COLOR(VRB_RIGHT(tmp, field), field) == VRB_BLACK)) {\ +- VRB_COLOR(tmp, field) = VRB_RED; \ +- elm = parent; \ +- parent = VRB_PARENT(elm, field); \ +- } else { \ +- if (VRB_LEFT(tmp, field) == NULL || \ +- VRB_COLOR(VRB_LEFT(tmp, field), field) == VRB_BLACK) {\ +- struct type *oright; \ +- if ((oright = VRB_RIGHT(tmp, field)) \ +- != NULL) \ +- VRB_COLOR(oright, field) = VRB_BLACK;\ +- VRB_COLOR(tmp, field) = VRB_RED;\ +- VRB_ROTATE_LEFT(head, tmp, oright, field);\ +- tmp = VRB_LEFT(parent, field); \ +- } \ +- VRB_COLOR(tmp, field) = VRB_COLOR(parent, field);\ +- VRB_COLOR(parent, field) = VRB_BLACK; \ +- if (VRB_LEFT(tmp, field)) \ +- VRB_COLOR(VRB_LEFT(tmp, field), field) = VRB_BLACK;\ +- VRB_ROTATE_RIGHT(head, parent, tmp, field);\ +- elm = VRB_ROOT(head); \ +- break; \ +- } \ +- } \ +- } \ +- if (elm) \ +- VRB_COLOR(elm, field) = VRB_BLACK; \ +-} \ +- \ +-attr struct type * \ +-name##_VRB_REMOVE(struct name *head, struct type *elm) \ +-{ \ +- struct type *child, *parent, *old = elm; \ +- int color; \ +- if (VRB_LEFT(elm, field) == NULL) \ +- child = VRB_RIGHT(elm, field); \ +- else if (VRB_RIGHT(elm, field) == NULL) \ +- child = VRB_LEFT(elm, field); \ +- else { \ +- struct type *left; \ +- elm = VRB_RIGHT(elm, field); \ +- while ((left = VRB_LEFT(elm, field)) != NULL) \ +- elm = left; \ +- child = VRB_RIGHT(elm, field); \ +- parent = VRB_PARENT(elm, field); \ +- color = VRB_COLOR(elm, field); \ +- if (child) \ +- VRB_PARENT(child, field) = parent; \ +- if (parent) { \ +- if (VRB_LEFT(parent, field) == elm) \ +- VRB_LEFT(parent, field) = child; \ +- else \ +- VRB_RIGHT(parent, field) = child; \ +- VRB_AUGMENT(parent); \ +- } else \ +- VRB_ROOT(head) = child; \ +- if (VRB_PARENT(elm, field) == old) \ +- parent = elm; \ +- (elm)->field = (old)->field; \ +- if (VRB_PARENT(old, field)) { \ +- if (VRB_LEFT(VRB_PARENT(old, field), field) == old)\ +- VRB_LEFT(VRB_PARENT(old, field), field) = elm;\ +- else \ +- VRB_RIGHT(VRB_PARENT(old, field), field) = elm;\ +- VRB_AUGMENT(VRB_PARENT(old, field)); \ +- } else \ +- VRB_ROOT(head) = elm; \ +- VRB_PARENT(VRB_LEFT(old, field), field) = elm; \ +- if (VRB_RIGHT(old, field)) \ +- VRB_PARENT(VRB_RIGHT(old, field), field) = elm; \ +- if (parent) { \ +- left = parent; \ +- do { \ +- VRB_AUGMENT(left); \ +- } while ((left = VRB_PARENT(left, field)) != NULL); \ +- } \ +- goto color; \ +- } \ +- parent = VRB_PARENT(elm, field); \ +- color = VRB_COLOR(elm, field); \ +- if (child) \ +- VRB_PARENT(child, field) = parent; \ +- if (parent) { \ +- if (VRB_LEFT(parent, field) == elm) \ +- VRB_LEFT(parent, field) = child; \ +- else \ +- VRB_RIGHT(parent, field) = child; \ +- VRB_AUGMENT(parent); \ +- } else \ +- VRB_ROOT(head) = child; \ +-color: \ +- if (color == VRB_BLACK) { \ +- name##_VRB_REMOVE_COLOR(head, parent, child); \ +- } \ +- return (old); \ +-} \ +- \ +-/* Inserts a node into the RB tree */ \ +-attr struct type * \ +-name##_VRB_INSERT(struct name *head, struct type *elm) \ +-{ \ +- struct type *tmp; \ +- struct type *parent = NULL; \ +- int comp = 0; \ +- tmp = VRB_ROOT(head); \ +- while (tmp) { \ +- parent = tmp; \ +- comp = (cmp)(elm, parent); \ +- if (comp < 0) \ +- tmp = VRB_LEFT(tmp, field); \ +- else if (comp > 0) \ +- tmp = VRB_RIGHT(tmp, field); \ +- else \ +- return (tmp); \ +- } \ +- VRB_SET(elm, parent, field); \ +- if (parent != NULL) { \ +- if (comp < 0) \ +- VRB_LEFT(parent, field) = elm; \ +- else \ +- VRB_RIGHT(parent, field) = elm; \ +- VRB_AUGMENT(parent); \ +- } else \ +- VRB_ROOT(head) = elm; \ +- name##_VRB_INSERT_COLOR(head, elm); \ +- return (NULL); \ +-} \ +- \ +-/* Finds the node with the same key as elm */ \ +-attr struct type * \ +-name##_VRB_FIND(const struct name *head, const struct type *elm) \ +-{ \ +- struct type *tmp = VRB_ROOT(head); \ +- int comp; \ +- while (tmp) { \ +- comp = cmp(elm, tmp); \ +- if (comp < 0) \ +- tmp = VRB_LEFT(tmp, field); \ +- else if (comp > 0) \ +- tmp = VRB_RIGHT(tmp, field); \ +- else \ +- return (tmp); \ +- } \ +- return (NULL); \ +-} \ +- \ +-/* Finds the first node greater than or equal to the search key */ \ +-attr struct type * \ +-name##_VRB_NFIND(const struct name *head, const struct type *elm) \ +-{ \ +- struct type *tmp = VRB_ROOT(head); \ +- struct type *res = NULL; \ +- int comp; \ +- while (tmp) { \ +- comp = cmp(elm, tmp); \ +- if (comp < 0) { \ +- res = tmp; \ +- tmp = VRB_LEFT(tmp, field); \ +- } \ +- else if (comp > 0) \ +- tmp = VRB_RIGHT(tmp, field); \ +- else \ +- return (tmp); \ +- } \ +- return (res); \ +-} \ +- \ +-/* ARGSUSED */ \ +-attr struct type * \ +-name##_VRB_NEXT(struct type *elm) \ +-{ \ +- if (VRB_RIGHT(elm, field)) { \ +- elm = VRB_RIGHT(elm, field); \ +- while (VRB_LEFT(elm, field)) \ +- elm = VRB_LEFT(elm, field); \ +- } else { \ +- if (VRB_PARENT(elm, field) && \ +- (elm == VRB_LEFT(VRB_PARENT(elm, field), field))) \ +- elm = VRB_PARENT(elm, field); \ +- else { \ +- while (VRB_PARENT(elm, field) && \ +- (elm == VRB_RIGHT(VRB_PARENT(elm, field), field)))\ +- elm = VRB_PARENT(elm, field); \ +- elm = VRB_PARENT(elm, field); \ +- } \ +- } \ +- return (elm); \ +-} \ +- \ +-/* ARGSUSED */ \ +-attr struct type * \ +-name##_VRB_PREV(struct type *elm) \ +-{ \ +- if (VRB_LEFT(elm, field)) { \ +- elm = VRB_LEFT(elm, field); \ +- while (VRB_RIGHT(elm, field)) \ +- elm = VRB_RIGHT(elm, field); \ +- } else { \ +- if (VRB_PARENT(elm, field) && \ +- (elm == VRB_RIGHT(VRB_PARENT(elm, field), field))) \ +- elm = VRB_PARENT(elm, field); \ +- else { \ +- while (VRB_PARENT(elm, field) && \ +- (elm == VRB_LEFT(VRB_PARENT(elm, field), field)))\ +- elm = VRB_PARENT(elm, field); \ +- elm = VRB_PARENT(elm, field); \ +- } \ +- } \ +- return (elm); \ +-} \ +- \ +-attr struct type * \ +-name##_VRB_MINMAX(const struct name *head, int val) \ +-{ \ +- struct type *tmp = VRB_ROOT(head); \ +- struct type *parent = NULL; \ +- while (tmp) { \ +- parent = tmp; \ +- if (val < 0) \ +- tmp = VRB_LEFT(tmp, field); \ +- else \ +- tmp = VRB_RIGHT(tmp, field); \ +- } \ +- return (parent); \ +-} +- +-#define VRB_NEGINF -1 +-#define VRB_INF 1 +- +-#define VRB_INSERT(name, x, y) name##_VRB_INSERT(x, y) +-#define VRB_REMOVE(name, x, y) name##_VRB_REMOVE(x, y) +-#define VRB_FIND(name, x, y) name##_VRB_FIND(x, y) +-#define VRB_NFIND(name, x, y) name##_VRB_NFIND(x, y) +-#define VRB_NEXT(name, x, y) name##_VRB_NEXT(y) +-#define VRB_PREV(name, x, y) name##_VRB_PREV(y) +-#define VRB_MIN(name, x) name##_VRB_MINMAX(x, VRB_NEGINF) +-#define VRB_MAX(name, x) name##_VRB_MINMAX(x, VRB_INF) +- +-#define VRB_FOREACH(x, name, head) \ +- for ((x) = VRB_MIN(name, head); \ +- (x) != NULL; \ +- (x) = name##_VRB_NEXT(x)) +- +-#define VRB_FOREACH_FROM(x, name, y) \ +- for ((x) = (y); \ +- ((x) != NULL) && ((y) = name##_VRB_NEXT(x), (x) != NULL); \ +- (x) = (y)) +- +-#define VRB_FOREACH_SAFE(x, name, head, y) \ +- for ((x) = VRB_MIN(name, head); \ +- ((x) != NULL) && ((y) = name##_VRB_NEXT(x), (x) != NULL); \ +- (x) = (y)) +- +-#define VRB_FOREACH_REVERSE(x, name, head) \ +- for ((x) = VRB_MAX(name, head); \ +- (x) != NULL; \ +- (x) = name##_VRB_PREV(x)) +- +-#define VRB_FOREACH_REVERSE_FROM(x, name, y) \ +- for ((x) = (y); \ +- ((x) != NULL) && ((y) = name##_VRB_PREV(x), (x) != NULL); \ +- (x) = (y)) +- +-#define VRB_FOREACH_REVERSE_SAFE(x, name, head, y) \ +- for ((x) = VRB_MAX(name, head); \ +- ((x) != NULL) && ((y) = name##_VRB_PREV(x), (x) != NULL); \ +- (x) = (y)) +- +-#endif /* _VTREE_H_ */ diff --git a/varnish-modules-0.15.0_fix_saintmode_for_varnish_6.1.patch b/varnish-modules-0.15.0_fix_saintmode_for_varnish_6.1.patch new file mode 100644 index 0000000..a38fc72 --- /dev/null +++ b/varnish-modules-0.15.0_fix_saintmode_for_varnish_6.1.patch @@ -0,0 +1,268 @@ +From 918057c889d850940b0c8d6f2b14dc94d543bae4 Mon Sep 17 00:00:00 2001 +From: Nils Goroll +Date: Wed, 25 Apr 2018 17:02:04 +0200 +Subject: [PATCH 1/4] update for master + a11939eb64427847ec11571d328696ba162f8c6a + +--- + src/vmod_saintmode.c | 21 ++++++++++++++++----- + 1 file changed, 16 insertions(+), 5 deletions(-) + +diff --git a/src/vmod_saintmode.c b/src/vmod_saintmode.c +index 26191cc..4b4b874 100644 +--- a/src/vmod_saintmode.c ++++ b/src/vmod_saintmode.c +@@ -43,6 +43,11 @@ + + #include "vcc_saintmode_if.h" + ++static unsigned ++healthy(const struct director *, const struct busyobj *, double *); ++static const struct director * ++resolve(const struct director *, struct worker *, struct busyobj *); ++ + struct trouble { + unsigned magic; + #define TROUBLE_MAGIC 0x4211ab21 +@@ -63,6 +68,14 @@ struct vmod_saintmode_saintmode { + VTAILQ_HEAD(, trouble) troublelist; + }; + ++static const struct director_methods vmod_saintmode_methods[1] = {{ ++ .magic = DIRECTOR_METHODS_MAGIC, ++ .type = "saintmode", ++ .healthy = healthy, ++ .resolve = resolve ++}}; ++ ++ + struct saintmode_objs { + unsigned magic; + #define SAINTMODE_OBJS_MAGIC 0x9aa7beec +@@ -269,7 +282,7 @@ healthy(const struct director *dir, const struct busyobj *bo, double *changed) + + /* Saintmode is disabled, or list is empty */ + if (sm->threshold == 0 || sm->n_trouble == 0) +- return (sm->be->healthy(sm->be, bo, changed)); ++ return (sm->be->methods->healthy(sm->be, bo, changed)); + + if (!bo) { + digest = NULL; +@@ -282,7 +295,7 @@ healthy(const struct director *dir, const struct busyobj *bo, double *changed) + } + + retval = is_digest_healthy(dir, digest, t_prev, vsl); +- return (retval ? sm->be->healthy(sm->be, bo, changed) : 0); ++ return (retval ? sm->be->methods->healthy(sm->be, bo, changed) : 0); + } + + VCL_BOOL +@@ -343,13 +356,11 @@ vmod_saintmode__init(VRT_CTX, struct vmod_saintmode_saintmode **smp, + VTAILQ_INIT(&sm->troublelist); + + sm->sdir->magic = DIRECTOR_MAGIC; +- sm->sdir->resolve = resolve; +- sm->sdir->healthy = healthy; ++ sm->sdir->methods = vmod_saintmode_methods; + #ifdef HAVE_DIRECTOR_ADMIN_HEALTH + sm->sdir->admin_health = VDI_AH_HEALTHY; + #endif + REPLACE(sm->sdir->vcl_name, vcl_name); +- sm->sdir->name = "saintmode"; + sm->sdir->priv = sm; + + if (!priv->priv) { + +From 2141fd9d37f435b4875c07c8fcc3fb1ea5968d74 Mon Sep 17 00:00:00 2001 +From: Nils Goroll +Date: Thu, 26 Apr 2018 14:50:30 +0200 +Subject: [PATCH 2/4] Output the health state as baseline information + +--- + src/tests/saintmode/test02.vtc | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/src/tests/saintmode/test02.vtc b/src/tests/saintmode/test02.vtc +index aa97734..bd8b537 100644 +--- a/src/tests/saintmode/test02.vtc ++++ b/src/tests/saintmode/test02.vtc +@@ -51,6 +51,8 @@ varnish v1 -vcl+backend { + + } -start + ++varnish v1 -cliok "backend.list" ++ + client c1 { + txreq -url "/a" + rxresp + +From 1e704bfd7d8aa8b1e20d918788f0c7c65488713e Mon Sep 17 00:00:00 2001 +From: Nils Goroll +Date: Thu, 26 Apr 2018 14:50:49 +0200 +Subject: [PATCH 3/4] changes required for latest varnish-cache + +The director API has changed and, in particular, the director healthy +callback is now only queried for AH_PROBE. +--- + src/vmod_saintmode.c | 31 ++++++++++++++++--------------- + 1 file changed, 16 insertions(+), 15 deletions(-) + +diff --git a/src/vmod_saintmode.c b/src/vmod_saintmode.c +index 4b4b874..95de050 100644 +--- a/src/vmod_saintmode.c ++++ b/src/vmod_saintmode.c +@@ -43,10 +43,10 @@ + + #include "vcc_saintmode_if.h" + +-static unsigned +-healthy(const struct director *, const struct busyobj *, double *); +-static const struct director * +-resolve(const struct director *, struct worker *, struct busyobj *); ++static VCL_BOOL v_matchproto_(vdi_healthy_f) ++healthy(VRT_CTX, VCL_BACKEND, VCL_TIME *); ++static VCL_BACKEND v_matchproto_(vdi_resolve_f) ++resolve(VRT_CTX, VCL_BACKEND); + + struct trouble { + unsigned magic; +@@ -266,23 +266,26 @@ is_digest_healthy(const struct director *dir, + } + + /* All adapted from PHK's saintmode implementation in Varnish 3.0 */ +-static unsigned +-healthy(const struct director *dir, const struct busyobj *bo, double *changed) ++static VCL_BOOL v_matchproto_(vdi_healthy_f) ++healthy(VRT_CTX, VCL_BACKEND dir, VCL_TIME *changed) + { + struct vmod_saintmode_saintmode *sm; ++ const struct busyobj *bo; + unsigned retval; + const uint8_t* digest; + double t_prev; + struct vsl_log* vsl; + ++ CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC); + CHECK_OBJ_NOTNULL(dir, DIRECTOR_MAGIC); + CAST_OBJ_NOTNULL(sm, dir->priv, VMOD_SAINTMODE_MAGIC); + CHECK_OBJ_NOTNULL(sm->be, DIRECTOR_MAGIC); ++ bo = ctx->bo; + CHECK_OBJ_ORNULL(bo, BUSYOBJ_MAGIC); + + /* Saintmode is disabled, or list is empty */ + if (sm->threshold == 0 || sm->n_trouble == 0) +- return (sm->be->methods->healthy(sm->be, bo, changed)); ++ return (VRT_Healthy(ctx, sm->be, changed)); + + if (!bo) { + digest = NULL; +@@ -295,7 +298,7 @@ healthy(const struct director *dir, const struct busyobj *bo, double *changed) + } + + retval = is_digest_healthy(dir, digest, t_prev, vsl); +- return (retval ? sm->be->methods->healthy(sm->be, bo, changed) : 0); ++ return (retval ? VRT_Healthy(ctx, sm->be, changed) : 0); + } + + VCL_BOOL +@@ -316,19 +319,17 @@ vmod_saintmode_is_healthy(VRT_CTX, struct vmod_saintmode_saintmode *sm) + return is_digest_healthy(sm->sdir, digest, + ctx->req->t_prev, ctx->req->vsl); + } else +- return healthy(sm->sdir, ctx->bo, NULL); ++ return healthy(ctx, sm->sdir, NULL); + } + +-static const struct director * +-resolve(const struct director *dir, struct worker *wrk, struct busyobj *bo) { ++static VCL_BACKEND v_matchproto_(vdi_resolve_f) ++resolve(VRT_CTX, VCL_BACKEND dir) { + struct vmod_saintmode_saintmode *sm; +- double changed = 0.0; + + CHECK_OBJ_NOTNULL(dir, DIRECTOR_MAGIC); + CAST_OBJ_NOTNULL(sm, dir->priv, VMOD_SAINTMODE_MAGIC); +- (void)wrk; + +- if (!healthy(dir, bo, &changed)) ++ if (!healthy(ctx, dir, NULL)) + return (NULL); + + return (sm->be); +@@ -358,7 +359,7 @@ vmod_saintmode__init(VRT_CTX, struct vmod_saintmode_saintmode **smp, + sm->sdir->magic = DIRECTOR_MAGIC; + sm->sdir->methods = vmod_saintmode_methods; + #ifdef HAVE_DIRECTOR_ADMIN_HEALTH +- sm->sdir->admin_health = VDI_AH_HEALTHY; ++ sm->sdir->admin_health = VDI_AH_PROBE; + #endif + REPLACE(sm->sdir->vcl_name, vcl_name); + sm->sdir->priv = sm; + +From c1c9b6ff4652c34aeff68f209d983be684caeb32 Mon Sep 17 00:00:00 2001 +From: Nils Goroll +Date: Tue, 1 May 2018 18:48:59 +0200 +Subject: [PATCH 4/4] reflect latest changes to the director API + +--- + src/vmod_saintmode.c | 19 +++++-------------- + 1 file changed, 5 insertions(+), 14 deletions(-) + +diff --git a/src/vmod_saintmode.c b/src/vmod_saintmode.c +index 95de050..b0e4ac6 100644 +--- a/src/vmod_saintmode.c ++++ b/src/vmod_saintmode.c +@@ -35,9 +35,6 @@ + + #include "vmod_config.h" + +-#include "cache/cache_director.h" +-#include "cache/cache_backend.h" +- + #include "vsb.h" + #include "vtim.h" + +@@ -59,7 +56,7 @@ struct trouble { + struct vmod_saintmode_saintmode { + unsigned magic; + #define VMOD_SAINTMODE_MAGIC 0xa03756e4 +- struct director sdir[1]; ++ const struct director *sdir; + const struct director *be; + pthread_mutex_t mtx; + unsigned threshold; +@@ -68,8 +65,8 @@ struct vmod_saintmode_saintmode { + VTAILQ_HEAD(, trouble) troublelist; + }; + +-static const struct director_methods vmod_saintmode_methods[1] = {{ +- .magic = DIRECTOR_METHODS_MAGIC, ++static const struct vdi_methods vmod_saintmode_methods[1] = {{ ++ .magic = VDI_METHODS_MAGIC, + .type = "saintmode", + .healthy = healthy, + .resolve = resolve +@@ -356,13 +353,7 @@ vmod_saintmode__init(VRT_CTX, struct vmod_saintmode_saintmode **smp, + sm->be = be; + VTAILQ_INIT(&sm->troublelist); + +- sm->sdir->magic = DIRECTOR_MAGIC; +- sm->sdir->methods = vmod_saintmode_methods; +-#ifdef HAVE_DIRECTOR_ADMIN_HEALTH +- sm->sdir->admin_health = VDI_AH_PROBE; +-#endif +- REPLACE(sm->sdir->vcl_name, vcl_name); +- sm->sdir->priv = sm; ++ sm->sdir = VRT_AddDirector(ctx, vmod_saintmode_methods, sm, "%s", vcl_name); + + if (!priv->priv) { + ALLOC_OBJ(sm_objs, SAINTMODE_OBJS_MAGIC); +@@ -392,7 +383,7 @@ vmod_saintmode__fini(struct vmod_saintmode_saintmode **smp) { + FREE_OBJ(tr); + } + +- free(sm->sdir->vcl_name); ++ VRT_DelDirector(&sm->sdir); + AZ(pthread_mutex_destroy(&sm->mtx)); + + /* We can no longer refer to the sm_objs after this diff --git a/varnish-modules-0.15.0_fix_simple_formatting_bug.patch b/varnish-modules-0.15.0_fix_simple_formatting_bug.patch new file mode 100644 index 0000000..9f05e8d --- /dev/null +++ b/varnish-modules-0.15.0_fix_simple_formatting_bug.patch @@ -0,0 +1,23 @@ +From 40a8cc063b2819fdd325061a1cb48a3adc3ec910 Mon Sep 17 00:00:00 2001 +From: Dridi Boukelmoune +Date: Wed, 17 Oct 2018 12:42:22 +0200 +Subject: [PATCH] 32bit-friendly format in vmod-tcp + +Fixes #122 +--- + src/vmod_tcp.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/vmod_tcp.c b/src/vmod_tcp.c +index fee4bd3..570be45 100644 +--- a/src/vmod_tcp.c ++++ b/src/vmod_tcp.c +@@ -174,7 +174,7 @@ vmod_set_socket_pace(VRT_CTX, VCL_INT rate) + sizeof(pacerate)) != 0) + VSLb(ctx->vsl, SLT_VCL_Error, "set_socket_pace(): Error setting pace rate."); + else +- VSLb(ctx->vsl, SLT_VCL_Log, "vmod-tcp: Socket paced to %lu KB/s.", rate); ++ VSLb(ctx->vsl, SLT_VCL_Log, "vmod-tcp: Socket paced to %jd KB/s.", (intmax_t)rate); + + # ifndef NDEBUG + int retval; diff --git a/varnish-modules.spec b/varnish-modules.spec new file mode 100644 index 0000000..35f22e4 --- /dev/null +++ b/varnish-modules.spec @@ -0,0 +1,155 @@ +%define abi 6.1 + +%bcond_without python2 +%bcond_with python3 + +%if %{with python2} == %{with python3} +%error Pick exactly one Python version +%endif + +Name: varnish-modules +Version: 0.15.0 +Release: 6%{?dist} +Summary: A collection of modules ("vmods") extending Varnish VCL + +Group: System Environment/Daemons +License: BSD +URL: https://github.com/varnish/%{name} + +Source: https://download.varnish-software.com/varnish-modules/%{name}-%{version}.tar.gz +BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) +Patch1: varnish-modules-0.15.0-vmod-tcp-int.patch + +BuildRequires: gcc +BuildRequires: make +BuildRequires: pkgconfig(varnishapi) +BuildRequires: varnish + +%if %{with python3} +BuildRequires: python3-docutils +BuildRequires: python3-devel +%define rst2man %{_bindir}/rst2man +%define vpython %{_bindir}/python3 +%else +BuildRequires: python-docutils +BuildRequires: python2-devel +%define rst2man %{_bindir}/rst2man +%define vpython %{_bindir}/python2 +%endif + +Requires: varnish + +Provides: vmod-bodyaccess = %{version}-%{release} +Provides: vmod-cookie = %{version}-%{release} +Provides: vmod-header = %{version}-%{release} +Provides: vmod-saintmode = %{version}-%{release} +Provides: vmod-tcp = %{version}-%{release} +Provides: vmod-var = %{version}-%{release} +Provides: vmod-vsthrottle = %{version}-%{release} +Provides: vmod-xkey = %{version}-%{release} + + +%description +This is a collection of modules ("vmods") extending Varnish VCL used +for describing HTTP request/response policies with additional +capabilities. This collection contains the following vmods (previously +kept individually): cookie, vsthrottle, header, saintmode, softpurge, +tcp, var, xkey + + +%prep +%setup -q + +%patch1 -p1 -b .vmod-tcp-int + +%build +%configure +%make_build + + +%install +%make_install +find %{buildroot}/%{_libdir}/ -name '*.la' -exec rm -f {} ';' + + +%check +make check VERBOSE=1 + + +%files +%doc docs AUTHORS CHANGES.rst COPYING README.rst LICENSE +%license LICENSE +%{_libdir}/varnish/vmods/* +%{_mandir}/man3/*.3* +%{_pkgdocdir} + + +%changelog +* Mon Jul 26 2021 Luboš Uhliarik - 0.15.0-6 +- Related: #1982862 - rebuild for new varnish version + +* Thu Apr 16 2020 Lubos Uhliarik - 0.15.0-5 +- Related: #1795673 - RFE: rebase varnish:6 to latest 6.0.x LTS + +* Mon Dec 03 2018 Lubos Uhliarik - 0.15.0-4 +- Resolves: #1649358 - Unable to install varnish module + +* Sun Aug 05 2018 Lubos Uhliarik - 0.15.0-2 +- Fixed i686 build failure + +* Sun Aug 05 2018 Lubos Uhliarik - 0.15.0-1 +- updated to newer version (0.15.0) +- Drop EPEL and older Fedora releases support +- Drop broken manual ABI dependency to Varnish +- Drop commented out references to past patches +- Verbose test suite +- Simplified configure step +- Dependencies cleanup + +* Tue Apr 24 2018 Luboš Uhliarik - 0.12.16 +- use rst2man binary for both python2 and python3 + +* Mon Apr 16 2018 Joe Orton - 0.12.1-4.4 +- fix abi requires (#1566046) + +* Thu Mar 29 2018 Joe Orton - 0.12.1-4.2 +- fix use of rst2man with python3-docutils + +* Wed Mar 28 2018 Joe Orton - 0.12.1-4.1 +- add Python build conditionals + +* Thu Aug 03 2017 Fedora Release Engineering - 0.12.1-4 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Binutils_Mass_Rebuild + +* Thu Jul 27 2017 Fedora Release Engineering - 0.12.1-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Mass_Rebuild + +* Thu Jun 01 2017 Ingvar Hagelund 0.12.1-2 +- Set correct varnishabi requirement for the different fedoras + +* Wed May 31 2017 Ingvar Hagelund 0.12.1-1 +- New uptream release +- Pull el5 support + +* Mon Mar 20 2017 Ingvar Hagelund 0.11.0-1 +- New upstream release + +* Sat Sep 24 2016 Ingvar Hagelund 0.9.2-0.1.20160924gitdaa4f1d +- Upstream git checkout with support for varnish-5.0 +- Removed patches that are included upstream +- el5 build fix + +* Fri Aug 05 2016 Ingvar Hagelund 0.9.1-1 +- New upstream release +- Build man pages, buildrequires python-docutils +- Added a patch for tests/cookie/08-overflow.vtc, upping workspace_client, + the default is too small on 32bit +- Removed extra cflags for el5, fixed with patch from upstream +- Force readable docs and debug files, they tend to end up with mode 600 + +* Tue Apr 05 2016 Ingvar Hagelund 0.9.0-1 +- First wrap for fedora +- Uses some old-style specfile components for el5 compatibility, including + the usage of the BuildRoot header and cleaning the buildroot before install + +