3144 lines
84 KiB
Diff
3144 lines
84 KiB
Diff
diff --git a/README.rst b/README.rst
|
|
index 7b757ed..709d82f 100644
|
|
--- a/README.rst
|
|
+++ b/README.rst
|
|
@@ -1,3 +1,10 @@
|
|
+Status of this repository
|
|
+=========================
|
|
+
|
|
+This repository is based upon
|
|
+https://github.com/varnish/varnish-modules with necessary adjustments
|
|
+for varnish-cache master. It is being maintained by https://uplex.de/
|
|
+
|
|
Varnish module collection by Varnish Software
|
|
=============================================
|
|
|
|
@@ -49,14 +56,13 @@ If you are using the distro provided packages::
|
|
|
|
Then proceed to the configure and build::
|
|
|
|
- ./bootstrap # If running from git.
|
|
./configure
|
|
make
|
|
make check # optional
|
|
sudo make install
|
|
|
|
|
|
-The resulting loadable modules (``libvmod_foo*.so`` files) will be installed to
|
|
+The resulting loadable modules (``libvmod_*.so`` files) will be installed to
|
|
the Varnish module directory. (default `/usr/lib/varnish/vmods/`)
|
|
|
|
|
|
@@ -86,6 +92,18 @@ The source git tree lives on Github: https://github.com/varnish/varnish-modules
|
|
All source code is placed in the master git branch. Pull requests and issue
|
|
reporting are appreciated.
|
|
|
|
+Unlike building from releases, you need to first bootstrap the build system
|
|
+when you work from git::
|
|
+
|
|
+ ./bootstrap
|
|
+ ./configure
|
|
+ make
|
|
+ make check # recommended
|
|
+
|
|
+If the ``configure`` step succeeds but the ``make`` step fails, check for
|
|
+warnings in the ``./configure`` output or the ``config.log`` file. You may be
|
|
+missing bootstrap dependencies not required by release archives.
|
|
+
|
|
Porting
|
|
-------
|
|
|
|
diff --git a/configure.ac b/configure.ac
|
|
index 294ece3..df55b41 100644
|
|
--- a/configure.ac
|
|
+++ b/configure.ac
|
|
@@ -7,7 +7,7 @@ AC_CONFIG_AUX_DIR([build-aux])
|
|
AC_CONFIG_SRCDIR(src/vmod_cookie.vcc)
|
|
AC_CONFIG_HEADERS(config.h)
|
|
|
|
-AM_INIT_AUTOMAKE([1.11 foreign])
|
|
+AM_INIT_AUTOMAKE([1.12.2 foreign])
|
|
AM_SILENT_RULES([yes])
|
|
AM_EXTRA_RECURSIVE_TARGETS([rst-docs])
|
|
|
|
diff --git a/docs/vmod_cookie.rst b/docs/vmod_cookie.rst
|
|
index d7a3666..892fe40 100644
|
|
--- a/docs/vmod_cookie.rst
|
|
+++ b/docs/vmod_cookie.rst
|
|
@@ -4,25 +4,42 @@
|
|
.. Edit vmod.vcc and run make instead
|
|
..
|
|
|
|
-.. role:: ref(emphasis)
|
|
|
|
-.. _vmod_cookie(3):
|
|
-
|
|
-===========
|
|
-vmod_cookie
|
|
-===========
|
|
+:tocdepth: 1
|
|
|
|
----------------------
|
|
-Varnish Cookie Module
|
|
----------------------
|
|
+.. _vmod_cookie(3):
|
|
|
|
-:Manual section: 3
|
|
+===================================
|
|
+VMOD cookie - Varnish Cookie Module
|
|
+===================================
|
|
|
|
SYNOPSIS
|
|
========
|
|
|
|
-import cookie [from "path"] ;
|
|
-
|
|
+.. parsed-literal::
|
|
+
|
|
+ import cookie [from "path"]
|
|
+
|
|
+ :ref:`vmod_cookie.clean`
|
|
+
|
|
+ :ref:`vmod_cookie.delete`
|
|
+
|
|
+ :ref:`vmod_cookie.filter`
|
|
+
|
|
+ :ref:`vmod_cookie.filter_except`
|
|
+
|
|
+ :ref:`vmod_cookie.format_rfc1123`
|
|
+
|
|
+ :ref:`vmod_cookie.get`
|
|
+
|
|
+ :ref:`vmod_cookie.get_string`
|
|
+
|
|
+ :ref:`vmod_cookie.isset`
|
|
+
|
|
+ :ref:`vmod_cookie.parse`
|
|
+
|
|
+ :ref:`vmod_cookie.set`
|
|
+
|
|
DESCRIPTION
|
|
===========
|
|
|
|
@@ -34,8 +51,8 @@ but a set comma-separated list of cookies. A filter() method removes a comma-
|
|
separated list of cookies.
|
|
|
|
A convenience function for formatting the Set-Cookie Expires date field
|
|
-is also included. To actually manipulate the Set-Cookie response headers,
|
|
-vmod-header should be used instead though.
|
|
+is also included. If there are multiple Set-Cookie headers vmod-header
|
|
+should be used.
|
|
|
|
The state loaded with cookie.parse() has a lifetime of the current request
|
|
or backend request context. To pass variables to the backend request, store
|
|
@@ -71,28 +88,11 @@ Filtering example::
|
|
|
|
.. vcl-end
|
|
|
|
-CONTENTS
|
|
-========
|
|
-
|
|
-* :ref:`func_clean`
|
|
-* :ref:`func_delete`
|
|
-* :ref:`func_filter`
|
|
-* :ref:`func_filter_except`
|
|
-* :ref:`func_format_rfc1123`
|
|
-* :ref:`func_get`
|
|
-* :ref:`func_get_string`
|
|
-* :ref:`func_isset`
|
|
-* :ref:`func_parse`
|
|
-* :ref:`func_set`
|
|
|
|
-.. _func_clean:
|
|
+.. _vmod_cookie.clean:
|
|
|
|
-clean
|
|
------
|
|
-
|
|
-::
|
|
-
|
|
- VOID clean(PRIV_TASK)
|
|
+VOID clean()
|
|
+------------
|
|
|
|
Description
|
|
Clean up previously parsed cookies. It is not necessary to run clean()
|
|
@@ -104,14 +104,10 @@ Example
|
|
cookie.clean();
|
|
}
|
|
|
|
-.. _func_delete:
|
|
-
|
|
-delete
|
|
-------
|
|
-
|
|
-::
|
|
+.. _vmod_cookie.delete:
|
|
|
|
- VOID delete(PRIV_TASK, STRING cookiename)
|
|
+VOID delete(STRING cookiename)
|
|
+------------------------------
|
|
|
|
Description
|
|
Delete `cookiename` from internal vmod storage if it exists.
|
|
@@ -125,14 +121,10 @@ Example
|
|
// get_string() will now yield "cookie1: value1";
|
|
}
|
|
|
|
-.. _func_filter:
|
|
+.. _vmod_cookie.filter:
|
|
|
|
-filter
|
|
-------
|
|
-
|
|
-::
|
|
-
|
|
- VOID filter(PRIV_TASK, STRING filterstring)
|
|
+VOID filter(STRING filterstring)
|
|
+--------------------------------
|
|
|
|
Description
|
|
Delete all cookies from internal vmod storage that are in the
|
|
@@ -148,14 +140,10 @@ Example
|
|
// "cookie3: value3";
|
|
}
|
|
|
|
-.. _func_filter_except:
|
|
-
|
|
-filter_except
|
|
--------------
|
|
-
|
|
-::
|
|
+.. _vmod_cookie.filter_except:
|
|
|
|
- VOID filter_except(PRIV_TASK, STRING filterstring)
|
|
+VOID filter_except(STRING filterstring)
|
|
+---------------------------------------
|
|
|
|
Description
|
|
Delete all cookies from internal vmod storage that is not in the
|
|
@@ -170,14 +158,10 @@ Example
|
|
// "cookie1: value1; cookie2: value2;";
|
|
}
|
|
|
|
-.. _func_format_rfc1123:
|
|
+.. _vmod_cookie.format_rfc1123:
|
|
|
|
-format_rfc1123
|
|
---------------
|
|
-
|
|
-::
|
|
-
|
|
- STRING format_rfc1123(TIME now, DURATION timedelta)
|
|
+STRING format_rfc1123(TIME now, DURATION timedelta)
|
|
+---------------------------------------------------
|
|
|
|
Description
|
|
Get a RFC1123 formatted date string suitable for inclusion in a
|
|
@@ -193,14 +177,10 @@ Example
|
|
set resp.http.Set-Cookie = "userid=" + req.http.userid + "; Expires=" + cookie.format_rfc1123(now, 5m) + "; httpOnly";
|
|
}
|
|
|
|
-.. _func_get:
|
|
-
|
|
-get
|
|
----
|
|
-
|
|
-::
|
|
+.. _vmod_cookie.get:
|
|
|
|
- STRING get(PRIV_TASK, STRING cookiename)
|
|
+STRING get(STRING cookiename)
|
|
+-----------------------------
|
|
|
|
Description
|
|
Get the value of `cookiename`, as stored in internal vmod storage. If `cookiename` does not exist an empty string is returned.
|
|
@@ -213,14 +193,10 @@ Example
|
|
std.log("cookie1 value is: " + cookie.get("cookie1"));
|
|
}
|
|
|
|
-.. _func_get_string:
|
|
+.. _vmod_cookie.get_string:
|
|
|
|
-get_string
|
|
-----------
|
|
-
|
|
-::
|
|
-
|
|
- STRING get_string(PRIV_TASK)
|
|
+STRING get_string()
|
|
+-------------------
|
|
|
|
Description
|
|
Get a Cookie string value with all cookies in internal vmod storage. Does
|
|
@@ -234,14 +210,10 @@ Example
|
|
set req.http.cookie = cookie.get_string();
|
|
}
|
|
|
|
-.. _func_isset:
|
|
-
|
|
-isset
|
|
------
|
|
-
|
|
-::
|
|
+.. _vmod_cookie.isset:
|
|
|
|
- BOOL isset(PRIV_TASK, STRING cookiename)
|
|
+BOOL isset(STRING cookiename)
|
|
+-----------------------------
|
|
|
|
Description
|
|
Check if `cookiename` is set in the internal vmod storage.
|
|
@@ -257,14 +229,10 @@ Example
|
|
}
|
|
}
|
|
|
|
-.. _func_parse:
|
|
+.. _vmod_cookie.parse:
|
|
|
|
-parse
|
|
------
|
|
-
|
|
-::
|
|
-
|
|
- VOID parse(PRIV_TASK, STRING cookieheader)
|
|
+VOID parse(STRING cookieheader)
|
|
+-------------------------------
|
|
|
|
Description
|
|
Parse the cookie string in `cookieheader`. If state already exists, clean() will be run first.
|
|
@@ -275,16 +243,10 @@ Example
|
|
cookie.parse(req.http.Cookie);
|
|
}
|
|
|
|
+.. _vmod_cookie.set:
|
|
|
|
-
|
|
-.. _func_set:
|
|
-
|
|
-set
|
|
----
|
|
-
|
|
-::
|
|
-
|
|
- VOID set(PRIV_TASK, STRING cookiename, STRING value)
|
|
+VOID set(STRING cookiename, STRING value)
|
|
+-----------------------------------------
|
|
|
|
Description
|
|
Set the internal vmod storage for `cookiename` to `value`.
|
|
@@ -296,5 +258,3 @@ Example
|
|
cookie.set("cookie1", "value1");
|
|
std.log("cookie1 value is: " + cookie.get("cookie1"));
|
|
}
|
|
-
|
|
-
|
|
diff --git a/docs/vmod_header.rst b/docs/vmod_header.rst
|
|
index 50f64db..1789727 100644
|
|
--- a/docs/vmod_header.rst
|
|
+++ b/docs/vmod_header.rst
|
|
@@ -4,25 +4,30 @@
|
|
.. Edit vmod.vcc and run make instead
|
|
..
|
|
|
|
-.. role:: ref(emphasis)
|
|
|
|
-.. _vmod_header(3):
|
|
+:tocdepth: 1
|
|
|
|
-===========
|
|
-vmod_header
|
|
-===========
|
|
-
|
|
------------------------
|
|
-Header VMOD for Varnish
|
|
------------------------
|
|
+.. _vmod_header(3):
|
|
|
|
-:Manual section: 3
|
|
+=====================================
|
|
+VMOD header - Header VMOD for Varnish
|
|
+=====================================
|
|
|
|
SYNOPSIS
|
|
========
|
|
|
|
-import header [from "path"] ;
|
|
-
|
|
+.. parsed-literal::
|
|
+
|
|
+ import header [from "path"]
|
|
+
|
|
+ :ref:`vmod_header.append`
|
|
+
|
|
+ :ref:`vmod_header.copy`
|
|
+
|
|
+ :ref:`vmod_header.get`
|
|
+
|
|
+ :ref:`vmod_header.remove`
|
|
+
|
|
DESCRIPTION
|
|
===========
|
|
|
|
@@ -51,22 +56,10 @@ Example::
|
|
.. vcl-end
|
|
|
|
|
|
-CONTENTS
|
|
-========
|
|
-
|
|
-* :ref:`func_append`
|
|
-* :ref:`func_copy`
|
|
-* :ref:`func_get`
|
|
-* :ref:`func_remove`
|
|
-
|
|
-.. _func_append:
|
|
+.. _vmod_header.append:
|
|
|
|
-append
|
|
-------
|
|
-
|
|
-::
|
|
-
|
|
- VOID append(HEADER, STRING)
|
|
+VOID append(HEADER, STRING)
|
|
+---------------------------
|
|
|
|
Description
|
|
Append an extra occurrence to an existing header.
|
|
@@ -74,14 +67,10 @@ Example
|
|
::
|
|
header.append(beresp.http.Set-Cookie, "foo=bar")
|
|
|
|
-.. _func_copy:
|
|
-
|
|
-copy
|
|
-----
|
|
+.. _vmod_header.copy:
|
|
|
|
-::
|
|
-
|
|
- VOID copy(HEADER, HEADER)
|
|
+VOID copy(HEADER, HEADER)
|
|
+-------------------------
|
|
|
|
Description
|
|
Copy all source headers to a new header.
|
|
@@ -89,14 +78,10 @@ Example
|
|
::
|
|
header.copy(beresp.http.set-cookie, beresp.http.x-old-cookie);
|
|
|
|
-.. _func_get:
|
|
-
|
|
-get
|
|
----
|
|
+.. _vmod_header.get:
|
|
|
|
-::
|
|
-
|
|
- STRING get(PRIV_CALL, HEADER header, STRING regex)
|
|
+STRING get(HEADER header, STRING regex)
|
|
+---------------------------------------
|
|
|
|
Description
|
|
Fetches the value of the first `header` that matches the given
|
|
@@ -105,17 +90,13 @@ Example
|
|
::
|
|
set beresp.http.xusr = header.get(beresp.http.set-cookie,"user=");
|
|
|
|
-.. _func_remove:
|
|
-
|
|
-remove
|
|
-------
|
|
+.. _vmod_header.remove:
|
|
|
|
-::
|
|
-
|
|
- VOID remove(PRIV_CALL, HEADER header, STRING regex)
|
|
+VOID remove(HEADER header, STRING regex)
|
|
+----------------------------------------
|
|
|
|
Description
|
|
- Remove all occurences of `header` that matches `regex`.
|
|
+ Remove all occurrences of `header` that matches `regex`.
|
|
Example
|
|
::
|
|
header.remove(beresp.http.set-cookie,"^(?!(funcookie=))");
|
|
@@ -135,4 +116,3 @@ BUGS
|
|
|
|
You can't use dynamic regular expressions, which also holds true for normal
|
|
regular expressions in regsub().
|
|
-
|
|
diff --git a/docs/vmod_saintmode.rst b/docs/vmod_saintmode.rst
|
|
index ceaaf44..801cc92 100644
|
|
--- a/docs/vmod_saintmode.rst
|
|
+++ b/docs/vmod_saintmode.rst
|
|
@@ -4,26 +4,34 @@
|
|
.. Edit vmod.vcc and run make instead
|
|
..
|
|
|
|
-.. role:: ref(emphasis)
|
|
|
|
-.. _vmod_saintmode(3):
|
|
-
|
|
-==============
|
|
-vmod_saintmode
|
|
-==============
|
|
+:tocdepth: 1
|
|
|
|
----------------------------
|
|
-Saint mode backend director
|
|
----------------------------
|
|
+.. _vmod_saintmode(3):
|
|
|
|
-:Manual section: 3
|
|
+============================================
|
|
+VMOD saintmode - Saint mode backend director
|
|
+============================================
|
|
|
|
SYNOPSIS
|
|
========
|
|
|
|
-import saintmode [from "path"] ;
|
|
-
|
|
-
|
|
+.. parsed-literal::
|
|
+
|
|
+ import saintmode [from "path"]
|
|
+
|
|
+ :ref:`vmod_saintmode.blacklist`
|
|
+
|
|
+ :ref:`vmod_saintmode.status`
|
|
+
|
|
+ :ref:`vmod_saintmode.saintmode`
|
|
+
|
|
+ :ref:`vmod_saintmode.saintmode.backend`
|
|
+
|
|
+ :ref:`vmod_saintmode.saintmode.blacklist_count`
|
|
+
|
|
+ :ref:`vmod_saintmode.saintmode.is_healthy`
|
|
+
|
|
DESCRIPTION
|
|
===========
|
|
|
|
@@ -95,23 +103,10 @@ Example::
|
|
.. vcl-end
|
|
|
|
|
|
-CONTENTS
|
|
-========
|
|
-
|
|
-* :ref:`func_blacklist`
|
|
-* :ref:`obj_saintmode`
|
|
-* :ref:`func_saintmode.backend`
|
|
-* :ref:`func_saintmode.blacklist_count`
|
|
-* :ref:`func_status`
|
|
-
|
|
-.. _func_blacklist:
|
|
-
|
|
-blacklist
|
|
----------
|
|
-
|
|
-::
|
|
+.. _vmod_saintmode.blacklist:
|
|
|
|
- VOID blacklist(PRIV_VCL, DURATION expires)
|
|
+VOID blacklist(DURATION expires)
|
|
+--------------------------------
|
|
|
|
Marks the backend as sick for a specific object. Used in vcl_backend_response.
|
|
Corresponds to the use of ``beresp.saintmode`` in Varnish 3.0. Only available
|
|
@@ -126,15 +121,10 @@ Example::
|
|
}
|
|
}
|
|
|
|
+.. _vmod_saintmode.status:
|
|
|
|
-.. _func_status:
|
|
-
|
|
-status
|
|
-------
|
|
-
|
|
-::
|
|
-
|
|
- STRING status(PRIV_VCL)
|
|
+STRING status()
|
|
+---------------
|
|
|
|
Returns a JSON formatted status string suitable for use in vcl_synth.
|
|
|
|
@@ -164,38 +154,33 @@ Example JSON output:
|
|
]
|
|
}
|
|
|
|
+.. _vmod_saintmode.saintmode:
|
|
|
|
-.. _obj_saintmode:
|
|
-
|
|
-saintmode
|
|
----------
|
|
+new xsaintmode = saintmode.saintmode(BACKEND backend, INT threshold)
|
|
+--------------------------------------------------------------------
|
|
|
|
::
|
|
|
|
- new OBJ = saintmode(PRIV_VCL, BACKEND backend, INT threshold)
|
|
+ new xsaintmode = saintmode.saintmode(
|
|
+ BACKEND backend,
|
|
+ INT threshold
|
|
+ )
|
|
|
|
Constructs a saintmode director object. The ``threshold`` argument sets
|
|
the saintmode threshold, which is the maximum number of items that can be
|
|
blacklisted before the whole backend is regarded as sick. Corresponds with the
|
|
``saintmode_threshold`` parameter of Varnish 3.0.
|
|
|
|
-Setting threshold to 0 disables saintmode, not the threshold.
|
|
-
|
|
Example::
|
|
|
|
sub vcl_init {
|
|
new sm = saintmode.saintmode(b, 10);
|
|
}
|
|
|
|
+.. _vmod_saintmode.saintmode.backend:
|
|
|
|
-.. _func_saintmode.backend:
|
|
-
|
|
-saintmode.backend
|
|
------------------
|
|
-
|
|
-::
|
|
-
|
|
- BACKEND saintmode.backend()
|
|
+BACKEND xsaintmode.backend()
|
|
+----------------------------
|
|
|
|
Used for assigning the backend from the saintmode object.
|
|
|
|
@@ -205,15 +190,10 @@ Example::
|
|
set bereq.backend = sm.backend();
|
|
}
|
|
|
|
+.. _vmod_saintmode.saintmode.blacklist_count:
|
|
|
|
-.. _func_saintmode.blacklist_count:
|
|
-
|
|
-saintmode.blacklist_count
|
|
--------------------------
|
|
-
|
|
-::
|
|
-
|
|
- INT saintmode.blacklist_count()
|
|
+INT xsaintmode.blacklist_count()
|
|
+--------------------------------
|
|
|
|
Returns the number of objects currently blacklisted for a saintmode
|
|
director object.
|
|
@@ -226,3 +206,11 @@ Example:
|
|
set resp.http.troublecount = sm.blacklist_count();
|
|
}
|
|
|
|
+.. _vmod_saintmode.saintmode.is_healthy:
|
|
+
|
|
+BOOL xsaintmode.is_healthy()
|
|
+----------------------------
|
|
+
|
|
+Checks if the object is currently blacklisted for a saintmode director object.
|
|
+If there are no valid objects available (called from vcl_hit or vcl_recv),
|
|
+the function will fall back to the backend's health function.
|
|
diff --git a/docs/vmod_tcp.rst b/docs/vmod_tcp.rst
|
|
index b37c863..5015443 100644
|
|
--- a/docs/vmod_tcp.rst
|
|
+++ b/docs/vmod_tcp.rst
|
|
@@ -4,25 +4,30 @@
|
|
.. Edit vmod.vcc and run make instead
|
|
..
|
|
|
|
-.. role:: ref(emphasis)
|
|
|
|
-.. _vmod_tcp(3):
|
|
-
|
|
-========
|
|
-vmod_tcp
|
|
-========
|
|
+:tocdepth: 1
|
|
|
|
---------
|
|
-TCP vmod
|
|
---------
|
|
+.. _vmod_tcp(3):
|
|
|
|
-:Manual section: 3
|
|
+===================
|
|
+VMOD tcp - TCP vmod
|
|
+===================
|
|
|
|
SYNOPSIS
|
|
========
|
|
|
|
-import tcp [from "path"] ;
|
|
-
|
|
+.. parsed-literal::
|
|
+
|
|
+ import tcp [from "path"]
|
|
+
|
|
+ :ref:`vmod_tcp.congestion_algorithm`
|
|
+
|
|
+ :ref:`vmod_tcp.dump_info`
|
|
+
|
|
+ :ref:`vmod_tcp.get_estimated_rtt`
|
|
+
|
|
+ :ref:`vmod_tcp.set_socket_pace`
|
|
+
|
|
DESCRIPTION
|
|
===========
|
|
|
|
@@ -59,22 +64,10 @@ Example::
|
|
.. vcl-end
|
|
|
|
|
|
-CONTENTS
|
|
-========
|
|
-
|
|
-* :ref:`func_congestion_algorithm`
|
|
-* :ref:`func_dump_info`
|
|
-* :ref:`func_get_estimated_rtt`
|
|
-* :ref:`func_set_socket_pace`
|
|
-
|
|
-.. _func_congestion_algorithm:
|
|
+.. _vmod_tcp.congestion_algorithm:
|
|
|
|
-congestion_algorithm
|
|
---------------------
|
|
-
|
|
-::
|
|
-
|
|
- INT congestion_algorithm(STRING algorithm)
|
|
+INT congestion_algorithm(STRING algorithm)
|
|
+------------------------------------------
|
|
|
|
Set the client socket congestion control algorithm to S. Returns 0 on success, and -1 on error.
|
|
|
|
@@ -84,15 +77,10 @@ Example::
|
|
set req.http.x-tcp = tcp.congestion_algorithm("cubic");
|
|
}
|
|
|
|
+.. _vmod_tcp.dump_info:
|
|
|
|
-.. _func_dump_info:
|
|
-
|
|
-dump_info
|
|
----------
|
|
-
|
|
-::
|
|
-
|
|
- VOID dump_info()
|
|
+VOID dump_info()
|
|
+----------------
|
|
|
|
Write the contents of the TCP_INFO data structure into varnishlog.
|
|
|
|
@@ -107,17 +95,10 @@ Example varnishlog output::
|
|
- VCL_Log tcpi2: pmtu=1500 rtt=152000 rttvar=76000 snd_cwnd=10 advmss=1448 reordering=3
|
|
- VCL_Log getsockopt() returned: cubic
|
|
|
|
+.. _vmod_tcp.get_estimated_rtt:
|
|
|
|
-
|
|
-
|
|
-.. _func_get_estimated_rtt:
|
|
-
|
|
-get_estimated_rtt
|
|
------------------
|
|
-
|
|
-::
|
|
-
|
|
- REAL get_estimated_rtt()
|
|
+REAL get_estimated_rtt()
|
|
+------------------------
|
|
|
|
Get the estimated round-trip-time for the client socket. Unit: milliseconds.
|
|
|
|
@@ -127,15 +108,10 @@ Example::
|
|
std.log("client is far away.");
|
|
}
|
|
|
|
+.. _vmod_tcp.set_socket_pace:
|
|
|
|
-.. _func_set_socket_pace:
|
|
-
|
|
-set_socket_pace
|
|
----------------
|
|
-
|
|
-::
|
|
-
|
|
- VOID set_socket_pace(INT)
|
|
+VOID set_socket_pace(INT)
|
|
+-------------------------
|
|
|
|
Set socket pacing on client-side TCP connection to PACE KB/s. Network interface
|
|
used must be using a supported scheduler. (fq)
|
|
@@ -143,5 +119,3 @@ used must be using a supported scheduler. (fq)
|
|
Example::
|
|
|
|
tcp.set_socket_pace(1000);
|
|
-
|
|
-
|
|
diff --git a/docs/vmod_var.rst b/docs/vmod_var.rst
|
|
index d5f7a2d..fe7dcce 100644
|
|
--- a/docs/vmod_var.rst
|
|
+++ b/docs/vmod_var.rst
|
|
@@ -4,26 +4,56 @@
|
|
.. Edit vmod.vcc and run make instead
|
|
..
|
|
|
|
-.. role:: ref(emphasis)
|
|
|
|
-.. _vmod_var(3):
|
|
+:tocdepth: 1
|
|
|
|
-========
|
|
-vmod_var
|
|
-========
|
|
-
|
|
---------------------------------
|
|
-Variable support for Varnish VCL
|
|
---------------------------------
|
|
+.. _vmod_var(3):
|
|
|
|
-:Manual section: 3
|
|
+===========================================
|
|
+VMOD var - Variable support for Varnish VCL
|
|
+===========================================
|
|
|
|
SYNOPSIS
|
|
========
|
|
|
|
-import var [from "path"] ;
|
|
-
|
|
-
|
|
+.. parsed-literal::
|
|
+
|
|
+ import var [from "path"]
|
|
+
|
|
+ :ref:`vmod_var.set`
|
|
+
|
|
+ :ref:`vmod_var.get`
|
|
+
|
|
+ :ref:`vmod_var.global_set`
|
|
+
|
|
+ :ref:`vmod_var.global_get`
|
|
+
|
|
+ :ref:`vmod_var.set_int`
|
|
+
|
|
+ :ref:`vmod_var.get_int`
|
|
+
|
|
+ :ref:`vmod_var.set_string`
|
|
+
|
|
+ :ref:`vmod_var.get_string`
|
|
+
|
|
+ :ref:`vmod_var.set_real`
|
|
+
|
|
+ :ref:`vmod_var.get_real`
|
|
+
|
|
+ :ref:`vmod_var.set_duration`
|
|
+
|
|
+ :ref:`vmod_var.get_duration`
|
|
+
|
|
+ :ref:`vmod_var.set_ip`
|
|
+
|
|
+ :ref:`vmod_var.get_ip`
|
|
+
|
|
+ :ref:`vmod_var.set_backend`
|
|
+
|
|
+ :ref:`vmod_var.get_backend`
|
|
+
|
|
+ :ref:`vmod_var.clear`
|
|
+
|
|
This VMOD implements basic variable support in VCL.
|
|
|
|
It supports strings, integers and real numbers. There are methods to get and
|
|
@@ -82,183 +112,122 @@ Example::
|
|
.. vcl-end
|
|
|
|
|
|
-CONTENTS
|
|
-========
|
|
-
|
|
-* :ref:`func_clear`
|
|
-* :ref:`func_get`
|
|
-* :ref:`func_get_duration`
|
|
-* :ref:`func_get_int`
|
|
-* :ref:`func_get_ip`
|
|
-* :ref:`func_get_real`
|
|
-* :ref:`func_get_string`
|
|
-* :ref:`func_global_get`
|
|
-* :ref:`func_global_set`
|
|
-* :ref:`func_set`
|
|
-* :ref:`func_set_duration`
|
|
-* :ref:`func_set_int`
|
|
-* :ref:`func_set_ip`
|
|
-* :ref:`func_set_real`
|
|
-* :ref:`func_set_string`
|
|
-
|
|
-.. _func_set:
|
|
+.. _vmod_var.set:
|
|
|
|
-set
|
|
----
|
|
-
|
|
-::
|
|
-
|
|
- VOID set(PRIV_TASK, STRING key, STRING value)
|
|
+VOID set(STRING key, STRING value)
|
|
+----------------------------------
|
|
|
|
Set `key` to `value`.
|
|
|
|
-.. _func_get:
|
|
-
|
|
-get
|
|
----
|
|
+.. _vmod_var.get:
|
|
|
|
-::
|
|
-
|
|
- STRING get(PRIV_TASK, STRING)
|
|
+STRING get(STRING)
|
|
+------------------
|
|
|
|
Get `key` with data type STRING. If stored `key` is not a STRING an empty string is returned.
|
|
|
|
-.. _func_global_set:
|
|
-
|
|
-global_set
|
|
-----------
|
|
+.. _vmod_var.global_set:
|
|
|
|
-::
|
|
+VOID global_set(STRING, STRING)
|
|
+-------------------------------
|
|
|
|
- VOID global_set(STRING, STRING)
|
|
|
|
-.. _func_global_get:
|
|
|
|
-global_get
|
|
-----------
|
|
+.. _vmod_var.global_get:
|
|
|
|
-::
|
|
+STRING global_get(STRING)
|
|
+-------------------------
|
|
|
|
- STRING global_get(STRING)
|
|
|
|
-.. _func_set_int:
|
|
|
|
-set_int
|
|
--------
|
|
+.. _vmod_var.set_int:
|
|
|
|
-::
|
|
-
|
|
- VOID set_int(PRIV_TASK, STRING key, INT value)
|
|
+VOID set_int(STRING key, INT value)
|
|
+-----------------------------------
|
|
|
|
Set `key` to `value`.
|
|
|
|
-.. _func_get_int:
|
|
-
|
|
-get_int
|
|
--------
|
|
-
|
|
-::
|
|
+.. _vmod_var.get_int:
|
|
|
|
- INT get_int(PRIV_TASK, STRING key)
|
|
+INT get_int(STRING key)
|
|
+-----------------------
|
|
|
|
Get `key` with data type INT. If stored `key` is not an INT zero will be returned.
|
|
|
|
-.. _func_set_string:
|
|
-
|
|
-set_string
|
|
-----------
|
|
-
|
|
-::
|
|
+.. _vmod_var.set_string:
|
|
|
|
- VOID set_string(PRIV_TASK, STRING key, STRING value)
|
|
+VOID set_string(STRING key, STRING value)
|
|
+-----------------------------------------
|
|
|
|
Identical to set().
|
|
|
|
-.. _func_get_string:
|
|
+.. _vmod_var.get_string:
|
|
|
|
-get_string
|
|
-----------
|
|
-
|
|
-::
|
|
-
|
|
- STRING get_string(PRIV_TASK, STRING key)
|
|
+STRING get_string(STRING key)
|
|
+-----------------------------
|
|
|
|
Identical to get().
|
|
|
|
-.. _func_set_real:
|
|
-
|
|
-set_real
|
|
---------
|
|
+.. _vmod_var.set_real:
|
|
|
|
-::
|
|
-
|
|
- VOID set_real(PRIV_TASK, STRING key, REAL value)
|
|
+VOID set_real(STRING key, REAL value)
|
|
+-------------------------------------
|
|
|
|
Set `key` to `value`.
|
|
|
|
-.. _func_get_real:
|
|
-
|
|
-get_real
|
|
---------
|
|
+.. _vmod_var.get_real:
|
|
|
|
-::
|
|
-
|
|
- REAL get_real(PRIV_TASK, STRING key)
|
|
+REAL get_real(STRING key)
|
|
+-------------------------
|
|
|
|
Get `key` with data type REAL. If stored `key` is not a REAL zero will be returned.
|
|
|
|
-.. _func_set_duration:
|
|
-
|
|
-set_duration
|
|
-------------
|
|
+.. _vmod_var.set_duration:
|
|
|
|
-::
|
|
-
|
|
- VOID set_duration(PRIV_TASK, STRING key, DURATION value)
|
|
+VOID set_duration(STRING key, DURATION value)
|
|
+---------------------------------------------
|
|
|
|
Set `key` to `value`.
|
|
|
|
-.. _func_get_duration:
|
|
+.. _vmod_var.get_duration:
|
|
|
|
-get_duration
|
|
-------------
|
|
-
|
|
-::
|
|
-
|
|
- DURATION get_duration(PRIV_TASK, STRING key)
|
|
+DURATION get_duration(STRING key)
|
|
+---------------------------------
|
|
|
|
Get `key` with data type DURATION. If stored `key` is not a DURATION zero will be returned.
|
|
|
|
-.. _func_set_ip:
|
|
+.. _vmod_var.set_ip:
|
|
|
|
-set_ip
|
|
-------
|
|
+VOID set_ip(STRING key, IP value)
|
|
+---------------------------------
|
|
|
|
-::
|
|
+Set `key` to `value`.
|
|
|
|
- VOID set_ip(PRIV_TASK, STRING key, IP value)
|
|
+.. _vmod_var.get_ip:
|
|
|
|
-Set `key` to `value`.
|
|
+IP get_ip(STRING key)
|
|
+---------------------
|
|
|
|
-.. _func_get_ip:
|
|
+Get `key` with data type IP. If stored `key` is not an IP null will be returned.
|
|
|
|
-get_ip
|
|
-------
|
|
+.. _vmod_var.set_backend:
|
|
|
|
-::
|
|
+VOID set_backend(STRING key, BACKEND value)
|
|
+-------------------------------------------
|
|
|
|
- IP get_ip(PRIV_TASK, STRING key)
|
|
+Set `key` to `value`.
|
|
|
|
-Get `key` with data type IP. If stored `key` is not an IP null will be returned.
|
|
+.. _vmod_var.get_backend:
|
|
|
|
-.. _func_clear:
|
|
+BACKEND get_backend(STRING key)
|
|
+-------------------------------
|
|
|
|
-clear
|
|
------
|
|
+Get `key` with data type BACKEND. If stored `key` is not a BACKEND,
|
|
+null will be returned.
|
|
|
|
-::
|
|
+.. _vmod_var.clear:
|
|
|
|
- VOID clear(PRIV_TASK)
|
|
+VOID clear()
|
|
+------------
|
|
|
|
Clear all non-global variables.
|
|
-
|
|
diff --git a/docs/vmod_vsthrottle.rst b/docs/vmod_vsthrottle.rst
|
|
index 2593224..403aa7f 100644
|
|
--- a/docs/vmod_vsthrottle.rst
|
|
+++ b/docs/vmod_vsthrottle.rst
|
|
@@ -4,25 +4,30 @@
|
|
.. Edit vmod.vcc and run make instead
|
|
..
|
|
|
|
-.. role:: ref(emphasis)
|
|
|
|
-.. _vmod_vsthrottle(3):
|
|
-
|
|
-===============
|
|
-vmod_vsthrottle
|
|
-===============
|
|
+:tocdepth: 1
|
|
|
|
----------------
|
|
-Throttling VMOD
|
|
----------------
|
|
+.. _vmod_vsthrottle(3):
|
|
|
|
-:Manual section: 3
|
|
+=================================
|
|
+VMOD vsthrottle - Throttling VMOD
|
|
+=================================
|
|
|
|
SYNOPSIS
|
|
========
|
|
|
|
-import vsthrottle [from "path"] ;
|
|
-
|
|
+.. parsed-literal::
|
|
+
|
|
+ import vsthrottle [from "path"]
|
|
+
|
|
+ :ref:`vmod_vsthrottle.is_denied`
|
|
+
|
|
+ :ref:`vmod_vsthrottle.return_token`
|
|
+
|
|
+ :ref:`vmod_vsthrottle.remaining`
|
|
+
|
|
+ :ref:`vmod_vsthrottle.blocked`
|
|
+
|
|
DESCRIPTION
|
|
===========
|
|
|
|
@@ -37,6 +42,12 @@ The request rate is specified as the number of requests permitted over
|
|
a period. To keep things simple, this is passed as two separate
|
|
parameters, 'limit' and 'period'.
|
|
|
|
+If an optional duration 'block' is specified, then access is denied
|
|
+altogether for that period of time after the rate limit is
|
|
+reached. This is a way to entirely turn away a particularly
|
|
+troublesome source of traffic for a while, rather than let them back
|
|
+in as soon as the rate slips back under the threshold.
|
|
+
|
|
This VMOD implements a `token bucket algorithm`_. State associated
|
|
with the token bucket for each key is stored in-memory using BSD's
|
|
red-black tree implementation.
|
|
@@ -58,8 +69,9 @@ Example::
|
|
sub vcl_recv {
|
|
# Varnish will set client.identity for you based on client IP.
|
|
|
|
- if (vsthrottle.is_denied(client.identity, 15, 10s)) {
|
|
- # Client has exceeded 15 reqs per 10s
|
|
+ if (vsthrottle.is_denied(client.identity, 15, 10s, 30s)) {
|
|
+ # Client has exceeded 15 reqs per 10s.
|
|
+ # When this happens, block altogether for the next 30s.
|
|
return (synth(429, "Too Many Requests"));
|
|
}
|
|
|
|
@@ -78,33 +90,37 @@ Example::
|
|
|
|
.. vcl-end
|
|
|
|
-CONTENTS
|
|
-========
|
|
-
|
|
-* :ref:`func_is_denied`
|
|
-* :ref:`func_remaining`
|
|
|
|
-.. _func_is_denied:
|
|
+.. _vmod_vsthrottle.is_denied:
|
|
|
|
-is_denied
|
|
----------
|
|
+BOOL is_denied(STRING key, INT limit, DURATION period, DURATION block)
|
|
+----------------------------------------------------------------------
|
|
|
|
::
|
|
|
|
- BOOL is_denied(STRING key, INT limit, DURATION period)
|
|
+ BOOL is_denied(
|
|
+ STRING key,
|
|
+ INT limit,
|
|
+ DURATION period,
|
|
+ DURATION block=0
|
|
+ )
|
|
|
|
Arguments:
|
|
|
|
- key: A unique identifier to define what is being throttled - more examples below
|
|
- limit: How many requests in the specified period
|
|
- period: The time period
|
|
+ - block: a period to deny all access after hitting the threshold. Default is 0s
|
|
|
|
Description
|
|
Can be used to rate limit the traffic for a specific key to a
|
|
- maximum of 'limit' requests per 'period' time. A token bucket
|
|
- is uniquely identified by the triplet of its key, limit and
|
|
- period, so using the same key multiple places with different
|
|
- rules will create multiple token buckets.
|
|
+ maximum of 'limit' requests per 'period' time. If 'block' is > 0s,
|
|
+ (0s by default), then always deny for 'key' for that length of time
|
|
+ after hitting the threshold.
|
|
+
|
|
+ Note: A token bucket is uniquely identified by the 4-tuple of its
|
|
+ key, limit, period and block, so using the same key multiple places
|
|
+ with different rules will create multiple token buckets.
|
|
|
|
Example
|
|
::
|
|
@@ -118,20 +134,69 @@ Example
|
|
# ...
|
|
}
|
|
|
|
+.. _vmod_vsthrottle.return_token:
|
|
+
|
|
+VOID return_token(STRING key, INT limit, DURATION period, DURATION block)
|
|
+-------------------------------------------------------------------------
|
|
+
|
|
+::
|
|
+
|
|
+ VOID return_token(
|
|
+ STRING key,
|
|
+ INT limit,
|
|
+ DURATION period,
|
|
+ DURATION block=0
|
|
+ )
|
|
|
|
-.. _func_remaining:
|
|
+Arguments:
|
|
+ - Same arguments as is_denied()
|
|
+
|
|
+Description
|
|
+ Increment (by one) the number of tokens in the specified bucket. is_denied()
|
|
+ decrements the bucket by one token and return_token() adds it back.
|
|
+ Using these two, you can effectively make a token bucket act like a limit on
|
|
+ concurrent requests instead of requests / time.
|
|
+
|
|
+ Note: This function doesn't enforce anything, it merely credits a token to
|
|
+ appropriate bucket.
|
|
+
|
|
+ Warning: If streaming is enabled (beresp.do_stream = true) as it is by
|
|
+ default now, vcl_deliver() is called *before* the response is sent
|
|
+ to the client (who may download it slowly). Thus you may credit the token
|
|
+ back too early if you use return_token() in vcl_backend_response().
|
|
+
|
|
+Example
|
|
+ ::
|
|
|
|
-remaining
|
|
----------
|
|
+ sub vcl_recv {
|
|
+ if (vsthrottle.is_denied(client.identity, 20, 20s)) {
|
|
+ # Client has more than 20 concurrent requests
|
|
+ return (synth(429, "Too Many Requests In Flight"));
|
|
+ }
|
|
+
|
|
+ # ...
|
|
+ }
|
|
+
|
|
+ sub vcl_deliver {
|
|
+ vsthrottle.return_token(client.identity, 10, 10s);
|
|
+ }
|
|
+
|
|
+.. _vmod_vsthrottle.remaining:
|
|
+
|
|
+INT remaining(STRING key, INT limit, DURATION period, DURATION block)
|
|
+---------------------------------------------------------------------
|
|
|
|
::
|
|
|
|
- INT remaining(STRING key, INT limit, DURATION period)
|
|
+ INT remaining(
|
|
+ STRING key,
|
|
+ INT limit,
|
|
+ DURATION period,
|
|
+ DURATION block=0
|
|
+ )
|
|
|
|
Arguments:
|
|
- - key: A unique identifier to define what is being throttled
|
|
- - limit: How many requests in the specified period
|
|
- - period: The time period
|
|
+ - Same arguments as is_denied()
|
|
|
|
Description
|
|
|
|
@@ -147,3 +212,36 @@ Example
|
|
set resp.http.X-RateLimit-Remaining = vsthrottle.remaining(client.identity, 15, 10s);
|
|
}
|
|
|
|
+.. _vmod_vsthrottle.blocked:
|
|
+
|
|
+DURATION blocked(STRING key, INT limit, DURATION period, DURATION block)
|
|
+------------------------------------------------------------------------
|
|
+
|
|
+::
|
|
+
|
|
+ DURATION blocked(
|
|
+ STRING key,
|
|
+ INT limit,
|
|
+ DURATION period,
|
|
+ DURATION block
|
|
+ )
|
|
+
|
|
+Arguments:
|
|
+ - Same arguments as is_denied()
|
|
+
|
|
+Description
|
|
+
|
|
+ If the token bucket identified by the four parameters has been
|
|
+ blocked by use of the 'block' parameter in 'is_denied()', then
|
|
+ return the time remaining in the block. If it is not blocked,
|
|
+ return 0s. This can be used to inform clients how long they
|
|
+ will be locked out.
|
|
+
|
|
+
|
|
+Example
|
|
+ ::
|
|
+
|
|
+ sub vcl_deliver {
|
|
+ set resp.http.Retry-After
|
|
+ = vsthrottle.blocked(client.identity, 15, 10s, 30s);
|
|
+ }
|
|
diff --git a/docs/vmod_xkey.rst b/docs/vmod_xkey.rst
|
|
index 40c22c2..86da183 100644
|
|
--- a/docs/vmod_xkey.rst
|
|
+++ b/docs/vmod_xkey.rst
|
|
@@ -4,25 +4,26 @@
|
|
.. Edit vmod.vcc and run make instead
|
|
..
|
|
|
|
-.. role:: ref(emphasis)
|
|
|
|
-.. _vmod_xkey(3):
|
|
-
|
|
-=========
|
|
-vmod_xkey
|
|
-=========
|
|
+:tocdepth: 1
|
|
|
|
-----------------------------------------
|
|
-Surrogate keys support for Varnish Cache
|
|
-----------------------------------------
|
|
+.. _vmod_xkey(3):
|
|
|
|
-:Manual section: 3
|
|
+====================================================
|
|
+VMOD xkey - Surrogate keys support for Varnish Cache
|
|
+====================================================
|
|
|
|
SYNOPSIS
|
|
========
|
|
|
|
-import xkey [from "path"] ;
|
|
+.. parsed-literal::
|
|
|
|
+ import xkey [from "path"]
|
|
+
|
|
+ :ref:`vmod_xkey.purge`
|
|
+
|
|
+ :ref:`vmod_xkey.softpurge`
|
|
+
|
|
DESCRIPTION
|
|
===========
|
|
|
|
@@ -39,13 +40,13 @@ Similarly with an e-commerce site, where various SKUs are often
|
|
referenced on a page.
|
|
|
|
Hash keys are specified in the ``xkey`` response header. Multiple keys
|
|
-can be specified per header line with a space
|
|
-separator. Alternatively, they can be specified in multiple ``xkey``
|
|
+can be specified per header line with spaces and/or commas as
|
|
+separators. Alternatively, they can be specified in multiple ``xkey``
|
|
response headers.
|
|
|
|
Preferably the secondary hash keys are set from the backend
|
|
-application, but can also be set from VCL in ``vcl_backend_response``
|
|
-as in the above example.
|
|
+application, but the header can also be set from VCL in
|
|
+``vcl_backend_response``.
|
|
|
|
.. vcl-start
|
|
|
|
@@ -65,10 +66,14 @@ VCL example::
|
|
if (client.ip !~ purgers) {
|
|
return (synth(403, "Forbidden"));
|
|
}
|
|
- set req.http.n-gone = xkey.purge(req.http.xkey-purge);
|
|
- # or: set req.http.n-gone = xkey.softpurge(req.http.xkey-purge)
|
|
-
|
|
- return (synth(200, "Invalidated "+req.http.n-gone+" objects"));
|
|
+ if (req.http.xkey) {
|
|
+ set req.http.n-gone = xkey.purge(req.http.xkey);
|
|
+ # or: set req.http.n-gone = xkey.softpurge(req.http.xkey)
|
|
+
|
|
+ return (synth(200, "Invalidated "+req.http.n-gone+" objects"));
|
|
+ } else {
|
|
+ return (purge);
|
|
+ }
|
|
}
|
|
}
|
|
|
|
@@ -88,9 +93,6 @@ header for a certain page might look like this::
|
|
xkey: 166412
|
|
xkey: 234323
|
|
|
|
-Alternatively you may instead use a single header with space separated
|
|
-values like ``xkey: 8155054 166412 234323``.
|
|
-
|
|
This requires a bit of VCL to be in place. The VCL can be found above.
|
|
|
|
Then, in order to keep the web in sync with the database, a trigger is
|
|
@@ -98,17 +100,10 @@ set up in the database. When an SKU is updated this will trigger an
|
|
HTTP request towards the Varnish server, clearing out every object
|
|
with the matching xkey header::
|
|
|
|
- PURGE / HTTP/1.1
|
|
+ GET / HTTP/1.1
|
|
Host: www.example.com
|
|
xkey-purge: 166412
|
|
|
|
-Several ``xkey-purge`` headers are also supported like in the response
|
|
-example above, and you may also here use a single header with space
|
|
-seperated values like ``xkey-purge: 166412 234323``.
|
|
-
|
|
-Unlike `xkey` header for responses, purge header is fully configurable
|
|
-by means of adjusting the name of the header in the VCL example above.
|
|
-
|
|
Note the xkey-purge header. It is probably a good idea to protect
|
|
this with an ACL so random people from the Internet cannot purge your
|
|
cache.
|
|
@@ -122,20 +117,11 @@ Varnish will find the objects and clear them out, responding with::
|
|
|
|
The objects are now cleared.
|
|
|
|
-CONTENTS
|
|
-========
|
|
-
|
|
-* :ref:`func_purge`
|
|
-* :ref:`func_softpurge`
|
|
-
|
|
-.. _func_purge:
|
|
|
|
-purge
|
|
------
|
|
+.. _vmod_xkey.purge:
|
|
|
|
-::
|
|
-
|
|
- INT purge(STRING keys)
|
|
+INT purge(STRING keys)
|
|
+----------------------
|
|
|
|
Description
|
|
Purges all objects hashed on any key found in the ``keys`` argument.
|
|
@@ -143,15 +129,10 @@ Description
|
|
|
|
The ``keys`` may contain a list of space-separated ids.
|
|
|
|
+.. _vmod_xkey.softpurge:
|
|
|
|
-.. _func_softpurge:
|
|
-
|
|
-softpurge
|
|
----------
|
|
-
|
|
-::
|
|
-
|
|
- INT softpurge(STRING keys)
|
|
+INT softpurge(STRING keys)
|
|
+--------------------------
|
|
|
|
Description
|
|
Performs a "soft purge" for all objects hashed on any key found in the
|
|
@@ -160,5 +141,3 @@ Description
|
|
A softpurge differs from a regular purge in that it resets an
|
|
object's TTL but keeps it available for grace mode and conditional
|
|
requests for the remainder of its configured grace and keep time.
|
|
-
|
|
-
|
|
diff --git a/src/Makefile.am b/src/Makefile.am
|
|
index 36ca267..6a4b48a 100644
|
|
--- a/src/Makefile.am
|
|
+++ b/src/Makefile.am
|
|
@@ -143,6 +143,7 @@ VMOD_TESTS = \
|
|
tests/vsthrottle/test01.vtc \
|
|
tests/vsthrottle/test02.vtc \
|
|
tests/vsthrottle/test03.vtc \
|
|
+ tests/vsthrottle/test04.vtc \
|
|
tests/xkey/test01.vtc \
|
|
tests/xkey/test02.vtc \
|
|
tests/xkey/test03.vtc \
|
|
@@ -150,7 +151,8 @@ VMOD_TESTS = \
|
|
tests/xkey/test05.vtc \
|
|
tests/xkey/test06.vtc \
|
|
tests/xkey/test07.vtc \
|
|
- tests/xkey/test08.vtc
|
|
+ tests/xkey/test08.vtc \
|
|
+ tests/xkey/test09.vtc
|
|
|
|
SOFTPURGE_TESTS = \
|
|
tests/softpurge/01-load.vtc \
|
|
diff --git a/src/tests/saintmode/test02.vtc b/src/tests/saintmode/test02.vtc
|
|
index aa97734..bd8b537 100644
|
|
--- a/src/tests/saintmode/test02.vtc
|
|
+++ b/src/tests/saintmode/test02.vtc
|
|
@@ -51,6 +51,8 @@ varnish v1 -vcl+backend {
|
|
|
|
} -start
|
|
|
|
+varnish v1 -cliok "backend.list"
|
|
+
|
|
client c1 {
|
|
txreq -url "/a"
|
|
rxresp
|
|
diff --git a/src/tests/tcp/02-congestion.vtc b/src/tests/tcp/02-congestion.vtc
|
|
index 2972f6d..e0c09a6 100644
|
|
--- a/src/tests/tcp/02-congestion.vtc
|
|
+++ b/src/tests/tcp/02-congestion.vtc
|
|
@@ -20,9 +20,12 @@ varnish v1 -vcl+backend {
|
|
client c1 {
|
|
txreq -url "/"
|
|
rxresp
|
|
- expect resp.http.a == "0"
|
|
- expect resp.http.b == "-1"
|
|
- expect resp.http.c == "0"
|
|
+ # XXX: it would be better to skip the test altogether when we
|
|
+ # don't have struct tcp_info available on the system.
|
|
+ # See https://github.com/varnish/varnish-modules/issues/115
|
|
+ expect resp.http.a <= 0
|
|
+ expect resp.http.b == -1
|
|
+ expect resp.http.c <= 0
|
|
}
|
|
|
|
client c1 -run
|
|
diff --git a/src/tests/vsthrottle/test04.vtc b/src/tests/vsthrottle/test04.vtc
|
|
new file mode 100644
|
|
index 0000000..bef9ecf
|
|
--- /dev/null
|
|
+++ b/src/tests/vsthrottle/test04.vtc
|
|
@@ -0,0 +1,40 @@
|
|
+varnishtest "Test vsthrottle return_token()"
|
|
+
|
|
+server s1 {
|
|
+ rxreq
|
|
+ txresp
|
|
+} -start
|
|
+
|
|
+varnish v1 -vcl+backend {
|
|
+ import vsthrottle from "${vmod_builddir}/.libs/libvmod_vsthrottle.so";
|
|
+
|
|
+ sub vcl_deliver {
|
|
+ set resp.http.foo-count0 = vsthrottle.remaining("foo", 1, 1s);
|
|
+ if (!vsthrottle.is_denied("foo", 1, 1s)) {
|
|
+ set resp.http.f1 = "OK";
|
|
+ }
|
|
+ set resp.http.foo-count1 = vsthrottle.remaining("foo", 1, 1s);
|
|
+
|
|
+ if (!vsthrottle.is_denied("foo", 1, 1s)) {
|
|
+ set resp.http.f2 = "OK";
|
|
+ }
|
|
+ set resp.http.foo-count2 = vsthrottle.remaining("foo", 1, 1s);
|
|
+
|
|
+ vsthrottle.return_token("foo", 1, 1s);
|
|
+
|
|
+ set resp.http.foo-count3 = vsthrottle.remaining("foo", 1, 1s);
|
|
+ }
|
|
+} -start
|
|
+
|
|
+client c1 {
|
|
+ txreq -url "/"
|
|
+ rxresp
|
|
+ expect resp.http.foo-count0 == "1"
|
|
+ expect resp.http.f1 == "OK"
|
|
+ expect resp.http.foo-count1 == "0"
|
|
+ expect resp.http.f2 == <undef>
|
|
+ expect resp.http.foo-count2 == "0"
|
|
+ expect resp.http.foo-count3 == "1"
|
|
+}
|
|
+
|
|
+client c1 -run
|
|
diff --git a/src/tests/xkey/test09.vtc b/src/tests/xkey/test09.vtc
|
|
new file mode 100644
|
|
index 0000000..c010c37
|
|
--- /dev/null
|
|
+++ b/src/tests/xkey/test09.vtc
|
|
@@ -0,0 +1,66 @@
|
|
+varnishtest "Test xkey vmod multiple keys, comma separated"
|
|
+# https://github.com/varnish/varnish-modules/issues/102
|
|
+
|
|
+server s1 {
|
|
+ rxreq
|
|
+ txresp -hdr "xkey: asdf, fdsa"
|
|
+
|
|
+ rxreq
|
|
+ txresp -hdr "xkey: qwer, rewq"
|
|
+} -start
|
|
+
|
|
+varnish v1 -vcl+backend {
|
|
+ import xkey from "${vmod_builddir}/.libs/libvmod_xkey.so";
|
|
+
|
|
+ sub vcl_recv {
|
|
+ if (req.http.xkey-purge) {
|
|
+ if (xkey.purge(req.http.xkey-purge) != 0) {
|
|
+ return (synth(200, "Purged"));
|
|
+ } else {
|
|
+ return (synth(404, "No key"));
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ sub vcl_backend_response {
|
|
+ set beresp.ttl = 60s;
|
|
+ set beresp.grace = 0s;
|
|
+ set beresp.keep = 0s;
|
|
+ }
|
|
+
|
|
+ sub vcl_synth {
|
|
+ set resp.http.reason = resp.reason;
|
|
+ }
|
|
+} -start
|
|
+
|
|
+client c1 {
|
|
+ txreq
|
|
+ rxresp
|
|
+ txreq -url /2
|
|
+ rxresp
|
|
+} -run
|
|
+
|
|
+varnish v1 -expect n_object == 2
|
|
+
|
|
+client c1 {
|
|
+ # Test the purge using the asdf key, so we check that we didn't include the comma
|
|
+ txreq -hdr "xkey-purge: asdf"
|
|
+ rxresp
|
|
+ expect resp.status == 200
|
|
+ expect resp.http.reason == "Purged"
|
|
+} -run
|
|
+
|
|
+delay 1
|
|
+
|
|
+varnish v1 -expect n_object == 1
|
|
+
|
|
+client c1 {
|
|
+ txreq -hdr "xkey-purge: rewq"
|
|
+ rxresp
|
|
+ expect resp.status == 200
|
|
+ expect resp.http.reason == "Purged"
|
|
+} -run
|
|
+
|
|
+delay 1
|
|
+
|
|
+varnish v1 -expect n_object == 0
|
|
diff --git a/src/vmod_bodyaccess.c b/src/vmod_bodyaccess.c
|
|
index d1f324f..a8a8951 100644
|
|
--- a/src/vmod_bodyaccess.c
|
|
+++ b/src/vmod_bodyaccess.c
|
|
@@ -114,6 +114,26 @@ bodyaccess_log_cb(struct req *req, void *priv, void *ptr, size_t len)
|
|
|
|
return (bodyaccess_log(priv, ptr, len));
|
|
}
|
|
+#elif defined(HAVE_OBJITERATE_F) && defined(OBJ_ITER_FLUSH)
|
|
+static int
|
|
+bodyaccess_bcat_cb(void *priv, unsigned flush, const void *ptr, ssize_t len)
|
|
+{
|
|
+
|
|
+ AN(priv);
|
|
+
|
|
+ (void)flush;
|
|
+ return (VSB_bcat(priv, ptr, len));
|
|
+}
|
|
+
|
|
+static int
|
|
+bodyaccess_log_cb(void *priv, unsigned flush, const void *ptr, ssize_t len)
|
|
+{
|
|
+
|
|
+ AN(priv);
|
|
+
|
|
+ (void)flush;
|
|
+ return (bodyaccess_log(priv, ptr, len));
|
|
+}
|
|
#elif defined(HAVE_OBJITERATE_F)
|
|
static int
|
|
bodyaccess_bcat_cb(void *priv, int flush, const void *ptr, ssize_t len)
|
|
@@ -187,6 +207,8 @@ vmod_hash_req_body(VRT_CTX)
|
|
VCL_INT
|
|
vmod_len_req_body(VRT_CTX)
|
|
{
|
|
+ uint64_t u;
|
|
+
|
|
CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
|
|
CHECK_OBJ_NOTNULL(ctx->req, REQ_MAGIC);
|
|
|
|
@@ -202,7 +224,10 @@ vmod_len_req_body(VRT_CTX)
|
|
return (-1);
|
|
}
|
|
|
|
- return (ctx->req->req_bodybytes);
|
|
+ AZ(ObjGetU64(ctx->req->wrk, ctx->req->body_oc, OA_LEN, &u));
|
|
+ AZ(u > INT64_MAX);
|
|
+
|
|
+ return ((VCL_INT)u);
|
|
}
|
|
|
|
VCL_INT
|
|
diff --git a/src/vmod_bodyaccess.vcc b/src/vmod_bodyaccess.vcc
|
|
index d5cdf61..55666fc 100644
|
|
--- a/src/vmod_bodyaccess.vcc
|
|
+++ b/src/vmod_bodyaccess.vcc
|
|
@@ -1,4 +1,4 @@
|
|
-$Module bodyaccess 3 Varnish Module for request body access
|
|
+$Module bodyaccess 3 "Varnish Module for request body access"
|
|
|
|
DESCRIPTION
|
|
===========
|
|
diff --git a/src/vmod_cookie.vcc b/src/vmod_cookie.vcc
|
|
index edd383a..0f49bad 100644
|
|
--- a/src/vmod_cookie.vcc
|
|
+++ b/src/vmod_cookie.vcc
|
|
@@ -1,4 +1,4 @@
|
|
-$Module cookie 3 Varnish Cookie Module
|
|
+$Module cookie 3 "Varnish Cookie Module"
|
|
DESCRIPTION
|
|
===========
|
|
|
|
diff --git a/src/vmod_header.c b/src/vmod_header.c
|
|
index 798afdb..ea6b5d5 100644
|
|
--- a/src/vmod_header.c
|
|
+++ b/src/vmod_header.c
|
|
@@ -193,7 +193,7 @@ header_http_cphdr(VRT_CTX, const struct http *hp, const char *hdr,
|
|
* vmod entrypoint. Sets up the header mutex.
|
|
*/
|
|
int
|
|
-event_function(VRT_CTX, struct vmod_priv *priv, enum vcl_event_e e)
|
|
+vmod_event_function(VRT_CTX, struct vmod_priv *priv, enum vcl_event_e e)
|
|
{
|
|
(void)ctx;
|
|
(void)priv;
|
|
diff --git a/src/vmod_header.vcc b/src/vmod_header.vcc
|
|
index 5df8dc6..4a5b27c 100644
|
|
--- a/src/vmod_header.vcc
|
|
+++ b/src/vmod_header.vcc
|
|
@@ -1,4 +1,4 @@
|
|
-$Module header 3 Header VMOD for Varnish
|
|
+$Module header 3 "Header VMOD for Varnish"
|
|
DESCRIPTION
|
|
===========
|
|
|
|
@@ -57,7 +57,7 @@ Example
|
|
$Function VOID remove(PRIV_CALL, HEADER header, STRING regex)
|
|
|
|
Description
|
|
- Remove all occurences of `header` that matches `regex`.
|
|
+ Remove all occurrences of `header` that matches `regex`.
|
|
Example
|
|
::
|
|
header.remove(beresp.http.set-cookie,"^(?!(funcookie=))");
|
|
diff --git a/src/vmod_saintmode.c b/src/vmod_saintmode.c
|
|
index 26191cc..b0e4ac6 100644
|
|
--- a/src/vmod_saintmode.c
|
|
+++ b/src/vmod_saintmode.c
|
|
@@ -35,14 +35,16 @@
|
|
|
|
#include "vmod_config.h"
|
|
|
|
-#include "cache/cache_director.h"
|
|
-#include "cache/cache_backend.h"
|
|
-
|
|
#include "vsb.h"
|
|
#include "vtim.h"
|
|
|
|
#include "vcc_saintmode_if.h"
|
|
|
|
+static VCL_BOOL v_matchproto_(vdi_healthy_f)
|
|
+healthy(VRT_CTX, VCL_BACKEND, VCL_TIME *);
|
|
+static VCL_BACKEND v_matchproto_(vdi_resolve_f)
|
|
+resolve(VRT_CTX, VCL_BACKEND);
|
|
+
|
|
struct trouble {
|
|
unsigned magic;
|
|
#define TROUBLE_MAGIC 0x4211ab21
|
|
@@ -54,7 +56,7 @@ struct trouble {
|
|
struct vmod_saintmode_saintmode {
|
|
unsigned magic;
|
|
#define VMOD_SAINTMODE_MAGIC 0xa03756e4
|
|
- struct director sdir[1];
|
|
+ const struct director *sdir;
|
|
const struct director *be;
|
|
pthread_mutex_t mtx;
|
|
unsigned threshold;
|
|
@@ -63,6 +65,14 @@ struct vmod_saintmode_saintmode {
|
|
VTAILQ_HEAD(, trouble) troublelist;
|
|
};
|
|
|
|
+static const struct vdi_methods vmod_saintmode_methods[1] = {{
|
|
+ .magic = VDI_METHODS_MAGIC,
|
|
+ .type = "saintmode",
|
|
+ .healthy = healthy,
|
|
+ .resolve = resolve
|
|
+}};
|
|
+
|
|
+
|
|
struct saintmode_objs {
|
|
unsigned magic;
|
|
#define SAINTMODE_OBJS_MAGIC 0x9aa7beec
|
|
@@ -253,23 +263,26 @@ is_digest_healthy(const struct director *dir,
|
|
}
|
|
|
|
/* All adapted from PHK's saintmode implementation in Varnish 3.0 */
|
|
-static unsigned
|
|
-healthy(const struct director *dir, const struct busyobj *bo, double *changed)
|
|
+static VCL_BOOL v_matchproto_(vdi_healthy_f)
|
|
+healthy(VRT_CTX, VCL_BACKEND dir, VCL_TIME *changed)
|
|
{
|
|
struct vmod_saintmode_saintmode *sm;
|
|
+ const struct busyobj *bo;
|
|
unsigned retval;
|
|
const uint8_t* digest;
|
|
double t_prev;
|
|
struct vsl_log* vsl;
|
|
|
|
+ CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
|
|
CHECK_OBJ_NOTNULL(dir, DIRECTOR_MAGIC);
|
|
CAST_OBJ_NOTNULL(sm, dir->priv, VMOD_SAINTMODE_MAGIC);
|
|
CHECK_OBJ_NOTNULL(sm->be, DIRECTOR_MAGIC);
|
|
+ bo = ctx->bo;
|
|
CHECK_OBJ_ORNULL(bo, BUSYOBJ_MAGIC);
|
|
|
|
/* Saintmode is disabled, or list is empty */
|
|
if (sm->threshold == 0 || sm->n_trouble == 0)
|
|
- return (sm->be->healthy(sm->be, bo, changed));
|
|
+ return (VRT_Healthy(ctx, sm->be, changed));
|
|
|
|
if (!bo) {
|
|
digest = NULL;
|
|
@@ -282,7 +295,7 @@ healthy(const struct director *dir, const struct busyobj *bo, double *changed)
|
|
}
|
|
|
|
retval = is_digest_healthy(dir, digest, t_prev, vsl);
|
|
- return (retval ? sm->be->healthy(sm->be, bo, changed) : 0);
|
|
+ return (retval ? VRT_Healthy(ctx, sm->be, changed) : 0);
|
|
}
|
|
|
|
VCL_BOOL
|
|
@@ -303,19 +316,17 @@ vmod_saintmode_is_healthy(VRT_CTX, struct vmod_saintmode_saintmode *sm)
|
|
return is_digest_healthy(sm->sdir, digest,
|
|
ctx->req->t_prev, ctx->req->vsl);
|
|
} else
|
|
- return healthy(sm->sdir, ctx->bo, NULL);
|
|
+ return healthy(ctx, sm->sdir, NULL);
|
|
}
|
|
|
|
-static const struct director *
|
|
-resolve(const struct director *dir, struct worker *wrk, struct busyobj *bo) {
|
|
+static VCL_BACKEND v_matchproto_(vdi_resolve_f)
|
|
+resolve(VRT_CTX, VCL_BACKEND dir) {
|
|
struct vmod_saintmode_saintmode *sm;
|
|
- double changed = 0.0;
|
|
|
|
CHECK_OBJ_NOTNULL(dir, DIRECTOR_MAGIC);
|
|
CAST_OBJ_NOTNULL(sm, dir->priv, VMOD_SAINTMODE_MAGIC);
|
|
- (void)wrk;
|
|
|
|
- if (!healthy(dir, bo, &changed))
|
|
+ if (!healthy(ctx, dir, NULL))
|
|
return (NULL);
|
|
|
|
return (sm->be);
|
|
@@ -342,15 +353,7 @@ vmod_saintmode__init(VRT_CTX, struct vmod_saintmode_saintmode **smp,
|
|
sm->be = be;
|
|
VTAILQ_INIT(&sm->troublelist);
|
|
|
|
- sm->sdir->magic = DIRECTOR_MAGIC;
|
|
- sm->sdir->resolve = resolve;
|
|
- sm->sdir->healthy = healthy;
|
|
-#ifdef HAVE_DIRECTOR_ADMIN_HEALTH
|
|
- sm->sdir->admin_health = VDI_AH_HEALTHY;
|
|
-#endif
|
|
- REPLACE(sm->sdir->vcl_name, vcl_name);
|
|
- sm->sdir->name = "saintmode";
|
|
- sm->sdir->priv = sm;
|
|
+ sm->sdir = VRT_AddDirector(ctx, vmod_saintmode_methods, sm, "%s", vcl_name);
|
|
|
|
if (!priv->priv) {
|
|
ALLOC_OBJ(sm_objs, SAINTMODE_OBJS_MAGIC);
|
|
@@ -380,7 +383,7 @@ vmod_saintmode__fini(struct vmod_saintmode_saintmode **smp) {
|
|
FREE_OBJ(tr);
|
|
}
|
|
|
|
- free(sm->sdir->vcl_name);
|
|
+ VRT_DelDirector(&sm->sdir);
|
|
AZ(pthread_mutex_destroy(&sm->mtx));
|
|
|
|
/* We can no longer refer to the sm_objs after this
|
|
diff --git a/src/vmod_saintmode.vcc b/src/vmod_saintmode.vcc
|
|
index 4315e43..27f8ba0 100644
|
|
--- a/src/vmod_saintmode.vcc
|
|
+++ b/src/vmod_saintmode.vcc
|
|
@@ -1,4 +1,4 @@
|
|
-$Module saintmode 3 Saint mode backend director
|
|
+$Module saintmode 3 "Saint mode backend director"
|
|
|
|
DESCRIPTION
|
|
===========
|
|
diff --git a/src/vmod_softpurge.c b/src/vmod_softpurge.c
|
|
index 9273de5..0e46c25 100644
|
|
--- a/src/vmod_softpurge.c
|
|
+++ b/src/vmod_softpurge.c
|
|
@@ -52,8 +52,12 @@ vmod_softpurge(VRT_CTX)
|
|
CHECK_OBJ_NOTNULL(ctx->req->wrk, WORKER_MAGIC);
|
|
oh = ctx->req->objcore->objhead;
|
|
CHECK_OBJ_NOTNULL(oh, OBJHEAD_MAGIC);
|
|
- spc = WS_Reserve(ctx->ws, 0);
|
|
- assert(spc >= sizeof *ocp);
|
|
+ spc = WS_ReserveAll(ctx->ws);
|
|
+ if (spc < sizeof *ocp) {
|
|
+ WS_Release(ctx->ws, 0);
|
|
+ VRT_fail(ctx, "insufficient workspace");
|
|
+ return;
|
|
+ }
|
|
|
|
nobj = 0;
|
|
ocp = (void*)ctx->ws->f;
|
|
diff --git a/src/vmod_softpurge.vcc b/src/vmod_softpurge.vcc
|
|
index 20252dd..aaf9a1c 100644
|
|
--- a/src/vmod_softpurge.vcc
|
|
+++ b/src/vmod_softpurge.vcc
|
|
@@ -1,4 +1,4 @@
|
|
-$Module softpurge Soft purge vmod
|
|
+$Module softpurge 3 "Soft purge vmod"
|
|
DESCRIPTION
|
|
===========
|
|
|
|
diff --git a/src/vmod_tcp.c b/src/vmod_tcp.c
|
|
index fee4bd3..570be45 100644
|
|
--- a/src/vmod_tcp.c
|
|
+++ b/src/vmod_tcp.c
|
|
@@ -174,7 +174,7 @@ vmod_set_socket_pace(VRT_CTX, VCL_INT rate)
|
|
sizeof(pacerate)) != 0)
|
|
VSLb(ctx->vsl, SLT_VCL_Error, "set_socket_pace(): Error setting pace rate.");
|
|
else
|
|
- VSLb(ctx->vsl, SLT_VCL_Log, "vmod-tcp: Socket paced to %lu KB/s.", rate);
|
|
+ VSLb(ctx->vsl, SLT_VCL_Log, "vmod-tcp: Socket paced to %jd KB/s.", (intmax_t)rate);
|
|
|
|
# ifndef NDEBUG
|
|
int retval;
|
|
diff --git a/src/vmod_tcp.vcc b/src/vmod_tcp.vcc
|
|
index 8659770..fddf7c6 100644
|
|
--- a/src/vmod_tcp.vcc
|
|
+++ b/src/vmod_tcp.vcc
|
|
@@ -1,4 +1,4 @@
|
|
-$Module tcp 3 TCP vmod
|
|
+$Module tcp 3 "TCP vmod"
|
|
DESCRIPTION
|
|
===========
|
|
|
|
diff --git a/src/vmod_var.vcc b/src/vmod_var.vcc
|
|
index 719b182..7af5cf1 100644
|
|
--- a/src/vmod_var.vcc
|
|
+++ b/src/vmod_var.vcc
|
|
@@ -1,4 +1,4 @@
|
|
-$Module var 3 Variable support for Varnish VCL
|
|
+$Module var 3 "Variable support for Varnish VCL"
|
|
|
|
This VMOD implements basic variable support in VCL.
|
|
|
|
diff --git a/src/vmod_vsthrottle.c b/src/vmod_vsthrottle.c
|
|
index ff1719c..e088860 100644
|
|
--- a/src/vmod_vsthrottle.c
|
|
+++ b/src/vmod_vsthrottle.c
|
|
@@ -58,7 +58,7 @@ struct tbucket {
|
|
double block;
|
|
long tokens;
|
|
long capacity;
|
|
- VRB_ENTRY(tbucket) tree;
|
|
+ VRBT_ENTRY(tbucket) tree;
|
|
};
|
|
|
|
static int
|
|
@@ -67,9 +67,9 @@ keycmp(const struct tbucket *b1, const struct tbucket *b2)
|
|
return (memcmp(b1->digest, b2->digest, sizeof b1->digest));
|
|
}
|
|
|
|
-VRB_HEAD(tbtree, tbucket);
|
|
-VRB_PROTOTYPE_STATIC(tbtree, tbucket, tree, keycmp);
|
|
-VRB_GENERATE_STATIC(tbtree, tbucket, tree, keycmp);
|
|
+VRBT_HEAD(tbtree, tbucket);
|
|
+VRBT_PROTOTYPE_STATIC(tbtree, tbucket, tree, keycmp);
|
|
+VRBT_GENERATE_STATIC(tbtree, tbucket, tree, keycmp);
|
|
|
|
/* To lessen potential mutex contention, we partition the buckets into
|
|
N_PART partitions. */
|
|
@@ -119,12 +119,12 @@ get_bucket(const unsigned char *digest, long limit, double period, double now)
|
|
|
|
INIT_OBJ(&k, TBUCKET_MAGIC);
|
|
memcpy(&k.digest, digest, sizeof k.digest);
|
|
- b = VRB_FIND(tbtree, &v->buckets, &k);
|
|
+ b = VRBT_FIND(tbtree, &v->buckets, &k);
|
|
if (b) {
|
|
CHECK_OBJ_NOTNULL(b, TBUCKET_MAGIC);
|
|
} else {
|
|
b = tb_alloc(digest, limit, period, now);
|
|
- AZ(VRB_INSERT(tbtree, &v->buckets, b));
|
|
+ AZ(VRBT_INSERT(tbtree, &v->buckets, b));
|
|
}
|
|
return (b);
|
|
}
|
|
@@ -205,6 +205,32 @@ vmod_is_denied(VRT_CTX, VCL_STRING key, VCL_INT limit, VCL_DURATION period,
|
|
return (ret);
|
|
}
|
|
|
|
+VCL_VOID
|
|
+vmod_return_token(VRT_CTX, VCL_STRING key, VCL_INT limit, VCL_DURATION period,
|
|
+ VCL_DURATION block)
|
|
+{
|
|
+ struct tbucket *b;
|
|
+ double now;
|
|
+
|
|
+ struct vsthrottle *v;
|
|
+ unsigned char digest[SHA256_LEN];
|
|
+ unsigned part;
|
|
+
|
|
+ (void)ctx;
|
|
+
|
|
+ if (!key)
|
|
+ return;
|
|
+ do_digest(digest, key, limit, period, block);
|
|
+
|
|
+ part = digest[0] & N_PART_MASK;
|
|
+ v = &vsthrottle[part];
|
|
+ AZ(pthread_mutex_lock(&v->mtx));
|
|
+ now = VTIM_mono();
|
|
+ b = get_bucket(digest, limit, period, now);
|
|
+ b->tokens++;
|
|
+ AZ(pthread_mutex_unlock(&v->mtx));
|
|
+}
|
|
+
|
|
/* Clean up expired entries. */
|
|
static void
|
|
run_gc(double now, unsigned part)
|
|
@@ -213,11 +239,11 @@ run_gc(double now, unsigned part)
|
|
struct tbtree *buckets = &vsthrottle[part].buckets;
|
|
|
|
/* XXX: Assert mtx is held ... */
|
|
- VRB_FOREACH_SAFE(x, tbtree, buckets, y) {
|
|
+ VRBT_FOREACH_SAFE(x, tbtree, buckets, y) {
|
|
CHECK_OBJ_NOTNULL(x, TBUCKET_MAGIC);
|
|
if (now - x->last_used > x->period) {
|
|
- VRB_REMOVE(tbtree, buckets, x);
|
|
- free(x);
|
|
+ VRBT_REMOVE(tbtree, buckets, x);
|
|
+ FREE_OBJ(x);
|
|
}
|
|
}
|
|
}
|
|
@@ -291,9 +317,9 @@ fini(void *priv)
|
|
|
|
for (p = 0; p < N_PART; ++p ) {
|
|
struct vsthrottle *v = &vsthrottle[p];
|
|
- VRB_FOREACH_SAFE(x, tbtree, &v->buckets, y) {
|
|
+ VRBT_FOREACH_SAFE(x, tbtree, &v->buckets, y) {
|
|
CHECK_OBJ_NOTNULL(x, TBUCKET_MAGIC);
|
|
- VRB_REMOVE(tbtree, &v->buckets, x);
|
|
+ VRBT_REMOVE(tbtree, &v->buckets, x);
|
|
free(x);
|
|
}
|
|
}
|
|
@@ -302,7 +328,7 @@ fini(void *priv)
|
|
}
|
|
|
|
int
|
|
-event_function(VRT_CTX, struct vmod_priv *priv, enum vcl_event_e e)
|
|
+vmod_event_function(VRT_CTX, struct vmod_priv *priv, enum vcl_event_e e)
|
|
{
|
|
if (e != VCL_EVENT_LOAD)
|
|
return (0);
|
|
@@ -318,7 +344,7 @@ event_function(VRT_CTX, struct vmod_priv *priv, enum vcl_event_e e)
|
|
struct vsthrottle *v = &vsthrottle[p];
|
|
v->magic = VSTHROTTLE_MAGIC;
|
|
AZ(pthread_mutex_init(&v->mtx, NULL));
|
|
- VRB_INIT(&v->buckets);
|
|
+ VRBT_INIT(&v->buckets);
|
|
}
|
|
}
|
|
n_init++;
|
|
diff --git a/src/vmod_vsthrottle.vcc b/src/vmod_vsthrottle.vcc
|
|
index 1f10080..72a8839 100644
|
|
--- a/src/vmod_vsthrottle.vcc
|
|
+++ b/src/vmod_vsthrottle.vcc
|
|
@@ -1,4 +1,4 @@
|
|
-$Module vsthrottle 3 Throttling VMOD
|
|
+$Module vsthrottle 3 "Throttling VMOD"
|
|
DESCRIPTION
|
|
===========
|
|
|
|
@@ -71,16 +71,17 @@ Arguments:
|
|
- key: A unique identifier to define what is being throttled - more examples below
|
|
- limit: How many requests in the specified period
|
|
- period: The time period
|
|
- - block: a period to deny all access after meeting the threshold
|
|
+ - block: a period to deny all access after hitting the threshold. Default is 0s
|
|
|
|
Description
|
|
Can be used to rate limit the traffic for a specific key to a
|
|
maximum of 'limit' requests per 'period' time. If 'block' is > 0s,
|
|
(0s by default), then always deny for 'key' for that length of time
|
|
- after hitting the threshold. A token bucket is uniquely identified
|
|
- by the 4-tuple of its key, limit, period and block, so using the
|
|
- same key multiple places with different rules will create multiple
|
|
- token buckets.
|
|
+ after hitting the threshold.
|
|
+
|
|
+ Note: A token bucket is uniquely identified by the 4-tuple of its
|
|
+ key, limit, period and block, so using the same key multiple places
|
|
+ with different rules will create multiple token buckets.
|
|
|
|
Example
|
|
::
|
|
@@ -95,14 +96,48 @@ Example
|
|
}
|
|
|
|
|
|
+$Function VOID return_token(STRING key, INT limit, DURATION period,
|
|
+ DURATION block=0)
|
|
+
|
|
+Arguments:
|
|
+ - Same arguments as is_denied()
|
|
+
|
|
+Description
|
|
+ Increment (by one) the number of tokens in the specified bucket. is_denied()
|
|
+ decrements the bucket by one token and return_token() adds it back.
|
|
+ Using these two, you can effectively make a token bucket act like a limit on
|
|
+ concurrent requests instead of requests / time.
|
|
+
|
|
+ Note: This function doesn't enforce anything, it merely credits a token to
|
|
+ appropriate bucket.
|
|
+
|
|
+ Warning: If streaming is enabled (beresp.do_stream = true) as it is by
|
|
+ default now, vcl_deliver() is called *before* the response is sent
|
|
+ to the client (who may download it slowly). Thus you may credit the token
|
|
+ back too early if you use return_token() in vcl_backend_response().
|
|
+
|
|
+Example
|
|
+ ::
|
|
+
|
|
+ sub vcl_recv {
|
|
+ if (vsthrottle.is_denied(client.identity, 20, 20s)) {
|
|
+ # Client has more than 20 concurrent requests
|
|
+ return (synth(429, "Too Many Requests In Flight"));
|
|
+ }
|
|
+
|
|
+ # ...
|
|
+ }
|
|
+
|
|
+ sub vcl_deliver {
|
|
+ vsthrottle.return_token(client.identity, 10, 10s);
|
|
+ }
|
|
+
|
|
+
|
|
$Function INT remaining(STRING key, INT limit, DURATION period,
|
|
DURATION block=0)
|
|
|
|
Arguments:
|
|
- - key: A unique identifier to define what is being throttled
|
|
- - limit: How many requests in the specified period
|
|
- - period: The time period
|
|
- - block: duration to block, defaults to 0s
|
|
+ - Same arguments as is_denied()
|
|
|
|
Description
|
|
|
|
@@ -122,10 +157,7 @@ $Function DURATION blocked(STRING key, INT limit, DURATION period,
|
|
DURATION block)
|
|
|
|
Arguments:
|
|
- - key: A unique identifier to define what is being throttled
|
|
- - limit: How many requests in the specified period
|
|
- - period: The time period
|
|
- - block: duration to block
|
|
+ - Same arguments as is_denied()
|
|
|
|
Description
|
|
|
|
diff --git a/src/vmod_xkey.c b/src/vmod_xkey.c
|
|
index 3cf8069..ff825fd 100644
|
|
--- a/src/vmod_xkey.c
|
|
+++ b/src/vmod_xkey.c
|
|
@@ -51,23 +51,23 @@ uintptr_t xkey_cb_handle;
|
|
|
|
struct xkey_hashkey {
|
|
char digest[DIGEST_LEN];
|
|
- VRB_ENTRY(xkey_hashkey) entry;
|
|
+ VRBT_ENTRY(xkey_hashkey) entry;
|
|
};
|
|
static int xkey_hashcmp(const struct xkey_hashkey *k1,
|
|
const struct xkey_hashkey *k2);
|
|
-VRB_HEAD(xkey_hashtree, xkey_hashkey);
|
|
-static struct xkey_hashtree xkey_hashtree = VRB_INITIALIZER(&xkey_hashtree);
|
|
-VRB_PROTOTYPE(xkey_hashtree, xkey_hashkey, entry, xkey_hashcmp);
|
|
+VRBT_HEAD(xkey_hashtree, xkey_hashkey);
|
|
+static struct xkey_hashtree xkey_hashtree = VRBT_INITIALIZER(&xkey_hashtree);
|
|
+VRBT_PROTOTYPE(xkey_hashtree, xkey_hashkey, entry, xkey_hashcmp);
|
|
|
|
struct xkey_ptrkey {
|
|
uintptr_t ptr;
|
|
- VRB_ENTRY(xkey_ptrkey) entry;
|
|
+ VRBT_ENTRY(xkey_ptrkey) entry;
|
|
};
|
|
static int xkey_ptrcmp(const struct xkey_ptrkey *k1,
|
|
const struct xkey_ptrkey *k2);
|
|
-VRB_HEAD(xkey_octree, xkey_ptrkey);
|
|
-static struct xkey_octree xkey_octree = VRB_INITIALIZER(&xkey_octree);
|
|
-VRB_PROTOTYPE(xkey_octree, xkey_ptrkey, entry, xkey_ptrcmp)
|
|
+VRBT_HEAD(xkey_octree, xkey_ptrkey);
|
|
+static struct xkey_octree xkey_octree = VRBT_INITIALIZER(&xkey_octree);
|
|
+VRBT_PROTOTYPE(xkey_octree, xkey_ptrkey, entry, xkey_ptrcmp)
|
|
|
|
struct xkey_hashhead;
|
|
struct xkey_ochead;
|
|
@@ -114,8 +114,8 @@ static struct {
|
|
|
|
/*******************/
|
|
|
|
-VRB_GENERATE(xkey_hashtree, xkey_hashkey, entry, xkey_hashcmp);
|
|
-VRB_GENERATE(xkey_octree, xkey_ptrkey, entry, xkey_ptrcmp);
|
|
+VRBT_GENERATE(xkey_hashtree, xkey_hashkey, entry, xkey_hashcmp);
|
|
+VRBT_GENERATE(xkey_octree, xkey_ptrkey, entry, xkey_ptrcmp);
|
|
|
|
static int
|
|
xkey_hashcmp(const struct xkey_hashkey *k1, const struct xkey_hashkey *k2)
|
|
@@ -246,7 +246,7 @@ xkey_hashtree_lookup(const unsigned char *digest, unsigned len)
|
|
AN(digest);
|
|
assert(len == sizeof(key.digest));
|
|
memcpy(&key.digest, digest, len);
|
|
- pkey = VRB_FIND(xkey_hashtree, &xkey_hashtree, &key);
|
|
+ pkey = VRBT_FIND(xkey_hashtree, &xkey_hashtree, &key);
|
|
if (pkey != NULL)
|
|
CAST_OBJ_NOTNULL(head, (void *)pkey, XKEY_HASHHEAD_MAGIC);
|
|
return (head);
|
|
@@ -262,7 +262,7 @@ xkey_hashtree_insert(const unsigned char *digest, unsigned len)
|
|
head = xkey_hashhead_new();
|
|
assert(len == sizeof(head->key.digest));
|
|
memcpy(&head->key.digest, digest, len);
|
|
- key = VRB_INSERT(xkey_hashtree, &xkey_hashtree, &head->key);
|
|
+ key = VRBT_INSERT(xkey_hashtree, &xkey_hashtree, &head->key);
|
|
if (key != NULL) {
|
|
xkey_hashhead_delete(&head);
|
|
CAST_OBJ_NOTNULL(head, (void *)key, XKEY_HASHHEAD_MAGIC);
|
|
@@ -278,7 +278,7 @@ xkey_octree_lookup(uintptr_t ptr)
|
|
|
|
AN(ptr);
|
|
key.ptr = ptr;
|
|
- pkey = VRB_FIND(xkey_octree, &xkey_octree, &key);
|
|
+ pkey = VRBT_FIND(xkey_octree, &xkey_octree, &key);
|
|
if (pkey)
|
|
CAST_OBJ_NOTNULL(head, (void *)pkey, XKEY_OCHEAD_MAGIC);
|
|
return (head);
|
|
@@ -293,7 +293,7 @@ xkey_octree_insert(uintptr_t ptr)
|
|
AN(ptr);
|
|
head = xkey_ochead_new();
|
|
head->key.ptr = ptr;
|
|
- key = VRB_INSERT(xkey_octree, &xkey_octree, &head->key);
|
|
+ key = VRBT_INSERT(xkey_octree, &xkey_octree, &head->key);
|
|
if (key != NULL) {
|
|
xkey_ochead_delete(&head);
|
|
CAST_OBJ_NOTNULL(head, (void *)key, XKEY_OCHEAD_MAGIC);
|
|
@@ -342,7 +342,7 @@ xkey_remove(struct xkey_ochead **pochead)
|
|
oc->hashhead = NULL;
|
|
VTAILQ_REMOVE(&hashhead->ocs, oc, list_hashhead);
|
|
if (VTAILQ_EMPTY(&hashhead->ocs)) {
|
|
- VRB_REMOVE(xkey_hashtree, &xkey_hashtree,
|
|
+ VRBT_REMOVE(xkey_hashtree, &xkey_hashtree,
|
|
&hashhead->key);
|
|
xkey_hashhead_delete(&hashhead);
|
|
}
|
|
@@ -351,7 +351,7 @@ xkey_remove(struct xkey_ochead **pochead)
|
|
xkey_oc_delete(&oc);
|
|
}
|
|
AN(VTAILQ_EMPTY(&ochead->ocs));
|
|
- VRB_REMOVE(xkey_octree, &xkey_octree, &ochead->key);
|
|
+ VRBT_REMOVE(xkey_octree, &xkey_octree, &ochead->key);
|
|
xkey_ochead_delete(&ochead);
|
|
}
|
|
|
|
@@ -364,19 +364,19 @@ xkey_cleanup(void)
|
|
struct xkey_ochead *ochead;
|
|
struct xkey_oc *oc;
|
|
|
|
- VRB_FOREACH(hashkey, xkey_hashtree, &xkey_hashtree) {
|
|
+ VRBT_FOREACH(hashkey, xkey_hashtree, &xkey_hashtree) {
|
|
CAST_OBJ_NOTNULL(hashhead, (void *)hashkey,
|
|
XKEY_HASHHEAD_MAGIC);
|
|
VTAILQ_CONCAT(&xkey_pool.ocs, &hashhead->ocs, list_ochead);
|
|
VTAILQ_INSERT_HEAD(&xkey_pool.hashheads, hashhead, list);
|
|
}
|
|
- VRB_INIT(&xkey_hashtree);
|
|
+ VRBT_INIT(&xkey_hashtree);
|
|
|
|
- VRB_FOREACH(ockey, xkey_octree, &xkey_octree) {
|
|
+ VRBT_FOREACH(ockey, xkey_octree, &xkey_octree) {
|
|
CAST_OBJ_NOTNULL(ochead, (void *)ockey, XKEY_OCHEAD_MAGIC);
|
|
VTAILQ_INSERT_HEAD(&xkey_pool.ocheads, ochead, list);
|
|
}
|
|
- VRB_INIT(&xkey_octree);
|
|
+ VRBT_INIT(&xkey_octree);
|
|
|
|
while (!VTAILQ_EMPTY(&xkey_pool.hashheads)) {
|
|
hashhead = VTAILQ_FIRST(&xkey_pool.hashheads);
|
|
@@ -410,11 +410,11 @@ xkey_tok(const char **b, const char **e)
|
|
t = *b;
|
|
AN(t);
|
|
|
|
- while (isblank(*t))
|
|
+ while (*t == ',' || isblank(*t))
|
|
t++;
|
|
*b = t;
|
|
|
|
- while (*t != '\0' && !isblank(*t))
|
|
+ while (*t != '\0' && *t != ',' && !isblank(*t))
|
|
t++;
|
|
*e = t;
|
|
return (*b < *e);
|
|
diff --git a/src/vmod_xkey.vcc b/src/vmod_xkey.vcc
|
|
index 637d4e8..3e6553c 100644
|
|
--- a/src/vmod_xkey.vcc
|
|
+++ b/src/vmod_xkey.vcc
|
|
@@ -1,4 +1,4 @@
|
|
-$Module xkey 3 Surrogate keys support for Varnish Cache
|
|
+$Module xkey 3 "Surrogate keys support for Varnish Cache"
|
|
DESCRIPTION
|
|
===========
|
|
|
|
@@ -15,8 +15,8 @@ Similarly with an e-commerce site, where various SKUs are often
|
|
referenced on a page.
|
|
|
|
Hash keys are specified in the ``xkey`` response header. Multiple keys
|
|
-can be specified per header line with a space
|
|
-separator. Alternatively, they can be specified in multiple ``xkey``
|
|
+can be specified per header line with spaces and/or commas as
|
|
+separators. Alternatively, they can be specified in multiple ``xkey``
|
|
response headers.
|
|
|
|
Preferably the secondary hash keys are set from the backend
|
|
@@ -93,7 +93,7 @@ Varnish will find the objects and clear them out, responding with::
|
|
The objects are now cleared.
|
|
|
|
$ABI strict
|
|
-$Event vmod_event
|
|
+$Event event
|
|
$Function INT purge(STRING keys)
|
|
|
|
Description
|
|
diff --git a/src/vtree.h b/src/vtree.h
|
|
deleted file mode 100644
|
|
index b88d9d2..0000000
|
|
--- a/src/vtree.h
|
|
+++ /dev/null
|
|
@@ -1,770 +0,0 @@
|
|
-/* $NetBSD: tree.h,v 1.8 2004/03/28 19:38:30 provos Exp $ */
|
|
-/* $OpenBSD: tree.h,v 1.7 2002/10/17 21:51:54 art Exp $ */
|
|
-/* $FreeBSD: release/9.0.0/sys/sys/tree.h 189204 2009-03-01 04:57:23Z bms $ */
|
|
-
|
|
-/*-
|
|
- * Copyright 2002 Niels Provos <provos@citi.umich.edu>
|
|
- * 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_ */
|