[PATCH 2/2] Add softban support

Tollef Fog Heen tfheen at varnish-software.com
Mon Oct 15 13:49:45 CEST 2012


Soft bans are like normal bans, but they are subject to grace per
usual rules.
---
 bin/varnishd/cache/cache.h       |    1 +
 bin/varnishd/cache/cache_ban.c   |   56 ++++++++++++++++++++++++++++++++----
 bin/varnishd/cache/cache_vrt.c   |    6 ++--
 bin/varnishtest/tests/s00004.vtc |   47 ++++++++++++++++++++++++++++++
 bin/varnishtest/tests/s00005.vtc |   59 ++++++++++++++++++++++++++++++++++++++
 include/vcli.h                   |    7 +++++
 include/vrt.h                    |    4 +--
 lib/libvcl/vcc_action.c          |   25 ++++++++++++++--
 8 files changed, 193 insertions(+), 12 deletions(-)
 create mode 100644 bin/varnishtest/tests/s00004.vtc
 create mode 100644 bin/varnishtest/tests/s00005.vtc

diff --git a/bin/varnishd/cache/cache.h b/bin/varnishd/cache/cache.h
index 1c6a833..01d5d07 100644
--- a/bin/varnishd/cache/cache.h
+++ b/bin/varnishd/cache/cache.h
@@ -757,6 +757,7 @@ void BAN_Compile(void);
 struct ban *BAN_RefBan(struct objcore *oc, double t0, const struct ban *tail);
 void BAN_TailDeref(struct ban **ban);
 double BAN_Time(const struct ban *ban);
+void BAN_Set_Soft(struct ban *b, int soft);
 
 /* cache_busyobj.c */
 void VBO_Init(void);
diff --git a/bin/varnishd/cache/cache_ban.c b/bin/varnishd/cache/cache_ban.c
index aa8bca9..9b34f0c 100644
--- a/bin/varnishd/cache/cache_ban.c
+++ b/bin/varnishd/cache/cache_ban.c
@@ -42,6 +42,7 @@
  *	8 bytes	- double: timestamp		XXX: Byteorder ?
  *	4 bytes - be32: length
  *	1 byte - flags: 0x01: BAN_F_REQ
+ *			0x02: BAN_F_SOFT
  *	N tests
  * A test have this form:
  *	1 byte - arg (see ban_vars.h col 3 "BAN_ARG_XXX")
@@ -82,6 +83,7 @@ struct ban {
 	unsigned		flags;
 #define BAN_F_GONE		(1 << 0)
 #define BAN_F_REQ		(1 << 2)
+#define BAN_F_SOFT		(1 << 3)	/* soft ban, aka grace */
 #define BAN_F_LURK		(3 << 6)	/* ban-lurker-color */
 	VTAILQ_HEAD(,objcore)	objcore;
 	struct vsb		*vsb;
@@ -392,7 +394,11 @@ BAN_Insert(struct ban *b)
 
 	t0 = VTIM_real();
 	memcpy(b->spec, &t0, sizeof t0);
-	b->spec[12] = (b->flags & BAN_F_REQ) ? 1 : 0;
+	b->spec[12] = 0;
+	if (b->flags & BAN_F_REQ)
+		b->spec[12] |= 0x1;
+	if (b->flags & BAN_F_SOFT)
+		b->spec[12] |= 0x2;
 	memcpy(b->spec + 13, VSB_data(b->vsb), ln);
 	ln += 13;
 	vbe32enc(b->spec + 8, ln);
@@ -551,8 +557,10 @@ BAN_Reload(const uint8_t *ban, unsigned len)
 	AN(b2->spec);
 	memcpy(b2->spec, ban, len);
 	b2->flags |= gone;
-	if (ban[12])
+	if (ban[12] & 0x01)
 		b2->flags |= BAN_F_REQ;
+	if (ban[12] & 0x02)
+		b2->flags |= BAN_F_SOFT;
 	if (b == NULL)
 		VTAILQ_INSERT_TAIL(&ban_head, b2, list);
 	else
@@ -587,6 +595,21 @@ BAN_Time(const struct ban *b)
 }
 
 /*--------------------------------------------------------------------
+ * Set/unset the soft flag on a ban
+ */
+
+void
+BAN_Set_Soft(struct ban *b, int soft)
+{
+
+	CHECK_OBJ_NOTNULL(b, BAN_MAGIC);
+	if (soft)
+		b->flags |= BAN_F_SOFT;
+	else
+		b->flags &= ~BAN_F_SOFT;
+}
+
+/*--------------------------------------------------------------------
  * All silos have read their bans, ready for action
  */
 
