[master] 26097fb09 esi: Implement <esi:include onerror="continue"/>

Dridi Boukelmoune dridi.boukelmoune at gmail.com
Mon Feb 21 10:23:05 UTC 2022


commit 26097fb091496f79ef9a38b22e3639735fb39280
Author: Dridi Boukelmoune <dridi.boukelmoune at gmail.com>
Date:   Fri Dec 31 15:31:14 2021 +0100

    esi: Implement <esi:include onerror="continue"/>
    
    This changes the default behavior of includes, matching the ESI language
    specification. The onerror attribute seems to only accept one value, so
    in its absence a failed ESI fragment delivery aborts the top request
    delivery.
    
    This new behavior requires the esi_include_onerror feature flag to be
    raised to take effect.
    
    In addition, it breaks the ESI object attribute format for persisted
    caches.

diff --git a/bin/varnishd/cache/cache_esi.h b/bin/varnishd/cache/cache_esi.h
index 1e5777b50..58eb85ed1 100644
--- a/bin/varnishd/cache/cache_esi.h
+++ b/bin/varnishd/cache/cache_esi.h
@@ -39,7 +39,8 @@
 #define	VEC_S1	(0x60 + 1)
 #define	VEC_S2	(0x60 + 2)
 #define	VEC_S8	(0x60 + 8)
-#define	VEC_INCL	'I'
+#define	VEC_INCL_ABRT	'I'
+#define	VEC_INCL_CONT	'i'
 
 typedef ssize_t vep_callback_t(struct vfp_ctx *, void *priv, ssize_t l,
     enum vgz_flag flg);
