diff --git a/bin/varnishd/VSC_main.vsc b/bin/varnishd/VSC_main.vsc index 8d6277ed7d7..4bb4697bf0b 100644 --- a/bin/varnishd/VSC_main.vsc +++ b/bin/varnishd/VSC_main.vsc @@ -601,6 +601,14 @@ Number of session closes with Error VCL_FAILURE (VCL failure) +.. varnish_vsc:: sc_rapid_reset + :level: diag + :oneliner: Session Err RAPID_RESET + + Number of times we failed an http/2 session because it hit its + configured limits for the number of permitted rapid stream + resets. + .. varnish_vsc:: client_resp_500 :level: diag :group: wrk diff --git a/bin/varnishd/http2/cache_http2.h b/bin/varnishd/http2/cache_http2.h index 533e00fa87d..4020b288729 100644 --- a/bin/varnishd/http2/cache_http2.h +++ b/bin/varnishd/http2/cache_http2.h @@ -47,6 +47,7 @@ struct h2_error_s { typedef const struct h2_error_s *h2_error; +#define H2_CUSTOM_ERRORS #define H2EC1(U,v,r,d) extern const struct h2_error_s H2CE_##U[1]; #define H2EC2(U,v,r,d) extern const struct h2_error_s H2SE_##U[1]; #define H2EC3(U,v,r,d) H2EC1(U,v,r,d) H2EC2(U,v,r,d) @@ -191,8 +192,16 @@ struct h2_sess { VTAILQ_HEAD(,h2_req) txqueue; h2_error error; - int open_streams; + + // rst rate limit parameters, copied from h2_* parameters + vtim_dur rapid_reset; + int64_t rapid_reset_limit; + vtim_dur rapid_reset_period; + + // rst rate limit state + double rst_budget; + vtim_real last_rst; }; #define ASSERT_RXTHR(h2) do {assert(h2->rxthr == pthread_self());} while(0) diff --git a/bin/varnishd/http2/cache_http2_proto.c b/bin/varnishd/http2/cache_http2_proto.c index df3c041aa94..afa9f03ef30 100644 --- a/bin/varnishd/http2/cache_http2_proto.c +++ b/bin/varnishd/http2/cache_http2_proto.c @@ -45,6 +45,7 @@ #include "vtcp.h" #include "vtim.h" +#define H2_CUSTOM_ERRORS #define H2EC1(U,v,r,d) const struct h2_error_s H2CE_##U[1] = {{#U,d,v,0,1,r}}; #define H2EC2(U,v,r,d) const struct h2_error_s H2SE_##U[1] = {{#U,d,v,1,0,r}}; #define H2EC3(U,v,r,d) H2EC1(U,v,r,d) H2EC2(U,v,r,d) @@ -314,9 +315,46 @@ h2_rx_push_promise(struct worker *wrk, struct h2_sess *h2, struct h2_req *r2) /********************************************************************** */ +static h2_error +h2_rapid_reset(struct worker *wrk, struct h2_sess *h2, struct h2_req *r2) +{ + vtim_real now; + vtim_dur d; + + CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC); + ASSERT_RXTHR(h2); + CHECK_OBJ_NOTNULL(r2, H2_REQ_MAGIC); + + if (h2->rapid_reset_limit == 0) + return (0); + + now = VTIM_real(); + CHECK_OBJ_NOTNULL(r2->req, REQ_MAGIC); + AN(r2->req->t_first); + if (now - r2->req->t_first > h2->rapid_reset) + return (0); + + d = now - h2->last_rst; + h2->rst_budget += h2->rapid_reset_limit * d / + h2->rapid_reset_period; + h2->rst_budget = vmin_t(double, h2->rst_budget, + h2->rapid_reset_limit); + h2->last_rst = now; + + if (h2->rst_budget < 1.0) { + Lck_Lock(&h2->sess->mtx); + VSLb(h2->vsl, SLT_Error, "H2: Hit RST limit. Closing session."); + Lck_Unlock(&h2->sess->mtx); + return (H2CE_RAPID_RESET); + } + h2->rst_budget -= 1.0; + return (0); +} + static h2_error v_matchproto_(h2_rxframe_f) h2_rx_rst_stream(struct worker *wrk, struct h2_sess *h2, struct h2_req *r2) { + h2_error h2e; CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC); ASSERT_RXTHR(h2); @@ -326,8 +364,9 @@ h2_rx_rst_stream(struct worker *wrk, struct h2_sess *h2, struct h2_req *r2) return (H2CE_FRAME_SIZE_ERROR); if (r2 == NULL) return (0); + h2e = h2_rapid_reset(wrk, h2, r2); h2_kill_req(wrk, h2, r2, h2_streamerror(vbe32dec(h2->rxf_data))); - return (0); + return (h2e); } /********************************************************************** diff --git a/bin/varnishd/http2/cache_http2_session.c b/bin/varnishd/http2/cache_http2_session.c index add9b7d6c65..06e625f33e2 100644 --- a/bin/varnishd/http2/cache_http2_session.c +++ b/bin/varnishd/http2/cache_http2_session.c @@ -128,6 +128,14 @@ h2_init_sess(const struct worker *wrk, struct sess *sp, h2->remote_settings = H2_proto_settings; h2->decode = decode; + h2->rapid_reset = cache_param->h2_rapid_reset; + h2->rapid_reset_limit = cache_param->h2_rapid_reset_limit; + h2->rapid_reset_period = cache_param->h2_rapid_reset_period; + + h2->rst_budget = h2->rapid_reset_limit; + h2->last_rst = sp->t_open; + AZ(isnan(h2->last_rst)); + AZ(VHT_Init(h2->dectbl, h2->local_settings.header_table_size)); *up = (uintptr_t)h2; diff --git a/bin/varnishtest/tests/r03996.vtc b/bin/varnishtest/tests/r03996.vtc new file mode 100644 index 00000000000..d9493d1f93c --- /dev/null +++ b/bin/varnishtest/tests/r03996.vtc @@ -0,0 +1,59 @@ +varnishtest "h2 rapid reset" + +barrier b1 sock 2 -cyclic +barrier b2 sock 5 -cyclic + +server s1 { + rxreq + txresp +} -start + +varnish v1 -cliok "param.set feature +http2" +varnish v1 -cliok "param.set debug +syncvsl" +varnish v1 -cliok "param.set h2_rapid_reset_limit 3" +varnish v1 -cliok "param.set h2_rapid_reset 5" + +varnish v1 -vcl+backend { + import vtc; + + sub vcl_recv { + if (req.http.barrier) { + vtc.barrier_sync(req.http.barrier); + } + vtc.barrier_sync("${b2_sock}"); + } + +} -start + +client c1 { + stream 0 { + rxgoaway + expect goaway.err == ENHANCE_YOUR_CALM + } -start + + stream 1 { + txreq -hdr barrier ${b1_sock} + barrier b1 sync + txrst + } -run + stream 3 { + txreq -hdr barrier ${b1_sock} + barrier b1 sync + txrst + } -run + stream 5 { + txreq -hdr barrier ${b1_sock} + barrier b1 sync + txrst + } -run + stream 7 { + txreq -hdr barrier ${b1_sock} + barrier b1 sync + txrst + } -run + + barrier b2 sync + stream 0 -wait +} -run + +varnish v1 -expect sc_rapid_reset == 1 diff --git a/bin/varnishtest/vmods.h b/bin/varnishtest/vmods.h index 06ab9b80c1e..81c12cbdb22 100644 --- a/bin/varnishtest/vmods.h +++ b/bin/varnishtest/vmods.h @@ -35,3 +35,4 @@ VTC_VMOD(vtc) VTC_VMOD(blob) VTC_VMOD(unix) VTC_VMOD(proxy) +VTC_VMOD(h2) diff --git a/bin/varnishtest/vmodtests/h2/b00000.vtc b/bin/varnishtest/vmodtests/h2/b00000.vtc new file mode 100644 index 00000000000..aedb6aad8c5 --- /dev/null +++ b/bin/varnishtest/vmodtests/h2/b00000.vtc @@ -0,0 +1,90 @@ +varnishtest "VMOD h2 basics" + +varnish v1 -arg "-p feature=+http2" -vcl { + import h2; + + backend proforma none; + + sub vcl_recv { + return(synth(200)); + } + + sub vcl_synth { + set resp.http.http2-is = h2.is(); + set resp.body = ""; + return (deliver); + } +} -start + +client c1 { + txreq + rxresp + expect resp.status == 200 + expect resp.http.http2-is == false +} -start + +client c2 { + stream 7 { + txreq + rxresp + expect resp.status == 200 + expect resp.http.http2-is == true + } -run +} -start + +client c1 -wait +client c2 -wait + +# coverage +varnish v1 -vcl { + import h2; + + backend proforma none; + + sub vcl_recv { + return(synth(200)); + } + + sub vcl_synth { + set resp.http.rapid-reset-o = h2.rapid_reset(10ms); + set resp.http.rapid-reset-n = h2.rapid_reset(); + set resp.http.rapid-reset-limit-o = h2.rapid_reset_limit(10); + set resp.http.rapid-reset-limit-n = h2.rapid_reset_limit(); + set resp.http.rapid-reset-period-o = h2.rapid_reset_period(10s); + set resp.http.rapid-reset-period-n = h2.rapid_reset_period(); + set resp.http.rapid-reset-budget = h2.rapid_reset_budget(); + set resp.body = ""; + return (deliver); + } +} + +client c1 { + txreq + rxresp + expect resp.status == 200 + expect resp.http.rapid-reset-o == -1.000 + expect resp.http.rapid-reset-n == -1.000 + expect resp.http.rapid-reset-limit-o == -1 + expect resp.http.rapid-reset-limit-n == -1 + expect resp.http.rapid-reset-period-o == -1.000 + expect resp.http.rapid-reset-period-n == -1.000 + expect resp.http.rapid-reset-budget == -1.000 +} -start + +client c2 { + stream 7 { + txreq + rxresp + expect resp.status == 200 + expect resp.http.rapid-reset-o == 1.000 + expect resp.http.rapid-reset-n == 0.010 + expect resp.http.rapid-reset-limit-o == 100 + expect resp.http.rapid-reset-limit-n == 10 + expect resp.http.rapid-reset-period-o == 60.000 + expect resp.http.rapid-reset-period-n == 10.000 + expect resp.http.rapid-reset-budget == 10.000 + } -run +} -start + +client c1 -wait +client c2 -wait diff --git a/configure.ac b/configure.ac index b5e8b45ab69..d7c7c33aef4 100644 --- a/configure.ac +++ b/configure.ac @@ -784,6 +784,7 @@ AC_CONFIG_FILES([ lib/libvmod_blob/Makefile lib/libvmod_unix/Makefile lib/libvmod_proxy/Makefile + lib/libvmod_h2/Makefile man/Makefile varnishapi.pc varnishapi-uninstalled.pc diff --git a/include/tbl/h2_error.h b/include/tbl/h2_error.h index 7481b9836f1..8f3485c244f 100644 --- a/include/tbl/h2_error.h +++ b/include/tbl/h2_error.h @@ -145,5 +145,17 @@ H2_ERROR( /* descr */ "Use HTTP/1.1 for the request" ) +#ifdef H2_CUSTOM_ERRORS +H2_ERROR( + /* name */ RAPID_RESET, + /* val */ 11, /* ENHANCE_YOUR_CALM */ + /* types */ 1, + /* reason */ SC_RAPID_RESET, + /* descr */ "http/2 rapid reset detected" +) + +# undef H2_CUSTOM_ERRORS +#endif + #undef H2_ERROR /*lint -restore */ diff --git a/include/tbl/params.h b/include/tbl/params.h index 59280f9cc7c..665303db444 100644 --- a/include/tbl/params.h +++ b/include/tbl/params.h @@ -1896,6 +1896,60 @@ PARAM( ) #endif +#define H2_RR_INFO \ + "Changes to this parameter affect the default for new HTTP2 " \ + "sessions." + +PARAM( + /* name */ h2_rapid_reset, + /* typ */ timeout, + /* min */ "0", + /* max */ NULL, + /* default */ "1.0", + /* units */ "seconds", + /* flags */ EXPERIMENTAL | DELAYED_EFFECT, + /* s-text */ + "The upper threshold for how soon an http/2 RST_STREAM frame has " + "to be parsed after a HEADERS frame for it to be treated as " + "suspect and subjected to the rate limits specified by " + "h2_rapid_reset_limit and h2_rapid_reset_period." H2_RR_INFO, + /* l-text */ "", + /* func */ NULL +) + + +PARAM( + /* name */ h2_rapid_reset_limit, + /* typ */ uint, + /* min */ "0", + /* max */ NULL, + /* default */ "100", + /* units */ NULL, + /* flags */ EXPERIMENTAL | DELAYED_EFFECT, + /* s-text */ + "HTTP2 RST Allowance.\n\n" + "Specifies the maximum number of allowed stream resets issued by " + "a client over a time period before the connection is closed. Setting " + "this parameter to 0 disables the limit." H2_RR_INFO, + /* l-text */ "", + /* func */ NULL +) + + +PARAM( + /* name */ h2_rapid_reset_period, + /* typ */ timeout, + /* min */ "1.000", + /* max */ NULL, + /* default */ "60.000", + /* units */ "seconds", + /* flags */ EXPERIMENTAL | DELAYED_EFFECT | WIZARD, + /* s-text */ + "HTTP2 sliding window duration for h2_rapid_reset_limit." H2_RR_INFO, + /* l-text */ "", + /* func */ NULL +) + #undef PARAM /*lint -restore */ diff --git a/include/tbl/sess_close.h b/include/tbl/sess_close.h index c20e71ca6d8..de130aa87c2 100644 --- a/include/tbl/sess_close.h +++ b/include/tbl/sess_close.h @@ -47,6 +47,7 @@ SESS_CLOSE(PIPE_OVERFLOW, pipe_overflow,1, "Session pipe overflow") SESS_CLOSE(RANGE_SHORT, range_short, 1, "Insufficient data for range") SESS_CLOSE(REQ_HTTP20, req_http20, 1, "HTTP2 not accepted") SESS_CLOSE(VCL_FAILURE, vcl_failure, 1, "VCL failure") +SESS_CLOSE(RAPID_RESET, rapid_reset, 1, "HTTP2 rapid reset") #undef SESS_CLOSE /*lint -restore */ diff --git a/include/vdef.h b/include/vdef.h index 60d833c3303..128f44148d5 100644 --- a/include/vdef.h +++ b/include/vdef.h @@ -120,6 +120,55 @@ #define RDN2(x, y) ((x)&(~((uintptr_t)(y)-1UL))) /* PWR2(y) true */ #define RUP2(x, y) (((x)+((y)-1))&(~((uintptr_t)(y)-1UL))) /* PWR2(y) true */ +/********************************************************************** + * Find the minimum or maximum values. + * Only evaluate the expression once and perform type checking. + */ + +/* ref: https://stackoverflow.com/a/17624752 */ + +#define VINDIRECT(a, b, c) a ## b ## c +#define VCOMBINE(a, b, c) VINDIRECT(a, b, c) + +#if defined(__COUNTER__) +# define VUNIQ_NAME(base) VCOMBINE(base, __LINE__, __COUNTER__) +#else +# define VUNIQ_NAME(base) VCOMBINE(base, __LINE__, 0) +#endif + +/* ref: https://gcc.gnu.org/onlinedocs/gcc/Typeof.html */ + +#define _vmin(a, b, _va, _vb) \ +({ \ + typeof (a) _va = (a); \ + typeof (b) _vb = (b); \ + (void)(&_va == &_vb); \ + _va < _vb ? _va : _vb; \ +}) + +#define _vmax(a, b, _va, _vb) \ +({ \ + typeof (a) _va = (a); \ + typeof (b) _vb = (b); \ + (void)(&_va == &_vb); \ + _va > _vb ? _va : _vb; \ +}) + +#define vmin(a, b) _vmin((a), (b), VUNIQ_NAME(_vmina), \ + VUNIQ_NAME(_vminb)) +#define vmax(a, b) _vmax((a), (b), VUNIQ_NAME(_vmaxa), \ + VUNIQ_NAME(_vmaxb)) + +#define vmin_t(type, a, b) vmin((type)(a), (type)(b)) +#define vmax_t(type, a, b) vmax((type)(a), (type)(b)) + +/********************************************************************** + * Clamp the value between two limits. + */ + +#define vlimit(a, l, u) vmax((l), vmin((a), (u))) +#define vlimit_t(type, a, l, u) vmax_t(type, (l), vmin_t(type, (a), (u))) + /********************************************************************** * FlexeLint and compiler shutuppery */ diff --git a/lib/Makefile.am b/lib/Makefile.am index 907ee4a5e64..986abf89006 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -12,4 +12,5 @@ SUBDIRS = \ libvmod_vtc \ libvmod_blob \ libvmod_unix \ - libvmod_proxy + libvmod_proxy \ + libvmod_h2 diff --git a/lib/libvmod_h2/Makefile.am b/lib/libvmod_h2/Makefile.am new file mode 100644 index 00000000000..5013d1de1a4 --- /dev/null +++ b/lib/libvmod_h2/Makefile.am @@ -0,0 +1,3 @@ + +libvmod_h2_la_SOURCES = vmod_h2.c +include $(srcdir)/automake_boilerplate.am diff --git a/lib/libvmod_h2/automake_boilerplate.am b/lib/libvmod_h2/automake_boilerplate.am new file mode 100644 index 00000000000..cf7dc7b1b8b --- /dev/null +++ b/lib/libvmod_h2/automake_boilerplate.am @@ -0,0 +1,37 @@ + +# Boilerplate generated by vmodtool.py - changes will be overwritten + +AM_LDFLAGS = $(AM_LT_LDFLAGS) + +AM_CPPFLAGS = \ + -I$(top_srcdir)/include \ + -I$(top_srcdir)/bin/varnishd \ + -I$(top_builddir)/include + +vmoddir = $(pkglibdir)/vmods +vmodtool = $(top_srcdir)/lib/libvcc/vmodtool.py +vmodtoolargs = --strict --boilerplate + +vmod_LTLIBRARIES = libvmod_h2.la + +libvmod_h2_la_CFLAGS = + +libvmod_h2_la_LDFLAGS = \ + $(AM_LDFLAGS) \ + $(VMOD_LDFLAGS) + +nodist_libvmod_h2_la_SOURCES = vcc_if.c vcc_if.h + +$(libvmod_h2_la_OBJECTS): vcc_if.h + +vcc_if.h vmod_h2.rst vmod_h2.man.rst: vcc_if.c + +vcc_if.c: $(vmodtool) $(srcdir)/vmod.vcc + @PYTHON@ $(vmodtool) $(vmodtoolargs) $(srcdir)/vmod.vcc + +EXTRA_DIST = vmod.vcc automake_boilerplate.am + +CLEANFILES = $(builddir)/vcc_if.c $(builddir)/vcc_if.h \ + $(builddir)/vmod_h2.rst \ + $(builddir)/vmod_h2.man.rst + diff --git a/lib/libvmod_h2/vmod.vcc b/lib/libvmod_h2/vmod.vcc new file mode 100644 index 00000000000..65702aaf6d7 --- /dev/null +++ b/lib/libvmod_h2/vmod.vcc @@ -0,0 +1,88 @@ +#- +# Copyright 2023 UPLEX - Nils Goroll Systemoptimierung +# All rights reserved. +# +# Author: Nils Goroll +# +# SPDX-License-Identifier: BSD-2-Clause +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. + +$ABI strict +$Module h2 3 "Module to control the built-in HTTP2 transport" + +DESCRIPTION +=========== + +This VMOD contains functions to control the HTTP2 transport built into +Varnish-Cache. + +$Function BOOL is() + +Returns true when called on a session handled by the built-in HTTP2 transport. + +$Function DURATION rapid_reset([DURATION threshold]) + +Get and optionally set the ``h2_rapid_reset`` parameter (See +:ref:`varnishd(1)`) for this HTTP2 session only. + +Returns -1 when used outside the HTTP2 transport. Otherwise returns +the previous value. + +If the call leads to a change in the rate limit parameters, the +current budget as retuned by +`h2.rapid_reset_budget()` is reset. + +$Function INT rapid_reset_limit([INT number]) + +Get and optionally set the ``h2_rapid_reset_limit`` parameter (See +:ref:`varnishd(1)`) for this HTTP2 session only. + +Returns -1 when used outside the HTTP2 transport. Otherwise returns +the previous value. + +If the call leads to a change in the rate limit parameters, the +current budget as retuned by +`h2.rapid_reset_budget()` is reset. + +$Function DURATION rapid_reset_period([DURATION duration]) + +Get and optionally set the ``h2_rapid_reset_period`` parameter (See +:ref:`varnishd(1)`) for this HTTP2 session only. + +Returns -1 when used outside the HTTP2 transport. Otherwise returns +the previous value. + +If the call leads to a change in the rate limit parameters, the +current budget as retuned by +`h2.rapid_reset_budget()` is reset. + +$Function REAL rapid_reset_budget() + +Return how many RST frames classified as "rapid" the client is still +allowed to send before the session is going to be closed. + +SEE ALSO +======== + +* :ref:`varnishd(1)` +* :ref:`vsl(7)` diff --git a/lib/libvmod_h2/vmod_h2.c b/lib/libvmod_h2/vmod_h2.c new file mode 100644 index 00000000000..cbab4d22e45 --- /dev/null +++ b/lib/libvmod_h2/vmod_h2.c @@ -0,0 +1,107 @@ +/*- + * Copyright 2023 UPLEX - Nils Goroll Systemoptimierung + * All rights reserved. + * + * Author: Nils Goroll + * + * SPDX-License-Identifier: BSD-2-Clause + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "config.h" + +#include "cache/cache_varnishd.h" + +#include "vcc_if.h" + +#include "cache/cache_transport.h" +#include "http2/cache_http2.h" + +static struct h2_sess * +h2get(VRT_CTX) +{ + struct h2_sess *h2; + uintptr_t *up; + + CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC); + if (ctx->req == NULL) { + VRT_fail(ctx, + "vmod_h2 can only be called from client-side VCL."); + return (NULL); + } + CHECK_OBJ_NOTNULL(ctx->req, REQ_MAGIC); + if (ctx->req->transport != &H2_transport) + return (NULL); + AZ(SES_Get_proto_priv(ctx->req->sp, &up)); + CAST_OBJ_NOTNULL(h2, (void *)*up, H2_SESS_MAGIC); + return (h2); +} +VCL_BOOL +vmod_is(VRT_CTX) +{ + struct h2_sess *h2 = h2get(ctx); + + return (h2 != NULL); +} + +#define GETSET(type, name, argname) \ +type \ +vmod_ ## name(VRT_CTX, struct vmod_##name##_arg *args) \ +{ \ + struct h2_sess *h2 = h2get(ctx); \ + type r; \ + \ + (void)args; \ + \ + if (h2 == NULL) \ + return (-1); \ + \ + if (! args->valid_ ## argname) \ + return (h2->name); \ + if (h2->name == args->argname) \ + return (h2->name); \ + \ + Lck_Lock(&h2->sess->mtx); \ + r = h2->name; \ + if (h2->name != args->argname) { \ + h2->name = args->argname; \ + h2->rst_budget = h2->rapid_reset_limit; \ + h2->last_rst = ctx->now; \ + } \ + Lck_Unlock(&h2->sess->mtx); \ + return (r); \ +} + +GETSET(VCL_DURATION, rapid_reset, threshold) +GETSET(VCL_INT, rapid_reset_limit, number) +GETSET(VCL_DURATION, rapid_reset_period, duration) + +VCL_REAL +vmod_rapid_reset_budget(VRT_CTX) +{ + struct h2_sess *h2 = h2get(ctx); + + if (h2 == NULL) + return (-1); + + return (h2->rst_budget); +} diff --git a/man/Makefile.am b/man/Makefile.am index 0e35dc5bf5f..1c2714872a6 100644 --- a/man/Makefile.am +++ b/man/Makefile.am @@ -21,7 +21,8 @@ dist_man_MANS = \ vmod_vtc.3 \ vmod_blob.3 \ vmod_unix.3 \ - vmod_proxy.3 + vmod_proxy.3 \ + vmod_h2.3 CLEANFILES = $(dist_man_MANS) @@ -109,4 +110,7 @@ vmod_unix.3: $(top_builddir)/lib/libvmod_unix/vmod_unix.man.rst vmod_proxy.3: $(top_builddir)/lib/libvmod_proxy/vmod_proxy.man.rst ${RST2MAN} $(RST2ANY_FLAGS) $? $@ +vmod_h2.3: $(top_builddir)/lib/libvmod_h2/vmod_h2.man.rst + ${RST2MAN} $(RST2ANY_FLAGS) $? $@ + .NOPATH: $(dist_man_MANS)