@@ -687,7 +710,7 @@ ban_check_object(struct object *o, struct vsl_log *vsl,
 	struct ban *b;
 	struct objcore *oc;
 	struct ban * volatile b0;
-	unsigned tests, skipped;
+	unsigned tests, skipped, soft;
 
 	CHECK_OBJ_NOTNULL(o, OBJECT_MAGIC);
 	CHECK_OBJ_ORNULL(req_http, HTTP_MAGIC);
@@ -708,6 +731,7 @@ ban_check_object(struct object *o, struct vsl_log *vsl,
 	 */
 	tests = 0;
 	skipped = 0;
+	soft = 0;
 	for (b = b0; b != oc->ban; b = VTAILQ_NEXT(b, list)) {
 		CHECK_OBJ_NOTNULL(b, BAN_MAGIC);
 		if (b->flags & BAN_F_GONE)
@@ -724,15 +748,21 @@ ban_check_object(struct object *o, struct vsl_log *vsl,
 			 * be other bans that match, so we soldier on
 			 */
 			skipped++;
-		} else if (ban_evaluate(b->spec, o->http, req_http, &tests))
+		} else if (ban_evaluate(b->spec, o->http, req_http, &tests)) {
+			if (b->flags & BAN_F_SOFT) {
+				soft = 1;
+				continue;
+			}
+			soft = 0; /* Found hard ban */
 			break;
+		}
 	}
 
 	Lck_Lock(&ban_mtx);
 	VSC_C_main->bans_tested++;
 	VSC_C_main->bans_tests_tested += tests;
 
-	if (b == oc->ban && skipped > 0) {
+	if (b == oc->ban && skipped > 0 && soft == 0) {
 		AZ(req_http);
 		Lck_Unlock(&ban_mtx);
 		/*
@@ -751,10 +781,20 @@ ban_check_object(struct object *o, struct vsl_log *vsl,
 	}
 	Lck_Unlock(&ban_mtx);
 
-	if (b == oc->ban) {	/* not banned */
+	if (soft == 0 && b == oc->ban) {	/* not banned */
 		oc->ban = b0;
 		oc_updatemeta(oc);
 		return (0);
+	} else if (soft) {
+		/* Softban, set ttl to now */
+		EXP_Set_ttl(&o->exp, VTIM_real() -
+			    EXP_Get_entered(&o->exp));
+		oc->ban = b0;
+		oc_updatemeta(oc);
+		/* XXX: no req in lurker */
+		VSLb(vsl, SLT_ExpBan, "%u was softbanned", o->vxid);
+		EXP_Rearm(o);
+		return (2);
 	} else {
 		EXP_Clr(&o->exp);
 		oc->ban = NULL;
@@ -1026,6 +1066,9 @@ ccf_ban(struct cli *cli, const char * const *av, void *priv)
 		VCLI_SetResult(cli, CLIS_CANT);
 		return;
 	}
+	if (strcmp(av[1], "softban") == 0) {
+		b->flags |= BAN_F_SOFT;
+	}
 	for (i = 0; i < narg; i += 4)
 		if (BAN_AddTest(cli, b, av[i + 2], av[i + 3], av[i + 4])) {
 			BAN_Free(b);
@@ -1125,6 +1168,7 @@ ccf_ban_list(struct cli *cli, const char * const *av, void *priv)
 static struct cli_proto ban_cmds[] = {
 	{ CLI_BAN_URL,				"", ccf_ban_url },
 	{ CLI_BAN,				"", ccf_ban },
+	{ CLI_SOFTBAN,				"", ccf_ban },
 	{ CLI_BAN_LIST,				"", ccf_ban_list },
 	{ NULL }
 };
diff --git a/bin/varnishd/cache/cache_vrt.c b/bin/varnishd/cache/cache_vrt.c
index a2dd8cd..7773869 100644
--- a/bin/varnishd/cache/cache_vrt.c
+++ b/bin/varnishd/cache/cache_vrt.c
@@ -415,7 +415,7 @@ VRT_synth_page(const struct req *req, unsigned flags, const char *str, ...)
 /*--------------------------------------------------------------------*/
 
 void
-VRT_ban(const struct req *req, char *cmds, ...)
+VRT_ban(const struct req *req, int soft, char *cmds, ...)
 {
 	char *a1, *a2, *a3;
 	va_list ap;
@@ -424,6 +424,7 @@ VRT_ban(const struct req *req, char *cmds, ...)
 
 	CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
 	b = BAN_New();
+	BAN_Set_Soft(b, soft);
 	va_start(ap, cmds);
 	a1 = cmds;
 	good = 0;
@@ -450,7 +451,7 @@ VRT_ban(const struct req *req, char *cmds, ...)
 /*--------------------------------------------------------------------*/
 
 void
-VRT_ban_string(const struct req *req, const char *str)
+VRT_ban_string(const struct req *req, int soft, const char *str)
 {
 	char *a1, *a2, *a3;
 	char **av;
@@ -466,6 +467,7 @@ VRT_ban_string(const struct req *req, const char *str)
 		return;
 	}
 	b = BAN_New();
+	BAN_Set_Soft(b, soft);
 	good = 0;
 	for (i = 1; ;) {
 		a1 = av[i++];
diff --git a/bin/varnishtest/tests/s00004.vtc b/bin/varnishtest/tests/s00004.vtc
new file mode 100644
index 0000000..781d1dc
--- /dev/null
+++ b/bin/varnishtest/tests/s00004.vtc
@@ -0,0 +1,47 @@
+varnishtest "Softbans from CLI"
+
+server s1 {
+	rxreq
+	txresp
+
+	sema r1 sync 3
+
+	rxreq
+	txresp
+} -start
+
+varnish v1 -vcl+backend {
+	sub vcl_hit {
+	    if (obj.ttl <= 0s) {
+	    	    set req.http.grace = "t";
+	    }
+	}
+	sub vcl_fetch { 
+		set beresp.ttl = 1m;
+		set beresp.grace = 1m; 
+	}
+	sub vcl_deliver {
+	    set resp.http.grace = req.http.grace;
+	}
+} -start
+
+client c1 {
+	txreq
+	rxresp
+	expect resp.status == 200
+	delay 1
+	txreq
+	sema r1 sync 3
+	rxresp
+} -start
+
+varnish v1 -cliok "softban req.url ~ ^"
+delay 1;
+
+client c2 {
+	sema r1 sync 3
+	txreq
+	rxresp
+	expect resp.http.grace == "t"
+	expect resp.status == 200
+} -run
diff --git a/bin/varnishtest/tests/s00005.vtc b/bin/varnishtest/tests/s00005.vtc
new file mode 100644
index 0000000..35d1a8a
--- /dev/null
+++ b/bin/varnishtest/tests/s00005.vtc
@@ -0,0 +1,59 @@
+varnishtest "Softbans from VCL"
+
+server s1 {
+	rxreq
+	txresp
+
+	sema r1 sync 3
+
+	rxreq
+	txresp
+} -start
+
+varnish v1 -vcl+backend {
+	sub vcl_recv {
+	    if (req.request == "SOFTBAN") {
+	       softban("req.url ~ " + req.url);
+	       error 410;
+	    }
+	}
+
+	sub vcl_hit {
+	    if (obj.ttl <= 0s) {
+	    	    set req.http.grace = "t";
+	    }
+	}
+	sub vcl_fetch { 
+		set beresp.ttl = 1m;
+		set beresp.grace = 1m; 
+	}
+	sub vcl_deliver {
+	    set resp.http.grace = req.http.grace;
+	}
+} -start
+
+client c1 {
+	txreq
+	rxresp
+	expect resp.status == 200
+	delay 1
+	txreq
+	sema r1 sync 3
+	rxresp
+} -start
+
+delay 1
+client c2 {
+	txreq -req SOFTBAN -url /
+	rxresp
+	expect resp.status == 410
+} -run
+
+
+client c3 {
+	sema r1 sync 3
+	txreq
+	rxresp
+	expect resp.http.grace == "t"
+	expect resp.status == 200
+} -run
diff --git a/include/vcli.h b/include/vcli.h
index 04ac11c..3d79e7b 100644
--- a/include/vcli.h
+++ b/include/vcli.h
@@ -78,6 +78,13 @@
 	"\tList the active bans.",					\
 	0, 0
 
+#define CLI_SOFTBAN							\
+	"softban",							\
+	"softban <field> <operator> <arg> [&& <field> <oper> <arg>]...",	\
+	"\tAll objects where the all the conditions match will be "	\
+	    "marked obsolete, but still eligible for grace.",		\
+	3, UINT_MAX
+
 #define CLI_VCL_LOAD							\
 	"vcl.load",							\
 	"vcl.load <configname> <filename>",				\
diff --git a/include/vrt.h b/include/vrt.h
index 2029f7a..9fed394 100644
--- a/include/vrt.h
+++ b/include/vrt.h
@@ -149,8 +149,8 @@ int VRT_re_match(struct req *, const char *, void *re);
 const char *VRT_regsub(struct req *, int all, const char *,
     void *, const char *);
 
-void VRT_ban(const struct req *, char *, ...);
-void VRT_ban_string(const struct req *, const char *);
+void VRT_ban(const struct req *, int, char *, ...);
+void VRT_ban_string(const struct req *, int, const char *);
 void VRT_purge(struct req *, double ttl, double grace);
 
 void VRT_count(struct req *, unsigned);
diff --git a/lib/libvcl/vcc_action.c b/lib/libvcl/vcc_action.c
index 7eecc33..4458e16 100644
--- a/lib/libvcl/vcc_action.c
+++ b/lib/libvcl/vcc_action.c
@@ -178,7 +178,7 @@ parse_ban(struct vcc *tl)
 	ExpectErr(tl, '(');
 	vcc_NextToken(tl);
 
-	Fb(tl, 1, "VRT_ban_string(req, ");
+	Fb(tl, 1, "VRT_ban_string(req, 0, ");
 	vcc_Expr(tl, STRING);
 	ERRCHK(tl);
 	Fb(tl, 0, ");\n");
@@ -197,7 +197,7 @@ parse_ban_url(struct vcc *tl)
 	ExpectErr(tl, '(');
 	vcc_NextToken(tl);
 
-	Fb(tl, 1, "VRT_ban(req, \"req.url\", \"~\", ");
+	Fb(tl, 1, "VRT_ban(req, 0, \"req.url\", \"~\", ");
 	vcc_Expr(tl, STRING);
 	ERRCHK(tl);
 	ExpectErr(tl, ')');
@@ -208,6 +208,26 @@ parse_ban_url(struct vcc *tl)
 /*--------------------------------------------------------------------*/
 
 static void
+parse_softban(struct vcc *tl)
+{
+
+	vcc_NextToken(tl);
+
+	ExpectErr(tl, '(');
+	vcc_NextToken(tl);
+
+	Fb(tl, 1, "VRT_ban_string(req, 1, ");
+	vcc_Expr(tl, STRING);
+	ERRCHK(tl);
+	Fb(tl, 0, ");\n");
+
+	ExpectErr(tl, ')');
+	vcc_NextToken(tl);
+}
+
+/*--------------------------------------------------------------------*/
+
+static void
 parse_new_syntax(struct vcc *tl)
 {
 
@@ -320,6 +340,7 @@ static struct action_table {
 	{ "hash_data",		parse_hash_data, VCL_MET_HASH },
 	{ "ban",		parse_ban },
 	{ "ban_url",		parse_ban_url },
+	{ "softban",		parse_softban },
 	{ "remove",		parse_unset }, /* backward compatibility */
 	{ "return",		parse_return },
 	{ "rollback",		parse_rollback },
-- 
1.7.10.4




More information about the varnish-dev mailing list