pax_global_header00006660000000000000000000000064131112441120014500gustar00rootroot0000000000000052 comment=d3348f82a20ab18530d187581cb08ff8714a7cbc varnish-modules-0.12.1/000077500000000000000000000000001311124411200147015ustar00rootroot00000000000000varnish-modules-0.12.1/.gitignore000066400000000000000000000006621311124411200166750ustar00rootroot00000000000000.deps/ .libs/ *.o *.la *.lo *~ *.[1-9] *.gz Makefile Makefile.in /aclocal.m4 /config.log /config.status /config.h /configure /autom4te.cache /build-aux/ /config.h.in /configure /libtool /m4/libtool.m4 /m4/ltoptions.m4 /m4/ltsugar.m4 /m4/ltversion.m4 /m4/lt~obsolete.m4 ar-lib stamp-h1 # vmodtool vcc_*_if.[ch] vmod_*.rst # docs ! /docs/vmod_*.rst # test suite *.log *.trs test-driver # ax_code_coverage *.gcno *.gcda *.info lcov/ varnish-modules-0.12.1/.placeholder000066400000000000000000000000001311124411200171520ustar00rootroot00000000000000varnish-modules-0.12.1/.travis.yml000066400000000000000000000010721311124411200170120ustar00rootroot00000000000000--- sudo: required dist: trusty language: c before_install: - sudo apt-get update -q - sudo apt-get install -qq apt-transport-https python-docutils - curl https://repo.varnish-cache.org/debian/GPG-key.txt | sudo apt-key add - - echo "deb https://repo.varnish-cache.org/ubuntu/ precise varnish-4.1" | sudo tee /etc/apt/sources.list.d/varnish-cache.list - sudo apt-get -q update - sudo apt-get install varnish varnish-dev before_script: - ./bootstrap - ./configure --prefix=/usr - make -j4 script: - make distcheck -j4 compiler: - clang - gcc varnish-modules-0.12.1/AUTHORS000066400000000000000000000007021311124411200157500ustar00rootroot00000000000000 AUTHORS ======= This file summarises the original vmod authors. All modules taken into this collection at creation time were copyright Varnish Software and are 2-clause BSD licensed. vmod-header was originally written by Kristian Lyngstøl. vmod-cookie, vmod-softpurge and vmod-tcp were written by Lasse Karstensen. vmod-vsthrottle and vmod-saintmode were written by Dag Haavi Finstad. vmod-xkey was originally written by Martin Blix Grydeland. varnish-modules-0.12.1/CHANGES.rst000066400000000000000000000051371311124411200165110ustar00rootroot00000000000000This is a running log of changes to varnish-modules. varnish-modules 0.12.1 (2017-05-24) ----------------------------------- * Bump configure.ac version number varnish-modules 0.12.0 (2017-05-23) ----------------------------------- * [vmod-bodyaccess] New vmod that enables simple matching and hashing on the client request body. * [vmod-xkey] Some documentation improvements * Build system fixes varnish-modules 0.11.0 (2017-02-07) ----------------------------------- * [vmod-saintmode] Fix a bug where saintmode.blacklist() would cause a crash if saintmode was not configured. (#54) * [vmod-cookie] New cookie.filter() function to blacklist cookies. varnish-modules 0.10.2 (2017-01-31) ----------------------------------- * Improved documentation and examples for vmod-saintmode, vmod-cookie, vmod-xkey, vmod-var, vmod-softpurge, vmod-header, vmod-tcp and vmod-vsthrottle * Various improvements in the build system * [vmod-cookie] Fix test case overflow issue for 32 bit systems (#35) * [vmod-vsthrottle] Fix OS X build issue (#37) * [vmod-saintmode] Add new log records for when vmod-saintmode marks a backend as unhealthy (#43) * [vmod-saintmode] Added saintmode.status() which outputs a JSON status string for use in vcl_synth (#43) * [vmod-vsthrottle] Added vsthrottle.remaining() which returns the current number of tokens for a given bucket. * Correct build with 4.1.4-beta1, 5.0.0 and master at rev dfcf44c6. * [vmod-xkey] Add support for purging multiple keys in a single invocation * [vmod-cookie] Fix a bug where we would crash on malicious input varnish-modules 0.9.1 (2016-07-07) ---------------------------------- Changes since 0.9.0: * Example for vmod-saintmode has been improved. (Issue #16) * Forgotten vmod-var documentation added. (Issue #24) * Licenses added to source files. (#9) * [vmod-cookie] Bugfixes from libvmod-cookie.git forgotten on initial import applied. More robust filter parsing, superfluous debug log entries removed, avoid reading past the end of invalid cookie headers, avoid invalid memory reference in filter_except(). This release is intended to work with Varnish Cache 4.1.3 and higher. varnish-modules 0.9.0 (2016-03-04) ---------------------------------- Initial release. This package contains the source files extracted from the following git repositories and commit identifiers: * b772825 in libvmod-cookie.git * 86da3be in libvmod-header.git * d8658c9 in libvmod-saintmode.git * e6c8ce1 in libvmod-softpurge.git * 8add5f8 in libvmod-tcp.git * c99cb30 in libvmod-var.git * 52c5d64 in libvmod-xkey.git This release is intended to work with Varnish Cache 4.1.2 and higher. varnish-modules-0.12.1/COPYING000066400000000000000000000000761311124411200157370ustar00rootroot00000000000000Copyright (c) 2016 Varnish Software See LICENSE for details. varnish-modules-0.12.1/LICENSE000066400000000000000000000023051311124411200157060ustar00rootroot00000000000000Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. varnish-modules-0.12.1/Makefile.am000066400000000000000000000005521311124411200167370ustar00rootroot00000000000000ACLOCAL_AMFLAGS = -I m4 -I ${VARNISHAPI_DATAROOTDIR}/aclocal SUBDIRS = src doc_DATA = README.rst LICENSE EXTRA_DIST = \ $(doc_DATA) \ docs \ CHANGES.rst # lcov support CODE_COVERAGE_OUTPUT_DIRECTORY = lcov CODE_COVERAGE_IGNORE_PATTERN = "/usr/*" CODE_COVERAGE_GENHTML_OPTIONS = --prefix $(abs_top_srcdir) @CODE_COVERAGE_RULES@ lcov: check-code-coverage varnish-modules-0.12.1/README.rst000066400000000000000000000072011311124411200163700ustar00rootroot00000000000000Varnish module collection by Varnish Software ============================================= This is a collection of modules ("vmods") extending Varnish VCL used for describing HTTP request/response policies with additional capabilities. Included: * Simpler handling of HTTP cookies * Variable support * Request and bandwidth throttling * Modify and change complex HTTP headers * 3.0-style saint mode, * Advanced cache invalidations, and more. * Client request body access This collection contains the following vmods (previously kept individually): cookie, vsthrottle, header, saintmode, softpurge, tcp, var, xkey, bodyaccess Supported Varnish version is described in the `CHANGES.rst` file. Normally this is the last public Varnish Cache release. See PORTING below for information on support for other versions of Varnish. Installation ------------ Source releases can be downloaded from: https://download.varnish-software.com/varnish-modules/ Installation requires an installed version of Varnish Cache, including the development files. Requirements can be found in the `Varnish documentation`_. .. _`Varnish documentation`: https://www.varnish-cache.org/docs/4.1/installation/install.html#compiling-varnish-from-source .. _`Varnish Project packages`: https://www.varnish-cache.org/releases/index.html Source code is built with autotools, you need to install the correct development packages first. If you are using the official `Varnish Project packages`_:: sudo apt-get install varnish-dev || sudo yum install varnish-devel If you are using the distro provided packages:: sudo apt-get install libvarnishapi-dev || sudo yum install varnish-libs-devel 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 Varnish module directory. (default `/usr/lib/varnish/vmods/`) Usage ----- Each module has a different set of functions and usage, described in separate documents in `docs/`. For completeness, here is a snippet from `docs/cookie.rst`:: import cookie; sub vcl_recv { cookie.parse(req.http.cookie); cookie.filter_except("SESSIONID,PHPSESSID"); set req.http.cookie = cookie.get_string(); # Only SESSIONID and PHPSESSID are left in req.http.cookie at this point. } Development ----------- 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. Porting ------- We encourage porting of the module package to other versions of Varnish Cache. Administrativa -------------- The goals of this collection are: * Simplify access to vmod code for Varnish users. One package to install, not 6. * Decrease the maintenance cost that comes with having 10 different git repositories, each with autotools and (previously) distribution packaging files. Expressed non-goals are: * Import vmods that require external libraries, like curl or geoip. This collection should be simple and maintenance free to run. * Support older releases of Varnish Cache. * Include every vmod under the sun. We'll add the important ones. Addition of further vmods is decided on a case-by-case basis. Code quality and maintenance requirements will be important in this decision. Contact ------- This code is maintained by Varnish Software. (https://www.varnish-software.com/) Issues can be reported via the Github issue tracker. Other inquires can be sent to opensource@__no_spam_please__varnish-software.com. varnish-modules-0.12.1/bootstrap000077500000000000000000000021061311124411200166430ustar00rootroot00000000000000#!/bin/sh warn() { echo "WARNING: $@" 1>&2 } case `uname -s` in Darwin) LIBTOOLIZE=glibtoolize ;; FreeBSD) LIBTOOLIZE=libtoolize ;; Linux) LIBTOOLIZE=libtoolize ;; SunOS) LIBTOOLIZE=libtoolize ;; *) warn "unrecognized platform:" `uname -s` LIBTOOLIZE=libtoolize esac automake_version=`automake --version | tr ' ' '\n' | egrep '^[0-9]\.[0-9a-z.-]+'` if [ -z "$automake_version" ] ; then warn "unable to determine automake version" else case $automake_version in 0.*|1.[0-8]|1.[0-8][.-]*) warn "automake ($automake_version) detected; 1.9 or newer recommended" ;; *) ;; esac fi # check for varnishapi.m4 in custom paths dataroot=$(pkg-config --variable=datarootdir varnishapi 2>/dev/null) if [ -z "$dataroot" ] ; then cat >&2 <<'EOF' Package varnishapi was not found in the pkg-config search path. Perhaps you should add the directory containing `varnishapi.pc' to the PKG_CONFIG_PATH environment variable EOF exit 1 fi set -ex mkdir -p m4 aclocal -I m4 -I ${dataroot}/aclocal $LIBTOOLIZE --copy --force autoheader automake --add-missing --copy --foreign autoconf varnish-modules-0.12.1/configure.ac000066400000000000000000000031711311124411200171710ustar00rootroot00000000000000AC_INIT([varnish-modules], [0.12.1]) AC_COPYRIGHT([Copyright (c) 2016 Varnish Software Group]) AC_PREREQ(2.63) AC_CONFIG_MACRO_DIR([m4]) 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_SILENT_RULES([yes]) AM_EXTRA_RECURSIVE_TARGETS([rst-docs]) LT_INIT([dlopen disable-static]) LT_PREREQ([2.2.6]) AX_PTHREAD(,[AC_MSG_ERROR([Could not configure pthreads support])]) AX_CODE_COVERAGE # Check for rst utilities AC_CHECK_PROGS(RST2MAN, [rst2man rst2man.py], "no") if test "x$RST2MAN" = "xno"; then AC_MSG_WARN([rst2man not found - not building man pages]) RST2MAN=: # no-op fi # Will be available after Varnish 5.0 PKG_CHECK_VAR([VARNISHAPI_LIBDIR], [varnishapi], [libdir]) AC_SUBST([VARNISH_LIBRARY_PATH], [$VARNISHAPI_LIBDIR:$VARNISHAPI_LIBDIR/varnish]) VARNISH_PREREQ([4.1.4]) VARNISH_VMODS([ bodyaccess cookie header saintmode softpurge tcp var vsthrottle xkey ]) save_CFLAGS="$CFLAGS" CFLAGS="$VARNISHAPI_CFLAGS" AC_CHECK_MEMBER(struct objcore.exp, [AC_DEFINE([HAVE_OBJCORE_EXP], [1], [Define if exp is present in the objcore struct])], [], [#include ]) AC_CHECK_TYPES([req_body_iter_f, objiterate_f], [], [], [#include ]) CFLAGS="$save_CFLAGS" AC_CHECK_TYPE(struct tcp_info, [AC_DEFINE([HAVE_TCP_INFO], [1], [Define if struct tcp_info is present])], [], [[ #include #include #include #include ]]) # Test suite VMOD_TESTS=$(cd $srcdir/src && echo tests/*/*.vtc) AC_SUBST([VMOD_TESTS]) AC_CONFIG_FILES([ Makefile src/Makefile ]) AC_OUTPUT varnish-modules-0.12.1/docs/000077500000000000000000000000001311124411200156315ustar00rootroot00000000000000varnish-modules-0.12.1/docs/vmod_cookie.rst000066400000000000000000000146371311124411200206740ustar00rootroot00000000000000.. .. NB: This file is machine generated, DO NOT EDIT! .. .. Edit vmod.vcc and run make instead .. .. role:: ref(emphasis) .. _vmod_cookie(3): =========== vmod_cookie =========== --------------------- Varnish Cookie Module --------------------- :Manual section: 3 SYNOPSIS ======== import cookie [from "path"] ; DESCRIPTION =========== Handle HTTP cookies easier in Varnish VCL. (without regex) Parses a cookie header into an internal data store, where per-cookie get/set/delete functions are available. A filter_except() method removes all 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. 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 the contents as fake bereq headers. .. vcl-start Filtering example:: vcl 4.0; import cookie; backend default { .host = "192.0.2.11"; .port = "8080"; } sub vcl_recv { if (req.http.cookie) { cookie.parse(req.http.cookie); # Either delete the ones you want to get rid of: cookie.delete("cookie2"); # or filter everything but a few: cookie.filter_except("SESSIONID,PHPSESSID"); # Store it back into req so it will be passed to the backend. set req.http.cookie = cookie.get_string(); # If empty, unset so the builtin VCL can consider it for caching. if (req.http.cookie == "") { unset req.http.cookie; } } } .. 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: clean ----- :: VOID clean(PRIV_TASK) Description Clean up previously parsed cookies. It is not necessary to run clean() in normal operations. Example :: sub vcl_recv { cookie.clean(); } .. _func_delete: delete ------ :: VOID delete(PRIV_TASK, STRING cookiename) Description Delete `cookiename` from internal vmod storage if it exists. Example :: sub vcl_recv { cookie.parse("cookie1: value1; cookie2: value2;"); cookie.delete("cookie2"); // get_string() will now yield "cookie1: value1"; } .. _func_filter: filter ------ :: VOID filter(PRIV_TASK, STRING filterstring) Description Delete all cookies from internal vmod storage that are in the comma-separated argument cookienames. Example :: sub vcl_recv { cookie.parse("cookie1: value1; cookie2: value2; cookie3: value3"); cookie.filter("cookie1,cookie2"); // get_string() will now yield // "cookie3: value3"; } .. _func_filter_except: filter_except ------------- :: VOID filter_except(PRIV_TASK, STRING filterstring) Description Delete all cookies from internal vmod storage that is not in the comma-separated argument cookienames. Example :: sub vcl_recv { cookie.parse("cookie1: value1; cookie2: value2; cookie3: value3"); cookie.filter_except("cookie1,cookie2"); // get_string() will now yield // "cookie1: value1; cookie2: value2;"; } .. _func_format_rfc1123: format_rfc1123 -------------- :: STRING format_rfc1123(TIME now, DURATION timedelta) Description Get a RFC1123 formatted date string suitable for inclusion in a Set-Cookie response header. Care should be taken if the response has multiple Set-Cookie headers. In that case the header vmod should be used. Example :: sub vcl_deliver { # Set a userid cookie on the client that lives for 5 minutes. set resp.http.Set-Cookie = "userid=" + req.http.userid + "; Expires=" + cookie.format_rfc1123(now, 5m) + "; httpOnly"; } .. _func_get: get --- :: STRING get(PRIV_TASK, STRING cookiename) Description Get the value of `cookiename`, as stored in internal vmod storage. If `cookiename` does not exist an empty string is returned. Example :: import std; sub vcl_recv { cookie.parse("cookie1: value1; cookie2: value2;"); std.log("cookie1 value is: " + cookie.get("cookie1")); } .. _func_get_string: get_string ---------- :: STRING get_string(PRIV_TASK) Description Get a Cookie string value with all cookies in internal vmod storage. Does not modify internal storage. Example :: sub vcl_recv { cookie.parse(req.http.cookie); cookie.filter_except("SESSIONID,PHPSESSID"); set req.http.cookie = cookie.get_string(); } .. _func_isset: isset ----- :: BOOL isset(PRIV_TASK, STRING cookiename) Description Check if `cookiename` is set in the internal vmod storage. Example :: import std; sub vcl_recv { cookie.parse("cookie1: value1; cookie2: value2;"); if (cookie.isset("cookie2")) { std.log("cookie2 is set."); } } .. _func_parse: parse ----- :: VOID parse(PRIV_TASK, STRING cookieheader) Description Parse the cookie string in `cookieheader`. If state already exists, clean() will be run first. Example :: sub vcl_recv { cookie.parse(req.http.Cookie); } .. _func_set: set --- :: VOID set(PRIV_TASK, STRING cookiename, STRING value) Description Set the internal vmod storage for `cookiename` to `value`. Example :: sub vcl_recv { cookie.set("cookie1", "value1"); std.log("cookie1 value is: " + cookie.get("cookie1")); } varnish-modules-0.12.1/docs/vmod_header.rst000066400000000000000000000044011311124411200206370ustar00rootroot00000000000000.. .. NB: This file is machine generated, DO NOT EDIT! .. .. Edit vmod.vcc and run make instead .. .. role:: ref(emphasis) .. _vmod_header(3): =========== vmod_header =========== ----------------------- Header VMOD for Varnish ----------------------- :Manual section: 3 SYNOPSIS ======== import header [from "path"] ; DESCRIPTION =========== Varnish Module for manipulation of duplicated HTTP headers, for instance multiple Set-Cookie headers. .. vcl-start Example:: vcl 4.0; import header; backend default { .host = "192.0.2.11"; .port = "8080"; } sub vcl_backend_response { if (beresp.http.Set-Cookie) { # Add another line of Set-Cookie in the response. header.append(beresp.http.Set-Cookie, "VSESS=abbabeef"); # CMS always set this, but doesn't really need it. header.remove(beresp.http.Set-Cookie, "JSESSIONID="); } } .. vcl-end CONTENTS ======== * :ref:`func_append` * :ref:`func_copy` * :ref:`func_get` * :ref:`func_remove` .. _func_append: append ------ :: VOID append(HEADER, STRING) Description Append an extra occurrence to an existing header. Example :: header.append(beresp.http.Set-Cookie, "foo=bar") .. _func_copy: copy ---- :: VOID copy(HEADER, HEADER) Description Copy all source headers to a new header. Example :: header.copy(beresp.http.set-cookie, beresp.http.x-old-cookie); .. _func_get: get --- :: STRING get(PRIV_CALL, HEADER header, STRING regex) Description Fetches the value of the first `header` that matches the given regular expression `regex`. Example :: set beresp.http.xusr = header.get(beresp.http.set-cookie,"user="); .. _func_remove: remove ------ :: VOID remove(PRIV_CALL, HEADER header, STRING regex) Description Remove all occurences of `header` that matches `regex`. Example :: header.remove(beresp.http.set-cookie,"^(?!(funcookie=))"); ACKNOWLEDGEMENTS ================ The development of this plugin was made possible by the sponsorship of Softonic, http://en.softonic.com/ . Also thanks to Imo Klabun and Anders Nordby for bug reports. BUGS ==== You can't use dynamic regular expressions, which also holds true for normal regular expressions in regsub(). varnish-modules-0.12.1/docs/vmod_saintmode.rst000066400000000000000000000117651311124411200214050ustar00rootroot00000000000000.. .. NB: This file is machine generated, DO NOT EDIT! .. .. Edit vmod.vcc and run make instead .. .. role:: ref(emphasis) .. _vmod_saintmode(3): ============== vmod_saintmode ============== --------------------------- Saint mode backend director --------------------------- :Manual section: 3 SYNOPSIS ======== import saintmode [from "path"] ; DESCRIPTION =========== This VMOD provides saintmode functionality for Varnish Cache 4.1 and newer. The code is in part based on Poul-Henning Kamp's saintmode implementation in Varnish 3.0. Saintmode lets you deal with a backend that is failing in random ways for specific requests. It maintains a blacklist per backend, marking the backend as sick for specific objects. When the number of objects marked as sick for a backend reaches a set threshold, the backend is considered sick for all requests. Each blacklisted object carries a TTL, which denotes the time it will stay blacklisted. Saintmode in Varnish 4.1 is implemented as a director VMOD. We instantiate a saintmode object and give it a backend as an argument. The resulting object can then be used in place of the backend, with the effect that it also has added saintmode capabilities. Any director will then be able to use the saintmode backends, and as backends marked sick are skipped by the director, this provides a way to have fine grained health status on the backends, and making sure that retries get a different backend than the one which failed. .. vcl-start Example:: vcl 4.0; import saintmode; import directors; backend tile1 { .host = "192.0.2.11"; .port = "80"; } backend tile2 { .host = "192.0.2.12"; .port = "80"; } sub vcl_init { # Instantiate sm1, sm2 for backends tile1, tile2 # with 10 blacklisted objects as the threshold for marking the # whole backend sick. new sm1 = saintmode.saintmode(tile1, 10); new sm2 = saintmode.saintmode(tile2, 10); # Add both to a director. Use sm0, sm1 in place of tile1, tile2. # Other director types can be used in place of random. new imagedirector = directors.random(); imagedirector.add_backend(sm1.backend(), 1); imagedirector.add_backend(sm2.backend(), 1); } sub vcl_backend_fetch { # Get a backend from the director. # When returning a backend, the director will only return backends # saintmode says are healthy. set bereq.backend = imagedirector.backend(); } sub vcl_backend_response { if (beresp.status >= 500) { # This marks the backend as sick for this specific # object for the next 20s. saintmode.blacklist(20s); # Retry the request. This will result in a different backend # being used. return (retry); } } .. vcl-end CONTENTS ======== * :ref:`func_blacklist` * :ref:`obj_saintmode` * :ref:`func_saintmode.backend` * :ref:`func_saintmode.blacklist_count` * :ref:`func_status` .. _func_blacklist: blacklist --------- :: VOID blacklist(PRIV_VCL, 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 in vcl_backend_response. Example:: sub vcl_backend_response { if (beresp.http.broken-app) { saintmode.blacklist(20s); return (retry); } } .. _func_status: status ------ :: STRING status(PRIV_VCL) Returns a JSON formatted status string suitable for use in vcl_synth. :: sub vcl_recv { if (req.url ~ "/saintmode-status") { return (synth(700, "OK")); } } sub vcl_synth { if (resp.status == 700) { synthetic(saintmode.status()); return (deliver); } } Example JSON output: :: { "saintmode" : [ { "name": "sm1", "backend": "foo", "count": "3", "threshold": "10" }, { "name": "sm2", "backend": "bar", "count": "2", "threshold": "5" } ] } .. _obj_saintmode: saintmode --------- :: new OBJ = saintmode(PRIV_VCL, 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. Example:: sub vcl_init { new sm = saintmode.saintmode(b, 10); } .. _func_saintmode.backend: saintmode.backend ----------------- :: BACKEND saintmode.backend() Used for assigning the backend from the saintmode object. Example:: sub vcl_backend_fetch { set bereq.backend = sm.backend(); } .. _func_saintmode.blacklist_count: saintmode.blacklist_count ------------------------- :: INT saintmode.blacklist_count() Returns the number of objects currently blacklisted for a saintmode director object. Example: :: sub vcl_deliver { set resp.http.troublecount = sm.blacklist_count(); } varnish-modules-0.12.1/docs/vmod_softpurge.rst000066400000000000000000000031201311124411200214220ustar00rootroot00000000000000.. .. NB: This file is machine generated, DO NOT EDIT! .. .. Edit vmod.vcc and run make instead .. .. role:: ref(emphasis) .. _vmod_softpurge(Soft): ============== vmod_softpurge ============== ---------- purge vmod ---------- :Manual section: Soft SYNOPSIS ======== import softpurge [from "path"] ; DESCRIPTION =========== ``Softpurge`` is cache invalidation in Varnish that reduces TTL but keeps the grace value of a resource. This makes it possible to serve stale content to users if the backend is unavailable and fresh content can not be fetched. .. vcl-start Example:: vcl 4.0; import softpurge; backend default { .host = "192.0.2.11"; .port = "8080"; } sub vcl_recv { # Return early to avoid return(pass) by builtin VCL. if (req.method == "PURGE") { return (hash); } } sub vcl_backend_response { # Set object grace so we keep them around after TTL has expired. set beresp.grace = 10m; } sub vcl_hit { if (req.method == "PURGE") { softpurge.softpurge(); return (synth(200, "Successful softpurge")); } } sub vcl_miss { if (req.method == "PURGE") { softpurge.softpurge(); return (synth(200, "Successful softpurge")); } } .. vcl-end CONTENTS ======== * :ref:`func_softpurge` .. _func_softpurge: softpurge --------- :: VOID softpurge() Performs a soft purge. Valid in vcl_hit and vcl_miss. Example:: sub vcl_hit { if (req.method == "PURGE") { softpurge.softpurge(); } } varnish-modules-0.12.1/docs/vmod_tcp.rst000066400000000000000000000046411311124411200202030ustar00rootroot00000000000000.. .. NB: This file is machine generated, DO NOT EDIT! .. .. Edit vmod.vcc and run make instead .. .. role:: ref(emphasis) .. _vmod_tcp(3): ======== vmod_tcp ======== -------- TCP vmod -------- :Manual section: 3 SYNOPSIS ======== import tcp [from "path"] ; DESCRIPTION =========== The TCP vmod opens for access and modification of client TCP connection attributes from VCL. Primary use is for rate limiting (pacing) using the fq network scheduler on recent Linux systems. .. vcl-start Example:: vcl 4.0; import tcp; backend default { .host = "192.0.2.11"; .port = "8080"; } sub vcl_recv { # Shape (pace) the data rate to avoid filling router buffers for a # single client. if (req.url ~ ".mp4$") { tcp.set_socket_pace(1000); # KB/s. } # We want to change the congestion control algorithm. if (tcp.get_estimated_rtt() > 300) { set req.http.x-tcp = tcp.congestion_algorithm("hybla"); } } .. vcl-end CONTENTS ======== * :ref:`func_congestion_algorithm` * :ref:`func_dump_info` * :ref:`func_get_estimated_rtt` * :ref:`func_set_socket_pace` .. _func_congestion_algorithm: congestion_algorithm -------------------- :: INT congestion_algorithm(STRING algorithm) Set the client socket congestion control algorithm to S. Returns 0 on success, and -1 on error. Example:: sub vcl_recv { set req.http.x-tcp = tcp.congestion_algorithm("cubic"); } .. _func_dump_info: dump_info --------- :: VOID dump_info() Write the contents of the TCP_INFO data structure into varnishlog. Example:: tcp.dump_info(); Example varnishlog output:: - VCL_Log tcpi: snd_mss=1448 rcv_mss=536 lost=0 retrans=0 - VCL_Log tcpi2: pmtu=1500 rtt=152000 rttvar=76000 snd_cwnd=10 advmss=1448 reordering=3 - VCL_Log getsockopt() returned: cubic .. _func_get_estimated_rtt: get_estimated_rtt ----------------- :: REAL get_estimated_rtt() Get the estimated round-trip-time for the client socket. Unit: milliseconds. Example:: if (tcp.get_estimated_rtt() > 300) { std.log("client is far away."); } .. _func_set_socket_pace: set_socket_pace --------------- :: 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) Example:: tcp.set_socket_pace(1000); varnish-modules-0.12.1/docs/vmod_var.rst000066400000000000000000000102441311124411200202010ustar00rootroot00000000000000.. .. NB: This file is machine generated, DO NOT EDIT! .. .. Edit vmod.vcc and run make instead .. .. role:: ref(emphasis) .. _vmod_var(3): ======== vmod_var ======== -------------------------------- Variable support for Varnish VCL -------------------------------- :Manual section: 3 SYNOPSIS ======== import var [from "path"] ; This VMOD implements basic variable support in VCL. It supports strings, integers and real numbers. There are methods to get and set each data type. Global variables have a lifespan that extends across requests and VCLs, for as long as the vmod is loaded. The remaining functions have PRIV_TASK lifespan and are local to a single request or backend request. .. vcl-start Example:: vcl 4.0; import var; backend default { .host = "192.0.2.11"; .port = "8080"; } sub vcl_recv { # Set and get some values. var.set("foo", "bar"); set req.http.x-foo = var.get("foo"); var.set_int("ten", 10); var.set_int("five", 5); set req.http.twenty = var.get_int("ten") + var.get_int("five") + 5; # VCL will use the first token to decide final data type. Headers are strings. # set req.http.X-lifetime = var.get_int("ten") + " seconds"; # Won't work. set req.http.X-lifetime = "" + var.get_int("ten") + " seconds"; # Works! var.set_duration("timedelta", 1m); # 60s set req.http.d1 = var.get_duration("timedelta"); var.set_ip("endpoint", client.ip); set req.http.x-client = var.get_ip("endpoint"); # Unset all non-global variables. var.clear(); # Demonstrate use of global variables as state flags. if (req.url ~ "/close$") { var.global_set("open", "no"); } else if (req.url ~ "/open$") { var.global_set("open", "yes"); } if (var.global_get("open") != "yes") { return (synth(200, "We are currently closed, sorry!")); } } .. 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: set --- :: VOID set(PRIV_TASK, STRING key, STRING value) Set `key` to `value`. .. _func_get: get --- :: STRING get(PRIV_TASK, STRING) Get `key` with data type STRING. If stored `key` is not a STRING an empty string is returned. .. _func_global_set: global_set ---------- :: VOID global_set(STRING, STRING) .. _func_global_get: global_get ---------- :: STRING global_get(STRING) .. _func_set_int: set_int ------- :: VOID set_int(PRIV_TASK, STRING key, INT value) Set `key` to `value`. .. _func_get_int: get_int ------- :: INT get_int(PRIV_TASK, STRING key) Get `key` with data type INT. If stored `key` is not an INT zero will be returned. .. _func_set_string: set_string ---------- :: VOID set_string(PRIV_TASK, STRING key, STRING value) Identical to set(). .. _func_get_string: get_string ---------- :: STRING get_string(PRIV_TASK, STRING key) Identical to get(). .. _func_set_real: set_real -------- :: VOID set_real(PRIV_TASK, STRING key, REAL value) Set `key` to `value`. .. _func_get_real: get_real -------- :: REAL get_real(PRIV_TASK, STRING key) Get `key` with data type REAL. If stored `key` is not a REAL zero will be returned. .. _func_set_duration: set_duration ------------ :: VOID set_duration(PRIV_TASK, STRING key, DURATION value) Set `key` to `value`. .. _func_get_duration: get_duration ------------ :: DURATION get_duration(PRIV_TASK, STRING key) Get `key` with data type DURATION. If stored `key` is not a DURATION zero will be returned. .. _func_set_ip: set_ip ------ :: VOID set_ip(PRIV_TASK, STRING key, IP value) Set `key` to `value`. .. _func_get_ip: get_ip ------ :: IP get_ip(PRIV_TASK, STRING key) Get `key` with data type IP. If stored `key` is not an IP null will be returned. .. _func_clear: clear ----- :: VOID clear(PRIV_TASK) Clear all non-global variables. varnish-modules-0.12.1/docs/vmod_vsthrottle.rst000066400000000000000000000064671311124411200216430ustar00rootroot00000000000000.. .. NB: This file is machine generated, DO NOT EDIT! .. .. Edit vmod.vcc and run make instead .. .. role:: ref(emphasis) .. _vmod_vsthrottle(3): =============== vmod_vsthrottle =============== --------------- Throttling VMOD --------------- :Manual section: 3 SYNOPSIS ======== import vsthrottle [from "path"] ; DESCRIPTION =========== A Varnish vmod for rate-limiting traffic on a single Varnish server. Offers a simple interface for throttling traffic on a per-key basis to a specific request rate. Keys can be specified from any VCL string, e.g. based on client.ip, a specific cookie value, an API token, etc. 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'. 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. Memory usage is around 100 bytes per key tracked. .. _token bucket algorithm: http://en.wikipedia.org/wiki/Token_bucket .. vcl-start Example:: vcl 4.0; import vsthrottle; backend default { .host = "192.0.2.11"; .port = "8080"; } 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 return (synth(429, "Too Many Requests")); } # There is a quota per API key that must be fulfilled. if (vsthrottle.is_denied("apikey:" + req.http.Key, 30, 60s)) { return (synth(429, "Too Many Requests")); } # Only allow a few POST/PUTs per client. if (req.method == "POST" || req.method == "PUT") { if (vsthrottle.is_denied("rw" + client.identity, 2, 10s)) { return (synth(429, "Too Many Requests")); } } } .. vcl-end CONTENTS ======== * :ref:`func_is_denied` * :ref:`func_remaining` .. _func_is_denied: is_denied --------- :: BOOL is_denied(STRING key, INT limit, DURATION period) 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 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. Example :: sub vcl_recv { if (vsthrottle.is_denied(client.identity, 15, 10s)) { # Client has exceeded 15 reqs per 10s return (synth(429, "Too Many Requests")); } # ... } .. _func_remaining: remaining --------- :: INT remaining(STRING key, INT limit, DURATION period) Arguments: - key: A unique identifier to define what is being throttled - limit: How many requests in the specified period - period: The time period Description Get the current number of tokens for a given token bucket. This can be used to create a response header to inform clients of their current quota. Example :: sub vcl_deliver { set resp.http.X-RateLimit-Remaining = vsthrottle.remaining(client.identity, 15, 10s); } varnish-modules-0.12.1/docs/vmod_xkey.rst000066400000000000000000000070551311124411200203770ustar00rootroot00000000000000.. .. NB: This file is machine generated, DO NOT EDIT! .. .. Edit vmod.vcc and run make instead .. .. role:: ref(emphasis) .. _vmod_xkey(3): ========= vmod_xkey ========= ---------------------------------------- Surrogate keys support for Varnish Cache ---------------------------------------- :Manual section: 3 SYNOPSIS ======== import xkey [from "path"] ; DESCRIPTION =========== This vmod adds secondary hashes to objects, allowing fast purging on all objects with this hash key. You can use this to indicate relationships, a bit like a "tag". Then clear out all object that have this tag set. Two good use cases are news sites, where one might add all the stories mentioned on a particular page by article ID, letting each article referenced create an xkey header. 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`` 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. .. vcl-start VCL example:: vcl 4.0; import xkey; backend default { .host = "192.0.2.11"; .port = "8080"; } acl purgers { "203.0.113.0"/24; } sub vcl_recv { if (req.method == "PURGE") { if (client.ip !~ purgers) { return (synth(403, "Forbidden")); } set req.http.n-gone = xkey.purge(req.http.key); # or: set req.http.n-gone = xkey.softpurge(req.http.key) return (synth(200, "Invalidated "+req.http.n-gone+" objects")); } } .. vcl-end Example ------- On an e-commerce site we have the backend application issue an xkey header for every product that is referenced on that page. So the header for a certain page might look like this:: HTTP/1.1 OK Server: Apache/2.2.15 xkey: 8155054 xkey: 166412 xkey: 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 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:: GET / HTTP/1.1 Host: www.example.com xkey-purge: 166412 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. Varnish will find the objects and clear them out, responding with:: HTTP/1.1 200 Purged Date: Thu, 24 Apr 2014 17:08:28 GMT X-Varnish: 1990228115 Via: 1.1 Varnish The objects are now cleared. CONTENTS ======== * :ref:`func_purge` * :ref:`func_softpurge` .. _func_purge: purge ----- :: INT purge(STRING keys) Description Purges all objects hashed on any key found in the ``keys`` argument. Returns the number of objects that were purged. The ``keys`` may contain a list of space-separated ids. .. _func_softpurge: softpurge --------- :: INT softpurge(STRING keys) Description Performs a "soft purge" for all objects hashed on any key found in the ``keys`` argument. Returns the number of objects that were purged. 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. varnish-modules-0.12.1/m4/000077500000000000000000000000001311124411200152215ustar00rootroot00000000000000varnish-modules-0.12.1/m4/ax_code_coverage.m4000066400000000000000000000273101311124411200207430ustar00rootroot00000000000000# =========================================================================== # http://www.gnu.org/software/autoconf-archive/ax_code_coverage.html # =========================================================================== # # SYNOPSIS # # AX_CODE_COVERAGE() # # DESCRIPTION # # Defines CODE_COVERAGE_CPPFLAGS, CODE_COVERAGE_CFLAGS, # CODE_COVERAGE_CXXFLAGS and CODE_COVERAGE_LDFLAGS which should be # included in the CPPFLAGS, CFLAGS CXXFLAGS and LIBS/LDFLAGS variables of # every build target (program or library) which should be built with code # coverage support. Also defines CODE_COVERAGE_RULES which should be # substituted in your Makefile; and $enable_code_coverage which can be # used in subsequent configure output. CODE_COVERAGE_ENABLED is defined # and substituted, and corresponds to the value of the # --enable-code-coverage option, which defaults to being disabled. # # Test also for gcov program and create GCOV variable that could be # substituted. # # Note that all optimisation flags in CFLAGS must be disabled when code # coverage is enabled. # # Usage example: # # configure.ac: # # AX_CODE_COVERAGE # # Makefile.am: # # @CODE_COVERAGE_RULES@ # my_program_LIBS = ... $(CODE_COVERAGE_LDFLAGS) ... # my_program_CPPFLAGS = ... $(CODE_COVERAGE_CPPFLAGS) ... # my_program_CFLAGS = ... $(CODE_COVERAGE_CFLAGS) ... # my_program_CXXFLAGS = ... $(CODE_COVERAGE_CXXFLAGS) ... # # This results in a "check-code-coverage" rule being added to any # Makefile.am which includes "@CODE_COVERAGE_RULES@" (assuming the module # has been configured with --enable-code-coverage). Running `make # check-code-coverage` in that directory will run the module's test suite # (`make check`) and build a code coverage report detailing the code which # was touched, then print the URI for the report. # # This code was derived from Makefile.decl in GLib, originally licenced # under LGPLv2.1+. # # LICENSE # # Copyright (c) 2012, 2016 Philip Withnall # Copyright (c) 2012 Xan Lopez # Copyright (c) 2012 Christian Persch # Copyright (c) 2012 Paolo Borelli # Copyright (c) 2012 Dan Winship # Copyright (c) 2015 Bastien ROUCARIES # # This library is free software; you can redistribute it and/or modify it # under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation; either version 2.1 of the License, or (at # your option) any later version. # # This library is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser # General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with this program. If not, see . #serial 15 AC_DEFUN([AX_CODE_COVERAGE],[ dnl Check for --enable-code-coverage AC_REQUIRE([AC_PROG_SED]) # allow to override gcov location AC_ARG_WITH([gcov], [AS_HELP_STRING([--with-gcov[=GCOV]], [use given GCOV for coverage (GCOV=gcov).])], [_AX_CODE_COVERAGE_GCOV_PROG_WITH=$with_gcov], [_AX_CODE_COVERAGE_GCOV_PROG_WITH=gcov]) AC_MSG_CHECKING([whether to build with code coverage support]) AC_ARG_ENABLE([code-coverage], AS_HELP_STRING([--enable-code-coverage], [Whether to enable code coverage support]),, enable_code_coverage=no) AM_CONDITIONAL([CODE_COVERAGE_ENABLED], [test x$enable_code_coverage = xyes]) AC_SUBST([CODE_COVERAGE_ENABLED], [$enable_code_coverage]) AC_MSG_RESULT($enable_code_coverage) AS_IF([ test "$enable_code_coverage" = "yes" ], [ # check for gcov AC_CHECK_TOOL([GCOV], [$_AX_CODE_COVERAGE_GCOV_PROG_WITH], [:]) AS_IF([test "X$GCOV" = "X:"], [AC_MSG_ERROR([gcov is needed to do coverage])]) AC_SUBST([GCOV]) dnl Check if gcc is being used AS_IF([ test "$GCC" = "no" ], [ AC_MSG_ERROR([not compiling with gcc, which is required for gcov code coverage]) ]) # List of supported lcov versions. lcov_version_list="1.6 1.7 1.8 1.9 1.10 1.11 1.12" AC_CHECK_PROG([LCOV], [lcov], [lcov]) AC_CHECK_PROG([GENHTML], [genhtml], [genhtml]) AS_IF([ test "$LCOV" ], [ AC_CACHE_CHECK([for lcov version], ax_cv_lcov_version, [ ax_cv_lcov_version=invalid lcov_version=`$LCOV -v 2>/dev/null | $SED -e 's/^.* //'` for lcov_check_version in $lcov_version_list; do if test "$lcov_version" = "$lcov_check_version"; then ax_cv_lcov_version="$lcov_check_version (ok)" fi done ]) ], [ lcov_msg="To enable code coverage reporting you must have one of the following lcov versions installed: $lcov_version_list" AC_MSG_ERROR([$lcov_msg]) ]) case $ax_cv_lcov_version in ""|invalid[)] lcov_msg="You must have one of the following versions of lcov: $lcov_version_list (found: $lcov_version)." AC_MSG_ERROR([$lcov_msg]) LCOV="exit 0;" ;; esac AS_IF([ test -z "$GENHTML" ], [ AC_MSG_ERROR([Could not find genhtml from the lcov package]) ]) dnl Build the code coverage flags CODE_COVERAGE_CPPFLAGS="-DNDEBUG" CODE_COVERAGE_CFLAGS="-O0 -g -fprofile-arcs -ftest-coverage" CODE_COVERAGE_CXXFLAGS="-O0 -g -fprofile-arcs -ftest-coverage" CODE_COVERAGE_LDFLAGS="-lgcov" AC_SUBST([CODE_COVERAGE_CPPFLAGS]) AC_SUBST([CODE_COVERAGE_CFLAGS]) AC_SUBST([CODE_COVERAGE_CXXFLAGS]) AC_SUBST([CODE_COVERAGE_LDFLAGS]) ]) [CODE_COVERAGE_RULES=' # Code coverage # # Optional: # - CODE_COVERAGE_DIRECTORY: Top-level directory for code coverage reporting. # Multiple directories may be specified, separated by whitespace. # (Default: $(top_builddir)) # - CODE_COVERAGE_OUTPUT_FILE: Filename and path for the .info file generated # by lcov for code coverage. (Default: # $(PACKAGE_NAME)-$(PACKAGE_VERSION)-coverage.info) # - CODE_COVERAGE_OUTPUT_DIRECTORY: Directory for generated code coverage # reports to be created. (Default: # $(PACKAGE_NAME)-$(PACKAGE_VERSION)-coverage) # - CODE_COVERAGE_BRANCH_COVERAGE: Set to 1 to enforce branch coverage, # set to 0 to disable it and leave empty to stay with the default. # (Default: empty) # - CODE_COVERAGE_LCOV_SHOPTS_DEFAULT: Extra options shared between both lcov # instances. (Default: based on $CODE_COVERAGE_BRANCH_COVERAGE) # - CODE_COVERAGE_LCOV_SHOPTS: Extra options to shared between both lcov # instances. (Default: $CODE_COVERAGE_LCOV_SHOPTS_DEFAULT) # - CODE_COVERAGE_LCOV_OPTIONS_GCOVPATH: --gcov-tool pathtogcov # - CODE_COVERAGE_LCOV_OPTIONS_DEFAULT: Extra options to pass to the # collecting lcov instance. (Default: $CODE_COVERAGE_LCOV_OPTIONS_GCOVPATH) # - CODE_COVERAGE_LCOV_OPTIONS: Extra options to pass to the collecting lcov # instance. (Default: $CODE_COVERAGE_LCOV_OPTIONS_DEFAULT) # - CODE_COVERAGE_LCOV_RMOPTS_DEFAULT: Extra options to pass to the filtering # lcov instance. (Default: empty) # - CODE_COVERAGE_LCOV_RMOPTS: Extra options to pass to the filtering lcov # instance. (Default: $CODE_COVERAGE_LCOV_RMOPTS_DEFAULT) # - CODE_COVERAGE_GENHTML_OPTIONS_DEFAULT: Extra options to pass to the # genhtml instance. (Default: based on $CODE_COVERAGE_BRANCH_COVERAGE) # - CODE_COVERAGE_GENHTML_OPTIONS: Extra options to pass to the genhtml # instance. (Default: $CODE_COVERAGE_GENHTML_OPTIONS_DEFAULT) # - CODE_COVERAGE_IGNORE_PATTERN: Extra glob pattern of files to ignore # # The generated report will be titled using the $(PACKAGE_NAME) and # $(PACKAGE_VERSION). In order to add the current git hash to the title, # use the git-version-gen script, available online. # Optional variables CODE_COVERAGE_DIRECTORY ?= $(top_builddir) CODE_COVERAGE_OUTPUT_FILE ?= $(PACKAGE_NAME)-$(PACKAGE_VERSION)-coverage.info CODE_COVERAGE_OUTPUT_DIRECTORY ?= $(PACKAGE_NAME)-$(PACKAGE_VERSION)-coverage CODE_COVERAGE_BRANCH_COVERAGE ?= CODE_COVERAGE_LCOV_SHOPTS_DEFAULT ?= $(if $(CODE_COVERAGE_BRANCH_COVERAGE),\ --rc lcov_branch_coverage=$(CODE_COVERAGE_BRANCH_COVERAGE)) CODE_COVERAGE_LCOV_SHOPTS ?= $(CODE_COVERAGE_LCOV_SHOPTS_DEFAULT) CODE_COVERAGE_LCOV_OPTIONS_GCOVPATH ?= --gcov-tool "$(GCOV)" CODE_COVERAGE_LCOV_OPTIONS_DEFAULT ?= $(CODE_COVERAGE_LCOV_OPTIONS_GCOVPATH) CODE_COVERAGE_LCOV_OPTIONS ?= $(CODE_COVERAGE_LCOV_OPTIONS_DEFAULT) CODE_COVERAGE_LCOV_RMOPTS_DEFAULT ?= CODE_COVERAGE_LCOV_RMOPTS ?= $(CODE_COVERAGE_LCOV_RMOPTS_DEFAULT) CODE_COVERAGE_GENHTML_OPTIONS_DEFAULT ?=\ $(if $(CODE_COVERAGE_BRANCH_COVERAGE),\ --rc genhtml_branch_coverage=$(CODE_COVERAGE_BRANCH_COVERAGE)) CODE_COVERAGE_GENHTML_OPTIONS ?= $(CODE_COVERAGE_GENHTML_OPTIONS_DEFAULTS) CODE_COVERAGE_IGNORE_PATTERN ?= code_coverage_v_lcov_cap = $(code_coverage_v_lcov_cap_$(V)) code_coverage_v_lcov_cap_ = $(code_coverage_v_lcov_cap_$(AM_DEFAULT_VERBOSITY)) code_coverage_v_lcov_cap_0 = @echo " LCOV --capture"\ $(CODE_COVERAGE_OUTPUT_FILE); code_coverage_v_lcov_ign = $(code_coverage_v_lcov_ign_$(V)) code_coverage_v_lcov_ign_ = $(code_coverage_v_lcov_ign_$(AM_DEFAULT_VERBOSITY)) code_coverage_v_lcov_ign_0 = @echo " LCOV --remove /tmp/*"\ $(CODE_COVERAGE_IGNORE_PATTERN); code_coverage_v_genhtml = $(code_coverage_v_genhtml_$(V)) code_coverage_v_genhtml_ = $(code_coverage_v_genhtml_$(AM_DEFAULT_VERBOSITY)) code_coverage_v_genhtml_0 = @echo " GEN " $(CODE_COVERAGE_OUTPUT_DIRECTORY); code_coverage_quiet = $(code_coverage_quiet_$(V)) code_coverage_quiet_ = $(code_coverage_quiet_$(AM_DEFAULT_VERBOSITY)) code_coverage_quiet_0 = --quiet # sanitizes the test-name: replaces with underscores: dashes and dots code_coverage_sanitize = $(subst -,_,$(subst .,_,$(1))) # Use recursive makes in order to ignore errors during check check-code-coverage: ifeq ($(CODE_COVERAGE_ENABLED),yes) -$(A''M_V_at)$(MAKE) $(AM_MAKEFLAGS) -k check $(A''M_V_at)$(MAKE) $(AM_MAKEFLAGS) code-coverage-capture else @echo "Need to reconfigure with --enable-code-coverage" endif # Capture code coverage data code-coverage-capture: code-coverage-capture-hook ifeq ($(CODE_COVERAGE_ENABLED),yes) $(code_coverage_v_lcov_cap)$(LCOV) $(code_coverage_quiet) $(addprefix --directory ,$(CODE_COVERAGE_DIRECTORY)) --capture --output-file "$(CODE_COVERAGE_OUTPUT_FILE).tmp" --test-name "$(call code_coverage_sanitize,$(PACKAGE_NAME)-$(PACKAGE_VERSION))" --no-checksum --compat-libtool $(CODE_COVERAGE_LCOV_SHOPTS) $(CODE_COVERAGE_LCOV_OPTIONS) $(code_coverage_v_lcov_ign)$(LCOV) $(code_coverage_quiet) $(addprefix --directory ,$(CODE_COVERAGE_DIRECTORY)) --remove "$(CODE_COVERAGE_OUTPUT_FILE).tmp" "/tmp/*" $(CODE_COVERAGE_IGNORE_PATTERN) --output-file "$(CODE_COVERAGE_OUTPUT_FILE)" $(CODE_COVERAGE_LCOV_SHOPTS) $(CODE_COVERAGE_LCOV_RMOPTS) -@rm -f $(CODE_COVERAGE_OUTPUT_FILE).tmp $(code_coverage_v_genhtml)LANG=C $(GENHTML) $(code_coverage_quiet) $(addprefix --prefix ,$(CODE_COVERAGE_DIRECTORY)) --output-directory "$(CODE_COVERAGE_OUTPUT_DIRECTORY)" --title "$(PACKAGE_NAME)-$(PACKAGE_VERSION) Code Coverage" --legend --show-details "$(CODE_COVERAGE_OUTPUT_FILE)" $(CODE_COVERAGE_GENHTML_OPTIONS) @echo "file://$(abs_builddir)/$(CODE_COVERAGE_OUTPUT_DIRECTORY)/index.html" else @echo "Need to reconfigure with --enable-code-coverage" endif # Hook rule executed before code-coverage-capture, overridable by the user code-coverage-capture-hook: ifeq ($(CODE_COVERAGE_ENABLED),yes) clean: code-coverage-clean code-coverage-clean: -$(LCOV) --directory $(top_builddir) -z -rm -rf $(CODE_COVERAGE_OUTPUT_FILE) $(CODE_COVERAGE_OUTPUT_FILE).tmp $(CODE_COVERAGE_OUTPUT_DIRECTORY) -find . -name "*.gcda" -o -name "*.gcov" -delete endif GITIGNOREFILES ?= GITIGNOREFILES += $(CODE_COVERAGE_OUTPUT_FILE) $(CODE_COVERAGE_OUTPUT_DIRECTORY) A''M_DISTCHECK_CONFIGURE_FLAGS ?= A''M_DISTCHECK_CONFIGURE_FLAGS += --disable-code-coverage .PHONY: check-code-coverage code-coverage-capture code-coverage-capture-hook code-coverage-clean '] AC_SUBST([CODE_COVERAGE_RULES]) m4_ifdef([_AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE([CODE_COVERAGE_RULES])]) ]) varnish-modules-0.12.1/m4/ax_pthread.m4000066400000000000000000000261011311124411200176020ustar00rootroot00000000000000# =========================================================================== # http://www.gnu.org/software/autoconf-archive/ax_pthread.html # =========================================================================== # # SYNOPSIS # # AX_PTHREAD([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]]) # # DESCRIPTION # # This macro figures out how to build C programs using POSIX threads. It # sets the PTHREAD_LIBS output variable to the threads library and linker # flags, and the PTHREAD_CFLAGS output variable to any special C compiler # flags that are needed. (The user can also force certain compiler # flags/libs to be tested by setting these environment variables.) # # Also sets PTHREAD_CC to any special C compiler that is needed for # multi-threaded programs (defaults to the value of CC otherwise). (This # is necessary on AIX to use the special cc_r compiler alias.) # # NOTE: You are assumed to not only compile your program with these flags, # but also link it with them as well. e.g. you should link with # $PTHREAD_CC $CFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS $LIBS # # If you are only building threads programs, you may wish to use these # variables in your default LIBS, CFLAGS, and CC: # # LIBS="$PTHREAD_LIBS $LIBS" # CFLAGS="$CFLAGS $PTHREAD_CFLAGS" # CC="$PTHREAD_CC" # # In addition, if the PTHREAD_CREATE_JOINABLE thread-attribute constant # has a nonstandard name, defines PTHREAD_CREATE_JOINABLE to that name # (e.g. PTHREAD_CREATE_UNDETACHED on AIX). # # ACTION-IF-FOUND is a list of shell commands to run if a threads library # is found, and ACTION-IF-NOT-FOUND is a list of commands to run it if it # is not found. If ACTION-IF-FOUND is not specified, the default action # will define HAVE_PTHREAD. # # Please let the authors know if this macro fails on any platform, or if # you have any other suggestions or comments. This macro was based on work # by SGJ on autoconf scripts for FFTW (http://www.fftw.org/) (with help # from M. Frigo), as well as ac_pthread and hb_pthread macros posted by # Alejandro Forero Cuervo to the autoconf macro repository. We are also # grateful for the helpful feedback of numerous users. # # LICENSE # # Copyright (c) 2008 Steven G. Johnson # # This program is free software: you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by the # Free Software Foundation, either version 3 of the License, or (at your # option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General # Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program. If not, see . # # As a special exception, the respective Autoconf Macro's copyright owner # gives unlimited permission to copy, distribute and modify the configure # scripts that are the output of Autoconf when processing the Macro. You # need not follow the terms of the GNU General Public License when using # or distributing such scripts, even though portions of the text of the # Macro appear in them. The GNU General Public License (GPL) does govern # all other use of the material that constitutes the Autoconf Macro. # # This special exception to the GPL applies to versions of the Autoconf # Macro released by the Autoconf Archive. When you make and distribute a # modified version of the Autoconf Macro, you may extend this special # exception to the GPL to apply to your modified version as well. #serial 7 AU_ALIAS([ACX_PTHREAD], [AX_PTHREAD]) AC_DEFUN([AX_PTHREAD], [ AC_REQUIRE([AC_CANONICAL_HOST]) AC_LANG_SAVE AC_LANG_C ax_pthread_ok=no # We used to check for pthread.h first, but this fails if pthread.h # requires special compiler flags (e.g. on True64 or Sequent). # It gets checked for in the link test anyway. # First of all, check if the user has set any of the PTHREAD_LIBS, # etcetera environment variables, and if threads linking works using # them: if test x"$PTHREAD_LIBS$PTHREAD_CFLAGS" != x; then save_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $PTHREAD_CFLAGS" save_LIBS="$LIBS" LIBS="$PTHREAD_LIBS $LIBS" AC_MSG_CHECKING([for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS]) AC_TRY_LINK_FUNC(pthread_join, ax_pthread_ok=yes) AC_MSG_RESULT($ax_pthread_ok) if test x"$ax_pthread_ok" = xno; then PTHREAD_LIBS="" PTHREAD_CFLAGS="" fi LIBS="$save_LIBS" CFLAGS="$save_CFLAGS" fi # We must check for the threads library under a number of different # names; the ordering is very important because some systems # (e.g. DEC) have both -lpthread and -lpthreads, where one of the # libraries is broken (non-POSIX). # Create a list of thread flags to try. Items starting with a "-" are # C compiler flags, and other items are library names, except for "none" # which indicates that we try without any flags at all, and "pthread-config" # which is a program returning the flags for the Pth emulation library. ax_pthread_flags="pthreads none -Kthread -kthread lthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config" # The ordering *is* (sometimes) important. Some notes on the # individual items follow: # pthreads: AIX (must check this before -lpthread) # none: in case threads are in libc; should be tried before -Kthread and # other compiler flags to prevent continual compiler warnings # -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h) # -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able) # lthread: LinuxThreads port on FreeBSD (also preferred to -pthread) # -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads) # -pthreads: Solaris/gcc # -mthreads: Mingw32/gcc, Lynx/gcc # -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it # doesn't hurt to check since this sometimes defines pthreads too; # also defines -D_REENTRANT) # ... -mt is also the pthreads flag for HP/aCC # pthread: Linux, etcetera # --thread-safe: KAI C++ # pthread-config: use pthread-config program (for GNU Pth library) case "${host_cpu}-${host_os}" in *solaris*) # On Solaris (at least, for some versions), libc contains stubbed # (non-functional) versions of the pthreads routines, so link-based # tests will erroneously succeed. (We need to link with -pthreads/-mt/ # -lpthread.) (The stubs are missing pthread_cleanup_push, or rather # a function called by this macro, so we could check for that, but # who knows whether they'll stub that too in a future libc.) So, # we'll just look for -pthreads and -lpthread first: ax_pthread_flags="-pthreads pthread -mt -pthread $ax_pthread_flags" ;; *-darwin*) acx_pthread_flags="-pthread $acx_pthread_flags" ;; esac if test x"$ax_pthread_ok" = xno; then for flag in $ax_pthread_flags; do case $flag in none) AC_MSG_CHECKING([whether pthreads work without any flags]) ;; -*) AC_MSG_CHECKING([whether pthreads work with $flag]) PTHREAD_CFLAGS="$flag" ;; pthread-config) AC_CHECK_PROG(ax_pthread_config, pthread-config, yes, no) if test x"$ax_pthread_config" = xno; then continue; fi PTHREAD_CFLAGS="`pthread-config --cflags`" PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`" ;; *) AC_MSG_CHECKING([for the pthreads library -l$flag]) PTHREAD_LIBS="-l$flag" ;; esac save_LIBS="$LIBS" save_CFLAGS="$CFLAGS" LIBS="$PTHREAD_LIBS $LIBS" CFLAGS="$CFLAGS $PTHREAD_CFLAGS" # Check for various functions. We must include pthread.h, # since some functions may be macros. (On the Sequent, we # need a special flag -Kthread to make this header compile.) # We check for pthread_join because it is in -lpthread on IRIX # while pthread_create is in libc. We check for pthread_attr_init # due to DEC craziness with -lpthreads. We check for # pthread_cleanup_push because it is one of the few pthread # functions on Solaris that doesn't have a non-functional libc stub. # We try pthread_create on general principles. AC_TRY_LINK([#include static void routine(void* a) {a=0;} static void* start_routine(void* a) {return a;}], [pthread_t th; pthread_attr_t attr; pthread_join(th, 0); pthread_attr_init(&attr); pthread_cleanup_push(routine, 0); pthread_create(&th,0,start_routine,0); pthread_cleanup_pop(0); ], [ax_pthread_ok=yes]) LIBS="$save_LIBS" CFLAGS="$save_CFLAGS" AC_MSG_RESULT($ax_pthread_ok) if test "x$ax_pthread_ok" = xyes; then break; fi PTHREAD_LIBS="" PTHREAD_CFLAGS="" done fi # Various other checks: if test "x$ax_pthread_ok" = xyes; then save_LIBS="$LIBS" LIBS="$PTHREAD_LIBS $LIBS" save_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $PTHREAD_CFLAGS" # Detect AIX lossage: JOINABLE attribute is called UNDETACHED. AC_MSG_CHECKING([for joinable pthread attribute]) attr_name=unknown for attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do AC_TRY_LINK([#include ], [int attr=$attr; return attr;], [attr_name=$attr; break]) done AC_MSG_RESULT($attr_name) if test "$attr_name" != PTHREAD_CREATE_JOINABLE; then AC_DEFINE_UNQUOTED(PTHREAD_CREATE_JOINABLE, $attr_name, [Define to necessary symbol if this constant uses a non-standard name on your system.]) fi AC_MSG_CHECKING([if more special flags are required for pthreads]) flag=no case "${host_cpu}-${host_os}" in *-aix* | *-freebsd* | *-darwin*) flag="-D_THREAD_SAFE";; *solaris* | *-osf* | *-hpux*) flag="-D_REENTRANT";; esac AC_MSG_RESULT(${flag}) if test "x$flag" != xno; then PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS" fi LIBS="$save_LIBS" CFLAGS="$save_CFLAGS" # More AIX lossage: must compile with xlc_r or cc_r if test x"$GCC" != xyes; then AC_CHECK_PROGS(PTHREAD_CC, xlc_r cc_r, ${CC}) else PTHREAD_CC=$CC fi else PTHREAD_CC="$CC" fi AC_SUBST(PTHREAD_LIBS) AC_SUBST(PTHREAD_CFLAGS) AC_SUBST(PTHREAD_CC) # Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND: if test x"$ax_pthread_ok" = xyes; then ifelse([$1],,AC_DEFINE(HAVE_PTHREAD,1,[Define if you have POSIX threads libraries and header files.]),[$1]) : else ax_pthread_ok=no $2 fi AC_LANG_RESTORE ])dnl AX_PTHREAD varnish-modules-0.12.1/man/000077500000000000000000000000001311124411200154545ustar00rootroot00000000000000varnish-modules-0.12.1/man/vmod_vsthrottle.man.rst000066400000000000000000000007661311124411200222340ustar00rootroot00000000000000.. .. NB: This file is machine generated, DO NOT EDIT! .. .. Edit vmod.vcc and run make instead .. =============== vmod_vsthrottle =============== --------------- Throttling VMOD --------------- :Manual section: 3 SYNOPSIS ======== import vsthrottle [from "path"] ; $Event event_function CONTENTS ======== * BOOL is_denied(STRING, INT, DURATION) .. _func_is_denied: BOOL is_denied(STRING, INT, DURATION) ------------------------------------- Prototype BOOL is_denied(STRING, INT, DURATION) varnish-modules-0.12.1/man/vmod_vsthrottle.rst000066400000000000000000000007461311124411200214600ustar00rootroot00000000000000.. .. NB: This file is machine generated, DO NOT EDIT! .. .. Edit vmod.vcc and run make instead .. =============== vmod_vsthrottle =============== --------------- Throttling VMOD --------------- :Manual section: 3 SYNOPSIS ======== import vsthrottle [from "path"] ; $Event event_function CONTENTS ======== * :ref:`func_is_denied` .. _func_is_denied: BOOL is_denied(STRING, INT, DURATION) ------------------------------------- Prototype BOOL is_denied(STRING, INT, DURATION) varnish-modules-0.12.1/src/000077500000000000000000000000001311124411200154705ustar00rootroot00000000000000varnish-modules-0.12.1/src/Makefile.am000066400000000000000000000056011311124411200175260ustar00rootroot00000000000000AM_CFLAGS = $(VARNISHAPI_CFLAGS) $(CODE_COVERAGE_CFLAGS) -I$(top_srcdir)/src/foreign \ -Wall \ -Werror \ -Wall \ -Wno-format-y2k \ -Wstrict-prototypes \ -Wmissing-prototypes \ -Werror=missing-field-initializers \ -Wpointer-arith \ -Wreturn-type \ -Wwrite-strings \ -Wcast-qual \ -Wswitch \ -Wshadow \ -Wunused-parameter \ -Wcast-align \ -Wchar-subscripts \ -Wnested-externs \ -Wextra \ -Wno-sign-compare AM_CPPFLAGS = $(CODE_COVERAGE_CPPFLAGS) AM_LDFLAGS = $(VARNISHAPI_LIBS) $(VMOD_LDFLAGS) $(CODE_COVERAGE_LDFLAGS) vmod_LTLIBRARIES = \ libvmod_bodyaccess.la \ libvmod_cookie.la \ libvmod_header.la \ libvmod_saintmode.la \ libvmod_softpurge.la \ libvmod_tcp.la \ libvmod_var.la \ libvmod_vsthrottle.la \ libvmod_xkey.la dist_man_MANS = \ vmod_bodyaccess.3 \ vmod_cookie.3 \ vmod_header.3 \ vmod_saintmode.3 \ vmod_softpurge.3 \ vmod_tcp.3 \ vmod_var.3 \ vmod_vsthrottle.3 \ vmod_xkey.3 generated_docs = $(dist_man_MANS:.3=.rst) libvmod_bodyaccess_la_SOURCES = vmod_bodyaccess.c libvmod_cookie_la_SOURCES = vmod_cookie.c libvmod_header_la_SOURCES = vmod_header.c libvmod_vsthrottle_la_SOURCES = vmod_vsthrottle.c libvmod_saintmode_la_SOURCES = vmod_saintmode.c libvmod_softpurge_la_SOURCES = vmod_softpurge.c foreign/hash/hash_slinger.h libvmod_tcp_la_SOURCES = vmod_tcp.c libvmod_var_la_SOURCES = vmod_var.c libvmod_xkey_la_SOURCES = vmod_xkey.c nodist_libvmod_bodyaccess_la_SOURCES = vcc_bodyaccess_if.c vcc_bodyaccess_if.h nodist_libvmod_cookie_la_SOURCES = vcc_cookie_if.c vcc_cookie_if.h nodist_libvmod_header_la_SOURCES = vcc_header_if.c vcc_header_if.h nodist_libvmod_vsthrottle_la_SOURCES = vcc_vsthrottle_if.c vcc_vsthrottle_if.h nodist_libvmod_saintmode_la_SOURCES = vcc_saintmode_if.c vcc_saintmode_if.h nodist_libvmod_softpurge_la_SOURCES = vcc_softpurge_if.c vcc_softpurge_if.h nodist_libvmod_tcp_la_SOURCES = vcc_tcp_if.c vcc_tcp_if.h nodist_libvmod_var_la_SOURCES = vcc_var_if.c vcc_var_if.h nodist_libvmod_xkey_la_SOURCES = vcc_xkey_if.c vcc_xkey_if.h @BUILD_VMOD_BODYACCESS@ @BUILD_VMOD_COOKIE@ @BUILD_VMOD_HEADER@ @BUILD_VMOD_SAINTMODE@ @BUILD_VMOD_SOFTPURGE@ @BUILD_VMOD_TCP@ @BUILD_VMOD_VAR@ @BUILD_VMOD_VSTHROTTLE@ @BUILD_VMOD_XKEY@ rst-docs: $(generated_docs) cp $(generated_docs) $(top_srcdir)/docs/ doctest: for i in *.vcc; do \ ../test-vclsnippet $$i; \ rst2html --halt=2 --exit-status=2 $$i 1>/dev/null; \ done; AM_TESTS_ENVIRONMENT = \ PATH="$(VMOD_TEST_PATH)" \ LD_LIBRARY_PATH="$(VARNISH_LIBRARY_PATH)" TEST_EXTENSIONS = .vtc VTC_LOG_COMPILER = varnishtest -v AM_VTC_LOG_FLAGS = \ -Dvmod_builddir=$(abs_top_builddir)/src \ -Dvmod_directors="directors from \"$(VARNISHAPI_VMODDIR)/libvmod_directors.so\"" TESTS = $(VMOD_TESTS) EXTRA_DIST = \ foreign \ vtree.h \ $(VMOD_TESTS) \ vmod_bodyaccess.vcc \ vmod_cookie.vcc \ vmod_header.vcc \ vmod_saintmode.vcc \ vmod_softpurge.vcc \ vmod_tcp.vcc \ vmod_var.vcc \ vmod_vsthrottle.vcc \ vmod_xkey.vcc varnish-modules-0.12.1/src/foreign/000077500000000000000000000000001311124411200171215ustar00rootroot00000000000000varnish-modules-0.12.1/src/foreign/hash/000077500000000000000000000000001311124411200200445ustar00rootroot00000000000000varnish-modules-0.12.1/src/foreign/hash/hash_slinger.h000066400000000000000000000075221311124411200226710ustar00rootroot00000000000000/*- * Copyright (c) 2006 Verdens Gang AS * Copyright (c) 2006-2014 Varnish Software AS * All rights reserved. * * Author: Poul-Henning Kamp * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ struct sess; struct req; struct objcore; struct busyobj; struct worker; struct object; typedef void hash_init_f(int ac, char * const *av); typedef void hash_start_f(void); typedef void hash_prep_f(struct worker *); typedef struct objhead *hash_lookup_f(struct worker *wrk, const void *digest, struct objhead **nobj); typedef int hash_deref_f(struct objhead *obj); struct hash_slinger { unsigned magic; #define SLINGER_MAGIC 0x1b720cba const char *name; hash_init_f *init; hash_start_f *start; hash_prep_f *prep; hash_lookup_f *lookup; hash_deref_f *deref; }; enum lookup_e { HSH_MISS, HSH_BUSY, HSH_HIT, HSH_EXP, HSH_EXPBUSY }; /* cache_hash.c */ void HSH_Cleanup(struct worker *w); enum lookup_e HSH_Lookup(struct req *, struct objcore **, struct objcore **, int wait_for_busy, int always_insert); void HSH_Ref(struct objcore *o); void HSH_Init(const struct hash_slinger *slinger); void HSH_AddString(struct req *, void *ctx, const char *str); void HSH_Insert(struct worker *, const void *hash, struct objcore *); void HSH_Purge(struct worker *, struct objhead *, double ttl, double grace, double keep); void HSH_config(const char *h_arg); struct busyobj *HSH_RefBusy(const struct objcore *oc); struct objcore *HSH_Private(struct worker *wrk); struct objcore *HSH_NewObjCore(struct worker *wrk); #ifdef VARNISH_CACHE_CHILD struct waitinglist { unsigned magic; #define WAITINGLIST_MAGIC 0x063a477a VTAILQ_HEAD(, req) list; }; struct objhead { unsigned magic; #define OBJHEAD_MAGIC 0x1b96615d int refcnt; struct lock mtx; VTAILQ_HEAD(,objcore) objcs; uint8_t digest[DIGEST_LEN]; struct waitinglist *waitinglist; /*---------------------------------------------------- * The fields below are for the sole private use of * the hash implementation(s). */ union { struct { VTAILQ_ENTRY(objhead) u_n_hoh_list; void *u_n_hoh_head; } n; } _u; #define hoh_list _u.n.u_n_hoh_list #define hoh_head _u.n.u_n_hoh_head }; void HSH_Fail(struct objcore *); void HSH_Unbusy(struct worker *, struct objcore *); void HSH_Complete(struct objcore *oc); void HSH_DeleteObjHead(struct worker *, struct objhead *oh); int HSH_DerefObjHead(struct worker *, struct objhead **poh); int HSH_DerefObjCore(struct worker *, struct objcore **ocp); #endif /* VARNISH_CACHE_CHILD */ extern const struct hash_slinger hsl_slinger; extern const struct hash_slinger hcl_slinger; extern const struct hash_slinger hcb_slinger; varnish-modules-0.12.1/src/tests/000077500000000000000000000000001311124411200166325ustar00rootroot00000000000000varnish-modules-0.12.1/src/tests/bodyaccess/000077500000000000000000000000001311124411200207515ustar00rootroot00000000000000varnish-modules-0.12.1/src/tests/bodyaccess/test01.vtc000066400000000000000000000014551311124411200226140ustar00rootroot00000000000000varnishtest "Test len of req body" server s1 { rxreq txresp rxreq txresp rxreq txresp } -start varnish v1 -vcl+backend { import bodyaccess from "${vmod_builddir}/.libs/libvmod_bodyaccess.so"; import std; sub vcl_recv { std.cache_req_body(110B); set req.http.x-len = bodyaccess.len_req_body(); } sub vcl_deliver { set resp.http.x-len = req.http.x-len; } } -start client c1 { txreq -req POST -nolen -hdr "Transfer-encoding: chunked" chunked {BLAS} delay .2 chunkedlen 110 expect_close } -run client c1 { txreq -req POST -nolen -hdr "Transfer-encoding: chunked" chunked {BLAS} delay .2 chunkedlen 90 delay .2 chunked {FOO} delay .2 chunkedlen 0 rxresp expect resp.http.x-len == 97 } -run client c2 { txreq -req POST -body "BANANE" rxresp expect resp.http.x-len == 6 } -run varnish-modules-0.12.1/src/tests/bodyaccess/test02.vtc000066400000000000000000000020661311124411200226140ustar00rootroot00000000000000varnishtest "Test hash on req body" server s1 { rxreq expect req.bodylen == 6 txresp -hdr "OK: yes" rxreq txresp -hdr "OK: yes" rxreq txresp -hdr "OK: yes" } -start varnish v1 -vcl+backend { import bodyaccess from "${vmod_builddir}/.libs/libvmod_bodyaccess.so"; import std; sub vcl_recv { if(req.method == "POST" && req.http.Pass =="yes") { std.cache_req_body(110B); return (pass); } return(hash); } sub vcl_hash { bodyaccess.hash_req_body(); return (lookup); } sub vcl_hit { set req.http.x-cache = "HIT"; } sub vcl_miss { set req.http.x-cache = "MISS"; } sub vcl_deliver { set resp.http.x-cache = req.http.x-cache; } } -start client c1 { txreq -req POST -hdr "Pass: yes" -body "BANANE" rxresp expect resp.http.ok == "yes" txreq rxresp expect resp.http.ok == "yes" expect resp.http.x-cache == "MISS" } -run client c2 { txreq -req POST -nolen -hdr "Transfer-encoding: chunked" chunked {BANA} delay .2 chunked {NE} delay .2 chunkedlen 0 rxresp expect resp.http.ok == "yes" expect resp.http.x-cache == "HIT" } -run varnish-modules-0.12.1/src/tests/bodyaccess/test03.vtc000066400000000000000000000026671311124411200226240ustar00rootroot00000000000000varnishtest "test regex match on req body" server s1 { rxreq txresp rxreq txresp } -start # rematch_req_body is case sensitive. varnish v1 -vcl+backend { import bodyaccess from "${vmod_builddir}/.libs/libvmod_bodyaccess.so"; import std; sub vcl_recv { std.cache_req_body(10KB); set req.http.x-re1 = bodyaccess.rematch_req_body(".*"); set req.http.x-re2 = bodyaccess.rematch_req_body("aRNI"); set req.http.x-re3 = bodyaccess.rematch_req_body("a"); set req.http.x-re4 = bodyaccess.rematch_req_body("F"); } sub vcl_deliver { set resp.http.x-re1 = req.http.x-re1; set resp.http.x-re2 = req.http.x-re2; set resp.http.x-re3 = req.http.x-re3; set resp.http.x-re4 = req.http.x-re4; } } varnish v1 -cliok "param.set debug +syncvsl" varnish v1 -cliok "param.set fetch_chunksize 4k" varnish v1 -cliok "param.set pcre_match_limit 10000" varnish v1 -cliok "param.set pcre_match_limit_recursion 10000" varnish v1 -start client c1 { txreq -req "POST" -body {VarNISH} rxresp expect resp.http.x-re1 == 1 expect resp.http.x-re2 == 0 expect resp.http.x-re3 == 1 expect resp.http.x-re4 == 0 } -run client c2 { txreq -req POST -nolen -hdr "Transfer-encoding: chunked" chunked {a5e2e2e1c2e2} delay .2 chunkedlen 4090 delay .2 chunked {VARNISH} delay .2 chunked { } chunked {FOO} delay .2 chunkedlen 0 rxresp expect resp.http.x-re1 == 1 expect resp.http.x-re2 == 0 expect resp.http.x-re3 == 1 expect resp.http.x-re4 == 1 } -run varnish-modules-0.12.1/src/tests/bodyaccess/test04.vtc000066400000000000000000000027401311124411200226150ustar00rootroot00000000000000varnishtest "chunked test" server s1 { rxreq txresp -hdr "OK: yes" rxreq txresp -hdr "OK: yes" rxreq txresp -hdr "OK: yes" } -start varnish v1 -cli "param.set vsl_mask +Hash" varnish v1 -vcl+backend { import bodyaccess from "${vmod_builddir}/.libs/libvmod_bodyaccess.so"; import std; # On cache things that look like model api calls sub vcl_recv { # Set the method as a header so we can reuse it for the fetch set req.http.x-meth = req.method; if (req.method == "POST") { # Buffer the request body std.cache_req_body(10KB); return (hash); } return (pass); } sub vcl_hash { if (req.method == "POST") { bodyaccess.hash_req_body(); } } sub vcl_backend_fetch { # Reuse our method we kept as a header in recv set bereq.method = bereq.http.x-meth; return(fetch); } sub vcl_backend_response { # Cache everything for 5 minutes set beresp.ttl = 5m; } sub vcl_deliver { if (obj.hits > 0) { set resp.http.X-Cache = "HIT"; } else { set resp.http.X-Cache = "MISS"; } set resp.http.x-len = req.http.x-len; } } -start client c1 { txreq -req POST -body "BANANE" rxresp expect resp.http.ok == "yes" expect resp.http.X-Cache == "MISS" txreq rxresp expect resp.http.ok == "yes" expect resp.http.X-Cache == "MISS" } -run client c2 { txreq -req POST -nolen -hdr "Transfer-encoding: chunked" chunked {BANA} delay .2 chunked {NE} delay .2 chunkedlen 0 rxresp expect resp.http.ok == "yes" expect resp.http.X-Cache == "HIT" } -run varnish-modules-0.12.1/src/tests/bodyaccess/test05.vtc000066400000000000000000000011641311124411200226150ustar00rootroot00000000000000varnishtest "Test logging POST body" server s1 { rxreq txresp } -start varnish v1 -vcl+backend { import bodyaccess from "${vmod_builddir}/.libs/libvmod_bodyaccess.so"; import std; sub vcl_recv { std.cache_req_body(10KB); bodyaccess.log_req_body("PREFIX:", 3); } } -start logexpect l1 -v v1 { expect * * Debug "^PREFIX:123$" expect 1 = Debug "^PREFIX:456$" expect 1 = Debug "^PREFIX:789$" expect 1 = Debug "^PREFIX:0AB$" expect 1 = Debug "^PREFIX:CDE$" expect 1 = Debug "^PREFIX:F$" } -start client c1 { txreq -req POST -body "1234567890ABCDEF" rxresp expect resp.status == 200 } -run logexpect l1 -wait varnish-modules-0.12.1/src/tests/cookie/000077500000000000000000000000001311124411200201035ustar00rootroot00000000000000varnish-modules-0.12.1/src/tests/cookie/01-set-delete-produce.vtc000066400000000000000000000012571311124411200245360ustar00rootroot00000000000000varnishtest "Test cookie vmod" server s1 { rxreq txresp } -start varnish v1 -vcl+backend { import cookie from "${vmod_builddir}/.libs/libvmod_cookie.so"; sub vcl_recv { cookie.set("cookie1", "cookie1BAD"); cookie.set("cookie2", "cookie2value"); cookie.set("cookie3", "cookie3value"); cookie.set("cookie4", "cookie4value"); cookie.set("cookie1", "cookie1value"); cookie.delete("cookie2"); set req.http.X-foo = cookie.get_string(); } sub vcl_deliver { set resp.http.X-foo = req.http.X-foo; } } -start client c1 { txreq -url "/" rxresp expect resp.http.X-foo == "cookie1=cookie1value; cookie3=cookie3value; cookie4=cookie4value;" } client c1 -run varnish-modules-0.12.1/src/tests/cookie/02-clean.vtc000066400000000000000000000007711311124411200221270ustar00rootroot00000000000000varnishtest "Test cookie.clean()" server s1 { rxreq txresp } -start varnish v1 -vcl+backend { import cookie from "${vmod_builddir}/.libs/libvmod_cookie.so"; sub vcl_recv { cookie.clean(); cookie.set("cookie1", "cookie1BAD"); } sub vcl_deliver { set resp.http.X-foo = cookie.get_string(); cookie.clean(); set resp.http.X-bar = cookie.get_string(); } } -start client c1 { txreq rxresp expect resp.http.X-foo == "cookie1=cookie1BAD;" expect resp.http.X-bar == "" } -run varnish-modules-0.12.1/src/tests/cookie/03-null.vtc000066400000000000000000000012641311124411200220160ustar00rootroot00000000000000varnishtest "NULL/empty value checks" server s1 { rxreq txresp } -start varnish v1 -vcl+backend { import cookie from "${vmod_builddir}/.libs/libvmod_cookie.so"; sub vcl_recv { // nothing in here at this point. set req.http.x-foo = cookie.get_string(); // empty name cookie.set("", "foo"); // empty value cookie.set("cookie1", ""); // proper NULL cookie.set(req.http.null, "foo"); // double delete cookie.delete("cookie2"); cookie.delete("cookie2"); cookie.delete(req.http.null); } sub vcl_deliver { set resp.http.x-foo = cookie.get_string(); } } -start client c1 { txreq -url "/" rxresp expect resp.http.x-foo == "" } client c1 -run varnish-modules-0.12.1/src/tests/cookie/04-filter_except.vtc000066400000000000000000000020561311124411200237020ustar00rootroot00000000000000varnishtest "Test filter_expect in vmod-cookie" server s1 { rxreq txresp } -start varnish v1 -vcl+backend { import cookie from "${vmod_builddir}/.libs/libvmod_cookie.so"; sub vcl_deliver { cookie.set("biscuit", "standard"); cookie.set("bredela", "eggwhites"); cookie.set("chocolatechip", "verychippy"); cookie.set("empire", "jellytots"); cookie.filter_except("bredela,empire,baz"); set resp.http.X-foo = cookie.get_string(); # Test exotic admin-supplied filter strings. cookie.parse("bredela=eggwhites; empire=jellytots;"); cookie.filter_except(",,,,bredela, ,empire,baz,"); set resp.http.X-bar = cookie.get_string(); cookie.filter_except(req.http.none); set resp.http.X-baz = cookie.get_string(); } } -start client c1 { txreq -url "/" rxresp expect resp.http.X-foo == "bredela=eggwhites; empire=jellytots;" expect resp.http.X-bar == "bredela=eggwhites; empire=jellytots;" expect resp.http.X-baz == "" } client c1 -run varnish-modules-0.12.1/src/tests/cookie/05-filter.vtc000066400000000000000000000017251311124411200223350ustar00rootroot00000000000000varnishtest "Test filter in vmod-cookie" server s1 { rxreq txresp } -start varnish v1 -vcl+backend { import cookie from "${vmod_builddir}/.libs/libvmod_cookie.so"; sub vcl_deliver { cookie.set("biscuit", "standard"); cookie.set("bredela", "eggwhites"); cookie.set("chocolatechip", "verychippy"); cookie.set("empire", "jellytots"); cookie.filter("bredela,empire,baz"); set resp.http.X-foo = cookie.get_string(); # Test exotic admin-supplied filter strings. cookie.parse("bredela=eggwhites; empire=jellytots;"); cookie.filter(",,,,bredela, ,baz,"); set resp.http.X-bar = cookie.get_string(); cookie.parse("foo=bar; bar=baz;"); cookie.filter(req.http.none); set resp.http.X-baz = cookie.get_string(); } } -start client c1 { txreq -url "/" rxresp expect resp.http.X-foo == "biscuit=standard; chocolatechip=verychippy;" expect resp.http.X-bar == "empire=jellytots;" expect resp.http.X-baz == "foo=bar; bar=baz;" } client c1 -run varnish-modules-0.12.1/src/tests/cookie/05-get.vtc000066400000000000000000000011001311124411200216120ustar00rootroot00000000000000varnishtest "Test cookie vmod" server s1 { rxreq txresp } -start varnish v1 -vcl+backend { import cookie from "${vmod_builddir}/.libs/libvmod_cookie.so"; sub vcl_recv { cookie.set("cookie1", "cookie1value"); cookie.set("cookie2", "cookie2value"); } sub vcl_deliver { set resp.http.X-foo = cookie.get("cookie2"); # Make sure we handle this gracefully. set resp.http.X-bar = "" + cookie.get("non-existing"); } } -start client c1 { txreq -url "/" rxresp expect resp.http.X-foo == "cookie2value" expect resp.http.X-bar == "" } client c1 -run varnish-modules-0.12.1/src/tests/cookie/06-concurrent-query.vtc000066400000000000000000000015411311124411200243720ustar00rootroot00000000000000varnishtest "Test cookie vmod concurrency" barrier b1 cond 2 server s1 { rxreq barrier b1 sync expect req.url == "/s1" txresp } -start server s2 { rxreq expect req.url == "/s2" txresp } -start varnish v1 -vcl+backend { import cookie from "${vmod_builddir}/.libs/libvmod_cookie.so"; sub vcl_recv { cookie.parse(req.http.cookie); if (req.url == "/s1") { set req.backend_hint = s1; } else { set req.backend_hint = s2; } set req.http.x-val = cookie.get("a"); } sub vcl_deliver { set resp.http.x-val = req.http.x-val; } } -start client c1 { txreq -url "/s1" -hdr "Cookie: a=bar" rxresp expect resp.http.x-val == "bar" } -start client c2 { barrier b1 sync txreq -url "/s2" -hdr "Cookie: a=foo" rxresp expect resp.http.x-val == "foo" } -run client c1 -wait varnish-modules-0.12.1/src/tests/cookie/07-isset.vtc000066400000000000000000000012351311124411200221750ustar00rootroot00000000000000varnishtest "Test cookie.isset()" server s1 { rxreq txresp } -start varnish v1 -vcl+backend { import cookie from "${vmod_builddir}/.libs/libvmod_cookie.so"; sub vcl_recv { cookie.set("cookie1", "cookie1value"); } sub vcl_deliver { set resp.http.does = cookie.isset("cookie1"); set resp.http.does-not = cookie.isset("non-existent"); set resp.http.null = cookie.isset(""); set resp.http.null2 = cookie.isset(req.http.probably-null); } } -start client c1 { txreq -url "/" rxresp expect resp.http.does == "true" expect resp.http.does-not == "false" expect resp.http.null == "false" expect resp.http.null2 == "false" } client c1 -run varnish-modules-0.12.1/src/tests/cookie/08-overflow.vtc000066400000000000000000000213601311124411200227130ustar00rootroot00000000000000varnishtest "Test cookie vmod" server s1 { rxreq txresp rxreq txresp } -start varnish v1 -cliok "param.set workspace_client 64k" -vcl+backend { import cookie from "${vmod_builddir}/.libs/libvmod_cookie.so"; sub vcl_deliver { cookie.parse(req.http.cookie); set resp.http.cookiestring = cookie.get_string(); set resp.http.cookie1 = cookie.isset("cookie1"); } } -start client c1 { # Insanely long cookie name. txreq -url "/" -hdr "Cookie: phohx8aingie6Ide7peephie5paip6ang4thooh4ooquai8ohvah7eiqueeki8ooth7viequ0Tha5thewiSheih5jaimaiTahr1wi8WooQuoe7loothieThahweeneichoo8cufeelu3tie5cei1iShiemiezoofox6ahcaevaihocheungai2aeghaichaingee0EiGie3Ees5ujaem5uquahpieFeelei7Ohngei1afaTooph4aiquum1aewaidatheshuh1fohhoor0hoo6aeTeiy9xougahf3jeapooshuhoob5deiwareingahth7ahf2fafeer8Oobiewai3rei8ofejohjeiye4die8Na7ze6eixajauCairoth0lek8vioyuom6eih0egho2aingoo7coh1at3niochu6osahthi0ue1Luchae1eifeupiuwaa0raidiewaijese4oozee4eihie5shaBaoreacooNg8uW9eru9kigh3Feesi8iex2pu7ohfaiBiezael6ifaujiek4nutae1aalohchoteiPuaM2chiefaicaopheKohsh6Ho1wiephieseef1daj3Pahfie2ooch8shaing5baXeiLiep9lahfe9uDaxeehielais2eix3iekiew8aiter9Foo8noo2hae7ohdie1iB7hoop3podeengooSothoojui4AhXu5Nain8ohqu8if1ue5iTheimei5oghie9sheiv4Hejah1veixahcaixie8ahyieT8Phay4daeTei1aRiemae6oicheef2miiNuoxeil1kae2nea1roh9Rei1keiwaT2eoJaiNgie0den6aideif3uechaishaec4cai2eozieb9aeN9sai9ahnielohdaeGh2kaeleiteitai0ietoo7eiCha0baiW7dai0im1jul5OWijaLo2ohh3kooxu2oFah3loob6feiw7pie9eighu8ik4chae0Athou2fah5ieQuuic0Mu1j=cookievalue" rxresp expect resp.http.cookiestring == "phohx8aingie6Ide7peephie5paip6ang4thooh4ooquai8ohvah7eiqueeki8ooth7viequ0Tha5thewiSheih5jaimaiTahr1wi8WooQuoe7loothieThahweeneichoo8cufeelu3tie5cei1iShiemiezoofox6ahcaevaihocheungai2aeghaichaingee0EiGie3Ees5ujaem5uquahpieFeelei7Ohngei1afaTooph4aiquum1aewaidatheshuh1fohhoor0hoo6aeTeiy9xougahf3jeapooshuhoob5deiwareingahth7ahf2fafeer8Oobiewai3rei8ofejohjeiye4die8Na7ze6eixajauCairoth0lek8vioyuom6eih0egho2aingoo7coh1at3niochu6osahthi0ue1Luchae1eifeupiuwaa0raidiewaijese4oozee4eihie5shaBaoreacooNg8uW9eru9kigh3Feesi8iex2pu7ohfaiBiezael6ifaujiek4nutae1aalohchoteiPuaM2chiefaicaopheKohsh6Ho1wiephieseef1daj3Pahfie2ooch8shaing5baXeiLiep9lahfe9uDaxeehielais2eix3iekiew8aiter9Foo8noo2hae7ohdie1iB7hoop3podeengooSothoojui4AhXu5Nain8ohqu8if1ue5iTheimei5oghie9sheiv4Hejah1veixahcaixie8ahyieT8Phay4daeTei1aRiemae6oicheef2miiNuoxeil1kae2nea1roh9Rei1keiwaT2eoJaiNgie0den6aideif3uechaishaec4cai2eozieb9aeN9sai9ahnielohdaeGh2kaeleiteitai0ietoo7eiCha0baiW7dai0im1jul5OWijaLo2ohh3kooxu2oFah3loob6feiw7pie9eighu8ik4chae0Athou2fah5ieQuuic0Mu1j=cookievalue;" # Insane 6KB cookie value. txreq -url "/" -hdr "Cookie: cookie1=foobarbazfoobarbazphohx8aingie6Ide7peephie5paip6ang4thooh4ooquai8ohvah7eiqueeki8ooth7viequ0Tha5thewiSheih5jaimaiTahr1wi8WooQuoe7loothieThahweeneichoo8cufeelu3tie5cei1iShiemiezoofox6ahcaevaihocheungai2aeghaichaingee0EiGie3Ees5ujaem5uquahpieFeelei7Ohngei1afaTooph4aiquum1aewaidatheshuh1fohhoor0hoo6aeTeiy9xougahf3jeapooshuhoob5deiwareingahth7ahf2fafeer8Oobiewai3rei8ofejohjeiye4die8Na7ze6eixajauCairoth0lek8vioyuom6eih0egho2aingoo7coh1at3niochu6osahthi0ue1Luchae1eifeupiuwaa0raidiewaijese4oozee4eihie5shaBaoreacooNg8uW9eru9kigh3Feesi8iex2pu7ohfaiBiezael6ifaujiek4nutae1aalohchoteiPuaM2chiefaicaopheKohsh6Ho1wiephieseef1daj3Pahfie2ooch8shaing5baXeiLiep9lahfe9uDaxeehielais2eix3iekiew8aiter9Foo8noo2hae7ohdie1iB7hoop3podeengooSothoojui4AhXu5Nain8ohqu8if1ue5iTheimei5oghie9sheiv4Hejah1veixahcaixie8ahyieT8Phay4daeTei1aRiemae6oicheef2miiNuoxeil1kae2nea1roh9Rei1keiwaT2eoJaiNgie0den6aideif3uechaishaec4cai2eozieb9aeN9sai9ahnielohdaeGh2kaeleiteitai0ietoo7eiCha0baiW7dai0im1jul5OWijaLo2ohh3kooxu2oFah3loob6feiw7pie9eighu8ik4chae0Athou2fah5ieQuuic0Mu1jphohx8aingie6Ide7peephie5paip6ang4thooh4ooquai8ohvah7eiqueeki8ooth7viequ0Tha5thewiSheih5jaimaiTahr1wi8WooQuoe7loothieThahweeneichoo8cufeelu3tie5cei1iShiemiezoofox6ahcaevaihocheungai2aeghaichaingee0EiGie3Ees5ujaem5uquahpieFeelei7Ohngei1afaTooph4aiquum1aewaidatheshuh1fohhoor0hoo6aeTeiy9xougahf3jeapooshuhoob5deiwareingahth7ahf2fafeer8Oobiewai3rei8ofejohjeiye4die8Na7ze6eixajauCairoth0lek8vioyuom6eih0egho2aingoo7coh1at3niochu6osahthi0ue1Luchae1eifeupiuwaa0raidiewaijese4oozee4eihie5shaBaoreacooNg8uW9eru9kigh3Feesi8iex2pu7ohfaiBiezael6ifaujiek4nutae1aalohchoteiPuaM2chiefaicaopheKohsh6Ho1wiephieseef1daj3Pahfie2ooch8shaing5baXeiLiep9lahfe9uDaxeehielais2eix3iekiew8aiter9Foo8noo2hae7ohdie1iB7hoop3podeengooSothoojui4AhXu5Nain8ohqu8if1ue5iTheimei5oghie9sheiv4Hejah1veixahcaixie8ahyieT8Phay4daeTei1aRiemae6oicheef2miiNuoxeil1kae2nea1roh9Rei1keiwaT2eoJaiNgie0den6aideif3uechaishaec4cai2eozieb9aeN9sai9ahnielohdaeGh2kaeleiteitai0ietoo7eiCha0baiW7dai0im1jul5OWijaLo2ohh3kooxu2oFah3loob6feiw7pie9eighu8ik4chae0Athou2fah5ieQuuic0Mu1jphohx8aingie6Ide7peephie5paip6ang4thooh4ooquai8ohvah7eiqueeki8ooth7viequ0Tha5thewiSheih5jaimaiTahr1wi8WooQuoe7loothieThahweeneichoo8cufeelu3tie5cei1iShiemiezoofox6ahcaevaihocheungai2aeghaichaingee0EiGie3Ees5ujaem5uquahpieFeelei7Ohngei1afaTooph4aiquum1aewaidatheshuh1fohhoor0hoo6aeTeiy9xougahf3jeapooshuhoob5deiwareingahth7ahf2fafeer8Oobiewai3rei8ofejohjeiye4die8Na7ze6eixajauCairoth0lek8vioyuom6eih0egho2aingoo7coh1at3niochu6osahthi0ue1Luchae1eifeupiuwaa0raidiewaijese4oozee4eihie5shaBaoreacooNg8uW9eru9kigh3Feesi8iex2pu7ohfaiBiezael6ifaujiek4nutae1aalohchoteiPuaM2chiefaicaopheKohsh6Ho1wiephieseef1daj3Pahfie2ooch8shaing5baXeiLiep9lahfe9uDaxeehielais2eix3iekiew8aiter9Foo8noo2hae7ohdie1iB7hoop3podeengooSothoojui4AhXu5Nain8ohqu8if1ue5iTheimei5oghie9sheiv4Hejah1veixahcaixie8ahyieT8Phay4daeTei1aRiemae6oicheef2miiNuoxeil1kae2nea1roh9Rei1keiwaT2eoJaiNgie0den6aideif3uechaishaec4cai2eozieb9aeN9sai9ahnielohdaeGh2kaeleiteitai0ietoo7eiCha0baiW7dai0im1jul5OWijaLo2ohh3kooxu2oFah3loob6feiw7pie9eighu8ik4chae0Athou2fah5ieQuuic0Mu1jphohx8aingie6Ide7peephie5paip6ang4thooh4ooquai8ohvah7eiqueeki8ooth7viequ0Tha5thewiSheih5jaimaiTahr1wi8WooQuoe7loothieThahweeneichoo8cufeelu3tie5cei1iShiemiezoofox6ahcaevaihocheungai2aeghaichaingee0EiGie3Ees5ujaem5uquahpieFeelei7Ohngei1afaTooph4aiquum1aewaidatheshuh1fohhoor0hoo6aeTeiy9xougahf3jeapooshuhoob5deiwareingahth7ahf2fafeer8Oobiewai3rei8ofejohjeiye4die8Na7ze6eixajauCairoth0lek8vioyuom6eih0egho2aingoo7coh1at3niochu6osahthi0ue1Luchae1eifeupiuwaa0raidiewaijese4oozee4eihie5shaBaoreacooNg8uW9eru9kigh3Feesi8iex2pu7ohfaiBiezael6ifaujiek4nutae1aalohchoteiPuaM2chiefaicaopheKohsh6Ho1wiephieseef1daj3Pahfie2ooch8shaing5baXeiLiep9lahfe9uDaxeehielais2eix3iekiew8aiter9Foo8noo2hae7ohdie1iB7hoop3podeengooSothoojui4AhXu5Nain8ohqu8if1ue5iTheimei5oghie9sheiv4Hejah1veixahcaixie8ahyieT8Phay4daeTei1aRiemae6oicheef2miiNuoxeil1kae2nea1roh9Rei1keiwaT2eoJaiNgie0den6aideif3uechaishaec4cai2eozieb9aeN9sai9ahnielohdaeGh2kaeleiteitai0ietoo7eiCha0baiW7dai0im1jul5OWijaLo2ohh3kooxu2oFah3loob6feiw7pie9eighu8ik4chae0Athou2fah5ieQuuic0Mu1jphohx8aingie6Ide7peephie5paip6ang4thooh4ooquai8ohvah7eiqueeki8ooth7viequ0Tha5thewiSheih5jaimaiTahr1wi8WooQuoe7loothieThahweeneichoo8cufeelu3tie5cei1iShiemiezoofox6ahcaevaihocheungai2aeghaichaingee0EiGie3Ees5ujaem5uquahpieFeelei7Ohngei1afaTooph4aiquum1aewaidatheshuh1fohhoor0hoo6aeTeiy9xougahf3jeapooshuhoob5deiwareingahth7ahf2fafeer8Oobiewai3rei8ofejohjeiye4die8Na7ze6eixajauCairoth0lek8vioyuom6eih0egho2aingoo7coh1at3niochu6osahthi0ue1Luchae1eifeupiuwaa0raidiewaijese4oozee4eihie5shaBaoreacooNg8uW9eru9kigh3Feesi8iex2pu7ohfaiBiezael6ifaujiek4nutae1aalohchoteiPuaM2chiefaicaopheKohsh6Ho1wiephieseef1daj3Pahfie2ooch8shaing5baXeiLiep9lahfe9uDaxeehielais2eix3iekiew8aiter9Foo8noo2hae7ohdie1iB7hoop3podeengooSothoojui4AhXu5Nain8ohqu8if1ue5iTheimei5oghie9sheiv4Hejah1veixahcaixie8ahyieT8Phay4daeTei1aRiemae6oicheef2miiNuoxeil1kae2nea1roh9Rei1keiwaT2eoJaiNgie0den6aideif3uechaishaec4cai2eozieb9aeN9sai9ahnielohdaeGh2kaeleiteitai0ietoo7eiCha0baiW7dai0im1jul5OWijaLo2ohh3kooxu2oFah3loob6feiw7pie9eighu8ik4chae0Athou2fah5ieQuuic0Mu1jphohx8aingie6Ide7peephie5paip6ang4thooh4ooquai8ohvah7eiqueeki8ooth7viequ0Tha5thewiSheih5jaimaiTahr1wi8WooQuoe7loothieThahweeneichoo8cufeelu3tie5cei1iShiemiezoofox6ahcaevaihocheungai2aeghaichaingee0EiGie3Ees5ujaem5uquahpieFeelei7Ohngei1afaTooph4aiquum1aewaidatheshuh1fohhoor0hoo6aeTeiy9xougahf3jeapooshuhoob5deiwareingahth7ahf2fafeer8Oobiewai3rei8ofejohjeiye4die8Na7ze6eixajauCairoth0lek8vioyuom6eih0egho2aingoo7coh1at3niochu6osahthi0ue1Luchae1eifeupiuwaa0raidiewaijese4oozee4eihie5shaBaoreacooNg8uW9eru9kigh3Feesi8iex2pu7ohfaiBiezael6ifaujiek4nutae1aalohchoteiPuaM2chiefaicaopheKohsh6Ho1wiephieseef1daj3Pahfie2ooch8shaing5baXeiLiep9lahfe9uDaxeehielais2eix3iekiew8aiter9Foo8noo2hae7ohdie1iB7hoop3podeengooSothoojui4AhXu5Nain8ohqu8if1ue5iTheimei5oghie9sheiv4Hejah1veixahcaixie8ahyieT8Phay4daeTei1aRiemae6oicheef2miiNuoxeil1kae2nea1roh9Rei1keiwaT2eoJaiNgie0den6aideif3uechaishaec4cai2eozieb9aeN9sai9ahnielohdaeGh2kaeleiteitai0ietoo7eiCha0baiW7dai0im1jul5OWijaLo2ohh3kooxu2oFah3loob6feiw7pie9eighu8ik4chae0Athou2fah5ieQuuic0Mu1j;" rxresp # We support long cookie values, should be fine. expect resp.http.cookie1 == "true" } client c1 -run varnish-modules-0.12.1/src/tests/cookie/10-parser.vtc000066400000000000000000000033051311124411200223340ustar00rootroot00000000000000varnishtest "Test cookie parser" server s1 -repeat 6 { rxreq txresp } -start varnish v1 -vcl+backend { import cookie from "${vmod_builddir}/.libs/libvmod_cookie.so"; sub vcl_deliver { cookie.parse(req.http.cookie); set resp.http.X-foo = cookie.get_string(); } } -start client c1 { txreq -hdr "Cookie: csrftoken=0e0c3616e41a6bd561b72b7f5fc1128f; sessionid=a707505310ddf259bb290d3ca63fc560" rxresp expect resp.http.X-foo == "csrftoken=0e0c3616e41a6bd561b72b7f5fc1128f; sessionid=a707505310ddf259bb290d3ca63fc560;" } -run client c2 { txreq -hdr "Cookie: __utmc=253898641; __utma=253898641.654622101.1372224466.1372224466.1372224466.1; __utmb=253898641.44.10.1372224466; __utmz=253898641.1372224466.1.1.utmcsr=google|utmccn=(organic)|utmcmd=organic|utmctr=index%2Bof%2Bccnp%2Bpdf" rxresp expect resp.http.X-foo == "__utmc=253898641; __utma=253898641.654622101.1372224466.1372224466.1372224466.1; __utmb=253898641.44.10.1372224466; __utmz=253898641.1372224466.1.1.utmcsr=google|utmccn=(organic)|utmcmd=organic|utmctr=index%2Bof%2Bccnp%2Bpdf;" } -run client c3 { txreq -hdr "Cookie: " rxresp expect resp.http.X-foo == "" } -run # An empty cookie is a non-existing cookie for us. client c4 { txreq -hdr "Cookie: emptycookie=" rxresp expect resp.http.X-foo == "" } -run # A single cookie should also work. client c5 { txreq -hdr "Cookie: cookie1=foobarbaz" rxresp expect resp.http.X-foo == "cookie1=foobarbaz;" } -run # Don't overflow the buffer with an edge case client c6 { txreq -hdr "Cookie: csrftoken=0e0c3616e41a6bd561b72b7f5fc1128a;=" -hdr "X-Not-Cookie: sessionid=a707505310ddf259bb290d3ca63fc561" rxresp expect resp.http.X-foo == "csrftoken=0e0c3616e41a6bd561b72b7f5fc1128a;" } -run varnish-modules-0.12.1/src/tests/cookie/30-format_1123.vtc000066400000000000000000000005751311124411200230060ustar00rootroot00000000000000varnishtest "Test rfc1123 string formatting function" server s1 { rxreq txresp } -start varnish v1 -vcl+backend { import cookie from "${vmod_builddir}/.libs/libvmod_cookie.so"; sub vcl_deliver { set resp.http.x-date = cookie.format_rfc1123(now, 1d); } } -start client c1 { txreq -url "/" rxresp expect resp.http.date != } -run varnish-modules-0.12.1/src/tests/cookie/r28.vtc000066400000000000000000000120141311124411200212320ustar00rootroot00000000000000varnishtest "Test issue #28" server s1 { rxreq txresp } -repeat 40 -start varnish v1 -vcl+backend { import cookie from "${vmod_builddir}/.libs/libvmod_cookie.so"; sub vcl_recv { cookie.parse(req.http.cookie); set req.http.X-foo = cookie.get_string(); } sub vcl_deliver { set resp.http.X-foo = req.http.x-foo; } } -start client c1 { txreq -hdr "Cookie: csrftoken=0e0c3616e41a6bd561b72b7f5fc1128f; sessionid=a707505310ddf259bb290d3ca63fc560" rxresp expect resp.http.X-foo == "csrftoken=0e0c3616e41a6bd561b72b7f5fc1128f; sessionid=a707505310ddf259bb290d3ca63fc560;" txreq -hdr "Cookie: csrftoken=0e0c3616e41a6bd561b72b7f5fc1128f; sessionid=a707505310ddf259bb290d3ca63fc560" rxresp expect resp.http.X-foo == "csrftoken=0e0c3616e41a6bd561b72b7f5fc1128f; sessionid=a707505310ddf259bb290d3ca63fc560;" txreq -hdr "Cookie: csrftoken=0e0c3616e41a6bd561b72b7f5fc1128f; sessionid=a707505310ddf259bb290d3ca63fc560" rxresp expect resp.http.X-foo == "csrftoken=0e0c3616e41a6bd561b72b7f5fc1128f; sessionid=a707505310ddf259bb290d3ca63fc560;" txreq -hdr "Cookie: csrftoken=0e0c3616e41a6bd561b72b7f5fc1128f; sessionid=a707505310ddf259bb290d3ca63fc560" rxresp expect resp.http.X-foo == "csrftoken=0e0c3616e41a6bd561b72b7f5fc1128f; sessionid=a707505310ddf259bb290d3ca63fc560;" txreq -hdr "Cookie: csrftoken=0e0c3616e41a6bd561b72b7f5fc1128f; sessionid=a707505310ddf259bb290d3ca63fc560" rxresp expect resp.http.X-foo == "csrftoken=0e0c3616e41a6bd561b72b7f5fc1128f; sessionid=a707505310ddf259bb290d3ca63fc560;" txreq -hdr "Cookie: csrftoken=0e0c3616e41a6bd561b72b7f5fc1128f; sessionid=a707505310ddf259bb290d3ca63fc560" rxresp expect resp.http.X-foo == "csrftoken=0e0c3616e41a6bd561b72b7f5fc1128f; sessionid=a707505310ddf259bb290d3ca63fc560;" txreq -hdr "Cookie: csrftoken=0e0c3616e41a6bd561b72b7f5fc1128f; sessionid=a707505310ddf259bb290d3ca63fc560" rxresp expect resp.http.X-foo == "csrftoken=0e0c3616e41a6bd561b72b7f5fc1128f; sessionid=a707505310ddf259bb290d3ca63fc560;" txreq -hdr "Cookie: csrftoken=0e0c3616e41a6bd561b72b7f5fc1128f; sessionid=a707505310ddf259bb290d3ca63fc560" rxresp expect resp.http.X-foo == "csrftoken=0e0c3616e41a6bd561b72b7f5fc1128f; sessionid=a707505310ddf259bb290d3ca63fc560;" txreq -hdr "Cookie: csrftoken=0e0c3616e41a6bd561b72b7f5fc1128f; sessionid=a707505310ddf259bb290d3ca63fc560" rxresp expect resp.http.X-foo == "csrftoken=0e0c3616e41a6bd561b72b7f5fc1128f; sessionid=a707505310ddf259bb290d3ca63fc560;" txreq -hdr "Cookie: csrftoken=0e0c3616e41a6bd561b72b7f5fc1128f; sessionid=a707505310ddf259bb290d3ca63fc560" rxresp expect resp.http.X-foo == "csrftoken=0e0c3616e41a6bd561b72b7f5fc1128f; sessionid=a707505310ddf259bb290d3ca63fc560;" } client c2 { txreq -hdr "Cookie: csrftoken=0e0c3616e41a6bd561b72b7f5fc1128f; sessionid=a707505310ddf259bb290d3ca63fc560" rxresp expect resp.http.X-foo == "csrftoken=0e0c3616e41a6bd561b72b7f5fc1128f; sessionid=a707505310ddf259bb290d3ca63fc560;" txreq -hdr "Cookie: csrftoken=0e0c3616e41a6bd561b72b7f5fc1128f; sessionid=a707505310ddf259bb290d3ca63fc560" rxresp expect resp.http.X-foo == "csrftoken=0e0c3616e41a6bd561b72b7f5fc1128f; sessionid=a707505310ddf259bb290d3ca63fc560;" txreq -hdr "Cookie: csrftoken=0e0c3616e41a6bd561b72b7f5fc1128f; sessionid=a707505310ddf259bb290d3ca63fc560" rxresp expect resp.http.X-foo == "csrftoken=0e0c3616e41a6bd561b72b7f5fc1128f; sessionid=a707505310ddf259bb290d3ca63fc560;" txreq -hdr "Cookie: csrftoken=0e0c3616e41a6bd561b72b7f5fc1128f; sessionid=a707505310ddf259bb290d3ca63fc560" rxresp expect resp.http.X-foo == "csrftoken=0e0c3616e41a6bd561b72b7f5fc1128f; sessionid=a707505310ddf259bb290d3ca63fc560;" txreq -hdr "Cookie: csrftoken=0e0c3616e41a6bd561b72b7f5fc1128f; sessionid=a707505310ddf259bb290d3ca63fc560" rxresp expect resp.http.X-foo == "csrftoken=0e0c3616e41a6bd561b72b7f5fc1128f; sessionid=a707505310ddf259bb290d3ca63fc560;" txreq -hdr "Cookie: csrftoken=0e0c3616e41a6bd561b72b7f5fc1128f; sessionid=a707505310ddf259bb290d3ca63fc560" rxresp expect resp.http.X-foo == "csrftoken=0e0c3616e41a6bd561b72b7f5fc1128f; sessionid=a707505310ddf259bb290d3ca63fc560;" txreq -hdr "Cookie: csrftoken=0e0c3616e41a6bd561b72b7f5fc1128f; sessionid=a707505310ddf259bb290d3ca63fc560" rxresp expect resp.http.X-foo == "csrftoken=0e0c3616e41a6bd561b72b7f5fc1128f; sessionid=a707505310ddf259bb290d3ca63fc560;" txreq -hdr "Cookie: csrftoken=0e0c3616e41a6bd561b72b7f5fc1128f; sessionid=a707505310ddf259bb290d3ca63fc560" rxresp expect resp.http.X-foo == "csrftoken=0e0c3616e41a6bd561b72b7f5fc1128f; sessionid=a707505310ddf259bb290d3ca63fc560;" txreq -hdr "Cookie: csrftoken=0e0c3616e41a6bd561b72b7f5fc1128f; sessionid=a707505310ddf259bb290d3ca63fc560" rxresp expect resp.http.X-foo == "csrftoken=0e0c3616e41a6bd561b72b7f5fc1128f; sessionid=a707505310ddf259bb290d3ca63fc560;" txreq -hdr "Cookie: csrftoken=0e0c3616e41a6bd561b72b7f5fc1128f; sessionid=a707505310ddf259bb290d3ca63fc560" rxresp expect resp.http.X-foo == "csrftoken=0e0c3616e41a6bd561b72b7f5fc1128f; sessionid=a707505310ddf259bb290d3ca63fc560;" } client c1 -repeat 2 -run client c2 -repeat 2 -run varnish-modules-0.12.1/src/tests/header/000077500000000000000000000000001311124411200200625ustar00rootroot00000000000000varnish-modules-0.12.1/src/tests/header/append.vtc000066400000000000000000000013501311124411200220460ustar00rootroot00000000000000varnishtest "Header-vmod: Test appending" server s1 { timeout 10 rxreq expect req.url == "/foo" txresp -status 200 -hdr "foo: 1" -hdr "foo: 2" rxreq expect req.url == "/bar" txresp -status 200 -hdr "foo: 1" -hdr "foo: 2" } -start varnish v1 -vcl+backend { import header from "${vmod_builddir}/.libs/libvmod_header.so"; sub vcl_backend_response { if (bereq.url == "/foo") { set beresp.http.foo = "null"; } elsif ( bereq.url == "/bar") { header.append(beresp.http.foo, "blatti"); } return(deliver); } } -start client c1 { txreq -url "/foo" rxresp expect resp.status == 200 expect resp.http.foo == "null" } -run client c2 { txreq -url "/bar" rxresp expect resp.status == 200 expect resp.http.foo == 1 } -run varnish-modules-0.12.1/src/tests/header/copy.vtc000066400000000000000000000024141311124411200215530ustar00rootroot00000000000000 # This assumes that: # 1. Append works # 2. The first header is the only tested in varnishtest when multiple # copies are present varnishtest "Header-vmod: Test copying" server s1 { timeout 10 rxreq expect req.url == "/foo" txresp -status 200 -hdr "foo: one=1" -hdr "foo: two=2" -hdr "foo: three=3" } -start varnish v1 -vcl+backend { import header from "${vmod_builddir}/.libs/libvmod_header.so"; sub vcl_backend_response { if (bereq.url == "/foo") { header.copy(beresp.http.foo,beresp.http.bar); set beresp.http.x-one = header.get(beresp.http.bar,"one"); set beresp.http.x-two = header.get(beresp.http.bar,"two"); set beresp.http.x-three = header.get(beresp.http.bar,"three"); set beresp.http.y-one = header.get(beresp.http.foo,"one"); set beresp.http.y-two = header.get(beresp.http.foo,"two"); set beresp.http.y-three = header.get(beresp.http.foo,"three"); } return(deliver); } } -start client c1 { txreq -url "/foo" rxresp expect resp.status == 200 expect resp.http.bar == "one=1" expect resp.http.foo == "one=1" expect resp.http.x-one == "one=1" expect resp.http.x-two == "two=2" expect resp.http.x-three == "three=3" expect resp.http.y-one == "one=1" expect resp.http.y-two == "two=2" expect resp.http.y-three == "three=3" } -run varnish-modules-0.12.1/src/tests/header/get.vtc000066400000000000000000000015341311124411200213620ustar00rootroot00000000000000 varnishtest "Header-vmod: Test fetching" server s1 { timeout 10 rxreq expect req.url == "/" txresp -status 200 -hdr "foo: sillycookie=blah" -hdr "foo: realcookie=YAI" rxreq expect req.url == "/two" txresp -status 200 -hdr "foo: sillycookie=blah" -hdr "foo: realcookie=YAI" } -start varnish v1 -vcl+backend { import header from "${vmod_builddir}/.libs/libvmod_header.so"; sub vcl_backend_response { if (bereq.url == "/") { set beresp.http.xusr = header.get(beresp.http.foo,"realcookie="); } elsif (bereq.url == "/two") { set beresp.http.xusr = header.get(beresp.http.foo,"^realcookie="); } return(deliver); } } -start client c1 { txreq -url "/" rxresp expect resp.status == 200 expect resp.http.xusr == "realcookie=YAI" txreq -url "/two" rxresp expect resp.status == 200 expect resp.http.xusr == "realcookie=YAI" } -run varnish-modules-0.12.1/src/tests/header/import.vtc000066400000000000000000000005261311124411200221150ustar00rootroot00000000000000varnishtest "Header-vmod: Test basic import" server s1 { timeout 10 rxreq expect req.url == "/" txresp -status 200 -hdr "foo: 1" } -start varnish v1 -vcl+backend { import header from "${vmod_builddir}/.libs/libvmod_header.so"; } -start client c1 { txreq -url "/" rxresp expect resp.status == 200 expect resp.http.foo == 1 } -run varnish-modules-0.12.1/src/tests/header/keep-others.vtc000066400000000000000000000023771311124411200230370ustar00rootroot00000000000000# Bug #1 ! # The remove-function was removing somewhat vigorously. # This check ensures that other headers are kept intact. varnishtest "Header-vmod: Ensure other headers remain untouched" server s1 { timeout 10 rxreq expect req.url == "/foo" txresp -status 200 -hdr "bar: xxx=y" -hdr "foo: one=1" -hdr "foo: two=2" -hdr "foo: three=3" rxreq expect req.url == "/foo" txresp -status 200 -hdr "Content-Type: text/html" -hdr "foo: one=1" -hdr "foo: two=2" -hdr "foo: three=3" rxreq expect req.url == "/foo" txresp -status 200 -hdr "foo: one=1" -hdr "foo: two=2" -hdr "foo: three=3" -hdr "Content-Type: text/html" } -start varnish v1 -vcl+backend { import header from "${vmod_builddir}/.libs/libvmod_header.so"; sub vcl_backend_response { if (bereq.url == "/foo") { header.remove(beresp.http.foo,"one=1"); } set beresp.uncacheable = true; } } -start client c1 { txreq -url "/foo" rxresp expect resp.status == 200 expect resp.http.foo == "two=2" expect resp.http.bar == "xxx=y" txreq -url "/foo" rxresp expect resp.status == 200 expect resp.http.foo == "two=2" expect resp.http.Content-Type == "text/html" txreq -url "/foo" rxresp expect resp.status == 200 expect resp.http.foo == "two=2" expect resp.http.Content-Type == "text/html" } -run varnish-modules-0.12.1/src/tests/header/remove.vtc000066400000000000000000000047461311124411200221100ustar00rootroot00000000000000 varnishtest "Header-vmod: Test removing" server s1 { timeout 10 rxreq expect req.url == "/foo" txresp -status 200 -hdr "foo: notok=1" -hdr "foo: ok2k" -hdr "foo: notok=2" rxreq expect req.url == "/bar" txresp -status 200 -hdr "foo: notok=1" -hdr "foo: ok2k" -hdr "foo: notok=2" rxreq expect req.url == "/nothing" txresp -status 200 -hdr "foo: notok=1" -hdr "foo: ok2k" -hdr "foo: notok=2" rxreq expect req.url == "/blatti1" txresp -status 200 -hdr "foo: notok=1" -hdr "foo:notok=3" -hdr "foo: ok2k" -hdr "foo: notok=2" rxreq expect req.url == "/blatti2" txresp -status 200 -hdr "foo: notok=1" -hdr "foo: ok2k" -hdr "foo:notok=3" -hdr "foo: notok=2" rxreq expect req.url == "/blatti3" txresp -status 200 -hdr "set-cookie: analytics=1" -hdr "set-cookie: funcookie=ok2k" -hdr "set-cookie: uglycookie=3" -hdr "set-cookie: notok=2" } -start varnish v1 -vcl+backend { import header from "${vmod_builddir}/.libs/libvmod_header.so"; sub vcl_backend_response { if (bereq.url == "/foo") { header.remove(beresp.http.foo,"notok"); } if (bereq.url == "/nothing") { header.remove(beresp.http.foo,"."); } if (bereq.url == "/blatti1") { header.remove(beresp.http.foo,"^ no.ok=.$"); } if (bereq.url == "/blatti2") { header.remove(beresp.http.foo,"^no.ok=.$"); } if (bereq.url == "/blatti3") { header.remove(beresp.http.set-cookie,"^(?!(funcookie=))"); } if (beresp.http.foo) { set beresp.http.foo-exists = "yes"; } else { set beresp.http.foo-exists = "no"; } return(deliver); } } -start client c1 { # Remove one, "notok". Assumes ok2k (second in line) is tested next txreq -url "/foo" rxresp expect resp.status == 200 expect resp.http.foo == "ok2k" # Remove nothing txreq -url "/bar" rxresp expect resp.status == 200 expect resp.http.foo == "notok=1" # Remove everything (confusing names, huh?) txreq -url "/nothing" rxresp expect resp.status == 200 expect resp.http.foo-exists == "no" # Remove with regex - fail due to whitespace in the regex txreq -url "/blatti1" rxresp expect resp.status == 200 expect resp.http.foo-exists == "yes" expect resp.http.foo == "notok=1" # Remove with regex - work (remove leading whitespace) txreq -url "/blatti2" rxresp expect resp.status == 200 expect resp.http.foo-exists == "yes" expect resp.http.foo == "ok2k" # Remove everything except fun-cookie txreq -url "/blatti3" rxresp expect resp.status == 200 expect resp.http.foo-exists == "no" expect resp.http.set-cookie == "funcookie=ok2k" } -run varnish-modules-0.12.1/src/tests/header/some-data.vtc000066400000000000000000000026031311124411200224530ustar00rootroot00000000000000# Got reports of no data being sent in return after 3.0.1, this test-case # tries to do some simple verification, though it fails to detect the # problem. varnishtest "Header-vmod: Send some data too" server s1 { timeout 10 rxreq expect req.url == "/foo" txresp -status 200 -hdr "bar: xxx=y" -hdr "foo: one=1" -hdr "foo: two=2" -hdr "foo: three=3" -body "Hello" rxreq expect req.url == "/foo" txresp -status 200 -hdr "Content-Type: text/html" -hdr "foo: one=1" -hdr "foo: two=2" -hdr "foo: three=3" -body "Hello" rxreq expect req.url == "/foo" txresp -status 200 -hdr "foo: one=1" -hdr "foo: two=2" -hdr "foo: three=3" -hdr "Content-Type: text/html" -body "Hello" } -start varnish v1 -vcl+backend { import header from "${vmod_builddir}/.libs/libvmod_header.so"; sub vcl_backend_response { if (bereq.url == "/foo") { header.remove(beresp.http.foo,"one=1"); } set beresp.uncacheable = true; } } -start client c1 { txreq -url "/foo" rxresp expect resp.status == 200 expect resp.http.foo == "two=2" expect resp.http.bar == "xxx=y" expect resp.bodylen == 5 txreq -url "/foo" rxresp expect resp.status == 200 expect resp.http.foo == "two=2" expect resp.http.Content-Type == "text/html" expect resp.bodylen == 5 txreq -url "/foo" rxresp expect resp.status == 200 expect resp.http.foo == "two=2" expect resp.http.Content-Type == "text/html" expect resp.bodylen == 5 } -run varnish-modules-0.12.1/src/tests/saintmode/000077500000000000000000000000001311124411200206155ustar00rootroot00000000000000varnish-modules-0.12.1/src/tests/saintmode/test01.vtc000066400000000000000000000034511311124411200224560ustar00rootroot00000000000000varnishtest "Test saintmode vmod" server s1 { rxreq expect req.url == "/a" txresp -hdr "Saint: yes" accept rxreq expect req.url == "/b" txresp rxreq expect req.url == "/a" txresp rxreq expect req.url == "/sick-0" txresp -hdr "Saint: yes" accept rxreq expect req.url == "/sick-1" txresp -hdr "Saint: yes" accept rxreq expect req.url == "/sick-2" txresp -hdr "Saint: yes" } -start varnish v1 -vcl+backend { import saintmode from "${vmod_builddir}/.libs/libvmod_saintmode.so"; sub vcl_init { new sm = saintmode.saintmode(s1, 3); } sub vcl_recv { return (pass); } sub vcl_backend_fetch { set bereq.backend = sm.backend(); } sub vcl_backend_response { if (beresp.http.Saint == "yes") { saintmode.blacklist(0.5s); return (retry); # -> 503 } } sub vcl_deliver { set resp.http.count = sm.blacklist_count(); } } -start client c1 { txreq -url "/a" rxresp expect resp.status == 503 expect resp.http.count == 1 # Should fail outright due to /a being on troublelist txreq -url "/a" rxresp expect resp.http.count == 1 expect resp.status == 503 txreq -url "/b" rxresp expect resp.http.count == 1 expect resp.status == 200 delay 0.5 txreq -url "/a" rxresp expect resp.http.count == 0 expect resp.status == 200 txreq -url "/sick-0" rxresp expect resp.http.count == 1 expect resp.status == 503 txreq -url "/sick-1" rxresp expect resp.http.count == 2 expect resp.status == 503 txreq -url "/sick-2" rxresp expect resp.http.count == 3 expect resp.status == 503 # saintmode threshold reached - should report 503 for all reqs txreq -url "/foo" rxresp expect resp.http.count == 3 expect resp.status == 503 } client c1 -run varnish-modules-0.12.1/src/tests/saintmode/test02.vtc000066400000000000000000000023461311124411200224610ustar00rootroot00000000000000varnishtest "Test saintmode vmod" server s1 { rxreq expect req.url == "/a" txresp -hdr "Saint: yes" -hdr "Backend: s1" accept rxreq expect req.url == "/b" txresp -hdr "Backend: s1" accept rxreq expect req.url == "/a" txresp -hdr "Backend: s1" } -start server s2 { rxreq expect req.url == "/a" txresp -hdr "Backend: s2" } -start varnish v1 -vcl+backend { import saintmode from "${vmod_builddir}/.libs/libvmod_saintmode.so"; import directors; sub vcl_init { new sm1 = saintmode.saintmode(s1, 5); new sm2 = saintmode.saintmode(s2, 5); new fb = directors.fallback(); fb.add_backend(sm1.backend()); fb.add_backend(sm2.backend()); } sub vcl_recv { return (pass); } sub vcl_backend_fetch { set bereq.backend = fb.backend(); } sub vcl_backend_response { if (beresp.http.Saint == "yes") { saintmode.blacklist(0.5s); return (retry); } } } -start client c1 { txreq -url "/a" rxresp expect resp.http.backend == "s2" expect resp.status == 200 txreq -url "/b" rxresp expect resp.http.backend == "s1" expect resp.status == 200 delay 0.5 txreq -url "/a" rxresp expect resp.http.backend == "s1" } client c1 -run varnish-modules-0.12.1/src/tests/saintmode/test03.vtc000066400000000000000000000010051311124411200224510ustar00rootroot00000000000000varnishtest "varnish-modules issue #54" server s1 { rxreq txresp rxreq txresp } -start varnish v1 -vcl+backend { import saintmode from "${vmod_builddir}/.libs/libvmod_saintmode.so"; import directors; sub vcl_recv { return (pass); } sub vcl_backend_response { # this should be a no-op when saintmode is not configured saintmode.blacklist(0.5s); } } -start client c1 { txreq rxresp expect resp.status == 200 txreq rxresp expect resp.status == 200 } client c1 -run varnish-modules-0.12.1/src/tests/softpurge/000077500000000000000000000000001311124411200206505ustar00rootroot00000000000000varnish-modules-0.12.1/src/tests/softpurge/01-load.vtc000066400000000000000000000004201311124411200225170ustar00rootroot00000000000000varnishtest "Test softpurge vmod" server s1 { rxreq txresp } -start varnish v1 -vcl+backend { import softpurge from "${vmod_builddir}/.libs/libvmod_softpurge.so"; } -start client c1 { txreq -url "/" rxresp expect resp.status == 200 } client c1 -run varnish-modules-0.12.1/src/tests/softpurge/02-simple-hit.vtc000066400000000000000000000006241311124411200236620ustar00rootroot00000000000000varnishtest "Test softpurge vmod" server s1 { rxreq txresp rxreq txresp } -start varnish v1 -vcl+backend { import softpurge from "${vmod_builddir}/.libs/libvmod_softpurge.so"; sub vcl_hit { softpurge.softpurge(); return (synth(760, "purged ok")); } } -start client c1 { txreq -url "/" rxresp txreq -url "/" rxresp expect resp.status == 760 } client c1 -run varnish-modules-0.12.1/src/tests/softpurge/03-simple-miss.vtc000066400000000000000000000005241311124411200240510ustar00rootroot00000000000000varnishtest "Test softpurge vmod" server s1 { rxreq txresp } -start varnish v1 -vcl+backend { import softpurge from "${vmod_builddir}/.libs/libvmod_softpurge.so"; sub vcl_miss { softpurge.softpurge(); return (synth(760, "purged ok")); } } -start client c1 { txreq -url "/" rxresp expect resp.status == 760 } -run varnish-modules-0.12.1/src/tests/softpurge/10-softpurge-hit.vtc000066400000000000000000000037171311124411200244140ustar00rootroot00000000000000varnishtest "Test softpurge vmod" server s1 { rxreq txresp # the purge restart target, avoiding vcl_error error which closes connection. rxreq txresp rxreq txresp rxreq txresp } -start varnish v1 -vcl+backend { import softpurge from "${vmod_builddir}/.libs/libvmod_softpurge.so"; backend b1 { .host = "${s1_addr}"; .port = "${s1_port}"; } backend b2 { .host = "127.0.0.1"; .port = "12313"; # Keep this for better results in having the backend marked as sick. ;-) .probe = { .initial = 0; } } sub vcl_recv { set req.http.grace = "none"; set req.backend_hint = b1; if (req.http.x-sick-please) { set req.backend_hint = b2; } if (req.method == "PURGE") { return(hash); } } sub vcl_backend_response { set beresp.ttl = 1s; set beresp.grace = 1h; } sub vcl_miss { set req.http.x-via = "miss"; } sub vcl_hit { set req.http.x-via = "hit"; if (req.method == "PURGE") { softpurge.softpurge(); set req.url = "/exists"; set req.method = "GET"; set req.http.x-via = "hit"; set req.http.x-msg = "softpurge"; return(deliver); } } sub vcl_deliver { set resp.http.grace = req.http.grace; set resp.http.backend = req.http.backend; set resp.http.x-via = req.http.x-via; set resp.http.x-msg = req.http.x-msg; } } -start # Warm the cache client c1 { txreq -url "/" rxresp txreq -url "/" rxresp expect resp.status == 200 expect resp.http.x-via == "hit" } -run # Softpurge the object. client c1 { txreq -req "PURGE" -url "/" rxresp expect resp.http.x-via == "hit" expect resp.http.x-msg == "softpurge" } -run # Pretend backend is dead, should give a graced hit. client c2 { txreq -url "/" -hdr "x-sick-please: yes" rxresp expect obj.grace != 0 expect resp.http.x-via == "hit" } -run # check that it has been purged for normal requests. # this will be a graced hit. client c3 { txreq -url "/" rxresp expect resp.http.x-via == "hit" expect resp.status == 200 expect obj.grace != 0 } -run varnish-modules-0.12.1/src/tests/softpurge/11-really-purged.vtc000066400000000000000000000032541311124411200243650ustar00rootroot00000000000000varnishtest "Test softpurge vmod" server s1 { rxreq txresp # the purge restart target, avoiding vcl_error error which closes connection. rxreq txresp rxreq txresp } -start varnish v1 -vcl+backend { import softpurge from "${vmod_builddir}/.libs/libvmod_softpurge.so"; backend b1 { .host = "${s1_addr}"; .port = "${s1_port}"; } sub vcl_recv { set req.backend_hint = b1; if (req.method == "PURGE") { return(hash); } } sub vcl_backend_response { set beresp.ttl = 1m; set beresp.grace = 10m; } sub vcl_miss { set req.http.x-via = "miss"; } sub vcl_hit { set req.http.x-via = "hit"; if (req.method == "PURGE") { softpurge.softpurge(); set req.url = "/exists"; set req.method = "GET"; unset req.http.x-via; return(restart); } set req.http.x-ttl = obj.ttl; set req.http.x-grace = obj.grace; } sub vcl_deliver { set resp.http.x-object-hits = obj.hits; set resp.http.x-via = req.http.x-via; set resp.http.x-object-ttl = req.http.x-ttl; set resp.http.x-object-grace = req.http.x-grace; } } -start client c1 { txreq -url "/" rxresp expect resp.http.x-via == "miss" txreq -url "/" rxresp expect resp.status == 200 expect resp.http.x-via == "hit" delay 2 # run the softpurge txreq -req "PURGE" -url "/" rxresp # check that grace is used. txreq -url "/" rxresp expect resp.http.x-via == "hit" expect resp.status == 200 expect resp.http.x-object-hits != 0 expect resp.http.x-object-ttl <= 0 # check that the obj was refreshed via a bgfetch in prev req txreq -url "/" rxresp expect resp.http.x-via == "hit" expect resp.status == 200 #This can be used in master branch #expect resp.http.x-object-hits == 0 } -run varnish-modules-0.12.1/src/tests/softpurge/12-softpurge-variants.vtc000066400000000000000000000034221311124411200254520ustar00rootroot00000000000000varnishtest "Test softpurge vmod" server s1 { rxreq txresp -hdr "Vary: User-Agent" -bodylen 1 } -repeat 6 -start varnish v1 -vcl+backend { import softpurge from "${vmod_builddir}/.libs/libvmod_softpurge.so"; #import std; sub vcl_recv { if (req.method == "PURGE") { return(hash); } } sub vcl_backend_response { set beresp.ttl = 1m; set beresp.grace = 10m; } sub vcl_miss { #std.log("inside vcl_miss"); set req.http.x-via = "miss"; } sub vcl_hit { #std.log("inside vcl_hit"); set req.http.x-via = "hit"; if (req.method == "PURGE") { softpurge.softpurge(); set req.url = "/exists"; set req.method = "GET"; unset req.http.x-via; return(restart); } # There is no greater-than in the test language. (?) if (obj.ttl < 0s) { set req.http.x-ttl-negative = true; } else { set req.http.x-ttl-negative = false; } set req.http.x-ttl = obj.ttl; set req.http.x-grace = obj.grace; } sub vcl_deliver { set resp.http.x-object-hits = obj.hits; set resp.http.x-object-ttl = req.http.x-ttl; set resp.http.x-object-grace = req.http.x-grace; set resp.http.x-via = req.http.x-via; set resp.http.x-ttl-negative = req.http.x-ttl-negative; #std.log("YY: ttl is " + req.http.x-ttl); #std.log("YY: grace is " + req.http.x-grace); } } -start client c1 { txreq -url "/" -hdr "User-Agent: one" rxresp expect resp.status == 200 expect resp.http.x-via == "miss" txreq -url "/" -hdr "User-Agent: two" rxresp expect resp.status == 200 expect resp.http.x-via == "miss" txreq -req "PURGE" -url "/" -hdr "User-Agent: one" rxresp txreq -url "/" -hdr "User-Agent: one" rxresp expect resp.status == 200 expect resp.http.x-via == "hit" txreq -url "/" -hdr "User-Agent: two" rxresp expect resp.status == 200 expect resp.http.x-via == "hit" } -run varnish-modules-0.12.1/src/tests/softpurge/13-softpurge-variants-sickbackend.vtc000066400000000000000000000050631311124411200277150ustar00rootroot00000000000000varnishtest "Test softpurge vmod" server s1 { rxreq txresp -hdr "Vary: User-Agent" } -repeat 6 -start varnish v1 -vcl+backend { import softpurge from "${vmod_builddir}/.libs/libvmod_softpurge.so"; #import std; backend b1 { .host = "${s1_addr}"; .port = "${s1_port}"; } # always sick backend b2 { .host = "127.0.0.1"; .port = "12313"; # Keep this for better results in having the backend marked as sick. ;-) .probe = { .initial = 0; } } sub purge_and_restart { softpurge.softpurge(); set req.url = "/exists"; set req.method = "GET"; unset req.http.x-via; return(restart); } sub vcl_recv { set req.backend_hint = b1; if (req.http.x-sick-please) { set req.backend_hint = b2; } if (req.method == "PURGE") { return(hash); } } sub vcl_backend_response { set beresp.ttl = 1s; set beresp.grace = 10m; #set req.http.x-ttl = beresp.ttl; #set req.http.x-grace = beresp.grace; } sub vcl_miss { #std.log("inside vcl_miss"); set req.http.x-via = "miss"; if (req.method == "PURGE") { call purge_and_restart; } } sub vcl_hit { #std.log("inside vcl_hit"); set req.http.x-via = "hit"; if (req.method == "PURGE") { call purge_and_restart; } # There is no greater-than in the test language. (?) if (obj.ttl < 0s) { set req.http.x-ttl-negative = true; } else { set req.http.x-ttl-negative = false; } set req.http.x-ttl = obj.ttl; set req.http.x-grace = obj.grace; } sub vcl_deliver { set resp.http.x-object-hits = obj.hits; set resp.http.x-object-ttl = req.http.x-ttl; set resp.http.x-object-grace = req.http.x-grace; set resp.http.x-via = req.http.x-via; set resp.http.x-ttl-negative = req.http.x-ttl-negative; } } -start client c1 { txreq -url "/" -hdr "User-Agent: one" rxresp expect resp.status == 200 expect resp.http.x-via == "miss" txreq -url "/" -hdr "User-Agent: two" rxresp expect resp.status == 200 expect resp.http.x-via == "miss" txreq -url "/" -hdr "User-Agent: one" rxresp expect resp.status == 200 expect resp.http.x-via == "hit" txreq -req "PURGE" -url "/" -hdr "User-Agent: one" rxresp # if the backend is sick, is the content still there? txreq -url "/" -hdr "User-Agent: two" -hdr "x-sick-please: yes" rxresp expect resp.http.x-via == "hit" expect resp.status == 200 # if the backend is sick, is the content still there? txreq -url "/" -hdr "User-Agent: one" -hdr "x-sick-please: yes" rxresp expect resp.http.x-via == "hit" expect resp.status == 200 txreq -url "/" -hdr "User-Agent: one" rxresp expect resp.http.x-via == "hit" expect resp.status == 200 } -run varnish-modules-0.12.1/src/tests/softpurge/14-mind-the-refcounters.vtc000066400000000000000000000026721311124411200256610ustar00rootroot00000000000000varnishtest "Test softpurge vmod" server s1 { rxreq txresp accept rxreq txresp } -start varnish v1 -vcl+backend { import softpurge from "${vmod_builddir}/.libs/libvmod_softpurge.so"; backend b1 { .host = "${s1_addr}"; .port = "${s1_port}"; } sub vcl_recv { set req.backend_hint = b1; if (req.method == "PURGE") { return(hash); } if (req.method == "SOFTPURGE") { return(hash); } } sub vcl_backend_response { set beresp.ttl = 1s; set beresp.grace = 1s; } sub vcl_miss { set req.http.x-via = "miss"; } sub vcl_hit { set req.http.x-via = "hit"; if (req.method == "SOFTPURGE") { set req.http.x-via = "hit"; set req.http.msg = "Softpurged"; softpurge.softpurge(); } if (req.method == "PURGE") { set req.http.msg = "Purged"; } } sub vcl_deliver { set resp.http.x-object-hits = obj.hits; set resp.http.x-via = req.http.x-via; set resp.http.msg = req.http.msg; } } -start client c1 { txreq -url "/" rxresp txreq -url "/" rxresp expect resp.status == 200 expect resp.http.x-via == "hit" # run the softpurge txreq -req "SOFTPURGE" -url "/" rxresp } -run client c2 { # check that it was a miss + successful fetch. txreq -url "/" rxresp expect resp.http.x-via == "hit" expect resp.status == 200 expect resp.http.x-object-hits != 0 txreq -req "PURGE" -url "/" rxresp expect resp.status == 200 expect resp.http.msg == "Purged" } -run delay 3 varnish v1 -expect n_object == 0 varnish-modules-0.12.1/src/tests/tcp/000077500000000000000000000000001311124411200174205ustar00rootroot00000000000000varnish-modules-0.12.1/src/tests/tcp/01-dumpinfo.vtc000066400000000000000000000004471311124411200222020ustar00rootroot00000000000000varnishtest "Test tcp vmod" server s1 { rxreq txresp } -start varnish v1 -vcl+backend { import tcp from "${vmod_builddir}/.libs/libvmod_tcp.so"; sub vcl_deliver { tcp.dump_info(); } } -start client c1 { txreq -url "/" rxresp expect resp.status == 200 } client c1 -run varnish-modules-0.12.1/src/tests/tcp/02-congestion.vtc000066400000000000000000000007701311124411200225310ustar00rootroot00000000000000varnishtest "Test tcp vmod" server s1 { rxreq txresp } -start varnish v1 -vcl+backend { import tcp from "${vmod_builddir}/.libs/libvmod_tcp.so"; sub vcl_deliver { set resp.http.a = tcp.congestion_algorithm("reno"); set resp.http.b = tcp.congestion_algorithm("slartibartfast"); set resp.http.c = tcp.congestion_algorithm("cubic"); } } -start client c1 { txreq -url "/" rxresp expect resp.http.a == "0" expect resp.http.b == "-1" expect resp.http.c == "0" } client c1 -run varnish-modules-0.12.1/src/tests/tcp/03-read-tcpinfo.vtc000066400000000000000000000005451311124411200227350ustar00rootroot00000000000000varnishtest "Test tcp vmod" server s1 { rxreq txresp } -start varnish v1 -vcl+backend { import tcp from "${vmod_builddir}/.libs/libvmod_tcp.so"; sub vcl_deliver { set resp.http.a = tcp.get_estimated_rtt(); } } -start client c1 { txreq -url "/" rxresp # Probably terribly brittle. # expect resp.http.a == "4.000" } client c1 -run varnish-modules-0.12.1/src/tests/tcp/04-pacing.vtc000066400000000000000000000004701311124411200216210ustar00rootroot00000000000000varnishtest "Test enabling pacing" server s1 { rxreq txresp } -start varnish v1 -vcl+backend { import tcp from "${vmod_builddir}/.libs/libvmod_tcp.so"; sub vcl_deliver { tcp.set_socket_pace(1000); } } -start client c1 { txreq -url "/" rxresp expect resp.status == 200 } client c1 -run varnish-modules-0.12.1/src/tests/var/000077500000000000000000000000001311124411200174225ustar00rootroot00000000000000varnish-modules-0.12.1/src/tests/var/test01.vtc000066400000000000000000000017071311124411200212650ustar00rootroot00000000000000varnishtest "Test var vmod" server s1 { rxreq txresp } -start varnish v1 -vcl+backend { import var from "${vmod_builddir}/.libs/libvmod_var.so"; sub vcl_deliver { var.set("foo", "bar"); set resp.http.x-foo = var.get("foo"); var.set("foo2", "bar2"); set resp.http.x-foo = var.get("foo"); var.clear(); set resp.http.x-foo2 = "" + var.get("foo2"); var.set_int("i1", 123); set resp.http.i1 = var.get_int("i1") + 1; var.set_real("r1", 2); var.set_real("r1", var.get_real("r1")); set resp.http.r1 = var.get_real("r1"); var.set_duration("d1", 1m); set resp.http.d1 = var.get_duration("d1"); var.set_ip("ip1", client.ip); set resp.http.ip1 = var.get_ip("ip1"); } } -start client c1 { txreq -url "/" rxresp expect resp.http.x-foo == "bar" expect resp.http.x-foo2 == "" expect resp.http.i1 == "124" expect resp.http.r1 == "2.000" expect resp.http.d1 == "60.000" expect resp.http.ip1 == "127.0.0.1" } client c1 -run varnish-modules-0.12.1/src/tests/var/test02.vtc000066400000000000000000000010401311124411200212540ustar00rootroot00000000000000varnishtest "Test global variables" server s1 { rxreq txresp } -start varnish v1 -vcl+backend { import var from "${vmod_builddir}/.libs/libvmod_var.so"; sub vcl_init { var.global_set("foo", "fooval"); var.global_set("bar", "barval"); var.global_set("bar", "altered_barval"); } sub vcl_deliver { set resp.http.x-foo = var.global_get("foo"); set resp.http.x-bar = var.global_get("bar"); } } -start client c1 { txreq rxresp expect resp.http.x-foo == "fooval" expect resp.http.x-bar == "altered_barval" } -run varnish-modules-0.12.1/src/tests/var/test03.vtc000066400000000000000000000017771311124411200212760ustar00rootroot00000000000000varnishtest "Test var vmod" server s1 { rxreq txresp } -start varnish v1 -vcl+backend { import var from "${vmod_builddir}/.libs/libvmod_var.so"; sub vcl_deliver { var.set("foo", "bar"); set resp.http.x-foo = var.get("foo"); var.set("foo2", "bar2"); set resp.http.x-foo = var.get("foo"); var.clear(); set resp.http.x-foo2 = "" + var.get("foo2"); var.set_int("i1", 123); set resp.http.i1 = var.get_int("i1") + 1; var.set_real("r1", 2); var.set_real("r1", var.get_real("r1")); set resp.http.r1 = var.get_real("r1"); var.set_duration("d1", 1m); set resp.http.d1 = var.get_duration("d1"); set resp.http.x-null = var.get("nosuchvar"); set resp.http.x-null2 = var.get(req.http.no-such-var); } } -start client c1 { txreq -url "/" rxresp expect resp.http.x-foo == "bar" expect resp.http.x-foo2 == "" expect resp.http.i1 == "124" expect resp.http.r1 == "2.000" expect resp.http.d1 == "60.000" expect resp.http.x-null == "" expect resp.http.x-null2 == "" } client c1 -run varnish-modules-0.12.1/src/tests/var/test04.vtc000066400000000000000000000011671311124411200212700ustar00rootroot00000000000000varnishtest "Test vcl reload" barrier b1 cond 2 server s1 { rxreq barrier b1 sync txresp } -start varnish v1 -vcl+backend { import var from "${vmod_builddir}/.libs/libvmod_var.so"; sub vcl_recv { var.set("foo", "bar"); } sub vcl_deliver { set resp.http.x-foo = var.get("foo"); } } -start client c1 { txreq -url "/" rxresp expect resp.http.x-foo == "bar" } -start varnish v1 -vcl+backend { import var from "${vmod_builddir}/.libs/libvmod_var.so"; sub vcl_recv { var.set("foo", "bar"); } sub vcl_deliver { set resp.http.x-foo = var.get("foo"); } } barrier b1 sync client c1 -wait varnish-modules-0.12.1/src/tests/var/test05.vtc000066400000000000000000000007471311124411200212740ustar00rootroot00000000000000varnishtest "Test that a var persists across a restart" server s1 { rxreq txresp rxreq txresp } -start varnish v1 -vcl+backend { import var from "${vmod_builddir}/.libs/libvmod_var.so"; sub vcl_deliver { if (req.restarts == 0 && req.url == "/a") { var.set("foo", "bar"); return (restart); } set resp.http.x-foo = var.get("foo"); } } -start client c1 { txreq -url "/a" rxresp expect resp.http.x-foo == "bar" txreq rxresp expect resp.http.x-foo == "" } -run varnish-modules-0.12.1/src/tests/vsthrottle/000077500000000000000000000000001311124411200210505ustar00rootroot00000000000000varnish-modules-0.12.1/src/tests/vsthrottle/test01.vtc000066400000000000000000000026521311124411200227130ustar00rootroot00000000000000varnishtest "Test vsthrottle vmod" 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); set resp.http.bar-count0 = vsthrottle.remaining("bar", 1, 1s); if (!vsthrottle.is_denied("bar", 1, 1s)) { set resp.http.f3 = "OK"; } set resp.http.bar-count1 = vsthrottle.remaining("bar", 1, 1s); set resp.http.f4-count0 = vsthrottle.remaining(req.http.not-found, 1, 1s); if (vsthrottle.is_denied(req.http.not-found, 10, 100s)) { set resp.http.f4 = "NULL"; } set resp.http.f4-count1 = vsthrottle.remaining(req.http.not-found, 1, 1s); } } -start client c1 { txreq -url "/" rxresp expect resp.http.f1 == "OK" expect resp.http.foo-count0 == "1" expect resp.http.foo-count1 == "0" expect resp.http.f2 == expect resp.http.foo-count2 == "0" expect resp.http.f3 == "OK" expect resp.http.bar-count0 == "1" expect resp.http.bar-count1 == "0" expect resp.http.f4 == "NULL" expect resp.http.f4-count0 == "-1" expect resp.http.f4-count1 == "-1" } client c1 -run varnish-modules-0.12.1/src/tests/vsthrottle/test02.vtc000066400000000000000000000013461311124411200227130ustar00rootroot00000000000000varnishtest "Test use in backend path" server s1 { rxreq txresp } -start varnish v1 -vcl+backend { import vsthrottle from "${vmod_builddir}/.libs/libvmod_vsthrottle.so"; sub vcl_backend_response { if (!vsthrottle.is_denied("foo", 1, 1s)) { set beresp.http.f1 = "OK"; } if (!vsthrottle.is_denied("foo", 1, 1s)) { set beresp.http.f2 = "OK"; } if (!vsthrottle.is_denied("bar", 1, 1s)) { set beresp.http.f3 = "OK"; } if (vsthrottle.is_denied(bereq.http.not-found, 10, 100s)) { set beresp.http.f4 = "NULL"; } } } -start client c1 { txreq -url "/" rxresp expect resp.http.f1 == "OK" expect resp.http.f2 == expect resp.http.f3 == "OK" expect resp.http.f4 == "NULL" } client c1 -run varnish-modules-0.12.1/src/tests/xkey/000077500000000000000000000000001311124411200176125ustar00rootroot00000000000000varnish-modules-0.12.1/src/tests/xkey/test01.vtc000066400000000000000000000022041311124411200214460ustar00rootroot00000000000000varnishtest "Test xkey vmod" server s1 { rxreq txresp -hdr "xkey: asdf" -hdr "X-HashTwo: fdsa" rxreq txresp -hdr "xkey: asdf" -hdr "X-HashTwo: fdsa" } -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 # First check xkey header client c1 { txreq rxresp } -run varnish v1 -expect n_object == 1 client c1 { txreq -hdr "xkey-purge: asdf" rxresp expect resp.status == 200 expect resp.http.reason == "Purged" } -run delay 1 varnish v1 -expect n_object == 0 # Check again using the legacy X-HashTwo header client c1 { txreq rxresp } -run varnish v1 -expect n_object == 1 client c1 { txreq -hdr "xkey-purge: fdsa" rxresp expect resp.status == 200 expect resp.http.reason == "Purged" } -run delay 1 varnish v1 -expect n_object == 0 varnish-modules-0.12.1/src/tests/xkey/test02.vtc000066400000000000000000000014211311124411200214470ustar00rootroot00000000000000varnishtest "Test xkey vmod multiple keys" server s1 { rxreq txresp -hdr "xkey: asdf fdsa" } -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 } -run varnish v1 -expect n_object == 1 client c1 { txreq -hdr "xkey-purge: fdsa" rxresp expect resp.status == 200 expect resp.http.reason == "Purged" } -run delay 1 varnish v1 -expect n_object == 0 varnish-modules-0.12.1/src/tests/xkey/test03.vtc000066400000000000000000000015711311124411200214560ustar00rootroot00000000000000varnishtest "Test xkey vmod multiple objects multiple keys" server s1 { rxreq txresp -hdr "xkey: asdf" -hdr "xkey: fdsa" rxreq txresp -hdr "xkey: asdf" -hdr "xkey: fdsa" } -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 { txreq -hdr "xkey-purge: asdf" rxresp expect resp.status == 200 expect resp.http.reason == "Purged" } -run delay 1 varnish v1 -expect n_object == 0 varnish-modules-0.12.1/src/tests/xkey/test04.vtc000066400000000000000000000017471311124411200214640ustar00rootroot00000000000000varnishtest "Test xkey vmod multiple objects multiple keys cleanup" # Testcase lays the ground work for the cleanup code to run, though it # has no way of learning the result :( Any asserts triggered should # show though. server s1 { rxreq txresp -hdr "xkey: asdf" -hdr "xkey: fdsa" rxreq txresp -hdr "xkey: asdf" -hdr "xkey: fdsa" } -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; } } -start client c1 { txreq rxresp txreq -url /2 rxresp } -run varnish v1 -expect n_object == 2 varnish v1 -vcl+backend { } varnish v1 -cliok "vcl.discard vcl1" delay 2 client c1 { txreq rxresp expect resp.status == 200 } -run varnish v1 -expect n_object == 2 varnish-modules-0.12.1/src/tests/xkey/test05.vtc000066400000000000000000000014361311124411200214600ustar00rootroot00000000000000varnishtest "Test xkey vmod multiple keys" server s1 { rxreq txresp -hdr "xkey: asdf" -hdr "xkey: fdsa" } -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 } -run varnish v1 -expect n_object == 1 client c1 { txreq -hdr "xkey-purge: fdsa" rxresp expect resp.status == 200 expect resp.http.reason == "Purged" } -run delay 1 varnish v1 -expect n_object == 0 varnish-modules-0.12.1/src/tests/xkey/test06.vtc000066400000000000000000000017651311124411200214660ustar00rootroot00000000000000varnishtest "Test xkey.softpurge()" server s1 { rxreq txresp -hdr "xkey: asdf" -hdr "xkey: fdsa" } -start varnish v1 -arg "-p default_keep=0 -p default_grace=0" -vcl+backend { import xkey from "${vmod_builddir}/.libs/libvmod_xkey.so"; sub vcl_recv { if (req.http.xkey-purge) { if (xkey.softpurge(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 = 0.5s; set beresp.keep = 0s; } sub vcl_synth { set resp.http.reason = resp.reason; } } -start client c1 { txreq rxresp expect resp.status == 200 } -run varnish v1 -expect n_object == 1 client c1 { txreq -hdr "xkey-purge: fdsa" rxresp expect resp.status == 200 expect resp.http.reason == "Purged" } -run varnish v1 -expect n_object == 1 varnish v1 -cliok "backend.set_health s1 sick" client c1 { txreq rxresp expect resp.status == 200 } -run delay 0.5 varnish v1 -expect n_object == 0 varnish-modules-0.12.1/src/tests/xkey/test07.vtc000066400000000000000000000017331311124411200214620ustar00rootroot00000000000000varnishtest "Test xkey vmod multiple objects purging multiple keys" server s1 { rxreq txresp -hdr "xkey: 1000 2000 3000" rxreq txresp -hdr "xkey: 4000 5000 6000" rxreq txresp -hdr "xkey: 7000 8000 9000" } -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 -url "/one" rxresp txreq -url "/two" rxresp txreq -url "/three" rxresp } -run varnish v1 -expect n_object == 3 client c1 { txreq -hdr "xkey-purge: 1000 5000 9000" rxresp expect resp.status == 200 expect resp.http.reason == "Purged" } -run delay 1 varnish v1 -expect n_object == 0 varnish-modules-0.12.1/src/tests/xkey/test08.vtc000066400000000000000000000014311311124411200214560ustar00rootroot00000000000000varnishtest "Test xkey vmod purge non-existant key" server s1 { rxreq txresp -hdr "xkey: asdf fdsa" } -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 } -run varnish v1 -expect n_object == 1 client c1 { txreq -hdr "xkey-purge: xyz" rxresp expect resp.status == 404 expect resp.http.reason == "No key" } -run delay 1 varnish v1 -expect n_object == 1 varnish-modules-0.12.1/src/vmod_bodyaccess.c000066400000000000000000000142521311124411200210040ustar00rootroot00000000000000/*- * Copyright (c) 2012-2016 Varnish Software * * Original author: Arianna Aondio * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "config.h" #include #include #include #include "cache/cache.h" #include "vre.h" #include "vrt.h" #include "vcl.h" #include "vsha256.h" #include "vcc_bodyaccess_if.h" struct bodyaccess_log_ctx { struct vsl_log *vsl; const char *pfx; size_t len; }; static int bodyaccess_log(struct bodyaccess_log_ctx *ctx, const void *ptr, size_t len) { txt txtbody; const char *str; char *buf; size_t size, prefix_len; str = ptr; if (ctx->len > 0) size = ctx->len; else size = len; prefix_len = strlen(ctx->pfx); size += prefix_len; buf = malloc(size); AN(buf); while (len > 0) { if (len > ctx->len && ctx->len > 0) size = ctx->len; else size = len; memcpy(buf, ctx->pfx, prefix_len); memcpy(buf + prefix_len, str, size); txtbody.b = buf; txtbody.e = buf + prefix_len + size; VSLbt(ctx->vsl, SLT_Debug, txtbody); len -= size; str += size; } free(buf); return (0); } #if defined(HAVE_REQ_BODY_ITER_F) static int __match_proto__(req_body_iter_f) bodyaccess_bcat_cb(struct req *req, void *priv, void *ptr, size_t len) { CHECK_OBJ_NOTNULL(req, REQ_MAGIC); AN(priv); return (VSB_bcat(priv, ptr, len)); } static int __match_proto__(req_body_iter_f) bodyaccess_log_cb(struct req *req, void *priv, void *ptr, size_t len) { CHECK_OBJ_NOTNULL(req, REQ_MAGIC); AN(priv); return (bodyaccess_log(priv, ptr, len)); } #elif defined(HAVE_OBJITERATE_F) static int __match_proto__(objiterate_f) bodyaccess_bcat_cb(void *priv, int flush, const void *ptr, ssize_t len) { AN(priv); (void)flush; return (VSB_bcat(priv, ptr, len)); } static int __match_proto__(objiterate_f) bodyaccess_log_cb(void *priv, int flush, const void *ptr, ssize_t len) { AN(priv); (void)flush; return (bodyaccess_log(priv, ptr, len)); } #else # error Unsupported VRB API #endif static void bodyaccess_bcat(VRT_CTX, struct vsb *vsb) { int l; CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC); CHECK_OBJ_NOTNULL(ctx->req, REQ_MAGIC); l = VRB_Iterate(ctx->req, bodyaccess_bcat_cb, vsb); AZ(VSB_finish(vsb)); if (l < 0) VSLb(ctx->vsl, SLT_VCL_Error, "Iteration on req.body didn't succeed."); } VCL_VOID vmod_hash_req_body(VRT_CTX) { struct vsb *vsb; txt txtbody; CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC); if (ctx->req->req_body_status != REQ_BODY_CACHED) { VSLb(ctx->vsl, SLT_VCL_Error, "Unbuffered req.body"); return; } if (ctx->method != VCL_MET_HASH) { VSLb(ctx->vsl, SLT_VCL_Error, "hash_req_body can only be used in vcl_hash{}"); return; } vsb = VSB_new_auto(); AN(vsb); bodyaccess_bcat(ctx, vsb); txtbody.b = VSB_data(vsb); txtbody.e = txtbody.b + VSB_len(vsb); SHA256_Update(ctx->specific, txtbody.b, txtbody.e - txtbody.b); VSLbt(ctx->vsl, SLT_Hash, txtbody); VSB_delete(vsb); } VCL_INT vmod_len_req_body(VRT_CTX) { CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC); CHECK_OBJ_NOTNULL(ctx->req, REQ_MAGIC); if (ctx->req->req_body_status != REQ_BODY_CACHED) { VSLb(ctx->vsl, SLT_VCL_Error, "Unbuffered req.body"); return (-1); } if (ctx->method != VCL_MET_RECV) { VSLb(ctx->vsl, SLT_VCL_Error, "len_req_body can only be used in vcl_recv{}"); return (-1); } return (ctx->req->req_bodybytes); } VCL_INT vmod_rematch_req_body(VRT_CTX, struct vmod_priv *priv_call, VCL_STRING re) { struct vsb *vsb; const char *error; int erroroffset; vre_t *t = NULL; int i; CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC); if (ctx->req->req_body_status != REQ_BODY_CACHED) { VSLb(ctx->vsl, SLT_VCL_Error, "Unbuffered req.body"); return(-1); } if (ctx->method != VCL_MET_RECV) { VSLb(ctx->vsl, SLT_VCL_Error, "rematch_req_body can be used only in vcl_recv{}"); return (-1); } AN(re); if(priv_call->priv == NULL) { t = VRE_compile(re, 0, &error, &erroroffset); if (t == NULL) { VSLb(ctx->vsl, SLT_VCL_Error, "Regular expression not valid"); return (-1); } priv_call->priv = t; priv_call->free = free; } vsb = VSB_new_auto(); AN(vsb); bodyaccess_bcat(ctx, vsb); i = VRE_exec(priv_call->priv, VSB_data(vsb), VSB_len(vsb), 0, 0, NULL, 0, NULL); VSB_delete(vsb); if (i > 0) return (1); if (i == VRE_ERROR_NOMATCH ) return (0); VSLb(ctx->vsl, SLT_VCL_Error, "Regexp matching returned %d", i); return (-1); } VCL_VOID vmod_log_req_body(VRT_CTX, VCL_STRING prefix, VCL_INT length) { struct bodyaccess_log_ctx log_ctx; int ret; CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC); CHECK_OBJ_NOTNULL(ctx->req, REQ_MAGIC); AN(ctx->vsl); if (!prefix) prefix = ""; log_ctx.vsl = ctx->vsl; log_ctx.pfx = prefix; log_ctx.len = length; if (ctx->req->req_body_status != REQ_BODY_CACHED) { VSLb(ctx->vsl, SLT_VCL_Error, "Unbuffered req.body"); return; } ret = VRB_Iterate(ctx->req, bodyaccess_log_cb, &log_ctx); if (ret < 0) { VSLb(ctx->vsl, SLT_VCL_Error, "Iteration on req.body didn't succeed."); return; } } varnish-modules-0.12.1/src/vmod_bodyaccess.vcc000066400000000000000000000056171311124411200213420ustar00rootroot00000000000000$Module bodyaccess 3 Varnish Module for request body access DESCRIPTION =========== Varnish module that lets you access the request body. .. vcl-start VCL example:: vcl 4.0; import std; import bodyaccess; backend default { .host = "192.0.2.11"; .port = "8080"; } sub vcl_recv { if (req.method == "POST") { set req.http.x-method = req.method; if (std.cache_req_body(110KB)) { set req.http.x-len = bodyaccess.len_req_body(); set req.http.x-re = bodyaccess.rematch_req_body("Regex"); bodyaccess.log_req_body("PREFIX:", 3); } } return(hash); } sub vcl_hash { bodyaccess.hash_req_body(); return (lookup); } sub vcl_backend_fetch { set bereq.method = bereq.http.x-method; } sub vcl_deliver { set resp.http.x-len = req.http.x-len; set resp.http.x-re = req.http.x-re; } .. vcl-end N.B. The request body must be retrieved before doing any operations on it. It can be buffered using the cache_req_body() function from libvmod_std. These functions applies only to standard REST methods. Caching is *not* allowed on PUT requests. $Function INT rematch_req_body(PRIV_CALL, STRING re) Description Returns -1 if an error occurrs. Returns 0 if the request body doesn't contain the string *re*. Returns 1 if the request body contains the string *re*. Note that the comparison is case sensitive and the request body must be buffered. Example :: | std.cache_req_body(1KB); | | if (bodyaccess.rematch_req_body("FOO") == 1) { | std.log("is true"); | } $Function VOID hash_req_body() Description Adds available request body bytes to the lookup hash key. Note that this function can only be used in vcl_hash and the request body must be buffered. Example :: | sub vcl_recv { | std.cache_req_body(1KB); | } | | sub vcl_hash{ | bodyaccess.hash_req_body(); | } $Function INT len_req_body() Description Returns the request body length or -1 if an error occurs. Note that the request body must be buffered. Example :: | std.cache_req_body(1KB); | set req.http.x-len = bodyaccess.len_req_body(); $Function VOID log_req_body(STRING prefix = "", INT length = 200) Description Log the request body to the VSL, making it available for other components. When logging, it takes an optional prefix and a max line length, so the body could be split up across multiple lines as there is a limit to how large a single line can be. Example :: | std.cache_req_body(1KB); | bodyaccess.log_req_body("PREFIX:", 3); varnish-modules-0.12.1/src/vmod_cookie.c000066400000000000000000000202641311124411200201360ustar00rootroot00000000000000/*- * Copyright (c) 2012-2016 Varnish Software * * Author: Lasse Karstensen * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * Cookie VMOD that simplifies handling of the Cookie request header. */ #define _GNU_SOURCE #include "config.h" #include #include #include #include #include "cache/cache.h" #include "vrt.h" #include "vqueue.h" #include "vcc_cookie_if.h" struct cookie { unsigned magic; #define VMOD_COOKIE_ENTRY_MAGIC 0x3BB41543 char *name; char *value; VTAILQ_ENTRY(cookie) list; }; /* A structure to represent both whitelists and blacklists */ struct matchlist { char *name; VTAILQ_ENTRY(matchlist) list; }; struct vmod_cookie { unsigned magic; #define VMOD_COOKIE_MAGIC 0x4EE5FB2E VTAILQ_HEAD(, cookie) cookielist; }; static void cobj_free(void *p) { struct vmod_cookie *vcp; CAST_OBJ_NOTNULL(vcp, p, VMOD_COOKIE_MAGIC); FREE_OBJ(vcp); } static struct vmod_cookie * cobj_get(struct vmod_priv *priv) { struct vmod_cookie *vcp; if (priv->priv == NULL) { ALLOC_OBJ(vcp, VMOD_COOKIE_MAGIC); AN(vcp); VTAILQ_INIT(&vcp->cookielist); priv->priv = vcp; priv->free = cobj_free; } else CAST_OBJ_NOTNULL(vcp, priv->priv, VMOD_COOKIE_MAGIC); return (vcp); } VCL_VOID vmod_parse(VRT_CTX, struct vmod_priv *priv, VCL_STRING cookieheader) { struct vmod_cookie *vcp = cobj_get(priv); char *name, *value; const char *p, *sep; int i = 0; if (!cookieheader || *cookieheader == '\0') { VSLb(ctx->vsl, SLT_VCL_Log, "cookie: nothing to parse"); return; } if (!VTAILQ_EMPTY(&vcp->cookielist)) { /* If called twice during the same request, clean out old state */ vmod_clean(ctx, priv); } p = cookieheader; while (*p != '\0') { while (isspace(*p)) p++; sep = strchr(p, '='); if (sep == NULL) break; name = strndup(p, pdiff(p, sep)); p = sep + 1; sep = p; while (*sep != '\0' && *sep != ';') sep++; value = strndup(p, pdiff(p, sep)); vmod_set(ctx, priv, name, value); free(name); free(value); i++; if (*sep == '\0') break; p = sep + 1; } VSLb(ctx->vsl, SLT_VCL_Log, "cookie: parsed %i cookies.", i); } static struct cookie * find_cookie(struct vmod_cookie *vcp, VCL_STRING name) { struct cookie *cookie; VTAILQ_FOREACH(cookie, &vcp->cookielist, list) { CHECK_OBJ_NOTNULL(cookie, VMOD_COOKIE_ENTRY_MAGIC); if (!strcmp(cookie->name, name)) break; } return(cookie); } VCL_VOID vmod_set(VRT_CTX, struct vmod_priv *priv, VCL_STRING name, VCL_STRING value) { struct vmod_cookie *vcp = cobj_get(priv); /* Empty cookies should be ignored. */ if (name == NULL || *name == '\0') return; if (value == NULL || *value == '\0') return; char *p; struct cookie *cookie = find_cookie(vcp, name); if (cookie != NULL) { p = WS_Printf(ctx->ws, "%s", value); if (p == NULL) { VSLb(ctx->vsl, SLT_VCL_Log, "cookie: Workspace overflow in set()"); } else cookie->value = p; return; } cookie = WS_Alloc(ctx->ws, sizeof(struct cookie)); if (cookie == NULL) { VSLb(ctx->vsl, SLT_VCL_Log, "cookie: unable to get storage for cookie"); return; } cookie->magic = VMOD_COOKIE_ENTRY_MAGIC; cookie->name = WS_Printf(ctx->ws, "%s", name); cookie->value = WS_Printf(ctx->ws, "%s", value); if (cookie->name == NULL || cookie->value == NULL) { VSLb(ctx->vsl, SLT_VCL_Log, "cookie: unable to get storage for cookie"); return; } VTAILQ_INSERT_TAIL(&vcp->cookielist, cookie, list); } VCL_BOOL vmod_isset(VRT_CTX, struct vmod_priv *priv, const char *name) { struct vmod_cookie *vcp = cobj_get(priv); (void)ctx; if (name == NULL || *name == '\0') return(0); struct cookie *cookie = find_cookie(vcp, name); return (cookie ? 1 : 0); } VCL_STRING vmod_get(VRT_CTX, struct vmod_priv *priv, VCL_STRING name) { struct vmod_cookie *vcp = cobj_get(priv); (void)ctx; if (name == NULL || *name == '\0') return(NULL); struct cookie *cookie = find_cookie(vcp, name); return (cookie ? cookie->value : NULL); } VCL_VOID vmod_delete(VRT_CTX, struct vmod_priv *priv, VCL_STRING name) { struct vmod_cookie *vcp = cobj_get(priv); (void)ctx; if (name == NULL || *name == '\0') return; struct cookie *cookie = find_cookie(vcp, name); if (cookie != NULL) VTAILQ_REMOVE(&vcp->cookielist, cookie, list); } VCL_VOID vmod_clean(VRT_CTX, struct vmod_priv *priv) { struct vmod_cookie *vcp = cobj_get(priv); (void)ctx; AN(&vcp->cookielist); VTAILQ_INIT(&vcp->cookielist); } #define FILTER_ACTION_BLACKLIST 0 #define FILTER_ACTION_WHITELIST 1 static void filter_cookies(struct vmod_priv *priv, VCL_STRING list_s, VCL_BOOL filter_action) { struct cookie *cookieptr, *safeptr; struct vmod_cookie *vcp = cobj_get(priv); struct matchlist *mlentry, *mlsafe; char const *p = list_s, *q; int matched = 0; VTAILQ_HEAD(, matchlist) matchlist_head; VTAILQ_INIT(&matchlist_head); /* Parse the supplied list. */ while (p && *p != '\0') { while (isspace(*p)) p++; if (*p == '\0') break; q = p; while (*q != '\0' && *q != ',') q++; if (q == p) { p++; continue; } mlentry = malloc(sizeof(struct matchlist)); AN(mlentry); mlentry->name = strndup(p, q - p); AN(mlentry->name); VTAILQ_INSERT_TAIL(&matchlist_head, mlentry, list); p = q; if (*p != '\0') p++; } /* Filter existing cookies that either aren't in the whitelist or * are in the blacklist (depending on the filter_action) */ VTAILQ_FOREACH_SAFE(cookieptr, &vcp->cookielist, list, safeptr) { CHECK_OBJ_NOTNULL(cookieptr, VMOD_COOKIE_ENTRY_MAGIC); matched = 0; VTAILQ_FOREACH(mlentry, &matchlist_head, list) { if (strcmp(cookieptr->name, mlentry->name) == 0) { matched = 1; break; } } if (matched != filter_action) VTAILQ_REMOVE(&vcp->cookielist, cookieptr, list); } VTAILQ_FOREACH_SAFE(mlentry, &matchlist_head, list, mlsafe) { VTAILQ_REMOVE(&matchlist_head, mlentry, list); free(mlentry->name); free(mlentry); } } VCL_VOID vmod_filter_except(VRT_CTX, struct vmod_priv *priv, VCL_STRING whitelist_s) { (void)ctx; filter_cookies(priv, whitelist_s, FILTER_ACTION_WHITELIST); } VCL_VOID vmod_filter(VRT_CTX, struct vmod_priv *priv, VCL_STRING blacklist_s) { (void)ctx; filter_cookies(priv, blacklist_s, FILTER_ACTION_BLACKLIST); } VCL_STRING vmod_get_string(VRT_CTX, struct vmod_priv *priv) { struct cookie *curr; struct vsb *output; void *u; struct vmod_cookie *vcp = cobj_get(priv); output = VSB_new_auto(); AN(output); VTAILQ_FOREACH(curr, &vcp->cookielist, list) { CHECK_OBJ_NOTNULL(curr, VMOD_COOKIE_ENTRY_MAGIC); AN(curr->name); AN(curr->value); VSB_printf(output, "%s%s=%s;", (curr == VTAILQ_FIRST(&vcp->cookielist)) ? "" : " ", curr->name, curr->value); } VSB_finish(output); u = WS_Alloc(ctx->ws, VSB_len(output) + 1); if (!u) { VSLb(ctx->vsl, SLT_VCL_Log, "cookie: Workspace overflow"); VSB_delete(output); return(NULL); } strcpy(u, VSB_data(output)); VSB_delete(output); return (u); } VCL_STRING vmod_format_rfc1123(VRT_CTX, VCL_TIME ts, VCL_DURATION duration) { return VRT_TIME_string(ctx, ts + duration); } varnish-modules-0.12.1/src/vmod_cookie.vcc000066400000000000000000000131001311124411200204560ustar00rootroot00000000000000$Module cookie 3 Varnish Cookie Module DESCRIPTION =========== Handle HTTP cookies easier in Varnish VCL. (without regex) Parses a cookie header into an internal data store, where per-cookie get/set/delete functions are available. A filter_except() method removes all 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. 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 the contents as fake bereq headers. .. vcl-start Filtering example:: vcl 4.0; import cookie; backend default { .host = "192.0.2.11"; .port = "8080"; } sub vcl_recv { if (req.http.cookie) { cookie.parse(req.http.cookie); # Either delete the ones you want to get rid of: cookie.delete("cookie2"); # or filter everything but a few: cookie.filter_except("SESSIONID,PHPSESSID"); # Store it back into req so it will be passed to the backend. set req.http.cookie = cookie.get_string(); # If empty, unset so the builtin VCL can consider it for caching. if (req.http.cookie == "") { unset req.http.cookie; } } } .. vcl-end $Function VOID clean(PRIV_TASK) Description Clean up previously parsed cookies. It is not necessary to run clean() in normal operations. Example :: sub vcl_recv { cookie.clean(); } $Function VOID delete(PRIV_TASK, STRING cookiename) Description Delete `cookiename` from internal vmod storage if it exists. Example :: sub vcl_recv { cookie.parse("cookie1: value1; cookie2: value2;"); cookie.delete("cookie2"); // get_string() will now yield "cookie1: value1"; } $Function VOID filter(PRIV_TASK, STRING filterstring) Description Delete all cookies from internal vmod storage that are in the comma-separated argument cookienames. Example :: sub vcl_recv { cookie.parse("cookie1: value1; cookie2: value2; cookie3: value3"); cookie.filter("cookie1,cookie2"); // get_string() will now yield // "cookie3: value3"; } $Function VOID filter_except(PRIV_TASK, STRING filterstring) Description Delete all cookies from internal vmod storage that is not in the comma-separated argument cookienames. Example :: sub vcl_recv { cookie.parse("cookie1: value1; cookie2: value2; cookie3: value3"); cookie.filter_except("cookie1,cookie2"); // get_string() will now yield // "cookie1: value1; cookie2: value2;"; } $Function STRING format_rfc1123(TIME now, DURATION timedelta) Description Get a RFC1123 formatted date string suitable for inclusion in a Set-Cookie response header. Care should be taken if the response has multiple Set-Cookie headers. In that case the header vmod should be used. Example :: sub vcl_deliver { # Set a userid cookie on the client that lives for 5 minutes. set resp.http.Set-Cookie = "userid=" + req.http.userid + "; Expires=" + cookie.format_rfc1123(now, 5m) + "; httpOnly"; } $Function STRING get(PRIV_TASK, STRING cookiename) Description Get the value of `cookiename`, as stored in internal vmod storage. If `cookiename` does not exist an empty string is returned. Example :: import std; sub vcl_recv { cookie.parse("cookie1: value1; cookie2: value2;"); std.log("cookie1 value is: " + cookie.get("cookie1")); } $Function STRING get_string(PRIV_TASK) Description Get a Cookie string value with all cookies in internal vmod storage. Does not modify internal storage. Example :: sub vcl_recv { cookie.parse(req.http.cookie); cookie.filter_except("SESSIONID,PHPSESSID"); set req.http.cookie = cookie.get_string(); } $Function BOOL isset(PRIV_TASK, STRING cookiename) Description Check if `cookiename` is set in the internal vmod storage. Example :: import std; sub vcl_recv { cookie.parse("cookie1: value1; cookie2: value2;"); if (cookie.isset("cookie2")) { std.log("cookie2 is set."); } } $Function VOID parse(PRIV_TASK, STRING cookieheader) Description Parse the cookie string in `cookieheader`. If state already exists, clean() will be run first. Example :: sub vcl_recv { cookie.parse(req.http.Cookie); } $Function VOID set(PRIV_TASK, STRING cookiename, STRING value) Description Set the internal vmod storage for `cookiename` to `value`. Example :: sub vcl_recv { cookie.set("cookie1", "value1"); std.log("cookie1 value is: " + cookie.get("cookie1")); } varnish-modules-0.12.1/src/vmod_header.c000066400000000000000000000150341311124411200201140ustar00rootroot00000000000000/*- * Copyright (c) 2011-2016 Varnish Software * All rights reserved. * * Author: Kristian Lyngstol * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "config.h" #include #include #include "vcl.h" #include "cache/cache.h" #include "vrt.h" #include "vcc_header_if.h" /* * This mutex is used to avoid having two threads that initializes the same * regex at the same time. While it means that there's a single, global * lock for all libvmod-header actions dealing with regular expressions, * the contention only applies on the first request that calls that * specific function. */ pthread_mutex_t header_mutex; /* * Initialize the regex *s on priv, if it hasn't already been done. * XXX: We have to recheck the condition after grabbing the lock to avoid a * XXX: race condition. */ static void header_init_re(struct vmod_priv *priv, const char *s) { if (priv->priv == NULL) { assert(pthread_mutex_lock(&header_mutex) == 0); if (priv->priv == NULL) { VRT_re_init(&priv->priv, s); priv->free = VRT_re_fini; } pthread_mutex_unlock(&header_mutex); } } /* * Returns true if the *hdr header is the one pointed to by *hh. * * FIXME: duplication from varnishd. */ static int header_http_IsHdr(const txt *hh, const char *hdr) { unsigned l; Tcheck(*hh); AN(hdr); l = hdr[0]; assert(l == strlen(hdr + 1)); assert(hdr[l] == ':'); hdr++; return (!strncasecmp(hdr, hh->b, l)); } /* * Return true if the hp->hd[u] header matches *hdr and the regex *re * matches the content. * * If re is NULL, content is not tested and as long as it's the right * header, a match is returned. */ static int header_http_match(VRT_CTX, const struct http *hp, unsigned u, void *re, const char *hdr) { const char *start; unsigned l; assert(hdr); assert(hp); Tcheck(hp->hd[u]); if (hp->hd[u].b == NULL) return (0); l = hdr[0]; if (!header_http_IsHdr(&hp->hd[u], hdr)) return (0); if (re == NULL) return (1); start = hp->hd[u].b + l; while (*start != '\0' && *start == ' ') start++; if (!*start) return (0); if (VRT_re_match(ctx, start, re)) return (1); return (0); } /* * Returns the (first) header named as *hdr that also matches the regular * expression *re. */ static unsigned header_http_findhdr(VRT_CTX, const struct http *hp, const char *hdr, void *re) { unsigned u; for (u = HTTP_HDR_FIRST; u < hp->nhd; u++) { if (header_http_match(ctx, hp, u, re, hdr)) return (u); } return (0); } /* * Removes all copies of the header that matches *hdr with content that * matches *re. Same as http_Unset(), plus regex. */ static void header_http_Unset(VRT_CTX, struct http *hp, const char *hdr, void *re) { unsigned u, v; for (v = u = HTTP_HDR_FIRST; u < hp->nhd; u++) { if (hp->hd[u].b == NULL) continue; if (header_http_match(ctx, hp, u, re, hdr)) continue; if (v != u) { memcpy(&hp->hd[v], &hp->hd[u], sizeof *hp->hd); memcpy(&hp->hdf[v], &hp->hdf[u], sizeof *hp->hdf); } v++; } hp->nhd = v; } /* * Copies all occurrences of *hdr to a destination header *dst_h. Uses * vmod_header_append(), so all copies are kept intact. * * XXX: Not sure I like the idea of iterating a list of headers while * XXX: adding to it. It may be correct now, but perhaps not so much in * XXX: the future. */ static void header_http_cphdr(VRT_CTX, const struct http *hp, const char *hdr, VCL_HEADER dst) { unsigned u; const char *p; for (u = HTTP_HDR_FIRST; u < hp->nhd; u++) { if (!header_http_match(ctx, hp, u, NULL, hdr)) continue; p = hp->hd[u].b + hdr[0]; while (*p == ' ' || *p == '\t') p++; vmod_append(ctx, dst, p, vrt_magic_string_end); } } /* * vmod entrypoint. Sets up the header mutex. */ int event_function(VRT_CTX, struct vmod_priv *priv, enum vcl_event_e e) { (void)ctx; (void)priv; if (e != VCL_EVENT_LOAD) return (0); assert(pthread_mutex_init(&header_mutex, NULL) == 0); return (0); } VCL_VOID __match_proto__() vmod_append(VRT_CTX, VCL_HEADER hdr, const char *fmt, ...) { va_list ap; struct http *hp; const char *b; CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC); assert(fmt != NULL); hp = VRT_selecthttp(ctx, hdr->where); va_start(ap, fmt); b = VRT_String(hp->ws, hdr->what + 1, fmt, ap); if (b == NULL) VSLb(ctx->vsl, SLT_LostHeader, "vmod_header: %s", hdr->what + 1); else http_SetHeader(hp, b); va_end(ap); } VCL_STRING __match_proto__() vmod_get(VRT_CTX, struct vmod_priv *priv, VCL_HEADER hdr, VCL_STRING s) { struct http *hp; unsigned u; const char *p; CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC); header_init_re(priv, s); hp = VRT_selecthttp(ctx, hdr->where); u = header_http_findhdr(ctx, hp, hdr->what, priv->priv); if (u == 0) return (NULL); p = hp->hd[u].b + hdr->what[0]; while (*p == ' ' || *p == '\t') p++; return (p); } VCL_VOID __match_proto__() vmod_copy(VRT_CTX, VCL_HEADER src, VCL_HEADER dst) { struct http *src_hp; CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC); src_hp = VRT_selecthttp(ctx, src->where); header_http_cphdr(ctx, src_hp, src->what, dst); } VCL_VOID __match_proto__() vmod_remove(VRT_CTX, struct vmod_priv *priv, VCL_HEADER hdr, VCL_STRING s) { struct http *hp; CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC); header_init_re(priv, s); hp = VRT_selecthttp(ctx, hdr->where); header_http_Unset(ctx, hp, hdr->what, priv->priv); } varnish-modules-0.12.1/src/vmod_header.vcc000066400000000000000000000034711311124411200204470ustar00rootroot00000000000000$Module header 3 Header VMOD for Varnish DESCRIPTION =========== Varnish Module for manipulation of duplicated HTTP headers, for instance multiple Set-Cookie headers. .. vcl-start Example:: vcl 4.0; import header; backend default { .host = "192.0.2.11"; .port = "8080"; } sub vcl_backend_response { if (beresp.http.Set-Cookie) { # Add another line of Set-Cookie in the response. header.append(beresp.http.Set-Cookie, "VSESS=abbabeef"); # CMS always set this, but doesn't really need it. header.remove(beresp.http.Set-Cookie, "JSESSIONID="); } } .. vcl-end $Event event_function $Function VOID append(HEADER, STRING_LIST) Description Append an extra occurrence to an existing header. Example :: header.append(beresp.http.Set-Cookie, "foo=bar") $Function VOID copy(HEADER, HEADER) Description Copy all source headers to a new header. Example :: header.copy(beresp.http.set-cookie, beresp.http.x-old-cookie); $Function STRING get(PRIV_CALL, HEADER header, STRING regex) Description Fetches the value of the first `header` that matches the given regular expression `regex`. Example :: set beresp.http.xusr = header.get(beresp.http.set-cookie,"user="); $Function VOID remove(PRIV_CALL, HEADER header, STRING regex) Description Remove all occurences of `header` that matches `regex`. Example :: header.remove(beresp.http.set-cookie,"^(?!(funcookie=))"); ACKNOWLEDGEMENTS ================ The development of this plugin was made possible by the sponsorship of Softonic, http://en.softonic.com/ . Also thanks to Imo Klabun and Anders Nordby for bug reports. BUGS ==== You can't use dynamic regular expressions, which also holds true for normal regular expressions in regsub(). varnish-modules-0.12.1/src/vmod_saintmode.c000066400000000000000000000226541311124411200206550ustar00rootroot00000000000000/*- * Copyright (c) 2013-2015 Varnish Software * All rights reserved. * * Author: Dag Haavi Finstad * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "config.h" #include #include #include #include "vcl.h" #include "cache/cache.h" #include "vrt.h" #include "cache/cache_director.h" #include "cache/cache_backend.h" #include "vcc_saintmode_if.h" struct trouble { unsigned magic; #define TROUBLE_MAGIC 0x4211ab21 uint8_t digest[DIGEST_LEN]; double timeout; VTAILQ_ENTRY(trouble) list; }; struct vmod_saintmode_saintmode { unsigned magic; #define VMOD_SAINTMODE_MAGIC 0xa03756e4 struct director sdir[1]; const struct director *be; pthread_mutex_t mtx; unsigned threshold; unsigned n_trouble; VTAILQ_ENTRY(vmod_saintmode_saintmode) list; VTAILQ_HEAD(, trouble) troublelist; }; struct saintmode_objs { unsigned magic; #define SAINTMODE_OBJS_MAGIC 0x9aa7beec VTAILQ_HEAD(, vmod_saintmode_saintmode) sm_list; }; VCL_BACKEND __match_proto__(td_saintmode_saintmode_backend) vmod_saintmode_backend(VRT_CTX, struct vmod_saintmode_saintmode *sm) { CHECK_OBJ_NOTNULL(sm, VMOD_SAINTMODE_MAGIC); CHECK_OBJ_NOTNULL(sm->sdir, DIRECTOR_MAGIC); (void)ctx; return (sm->sdir); } static struct vmod_saintmode_saintmode * find_sm(const struct saintmode_objs *sm_objs, const struct director *be) { struct vmod_saintmode_saintmode *sm; CHECK_OBJ_NOTNULL(sm_objs, SAINTMODE_OBJS_MAGIC); CHECK_OBJ_NOTNULL(be, DIRECTOR_MAGIC); VTAILQ_FOREACH(sm, &sm_objs->sm_list, list) { CHECK_OBJ_NOTNULL(sm, VMOD_SAINTMODE_MAGIC); CHECK_OBJ_NOTNULL(sm->be, DIRECTOR_MAGIC); if (sm->be == be) return (sm); } return (NULL); } VCL_VOID __match_proto__(td_saintmode_blacklist) vmod_blacklist(VRT_CTX, struct vmod_priv *priv, VCL_DURATION expires) { struct trouble *tp; struct saintmode_objs *sm_objs; struct vmod_saintmode_saintmode *sm; if (priv->priv == NULL) { VSLb(ctx->vsl, SLT_VCL_Error, "saintmode.blacklist(): " "Saintmode is not configured"); return; } CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC); if (ctx->method != VCL_MET_BACKEND_RESPONSE) { VSLb(ctx->vsl, SLT_VCL_Error, "saintmode.blacklist() called" " outside of vcl_backend_response"); return; } CHECK_OBJ_NOTNULL(ctx->bo, BUSYOBJ_MAGIC); CAST_OBJ_NOTNULL(sm_objs, priv->priv, SAINTMODE_OBJS_MAGIC); sm = find_sm(sm_objs, ctx->bo->director_resp); if (!sm) { VSLb(ctx->vsl, SLT_VCL_Error, "Error: saintmode.blacklist(): " "Saintmode not configured for this backend."); return; } ALLOC_OBJ(tp, TROUBLE_MAGIC); AN(tp); memcpy(tp->digest, ctx->bo->digest, sizeof tp->digest); tp->timeout = ctx->bo->t_prev + expires; pthread_mutex_lock(&sm->mtx); VTAILQ_INSERT_HEAD(&sm->troublelist, tp, list); sm->n_trouble++; VSLb(ctx->vsl, SLT_VCL_Log, "saintmode: object put on blacklist " "for backend %s for %.2f seconds", sm->be->vcl_name, expires); pthread_mutex_unlock(&sm->mtx); } VCL_STRING vmod_status(VRT_CTX, struct vmod_priv *priv) { struct saintmode_objs *sm_objs; struct vmod_saintmode_saintmode *sm; struct vsb *vsb; void *p; CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC); CAST_OBJ_NOTNULL(sm_objs, priv->priv, SAINTMODE_OBJS_MAGIC); vsb = VSB_new_auto(); AN(vsb); VSB_cat(vsb, "{\n\t\"saintmode\": [\n"); VTAILQ_FOREACH(sm, &sm_objs->sm_list, list) { CHECK_OBJ_NOTNULL(sm, VMOD_SAINTMODE_MAGIC); CHECK_OBJ_NOTNULL(sm->be, DIRECTOR_MAGIC); pthread_mutex_lock(&sm->mtx); VSB_cat(vsb, "\t\t{ "); VSB_printf(vsb, "\"name\": \"%s\", ", sm->sdir->vcl_name); VSB_printf(vsb, "\"backend\": \"%s\", ", sm->be->vcl_name); VSB_printf(vsb, "\"count\": \"%u\", ", sm->n_trouble); VSB_printf(vsb, "\"threshold\": \"%u\" ", sm->threshold); VSB_cat(vsb, "}"); if (VTAILQ_NEXT(sm, list) != NULL) VSB_cat(vsb, ","); pthread_mutex_unlock(&sm->mtx); VSB_cat(vsb, "\n"); } VSB_cat(vsb, "\t]\n}\n"); VSB_finish(vsb); p = WS_Copy(ctx->ws, VSB_data(vsb), -1); if (p == NULL) VSLb(ctx->vsl, SLT_VCL_Log, "saintmode.vmod_status: workspace overflow"); VSB_delete(vsb); return (p); } VCL_INT vmod_saintmode_blacklist_count(VRT_CTX, struct vmod_saintmode_saintmode *sm) { unsigned c = 0; CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC); CHECK_OBJ_NOTNULL(sm, VMOD_SAINTMODE_MAGIC); pthread_mutex_lock(&sm->mtx); c = sm->n_trouble; pthread_mutex_unlock(&sm->mtx); return (c); } /* All adapted from PHK's saintmode implementation in Varnish 3.0 */ static unsigned __match_proto__(vdi_healthy_f) healthy(const struct director *dir, const struct busyobj *bo, double *changed) { struct trouble *tr; struct trouble *tr2; unsigned retval, bl; struct vmod_saintmode_saintmode *sm; VTAILQ_HEAD(, trouble) troublelist; double now; CHECK_OBJ_NOTNULL(dir, DIRECTOR_MAGIC); CAST_OBJ_NOTNULL(sm, dir->priv, VMOD_SAINTMODE_MAGIC); CHECK_OBJ_NOTNULL(sm->be, DIRECTOR_MAGIC); CHECK_OBJ_ORNULL(bo, BUSYOBJ_MAGIC); /* If we don't have a bo with a digest to look at, we can't * know if we are on the trouble list or not. Fall back to the * backend's healthy() function. */ if (!bo) return (sm->be->healthy(sm->be, bo, changed)); /* Saintmode is disabled, or list is empty */ if (sm->threshold == 0 || sm->n_trouble == 0) return (sm->be->healthy(sm->be, bo, changed)); now = bo->t_prev; retval = 1; VTAILQ_INIT(&troublelist); pthread_mutex_lock(&sm->mtx); VTAILQ_FOREACH_SAFE(tr, &sm->troublelist, list, tr2) { CHECK_OBJ_NOTNULL(tr, TROUBLE_MAGIC); if (tr->timeout < now) { VTAILQ_REMOVE(&sm->troublelist, tr, list); VTAILQ_INSERT_HEAD(&troublelist, tr, list); sm->n_trouble--; continue; } if (!memcmp(tr->digest, bo->digest, sizeof tr->digest)) { retval = 0; break; } } bl = !retval; if (sm->threshold <= sm->n_trouble) retval = 0; pthread_mutex_unlock(&sm->mtx); if (bl) VSLb(((struct busyobj *)TRUST_ME(bo))->vsl, SLT_VCL_Log, "saintmode: unhealthy: object blacklisted for backend %s", sm->be->vcl_name); else if (retval == 0) VSLb(((struct busyobj *)TRUST_ME(bo))->vsl, SLT_VCL_Log, "saintmode: unhealthy: hit threshold for backend %s", sm->be->vcl_name); VTAILQ_FOREACH_SAFE(tr, &troublelist, list, tr2) FREE_OBJ(tr); return (retval ? sm->be->healthy(sm->be, bo, changed) : 0); } static const struct director * __match_proto__(vdi_resolve_f) resolve(const struct director *dir, struct worker *wrk, struct busyobj *bo) { 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)) return (NULL); return (sm->be); } VCL_VOID __match_proto__(td_saintmode_saintmode__init) vmod_saintmode__init(VRT_CTX, struct vmod_saintmode_saintmode **smp, const char *vcl_name, struct vmod_priv *priv, VCL_BACKEND be, VCL_INT threshold) { struct vmod_saintmode_saintmode *sm; struct saintmode_objs *sm_objs; (void)ctx; AN(smp); AZ(*smp); ALLOC_OBJ(sm, VMOD_SAINTMODE_MAGIC); AN(sm); *smp = sm; sm->threshold = threshold; sm->n_trouble = 0; AZ(pthread_mutex_init(&sm->mtx, NULL)); CHECK_OBJ_NOTNULL(be, DIRECTOR_MAGIC); sm->be = be; VTAILQ_INIT(&sm->troublelist); sm->sdir->magic = DIRECTOR_MAGIC; sm->sdir->resolve = resolve; sm->sdir->healthy = healthy; REPLACE(sm->sdir->vcl_name, vcl_name); sm->sdir->name = "saintmode"; sm->sdir->priv = sm; if (!priv->priv) { ALLOC_OBJ(sm_objs, SAINTMODE_OBJS_MAGIC); AN(sm_objs); VTAILQ_INIT(&sm_objs->sm_list); priv->priv = sm_objs; priv->free = free; } CAST_OBJ_NOTNULL(sm_objs, priv->priv, SAINTMODE_OBJS_MAGIC); VTAILQ_INSERT_TAIL(&sm_objs->sm_list, sm, list); } VCL_VOID __match_proto__(td_saintmode_saintmode__fini) vmod_saintmode__fini(struct vmod_saintmode_saintmode **smp) { struct trouble *tr, *tr2; struct vmod_saintmode_saintmode *sm; AN(smp); CHECK_OBJ_NOTNULL(*smp, VMOD_SAINTMODE_MAGIC); sm = *smp; *smp = NULL; VTAILQ_FOREACH_SAFE(tr, &sm->troublelist, list, tr2) { CHECK_OBJ_NOTNULL(tr, TROUBLE_MAGIC); VTAILQ_REMOVE(&sm->troublelist, tr, list); FREE_OBJ(tr); } free(sm->sdir->vcl_name); AZ(pthread_mutex_destroy(&sm->mtx)); /* We can no longer refer to the sm_objs after this * free. Should be fine, as this fini function will be called * first when the VCL is getting unloaded. */ FREE_OBJ(sm); } varnish-modules-0.12.1/src/vmod_saintmode.vcc000066400000000000000000000104111311124411200211720ustar00rootroot00000000000000$Module saintmode 3 Saint mode backend director DESCRIPTION =========== This VMOD provides saintmode functionality for Varnish Cache 4.1 and newer. The code is in part based on Poul-Henning Kamp's saintmode implementation in Varnish 3.0. Saintmode lets you deal with a backend that is failing in random ways for specific requests. It maintains a blacklist per backend, marking the backend as sick for specific objects. When the number of objects marked as sick for a backend reaches a set threshold, the backend is considered sick for all requests. Each blacklisted object carries a TTL, which denotes the time it will stay blacklisted. Saintmode in Varnish 4.1 is implemented as a director VMOD. We instantiate a saintmode object and give it a backend as an argument. The resulting object can then be used in place of the backend, with the effect that it also has added saintmode capabilities. Any director will then be able to use the saintmode backends, and as backends marked sick are skipped by the director, this provides a way to have fine grained health status on the backends, and making sure that retries get a different backend than the one which failed. .. vcl-start Example:: vcl 4.0; import saintmode; import directors; backend tile1 { .host = "192.0.2.11"; .port = "80"; } backend tile2 { .host = "192.0.2.12"; .port = "80"; } sub vcl_init { # Instantiate sm1, sm2 for backends tile1, tile2 # with 10 blacklisted objects as the threshold for marking the # whole backend sick. new sm1 = saintmode.saintmode(tile1, 10); new sm2 = saintmode.saintmode(tile2, 10); # Add both to a director. Use sm0, sm1 in place of tile1, tile2. # Other director types can be used in place of random. new imagedirector = directors.random(); imagedirector.add_backend(sm1.backend(), 1); imagedirector.add_backend(sm2.backend(), 1); } sub vcl_backend_fetch { # Get a backend from the director. # When returning a backend, the director will only return backends # saintmode says are healthy. set bereq.backend = imagedirector.backend(); } sub vcl_backend_response { if (beresp.status >= 500) { # This marks the backend as sick for this specific # object for the next 20s. saintmode.blacklist(20s); # Retry the request. This will result in a different backend # being used. return (retry); } } .. vcl-end $Function VOID blacklist(PRIV_VCL, 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 in vcl_backend_response. Example:: sub vcl_backend_response { if (beresp.http.broken-app) { saintmode.blacklist(20s); return (retry); } } $Function STRING status(PRIV_VCL) Returns a JSON formatted status string suitable for use in vcl_synth. :: sub vcl_recv { if (req.url ~ "/saintmode-status") { return (synth(700, "OK")); } } sub vcl_synth { if (resp.status == 700) { synthetic(saintmode.status()); return (deliver); } } Example JSON output: :: { "saintmode" : [ { "name": "sm1", "backend": "foo", "count": "3", "threshold": "10" }, { "name": "sm2", "backend": "bar", "count": "2", "threshold": "5" } ] } $Object saintmode(PRIV_VCL, 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. Example:: sub vcl_init { new sm = saintmode.saintmode(b, 10); } $Method BACKEND .backend() Used for assigning the backend from the saintmode object. Example:: sub vcl_backend_fetch { set bereq.backend = sm.backend(); } $Method INT .blacklist_count() Returns the number of objects currently blacklisted for a saintmode director object. Example: :: sub vcl_deliver { set resp.http.troublecount = sm.blacklist_count(); } varnish-modules-0.12.1/src/vmod_softpurge.c000066400000000000000000000063031311124411200207010ustar00rootroot00000000000000/*- * Copyright (c) 2012-2016 Varnish Software * * Original author: Lasse Karstensen * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "config.h" #include "cache/cache.h" #include "vrt.h" #include "vcl.h" #include "hash/hash_slinger.h" #include "vcc_softpurge_if.h" void vmod_softpurge(VRT_CTX) { struct objcore *oc, **ocp; struct objhead *oh; unsigned spc, nobj, n; double now; CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC); if (ctx->method != VCL_MET_HIT && ctx->method != VCL_MET_MISS) { VSLb(ctx->vsl, SLT_VCL_Error, "softpurge() is only " "available in vcl_hit{} and vcl_miss{}"); return; } CHECK_OBJ_NOTNULL(ctx->req, REQ_MAGIC); 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); nobj = 0; ocp = (void*)ctx->ws->f; now = ctx->req->t_prev; Lck_Lock(&oh->mtx); assert(oh->refcnt > 0); #if defined VARNISH_PLUS || defined OC_EF_DYING VTAILQ_FOREACH(oc, &oh->objcs, list) { CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC); assert(oc->objhead == oh); if (oc->flags & OC_F_BUSY) continue; if (oc->exp_flags & OC_EF_DYING) continue; if (spc < sizeof *ocp) break; oc->refcnt++; spc -= sizeof *ocp; ocp[nobj++] = oc; } #else VTAILQ_FOREACH(oc, &oh->objcs, hsh_list) { CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC); assert(oc->objhead == oh); if (oc->flags & (OC_F_BUSY|OC_F_DYING)) continue; if (spc < sizeof *ocp) break; oc->refcnt++; spc -= sizeof *ocp; ocp[nobj++] = oc; } #endif Lck_Unlock(&oh->mtx); for (n = 0; n < nobj; n++) { oc = ocp[n]; CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC); #if defined VARNISH_PLUS EXP_Rearm(ctx->req->wrk, oc, now, 0, oc->exp.grace, oc->exp.keep); #elif defined HAVE_OBJCORE_EXP EXP_Rearm(oc, now, 0, oc->exp.grace, oc->exp.keep); #else EXP_Rearm(oc, now, 0, oc->grace, oc->keep); #endif (void)HSH_DerefObjCore(ctx->req->wrk, &oc); } WS_Release(ctx->ws, 0); } varnish-modules-0.12.1/src/vmod_softpurge.vcc000066400000000000000000000023531311124411200212330ustar00rootroot00000000000000$Module softpurge Soft purge vmod DESCRIPTION =========== ``Softpurge`` is cache invalidation in Varnish that reduces TTL but keeps the grace value of a resource. This makes it possible to serve stale content to users if the backend is unavailable and fresh content can not be fetched. .. vcl-start Example:: vcl 4.0; import softpurge; backend default { .host = "192.0.2.11"; .port = "8080"; } sub vcl_recv { # Return early to avoid return(pass) by builtin VCL. if (req.method == "PURGE") { return (hash); } } sub vcl_backend_response { # Set object grace so we keep them around after TTL has expired. set beresp.grace = 10m; } sub vcl_hit { if (req.method == "PURGE") { softpurge.softpurge(); return (synth(200, "Successful softpurge")); } } sub vcl_miss { if (req.method == "PURGE") { softpurge.softpurge(); return (synth(200, "Successful softpurge")); } } .. vcl-end $Function VOID softpurge() Performs a soft purge. Valid in vcl_hit and vcl_miss. Example:: sub vcl_hit { if (req.method == "PURGE") { softpurge.softpurge(); } } varnish-modules-0.12.1/src/vmod_tcp.c000066400000000000000000000133031311124411200174470ustar00rootroot00000000000000/* * Copyright (c) 2012-2016 Varnish Software * * Author: Lasse Karstensen * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "config.h" #include #include #include "cache/cache.h" #include "vrt.h" #include "vcc_tcp_if.h" #include #include #include #include #ifndef TCP_CONGESTION #define TCP_CONGESTION 13 #endif #define TCP_CA_NAME_MAX 16 /* * Based on the information found here: * http://linuxgazette.net/136/pfeiffer.html */ void vmod_dump_info(const struct vrt_ctx *ctx) { CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC); if (ctx->req == NULL) { return; } #ifdef HAVE_TCP_INFO CHECK_OBJ_NOTNULL(ctx->req, REQ_MAGIC); CHECK_OBJ_NOTNULL(ctx->req->sp, SESS_MAGIC); AN(ctx->req->sp->fd); struct tcp_info tcpinfo; socklen_t tlen = sizeof(struct tcp_info); if (getsockopt(ctx->req->sp->fd, SOL_TCP, TCP_INFO, (void*)&tcpinfo, &tlen) < 0) { VSLb(ctx->vsl, SLT_VCL_Error, "getsockopt() failed"); return; } VSLb(ctx->vsl, SLT_VCL_Log, "tcpi: snd_mss=%i rcv_mss=%i lost=%i retrans=%i", tcpinfo.tcpi_snd_mss, tcpinfo.tcpi_rcv_mss, tcpinfo.tcpi_lost, tcpinfo.tcpi_retrans); VSLb(ctx->vsl, SLT_VCL_Log, "tcpi2: pmtu=%i rtt=%i rttvar=%i snd_cwnd=%i advmss=%i reordering=%i", tcpinfo.tcpi_pmtu, tcpinfo.tcpi_rtt, tcpinfo.tcpi_rttvar, tcpinfo.tcpi_snd_cwnd, tcpinfo.tcpi_advmss, tcpinfo.tcpi_reordering); #endif } /* TODO: Use a vmod object for these getters. */ VCL_REAL vmod_get_estimated_rtt(const struct vrt_ctx *ctx) { CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC); if (ctx->req == NULL) { return(0.0); } #ifdef HAVE_TCP_INFO CHECK_OBJ_NOTNULL(ctx->req, REQ_MAGIC); CHECK_OBJ_NOTNULL(ctx->req->sp, SESS_MAGIC); AN(ctx->req->sp->fd); struct tcp_info tcpinfo; socklen_t tlen = sizeof(struct tcp_info); if (getsockopt(ctx->req->sp->fd, SOL_TCP, TCP_INFO, (void*)&tcpinfo, &tlen) < 0) { VSLb(ctx->vsl, SLT_VCL_Error, "getsockopt() failed"); return(0.0); } /* * This should really take into account the rtt variance as well, * but I haven't got a clear view of how that would best be done within * the VCL constraints. */ return (tcpinfo.tcpi_rtt / 1000); #else return (-1.); #endif } // http://sgros.blogspot.com/2012/12/controlling-which-congestion-control.html // https://fasterdata.es.net/host-tuning/linux/ VCL_INT vmod_congestion_algorithm(const struct vrt_ctx *ctx, VCL_STRING new) { #ifdef HAVE_TCP_INFO char strategy[TCP_CA_NAME_MAX + 1]; #endif CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC); if (ctx->req == NULL) { return(-1); } #ifdef HAVE_TCP_INFO CHECK_OBJ_NOTNULL(ctx->req, REQ_MAGIC); CHECK_OBJ_NOTNULL(ctx->req->sp, SESS_MAGIC); AN(ctx->req->sp->fd); strncpy(strategy, new, TCP_CA_NAME_MAX); strategy[TCP_CA_NAME_MAX] = '\0'; socklen_t l = strlen(strategy); if (setsockopt(ctx->req->sp->fd, IPPROTO_TCP, TCP_CONGESTION, strategy, l) < 0) { VSLb(ctx->vsl, SLT_VCL_Error, "TCP_CONGESTION setsockopt() for \"%s\" failed.", strategy); return(-1); } # ifndef NDEBUG l = TCP_CA_NAME_MAX; if (getsockopt(ctx->req->sp->fd, IPPROTO_TCP, TCP_CONGESTION, strategy, &l) < 0) { VSLb(ctx->vsl, SLT_VCL_Error, "getsockopt() failed."); } else { VSLb(ctx->vsl, SLT_VCL_Log, "getsockopt() returned: %s", strategy); } # endif return(0); #else (void)new; return (-1); #endif /* ifdef HAVE_TCP_INFO */ } /* * net.ipv4.tcp_allowed_congestion_control = cubic reno * net.ipv4.tcp_available_congestion_control = cubic reno * net.ipv4.tcp_congestion_control = cubic * * */ VCL_VOID __match_proto__(td_std_set_socket_pace) vmod_set_socket_pace(const struct vrt_ctx *ctx, const long rate) { #ifndef SO_MAX_PACING_RATE #define SO_MAX_PACING_RATE 47 #endif CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC); #ifdef HAVE_TCP_INFO int pacerate = rate * 1024; if (setsockopt(ctx->req->sp->fd, SOL_SOCKET, SO_MAX_PACING_RATE, &pacerate, 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); # ifndef NDEBUG int retval; unsigned int current_rate = 0; socklen_t f = sizeof(current_rate); retval = getsockopt(ctx->req->sp->fd, SOL_SOCKET, SO_MAX_PACING_RATE, ¤t_rate, &f); VSLb(ctx->vsl, SLT_VCL_Log, "getsockopt() %i %i", retval, current_rate); # endif #else (void)rate; #endif /* ifdef HAVE_TCP_INFO */ } varnish-modules-0.12.1/src/vmod_tcp.vcc000066400000000000000000000035001311124411200177760ustar00rootroot00000000000000$Module tcp 3 TCP vmod DESCRIPTION =========== The TCP vmod opens for access and modification of client TCP connection attributes from VCL. Primary use is for rate limiting (pacing) using the fq network scheduler on recent Linux systems. .. vcl-start Example:: vcl 4.0; import tcp; backend default { .host = "192.0.2.11"; .port = "8080"; } sub vcl_recv { # Shape (pace) the data rate to avoid filling router buffers for a # single client. if (req.url ~ ".mp4$") { tcp.set_socket_pace(1000); # KB/s. } # We want to change the congestion control algorithm. if (tcp.get_estimated_rtt() > 300) { set req.http.x-tcp = tcp.congestion_algorithm("hybla"); } } .. vcl-end $Function INT congestion_algorithm(STRING algorithm) Set the client socket congestion control algorithm to S. Returns 0 on success, and -1 on error. Example:: sub vcl_recv { set req.http.x-tcp = tcp.congestion_algorithm("cubic"); } $Function VOID dump_info() Write the contents of the TCP_INFO data structure into varnishlog. Example:: tcp.dump_info(); Example varnishlog output:: - VCL_Log tcpi: snd_mss=1448 rcv_mss=536 lost=0 retrans=0 - VCL_Log tcpi2: pmtu=1500 rtt=152000 rttvar=76000 snd_cwnd=10 advmss=1448 reordering=3 - VCL_Log getsockopt() returned: cubic $Function REAL get_estimated_rtt() Get the estimated round-trip-time for the client socket. Unit: milliseconds. Example:: if (tcp.get_estimated_rtt() > 300) { std.log("client is far away."); } $Function 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) Example:: tcp.set_socket_pace(1000); varnish-modules-0.12.1/src/vmod_var.c000066400000000000000000000156361311124411200174640ustar00rootroot00000000000000/*- * Copyright (c) 2012-2016 Varnish Software * * Original author: Tollef Fog Heen * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "config.h" #include #include #include #include "cache/cache.h" #include "vrt.h" #include "vsa.h" #include "vcc_var_if.h" enum VAR_TYPE { STRING, INT, REAL, DURATION, IP }; struct var { unsigned magic; #define VAR_MAGIC 0x8A21A651 char *name; enum VAR_TYPE type; union { char *STRING; int INT; double REAL; double DURATION; VCL_IP IP; } value; VTAILQ_ENTRY(var) list; }; struct var_head { unsigned magic; #define VAR_HEAD_MAGIC 0x64F33E2F VTAILQ_HEAD(, var) vars; }; static VTAILQ_HEAD(, var) global_vars = VTAILQ_HEAD_INITIALIZER(global_vars); static pthread_mutex_t var_list_mtx = PTHREAD_MUTEX_INITIALIZER; static void vh_init(struct var_head *vh) { AN(vh); memset(vh, 0, sizeof *vh); vh->magic = VAR_HEAD_MAGIC; VTAILQ_INIT(&vh->vars); } static struct var * vh_get_var(struct var_head *vh, const char *name) { struct var *v; AN(vh); AN(name); VTAILQ_FOREACH(v, &vh->vars, list) { CHECK_OBJ_NOTNULL(v, VAR_MAGIC); AN(v->name); if (strcmp(v->name, name) == 0) return v; } return NULL; } static struct var * vh_get_var_alloc(struct var_head *vh, const char *name, const struct vrt_ctx *ctx) { struct var *v; v = vh_get_var(vh, name); if (!v) { /* Allocate and add */ v = (struct var*)WS_Alloc(ctx->ws, sizeof(struct var)); AN(v); v->magic = VAR_MAGIC; v->name = WS_Copy(ctx->ws, name, -1); AN(v->name); VTAILQ_INSERT_HEAD(&vh->vars, v, list); } return v; } static void free_func(void *p) { struct var_head *vh; CAST_OBJ_NOTNULL(vh, p, VAR_HEAD_MAGIC); FREE_OBJ(vh); } static struct var_head * get_vh(struct vmod_priv *priv) { struct var_head *vh; if (priv->priv == NULL) { ALLOC_OBJ(vh, VAR_HEAD_MAGIC); priv->priv = vh; priv->free = free_func; } else CAST_OBJ_NOTNULL(vh, priv->priv, VAR_HEAD_MAGIC); return (vh); } VCL_VOID vmod_set(const struct vrt_ctx *ctx, struct vmod_priv *priv, VCL_STRING name, VCL_STRING value) { vmod_set_string(ctx, priv, name, value); } VCL_STRING vmod_get(const struct vrt_ctx *ctx, struct vmod_priv *priv, VCL_STRING name) { return vmod_get_string(ctx, priv, name); } VCL_VOID vmod_set_string(const struct vrt_ctx *ctx, struct vmod_priv *priv, VCL_STRING name, VCL_STRING value) { struct var *v; if (name == NULL) return; v = vh_get_var_alloc(get_vh(priv), name, ctx); AN(v); v->type = STRING; if (value == NULL) value = ""; v->value.STRING = WS_Copy(ctx->ws, value, -1); } VCL_STRING vmod_get_string(const struct vrt_ctx *ctx, struct vmod_priv *priv, VCL_STRING name) { struct var *v; (void)ctx; if (name == NULL) return (NULL); v = vh_get_var(get_vh(priv), name); if (!v || v->type != STRING) return NULL; return (v->value.STRING); } VCL_VOID vmod_set_ip(const struct vrt_ctx *ctx, struct vmod_priv *priv, VCL_STRING name, VCL_IP ip) { struct var *v; if (name == NULL) return; v = vh_get_var_alloc(get_vh(priv), name, ctx); AN(v); v->type = IP; AN(ip); v->value.IP = WS_Copy(ctx->ws, ip, vsa_suckaddr_len);; } VCL_IP vmod_get_ip(const struct vrt_ctx *ctx, struct vmod_priv *priv, VCL_STRING name) { struct var *v; (void)ctx; if (name == NULL) return (NULL); v = vh_get_var(get_vh(priv), name); if (!v || v->type != IP) return NULL; return (v->value.IP); } #define VMOD_SET_X(vcl_type_u, vcl_type_l, ctype) \ VCL_VOID \ vmod_set_##vcl_type_l(const struct vrt_ctx *ctx, struct vmod_priv *priv,\ const char *name, ctype value) \ { \ struct var *v; \ if (name == NULL) \ return; \ v = vh_get_var_alloc(get_vh(priv), name, ctx); \ AN(v); \ v->type = vcl_type_u; \ v->value.vcl_type_u = value; \ } VMOD_SET_X(INT, int, VCL_INT) VMOD_SET_X(REAL, real, VCL_REAL) VMOD_SET_X(DURATION, duration, VCL_DURATION) #define VMOD_GET_X(vcl_type_u, vcl_type_l, ctype) \ ctype \ vmod_get_##vcl_type_l(const struct vrt_ctx *ctx, struct vmod_priv *priv,\ const char *name) \ { \ struct var *v; \ (void)ctx; \ \ if (name == NULL) \ return 0; \ v = vh_get_var(get_vh(priv), name); \ \ if (!v || v->type != vcl_type_u) \ return 0; \ return (v->value.vcl_type_u); \ } VMOD_GET_X(INT, int, VCL_INT) VMOD_GET_X(REAL, real, VCL_REAL) VMOD_GET_X(DURATION, duration, VCL_DURATION) VCL_VOID vmod_clear(const struct vrt_ctx *ctx, struct vmod_priv *priv) { struct var_head *vh; (void)ctx; vh = get_vh(priv); vh_init(vh); } VCL_VOID vmod_global_set(const struct vrt_ctx *ctx, VCL_STRING name, VCL_STRING value) { struct var *v; (void)ctx; if (name == NULL) return; AZ(pthread_mutex_lock(&var_list_mtx)); VTAILQ_FOREACH(v, &global_vars, list) { CHECK_OBJ_NOTNULL(v, VAR_MAGIC); AN(v->name); if (strcmp(v->name, name) == 0) break; } if (v) { VTAILQ_REMOVE(&global_vars, v, list); free(v->name); v->name = NULL; } else ALLOC_OBJ(v, VAR_MAGIC); AN(v); v->name = strdup(name); AN(v->name); VTAILQ_INSERT_HEAD(&global_vars, v, list); if (v->type == STRING) free(v->value.STRING); v->value.STRING = NULL; v->type = STRING; if (value != NULL) v->value.STRING = strdup(value); AZ(pthread_mutex_unlock(&var_list_mtx)); } VCL_STRING vmod_global_get(const struct vrt_ctx *ctx, VCL_STRING name) { struct var *v; const char *r = NULL; AZ(pthread_mutex_lock(&var_list_mtx)); VTAILQ_FOREACH(v, &global_vars, list) { CHECK_OBJ_NOTNULL(v, VAR_MAGIC); AN(v->name); if (strcmp(v->name, name) == 0) break; } if (v && v->value.STRING != NULL) { r = WS_Copy(ctx->ws, v->value.STRING, -1); AN(r); } AZ(pthread_mutex_unlock(&var_list_mtx)); return(r); } varnish-modules-0.12.1/src/vmod_var.vcc000066400000000000000000000060411311124411200200030ustar00rootroot00000000000000$Module var 3 Variable support for Varnish VCL This VMOD implements basic variable support in VCL. It supports strings, integers and real numbers. There are methods to get and set each data type. Global variables have a lifespan that extends across requests and VCLs, for as long as the vmod is loaded. The remaining functions have PRIV_TASK lifespan and are local to a single request or backend request. .. vcl-start Example:: vcl 4.0; import var; backend default { .host = "192.0.2.11"; .port = "8080"; } sub vcl_recv { # Set and get some values. var.set("foo", "bar"); set req.http.x-foo = var.get("foo"); var.set_int("ten", 10); var.set_int("five", 5); set req.http.twenty = var.get_int("ten") + var.get_int("five") + 5; # VCL will use the first token to decide final data type. Headers are strings. # set req.http.X-lifetime = var.get_int("ten") + " seconds"; # Won't work. set req.http.X-lifetime = "" + var.get_int("ten") + " seconds"; # Works! var.set_duration("timedelta", 1m); # 60s set req.http.d1 = var.get_duration("timedelta"); var.set_ip("endpoint", client.ip); set req.http.x-client = var.get_ip("endpoint"); # Unset all non-global variables. var.clear(); # Demonstrate use of global variables as state flags. if (req.url ~ "/close$") { var.global_set("open", "no"); } else if (req.url ~ "/open$") { var.global_set("open", "yes"); } if (var.global_get("open") != "yes") { return (synth(200, "We are currently closed, sorry!")); } } .. vcl-end $Function VOID set(PRIV_TASK, STRING key, STRING value) Set `key` to `value`. $Function STRING get(PRIV_TASK, STRING ) Get `key` with data type STRING. If stored `key` is not a STRING an empty string is returned. $Function VOID global_set(STRING, STRING) $Function STRING global_get(STRING) $Function VOID set_int(PRIV_TASK, STRING key, INT value) Set `key` to `value`. $Function INT get_int(PRIV_TASK, STRING key) Get `key` with data type INT. If stored `key` is not an INT zero will be returned. $Function VOID set_string(PRIV_TASK, STRING key, STRING value) Identical to set(). $Function STRING get_string(PRIV_TASK, STRING key) Identical to get(). $Function VOID set_real(PRIV_TASK, STRING key, REAL value) Set `key` to `value`. $Function REAL get_real(PRIV_TASK, STRING key) Get `key` with data type REAL. If stored `key` is not a REAL zero will be returned. $Function VOID set_duration(PRIV_TASK, STRING key, DURATION value) Set `key` to `value`. $Function DURATION get_duration(PRIV_TASK, STRING key) Get `key` with data type DURATION. If stored `key` is not a DURATION zero will be returned. $Function VOID set_ip(PRIV_TASK, STRING key, IP value) Set `key` to `value`. $Function IP get_ip(PRIV_TASK, STRING key) Get `key` with data type IP. If stored `key` is not an IP null will be returned. $Function VOID clear(PRIV_TASK) Clear all non-global variables. varnish-modules-0.12.1/src/vmod_vsthrottle.c000066400000000000000000000153051311124411200211030ustar00rootroot00000000000000/*- * Copyright (c) 2013-2015 Varnish Software * All rights reserved. * * Author: Dag Haavi Finstad * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "config.h" #include #include #include #include #include #include #include #include "vcl.h" #include "vdef.h" #include "vrt.h" #include "vas.h" #include "vtim.h" #include "miniobj.h" #include "vsha256.h" #include "vtree.h" #include #include "vcc_vsthrottle_if.h" /* Represents a token bucket for a specific key. */ struct tbucket { unsigned magic; #define TBUCKET_MAGIC 0x53345eb9 unsigned char digest[SHA256_LEN]; double last_used; double period; long tokens; long capacity; VRB_ENTRY(tbucket) tree; }; static int 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); /* To lessen potential mutex contention, we partition the buckets into N_PART partitions. */ #define N_PART 16 /* must be 2^n */ #define N_PART_MASK (N_PART - 1) static unsigned n_init; static pthread_mutex_t init_mtx = PTHREAD_MUTEX_INITIALIZER; /* GC_INTVL: How often (in #calls per partition) we invoke the garbage collector. */ #define GC_INTVL 1000 static void run_gc(double now, unsigned part); static struct vsthrottle { unsigned magic; #define VSTHROTTLE_MAGIC 0x99fdbef8 pthread_mutex_t mtx; struct tbtree buckets; unsigned gc_count; } vsthrottle[N_PART]; static struct tbucket * tb_alloc(const unsigned char *digest, long limit, double period, double now) { struct tbucket *tb = malloc(sizeof *tb); AN(tb); memcpy(tb->digest, digest, sizeof tb->digest); tb->magic = TBUCKET_MAGIC; tb->last_used = now; tb->period = period; tb->tokens = limit; tb->capacity = limit; return (tb); } static struct tbucket * get_bucket(const unsigned char *digest, long limit, double period, double now) { struct tbucket *b; struct tbucket k; unsigned part = digest[0] & N_PART_MASK; struct vsthrottle *v = &vsthrottle[part]; INIT_OBJ(&k, TBUCKET_MAGIC); memcpy(&k.digest, digest, sizeof k.digest); b = VRB_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)); } return (b); } static void calc_tokens(struct tbucket *b, double now) { double delta = now - b->last_used; assert(delta >= 0); b->tokens += (long) ((delta / b->period) * b->capacity); if (b->tokens > b->capacity) b->tokens = b->capacity; /* VSL(SLT_VCL_Log, 0, "tokens: %ld", b->tokens); */ } static void do_digest(unsigned char *out, const char *s, VCL_INT l, VCL_DURATION p) { SHA256_CTX sctx; SHA256_Init(&sctx); SHA256_Update(&sctx, s, strlen(s)); SHA256_Update(&sctx, &l, sizeof (l)); SHA256_Update(&sctx, &p, sizeof (p)); SHA256_Final(out, &sctx); } VCL_BOOL vmod_is_denied(VRT_CTX, VCL_STRING key, VCL_INT limit, VCL_DURATION period) { unsigned ret = 1; struct tbucket *b; double now; struct vsthrottle *v; unsigned char digest[SHA256_LEN]; unsigned part; (void)ctx; if (!key) return (1); do_digest(digest, key, limit, period); 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); calc_tokens(b, now); if (b->tokens > 0) { b->tokens -= 1; ret = 0; b->last_used = now; } v->gc_count++; if (v->gc_count == GC_INTVL) { run_gc(now, part); v->gc_count = 0; } AZ(pthread_mutex_unlock(&v->mtx)); return (ret); } /* Clean up expired entries. */ static void run_gc(double now, unsigned part) { struct tbucket *x, *y; struct tbtree *buckets = &vsthrottle[part].buckets; /* XXX: Assert mtx is held ... */ VRB_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); } } } VCL_INT vmod_remaining(VRT_CTX, VCL_STRING key, VCL_INT limit, VCL_DURATION period) { unsigned ret; struct tbucket *b; double now; struct vsthrottle *v; unsigned char digest[SHA256_LEN]; unsigned part; (void)ctx; if (!key) return (-1); do_digest(digest, key, limit, period); 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); calc_tokens(b, now); ret = b->tokens; AZ(pthread_mutex_unlock(&v->mtx)); return (ret); } static void fini(void *priv) { assert(priv == &n_init); AZ(pthread_mutex_lock(&init_mtx)); assert(n_init > 0); n_init--; if (n_init == 0) { struct tbucket *x, *y; unsigned p; for (p = 0; p < N_PART; ++p ) { struct vsthrottle *v = &vsthrottle[p]; VRB_FOREACH_SAFE(x, tbtree, &v->buckets, y) { CHECK_OBJ_NOTNULL(x, TBUCKET_MAGIC); VRB_REMOVE(tbtree, &v->buckets, x); free(x); } } } AZ(pthread_mutex_unlock(&init_mtx)); } int event_function(VRT_CTX, struct vmod_priv *priv, enum vcl_event_e e) { if (e != VCL_EVENT_LOAD) return (0); (void)ctx; priv->priv = &n_init; priv->free = fini; AZ(pthread_mutex_lock(&init_mtx)); if (n_init == 0) { unsigned p; for (p = 0; p < N_PART; ++p) { struct vsthrottle *v = &vsthrottle[p]; v->magic = VSTHROTTLE_MAGIC; AZ(pthread_mutex_init(&v->mtx, NULL)); VRB_INIT(&v->buckets); } } n_init++; AZ(pthread_mutex_unlock(&init_mtx)); return (0); } varnish-modules-0.12.1/src/vmod_vsthrottle.vcc000066400000000000000000000056411311124411200214360ustar00rootroot00000000000000$Module vsthrottle 3 Throttling VMOD DESCRIPTION =========== A Varnish vmod for rate-limiting traffic on a single Varnish server. Offers a simple interface for throttling traffic on a per-key basis to a specific request rate. Keys can be specified from any VCL string, e.g. based on client.ip, a specific cookie value, an API token, etc. 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'. 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. Memory usage is around 100 bytes per key tracked. .. _token bucket algorithm: http://en.wikipedia.org/wiki/Token_bucket .. vcl-start Example:: vcl 4.0; import vsthrottle; backend default { .host = "192.0.2.11"; .port = "8080"; } 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 return (synth(429, "Too Many Requests")); } # There is a quota per API key that must be fulfilled. if (vsthrottle.is_denied("apikey:" + req.http.Key, 30, 60s)) { return (synth(429, "Too Many Requests")); } # Only allow a few POST/PUTs per client. if (req.method == "POST" || req.method == "PUT") { if (vsthrottle.is_denied("rw" + client.identity, 2, 10s)) { return (synth(429, "Too Many Requests")); } } } .. vcl-end $Event event_function $Function BOOL is_denied(STRING key, INT limit, DURATION period) 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 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. Example :: sub vcl_recv { if (vsthrottle.is_denied(client.identity, 15, 10s)) { # Client has exceeded 15 reqs per 10s return (synth(429, "Too Many Requests")); } # ... } $Function INT remaining(STRING key, INT limit, DURATION period) Arguments: - key: A unique identifier to define what is being throttled - limit: How many requests in the specified period - period: The time period Description Get the current number of tokens for a given token bucket. This can be used to create a response header to inform clients of their current quota. Example :: sub vcl_deliver { set resp.http.X-RateLimit-Remaining = vsthrottle.remaining(client.identity, 15, 10s); } varnish-modules-0.12.1/src/vmod_xkey.c000066400000000000000000000354451311124411200176540ustar00rootroot00000000000000/*- * Copyright (c) 2015 Varnish Software * All rights reserved. * * Author: Martin Blix Grydeland * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "config.h" #include #include #include #include "vcl.h" #include "cache/cache.h" #include "vrt.h" #include "vsha256.h" #include "vtree.h" #include "vcc_xkey_if.h" static pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER; static int n_init = 0; uintptr_t xkey_cb_handle; struct xkey_hashkey { char digest[DIGEST_LEN]; VRB_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); struct xkey_ptrkey { uintptr_t ptr; VRB_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) struct xkey_hashhead; struct xkey_ochead; struct xkey_oc; struct xkey_hashhead { struct xkey_hashkey key; unsigned magic; #define XKEY_HASHHEAD_MAGIC 0x9553B65C VTAILQ_ENTRY(xkey_hashhead) list; VTAILQ_HEAD(,xkey_oc) ocs; }; struct xkey_ochead { struct xkey_ptrkey key; unsigned magic; #define XKEY_OCHEAD_MAGIC 0x1E62445D VTAILQ_ENTRY(xkey_ochead) list; VTAILQ_HEAD(,xkey_oc) ocs; }; struct xkey_oc { unsigned magic; #define XKEY_OC_MAGIC 0xC688B0C0 VTAILQ_ENTRY(xkey_oc) list_ochead; VTAILQ_ENTRY(xkey_oc) list_hashhead; struct xkey_hashhead *hashhead; struct objcore *objcore; }; #define POOL_MAX 5 static struct { VTAILQ_HEAD(,xkey_hashhead) hashheads; int n_hashhead; VTAILQ_HEAD(,xkey_ochead) ocheads; int n_ochead; VTAILQ_HEAD(,xkey_oc) ocs; int n_oc; } xkey_pool = { VTAILQ_HEAD_INITIALIZER(xkey_pool.hashheads), 0, VTAILQ_HEAD_INITIALIZER(xkey_pool.ocheads), 0, VTAILQ_HEAD_INITIALIZER(xkey_pool.ocs), 0 }; /*******************/ VRB_GENERATE(xkey_hashtree, xkey_hashkey, entry, xkey_hashcmp); VRB_GENERATE(xkey_octree, xkey_ptrkey, entry, xkey_ptrcmp); static int xkey_hashcmp(const struct xkey_hashkey *k1, const struct xkey_hashkey *k2) { return (memcmp(k1->digest, k2->digest, sizeof(k1->digest))); } static int xkey_ptrcmp(const struct xkey_ptrkey *k1, const struct xkey_ptrkey *k2) { if (k1->ptr < k2->ptr) return (-1); if (k1->ptr > k2->ptr) return (1); return (0); } static struct xkey_hashhead * xkey_hashhead_new(void) { struct xkey_hashhead *head; if (xkey_pool.n_hashhead > 0) { head = VTAILQ_FIRST(&xkey_pool.hashheads); CHECK_OBJ_NOTNULL(head, XKEY_HASHHEAD_MAGIC); VTAILQ_REMOVE(&xkey_pool.hashheads, head, list); xkey_pool.n_hashhead--; } else { ALLOC_OBJ(head, XKEY_HASHHEAD_MAGIC); AN(head); VTAILQ_INIT(&head->ocs); } return (head); } static void xkey_hashhead_delete(struct xkey_hashhead **phead) { struct xkey_hashhead *head; head = *phead; *phead = NULL; CHECK_OBJ_NOTNULL(head, XKEY_HASHHEAD_MAGIC); AN(VTAILQ_EMPTY(&head->ocs)); if (xkey_pool.n_hashhead < POOL_MAX) { memset(&head->key, 0, sizeof(head->key)); VTAILQ_INSERT_HEAD(&xkey_pool.hashheads, head, list); xkey_pool.n_hashhead++; return; } FREE_OBJ(head); } static struct xkey_ochead * xkey_ochead_new(void) { struct xkey_ochead *head; if (xkey_pool.n_ochead > 0) { head = VTAILQ_FIRST(&xkey_pool.ocheads); VTAILQ_REMOVE(&xkey_pool.ocheads, head, list); xkey_pool.n_ochead--; } else { ALLOC_OBJ(head, XKEY_OCHEAD_MAGIC); AN(head); VTAILQ_INIT(&head->ocs); } return (head); } static void xkey_ochead_delete(struct xkey_ochead **phead) { struct xkey_ochead *head; head = *phead; *phead = NULL; CHECK_OBJ_NOTNULL(head, XKEY_OCHEAD_MAGIC); AN(VTAILQ_EMPTY(&head->ocs)); if (xkey_pool.n_ochead < POOL_MAX) { memset(&head->key, 0, sizeof(head->key)); VTAILQ_INSERT_HEAD(&xkey_pool.ocheads, head, list); xkey_pool.n_ochead++; return; } FREE_OBJ(head); } static struct xkey_oc * xkey_oc_new(void) { struct xkey_oc *oc; if (xkey_pool.n_oc > 0) { oc = VTAILQ_FIRST(&xkey_pool.ocs); VTAILQ_REMOVE(&xkey_pool.ocs, oc, list_hashhead); xkey_pool.n_oc--; } else { ALLOC_OBJ(oc, XKEY_OC_MAGIC); AN(oc); } return (oc); } static void xkey_oc_delete(struct xkey_oc **poc) { struct xkey_oc *oc; oc = *poc; *poc = NULL; CHECK_OBJ_NOTNULL(oc, XKEY_OC_MAGIC); AZ(oc->objcore); if (xkey_pool.n_oc < POOL_MAX) { VTAILQ_INSERT_HEAD(&xkey_pool.ocs, oc, list_hashhead); xkey_pool.n_oc++; return; } FREE_OBJ(oc); } static struct xkey_hashhead * xkey_hashtree_lookup(const unsigned char *digest, unsigned len) { struct xkey_hashkey key, *pkey; struct xkey_hashhead *head = NULL; AN(digest); assert(len == sizeof(key.digest)); memcpy(&key.digest, digest, len); pkey = VRB_FIND(xkey_hashtree, &xkey_hashtree, &key); if (pkey != NULL) CAST_OBJ_NOTNULL(head, (void *)pkey, XKEY_HASHHEAD_MAGIC); return (head); } static struct xkey_hashhead * xkey_hashtree_insert(const unsigned char *digest, unsigned len) { struct xkey_hashkey *key; struct xkey_hashhead *head; AN(digest); 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); if (key != NULL) { xkey_hashhead_delete(&head); CAST_OBJ_NOTNULL(head, (void *)key, XKEY_HASHHEAD_MAGIC); } return (head); } static struct xkey_ochead * xkey_octree_lookup(uintptr_t ptr) { struct xkey_ptrkey key, *pkey; struct xkey_ochead *head = NULL; AN(ptr); key.ptr = ptr; pkey = VRB_FIND(xkey_octree, &xkey_octree, &key); if (pkey) CAST_OBJ_NOTNULL(head, (void *)pkey, XKEY_OCHEAD_MAGIC); return (head); } static struct xkey_ochead * xkey_octree_insert(uintptr_t ptr) { struct xkey_ptrkey *key; struct xkey_ochead *head; AN(ptr); head = xkey_ochead_new(); head->key.ptr = ptr; key = VRB_INSERT(xkey_octree, &xkey_octree, &head->key); if (key != NULL) { xkey_ochead_delete(&head); CAST_OBJ_NOTNULL(head, (void *)key, XKEY_OCHEAD_MAGIC); } return (head); } static void xkey_insert(struct objcore *objcore, const unsigned char *digest, unsigned len) { struct xkey_ochead *ochead; struct xkey_hashhead *hashhead; struct xkey_oc *oc; CHECK_OBJ_NOTNULL(objcore, OBJCORE_MAGIC); AN(digest); assert(len == DIGEST_LEN); ochead = xkey_octree_insert((uintptr_t)objcore); AN(ochead); hashhead = xkey_hashtree_insert(digest, len); AN(hashhead); oc = xkey_oc_new(); AN(oc); VTAILQ_INSERT_TAIL(&ochead->ocs, oc, list_ochead); VTAILQ_INSERT_TAIL(&hashhead->ocs, oc, list_hashhead); oc->objcore = objcore; oc->hashhead = hashhead; } static void xkey_remove(struct xkey_ochead **pochead) { struct xkey_ochead *ochead; struct xkey_hashhead *hashhead; struct xkey_oc *oc, *oc2; ochead = *pochead; *pochead = NULL; CHECK_OBJ_NOTNULL(ochead, XKEY_OCHEAD_MAGIC); VTAILQ_FOREACH_SAFE(oc, &ochead->ocs, list_ochead, oc2) { hashhead = oc->hashhead; oc->hashhead = NULL; VTAILQ_REMOVE(&hashhead->ocs, oc, list_hashhead); if (VTAILQ_EMPTY(&hashhead->ocs)) { VRB_REMOVE(xkey_hashtree, &xkey_hashtree, &hashhead->key); xkey_hashhead_delete(&hashhead); } oc->objcore = NULL; VTAILQ_REMOVE(&ochead->ocs, oc, list_ochead); xkey_oc_delete(&oc); } AN(VTAILQ_EMPTY(&ochead->ocs)); VRB_REMOVE(xkey_octree, &xkey_octree, &ochead->key); xkey_ochead_delete(&ochead); } static void xkey_cleanup(void) { struct xkey_hashkey *hashkey; struct xkey_hashhead *hashhead; struct xkey_ptrkey *ockey; struct xkey_ochead *ochead; struct xkey_oc *oc; VRB_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); VRB_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); while (!VTAILQ_EMPTY(&xkey_pool.hashheads)) { hashhead = VTAILQ_FIRST(&xkey_pool.hashheads); VTAILQ_REMOVE(&xkey_pool.hashheads, hashhead, list); FREE_OBJ(hashhead); } while (!VTAILQ_EMPTY(&xkey_pool.ocheads)) { ochead = VTAILQ_FIRST(&xkey_pool.ocheads); VTAILQ_REMOVE(&xkey_pool.ocheads, ochead, list); FREE_OBJ(ochead); } while (!VTAILQ_EMPTY(&xkey_pool.ocs)) { oc = VTAILQ_FIRST(&xkey_pool.ocs); VTAILQ_REMOVE(&xkey_pool.ocs, oc, list_ochead); FREE_OBJ(oc); } } /**************************/ static unsigned xkey_tok(const char **b, const char **e) { const char *t; AN(b); AN(e); t = *b; AN(t); while (isblank(*t)) t++; *b = t; while (*t != '\0' && !isblank(*t)) t++; *e = t; return (*b < *e); } static void xkey_cb_insert(struct worker *wrk, struct objcore *objcore) { SHA256_CTX sha_ctx; unsigned char digest[DIGEST_LEN]; const char hdr_xkey[] = "xkey:"; const char hdr_h2[] = "X-HashTwo:"; const char *ep, *sp; CHECK_OBJ_NOTNULL(objcore, OBJCORE_MAGIC); HTTP_FOREACH_PACK(wrk, objcore, sp) { if (strncasecmp(sp, hdr_xkey, sizeof(hdr_xkey) - 1) && strncasecmp(sp, hdr_h2, sizeof(hdr_h2) - 1)) continue; sp = strchr(sp, ':'); AN(sp); sp++; while (xkey_tok(&sp, &ep)) { SHA256_Init(&sha_ctx); SHA256_Update(&sha_ctx, sp, ep - sp); SHA256_Final(digest, &sha_ctx); AZ(pthread_mutex_lock(&mtx)); xkey_insert(objcore, digest, sizeof(digest)); AZ(pthread_mutex_unlock(&mtx)); sp = ep; } } } static void xkey_cb_remove(struct objcore *objcore) { struct xkey_ochead *ochead; CHECK_OBJ_NOTNULL(objcore, OBJCORE_MAGIC); AZ(pthread_mutex_lock(&mtx)); ochead = xkey_octree_lookup((uintptr_t)objcore); if (ochead != NULL) xkey_remove(&ochead); AZ(pthread_mutex_unlock(&mtx)); } #if defined VARNISH_PLUS || !defined OEV_INSERT static void __match_proto__(exp_callback_f) xkey_cb(struct worker *wrk, struct objcore *objcore, enum exp_event_e event, void *priv) { CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC); CHECK_OBJ_NOTNULL(objcore, OBJCORE_MAGIC); AZ(priv); switch (event) { case EXP_INSERT: case EXP_INJECT: xkey_cb_insert(wrk, objcore); break; case EXP_REMOVE: xkey_cb_remove(objcore); break; default: WRONG("enum exp_event_e"); } } #else static void __match_proto__(obj_event_f) xkey_cb(struct worker *wrk, void *priv, struct objcore *oc, unsigned ev) { CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC); CHECK_OBJ_NOTNULL(oc, OBJCORE_MAGIC); AZ(priv); AN(ev); switch (ev) { case OEV_INSERT: xkey_cb_insert(wrk, oc); break; case OEV_EXPIRE: xkey_cb_remove(oc); break; default: WRONG("Unexpected event"); } } #endif /**************************/ static VCL_INT purge(VRT_CTX, VCL_STRING key, VCL_INT do_soft) { SHA256_CTX sha_ctx; unsigned char digest[DIGEST_LEN]; struct xkey_hashhead *hashhead; struct xkey_oc *oc; const char *ep, *sp; int i = 0; CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC); CHECK_OBJ_NOTNULL(ctx->req, REQ_MAGIC); CHECK_OBJ_NOTNULL(ctx->req->wrk, WORKER_MAGIC); if (!key || !*key) return (0); sp = key; AZ(pthread_mutex_lock(&mtx)); while (xkey_tok(&sp, &ep)) { SHA256_Init(&sha_ctx); SHA256_Update(&sha_ctx, sp, ep - sp); SHA256_Final(digest, &sha_ctx); hashhead = xkey_hashtree_lookup(digest, sizeof(digest)); if (hashhead != NULL) { VTAILQ_FOREACH(oc, &hashhead->ocs, list_hashhead) { CHECK_OBJ_NOTNULL(oc->objcore, OBJCORE_MAGIC); if (oc->objcore->flags & OC_F_BUSY) continue; #if defined HAVE_OBJCORE_EXP if (do_soft && oc->objcore->exp.ttl <= (ctx->now - oc->objcore->exp.t_origin)) continue; #else if (do_soft && oc->objcore->ttl <= (ctx->now - oc->objcore->t_origin)) continue; #endif #ifdef VARNISH_PLUS if (do_soft) EXP_Rearm(ctx->req->wrk, oc->objcore, ctx->now, 0, oc->objcore->exp.grace, oc->objcore->exp.keep); else EXP_Rearm(ctx->req->wrk, oc->objcore, oc->objcore->exp.t_origin, 0, 0, 0); #elif defined HAVE_OBJCORE_EXP if (do_soft) EXP_Rearm(oc->objcore, ctx->now, 0, oc->objcore->exp.grace, oc->objcore->exp.keep); else EXP_Rearm(oc->objcore, oc->objcore->exp.t_origin, 0, 0, 0); #else if (do_soft) EXP_Rearm(oc->objcore, ctx->now, 0, oc->objcore->grace, oc->objcore->keep); else EXP_Rearm(oc->objcore, oc->objcore->t_origin, 0, 0, 0); #endif i++; } } sp = ep; } AZ(pthread_mutex_unlock(&mtx)); return (i); } VCL_INT __match_proto__(td_xkey_purge) vmod_purge(VRT_CTX, VCL_STRING key) { return (purge(ctx, key, 0)); } VCL_INT __match_proto__(td_xkey_softpurge) vmod_softpurge(VRT_CTX, VCL_STRING key) { return (purge(ctx, key, 1)); } int __match_proto__(vmod_event_f) vmod_event(VRT_CTX, struct vmod_priv *priv, enum vcl_event_e e) { (void)ctx; (void)priv; switch (e) { case VCL_EVENT_LOAD: AZ(pthread_mutex_lock(&mtx)); if (n_init == 0) #if defined VARNISH_PLUS || !defined OEV_INSERT xkey_cb_handle = EXP_Register_Callback(xkey_cb, NULL); #else xkey_cb_handle = ObjSubscribeEvents(xkey_cb, NULL, OEV_INSERT|OEV_EXPIRE); #endif AN(xkey_cb_handle); n_init++; AZ(pthread_mutex_unlock(&mtx)); break; case VCL_EVENT_DISCARD: AZ(pthread_mutex_lock(&mtx)); assert(n_init > 0); n_init--; AN(xkey_cb_handle); if (n_init == 0) { /* Do cleanup */ #if defined VARNISH_PLUS || !defined OEV_INSERT EXP_Deregister_Callback(&xkey_cb_handle); #else ObjUnsubscribeEvents(&xkey_cb_handle); #endif AZ(xkey_cb_handle); xkey_cleanup(); } AZ(pthread_mutex_unlock(&mtx)); break; default: break; } return (0); } varnish-modules-0.12.1/src/vmod_xkey.vcc000066400000000000000000000062371311124411200202020ustar00rootroot00000000000000$Module xkey 3 Surrogate keys support for Varnish Cache DESCRIPTION =========== This vmod adds secondary hashes to objects, allowing fast purging on all objects with this hash key. You can use this to indicate relationships, a bit like a "tag". Then clear out all object that have this tag set. Two good use cases are news sites, where one might add all the stories mentioned on a particular page by article ID, letting each article referenced create an xkey header. 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`` response headers. Preferably the secondary hash keys are set from the backend application, but the header can also be set from VCL in ``vcl_backend_response``. .. vcl-start VCL example:: vcl 4.0; import xkey; backend default { .host = "192.0.2.11"; .port = "8080"; } acl purgers { "203.0.113.0"/24; } sub vcl_recv { if (req.method == "PURGE") { if (client.ip !~ purgers) { return (synth(403, "Forbidden")); } if (req.http.xkey) { set req.http.n-gone = xkey.purge(req.http.key); # or: set req.http.n-gone = xkey.softpurge(req.http.key) return (synth(200, "Invalidated "+req.http.n-gone+" objects")); } else { return (purge); } } } .. vcl-end Example ------- On an e-commerce site we have the backend application issue an xkey header for every product that is referenced on that page. So the header for a certain page might look like this:: HTTP/1.1 OK Server: Apache/2.2.15 xkey: 8155054 xkey: 166412 xkey: 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 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:: GET / HTTP/1.1 Host: www.example.com xkey-purge: 166412 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. Varnish will find the objects and clear them out, responding with:: HTTP/1.1 200 Purged Date: Thu, 24 Apr 2014 17:08:28 GMT X-Varnish: 1990228115 Via: 1.1 Varnish The objects are now cleared. $Event vmod_event $Function INT purge(STRING keys) Description Purges all objects hashed on any key found in the ``keys`` argument. Returns the number of objects that were purged. The ``keys`` may contain a list of space-separated ids. $Function INT softpurge(STRING keys) Description Performs a "soft purge" for all objects hashed on any key found in the ``keys`` argument. Returns the number of objects that were purged. 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. varnish-modules-0.12.1/src/vtree.h000066400000000000000000000634411311124411200167760ustar00rootroot00000000000000/* $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_ */ varnish-modules-0.12.1/test-vclsnippet000077500000000000000000000014701311124411200177750ustar00rootroot00000000000000#!/bin/bash # # Script that picks out a VCL section from a .vcc file and runs # varnishd -C on it to check syntax. # # Author: Lasse Karstensen # TMPFILE=$(mktemp) cleanup() { rm -f "$TMPFILE" test -d "$VDIR" && rmdir "$VDIR" } trap cleanup EXIT if [ ! -f "$1" ]; then echo "ERROR: Can't read $1" exit 1 fi if [ -z "`grep ".. vcl-start" $1`" ]; then echo "SKIP: No VCL marker in $1" exit 0 fi sed -e '1,/^.. vcl-start/d' \ -e '/.. vcl-end/,$d' \ -e '/^[^ ]/d' $1 > $TMPFILE #cat $TMPFILE VDIR=/tmp/varnishd$RANDOM$RANDOM #RES=$(/usr/sbin/varnishd -C -f $TMPFILE -n $VDIR -p vmod_dir=/usr/lib/x86_64-linux-gnu/varnish/vmods/ 2>&1) RES=$(/usr/sbin/varnishd -C -f $TMPFILE -n $VDIR 2>&1) RCODE=$? if [ $RCODE != 0 ]; then printf "$RES" else echo "OK" fi cleanup exit $RCODE