diff --git a/bin/varnishd/cache/cache_esi_deliver.c b/bin/varnishd/cache/cache_esi_deliver.c
index fb63fb12f..ac40a77b7 100644
--- a/bin/varnishd/cache/cache_esi_deliver.c
+++ b/bin/varnishd/cache/cache_esi_deliver.c
@@ -65,6 +65,7 @@ struct ecx {
 	ssize_t		l;
 	int		isgzip;
 	int		woken;
+	int		abrt;
 
 	struct req	*preq;
 	struct ecx	*pecx;
@@ -378,7 +379,11 @@ ved_vdp_esi_bytes(struct vdp_ctx *vdx, enum vdp_action act, void **priv,
 				Debug("SKIP1(%d)\n", (int)ecx->l);
 				ecx->state = 4;
 				break;
-			case VEC_INCL:
+			case VEC_INCL_ABRT:
+				ecx->abrt =
+				    FEATURE(FEATURE_ESI_INCLUDE_ONERROR);
+				/* FALLTHROUGH */
+			case VEC_INCL_CONT:
 				ecx->p++;
 				q = (void*)strchr((const char*)ecx->p, '\0');
 				AN(q);
@@ -905,4 +910,9 @@ ved_deliver(struct req *req, struct boc *boc, int wantbody)
 		req->doclose = SC_REM_CLOSE;
 
 	req->acct.resp_bodybytes += VDP_Close(req->vdc);
+
+	if (i && ecx->abrt) {
+		req->top->topreq->vdc->retval = -1;
+		req->top->topreq->doclose = req->doclose;
+	}
 }
diff --git a/bin/varnishd/cache/cache_esi_parse.c b/bin/varnishd/cache/cache_esi_parse.c
index 7e0458556..2716c60b2 100644
--- a/bin/varnishd/cache/cache_esi_parse.c
+++ b/bin/varnishd/cache/cache_esi_parse.c
@@ -114,6 +114,7 @@ struct vep_state {
 	dostuff_f		*dostuff;
 
 	struct vsb		*include_src;
+	unsigned		include_continue;
 
 	unsigned		nm_skip;
 	unsigned		nm_verbatim;
@@ -181,6 +182,7 @@ static struct vep_match vep_match_esi[] = {
 
 static struct vep_match vep_match_attr_include[] = {
 	{ "src=",	&VEP_ATTRGETVAL },
+	{ "onerror=",	&VEP_ATTRGETVAL },
 	{ NULL,		&VEP_SKIPATTR }
 };
 
@@ -460,11 +462,20 @@ include_attr_src(struct vep_state *vep)
 	vep->attr_vsb = NULL;
 }
 
+static void
+include_attr_onerror(struct vep_state *vep)
+{
+
+	vep->include_continue = !strcmp("continue", VSB_data(vep->attr_vsb));
+	VSB_destroy(&vep->attr_vsb);
+}
+
 static void v_matchproto_()
 vep_do_include(struct vep_state *vep, enum dowhat what)
 {
 	const char *p, *q, *h;
 	ssize_t l;
+	char incl;
 
 	Debug("DO_INCLUDE(%d)\n", what);
 	if (what == DO_ATTR) {
@@ -474,6 +485,10 @@ vep_do_include(struct vep_state *vep, enum dowhat what)
 			include_attr_src(vep);
 			return;
 		}
+		if (!strcmp("onerror=", vep->match_hit->match)) {
+			include_attr_onerror(vep);
+			return;
+		}
 		WRONG("Unhandled <esi:include> attribute");
 	}
 	assert(what == DO_TAG);
@@ -499,6 +514,8 @@ vep_do_include(struct vep_state *vep, enum dowhat what)
 	l = VSB_len(vep->include_src);
 	h = 0;
 
+	incl = vep->include_continue ? VEC_INCL_CONT : VEC_INCL_ABRT;
+
 	if (l > 7 && !memcmp(p, "http://", 7)) {
 		h = p + 7;
 		p = strchr(h, '/');
@@ -511,7 +528,7 @@ vep_do_include(struct vep_state *vep, enum dowhat what)
 			return;
 		}
 		Debug("HOST <%.*s> PATH <%s>\n", (int)(p-h),h, p);
-		VSB_printf(vep->vsb, "%c", VEC_INCL);
+		VSB_printf(vep->vsb, "%c", incl);
 		VSB_printf(vep->vsb, "Host: %.*s%c", (int)(p-h), h, 0);
 	} else if (l > 8 && !memcmp(p, "https://", 8)) {
 		if (!FEATURE(FEATURE_ESI_IGNORE_HTTPS)) {
@@ -534,13 +551,13 @@ vep_do_include(struct vep_state *vep, enum dowhat what)
 			VSB_destroy(&vep->include_src);
 			return;
 		}
-		VSB_printf(vep->vsb, "%c", VEC_INCL);
+		VSB_printf(vep->vsb, "%c", incl);
 		VSB_printf(vep->vsb, "Host: %.*s%c", (int)(p-h), h, 0);
 	} else if (*p == '/') {
-		VSB_printf(vep->vsb, "%c", VEC_INCL);
+		VSB_printf(vep->vsb, "%c", incl);
 		VSB_printf(vep->vsb, "%c", 0);
 	} else {
-		VSB_printf(vep->vsb, "%c", VEC_INCL);
+		VSB_printf(vep->vsb, "%c", incl);
 		VSB_printf(vep->vsb, "%c", 0);
 		/* Look for the last / before a '?' */
 		h = NULL;
@@ -574,6 +591,7 @@ vep_do_include(struct vep_state *vep, enum dowhat what)
 #undef R
 	VSB_printf(vep->vsb, "%c", 0);
 	VSB_destroy(&vep->include_src);
+	vep->include_continue = 0;
 }
 
 /*---------------------------------------------------------------------
diff --git a/bin/varnishtest/tests/e00035.vtc b/bin/varnishtest/tests/e00035.vtc
new file mode 100644
index 000000000..0fc59d749
--- /dev/null
+++ b/bin/varnishtest/tests/e00035.vtc
@@ -0,0 +1,50 @@
+varnishtest "ESI fragment fetch fail"
+
+server s1 {
+	rxreq
+	expect req.url == "/abort"
+	txresp -hdr {surrogate-control: content="ESI/1.0"} \
+	    -body {before <esi:include src="/fail"/> after}
+
+	rxreq
+	expect req.url == "/fail"
+	txresp -hdr "content-length: 100" -nolen
+	delay 0.1
+} -start
+
+varnish v1 -cliok "param.set feature +esi_disable_xml_check"
+varnish v1 -cliok "param.set feature +esi_include_onerror"
+varnish v1 -vcl+backend {
+	sub vcl_backend_response {
+		set beresp.do_esi = beresp.http.surrogate-control ~ "ESI/1.0";
+		unset beresp.http.surrogate-control;
+	}
+} -start
+
+client c1 {
+	txreq -url "/abort"
+	non_fatal
+	rxresp
+	expect resp.body == "before "
+} -run
+
+server s1 -wait
+
+server s1 {
+	rxreq
+	expect req.url == "/continue"
+	txresp -hdr {surrogate-control: content="ESI/1.0"} \
+	    -body {before <esi:include src="/fail" onerror="continue"/> after}
+
+	rxreq
+	expect req.url == "/fail"
+	txresp -hdr "content-length: 100" -nolen
+	delay 0.1
+} -start
+
+client c1 {
+	fatal
+	txreq -url "/continue"
+	rxresp
+	expect resp.body == "before  after"
+} -run
diff --git a/include/tbl/feature_bits.h b/include/tbl/feature_bits.h
index d51b22cef..68ae39839 100644
--- a/include/tbl/feature_bits.h
+++ b/include/tbl/feature_bits.h
@@ -70,6 +70,10 @@ FEATURE_BIT(ESI_REMOVE_BOM,		esi_remove_bom,
     "Ignore UTF-8 BOM in ESI bodies."
 )
 
+FEATURE_BIT(ESI_INCLUDE_ONERROR,	esi_include_onerror,
+    "Parse the onerror attribute of <esi:include> tags."
+)
+
 FEATURE_BIT(WAIT_SILO,			wait_silo,
     "Wait for persistent silos to completely load before serving requests."
 )


More information about the varnish-commit mailing list