[master] e5b99ec Split H2 session from protocol/frame processing

Poul-Henning Kamp phk at FreeBSD.org
Thu Mar 2 16:10:07 CET 2017


commit e5b99ec618d95cdd11160ccfbdc6aa408c5333f4
Author: Poul-Henning Kamp <phk at FreeBSD.org>
Date:   Thu Mar 2 10:49:17 2017 +0000

    Split H2 session from protocol/frame processing

diff --git a/bin/varnishd/Makefile.am b/bin/varnishd/Makefile.am
index edc23f4..b07e9f0 100644
--- a/bin/varnishd/Makefile.am
+++ b/bin/varnishd/Makefile.am
@@ -71,6 +71,7 @@ varnishd_SOURCES = \
 	http2/cache_http2_hpack.c \
 	http2/cache_http2_panic.c \
 	http2/cache_http2_proto.c \
+	http2/cache_http2_session.c \
 	http2/cache_http2_send.c \
 	mgt/mgt_acceptor.c \
 	mgt/mgt_child.c \
diff --git a/bin/varnishd/http2/cache_http2.h b/bin/varnishd/http2/cache_http2.h
index f832e80..58acdeb 100644
--- a/bin/varnishd/http2/cache_http2.h
+++ b/bin/varnishd/http2/cache_http2.h
@@ -158,8 +158,9 @@ int H2_Send_Frame(struct worker *, const struct h2_sess *,
 int H2_Send(struct worker *, struct h2_req *, int flush,
     enum h2_frame_e type, uint8_t flags, uint32_t len, const void *);
 
-typedef h2_error h2_frame_f(struct worker *, struct h2_sess *,
-    struct h2_req *);
-#define H2_FRAME(l,u,t,f) h2_frame_f h2_rx_##l ;
-#include "tbl/h2_frames.h"
+/* cache_http2_proto.c */
+int h2_rxframe(struct worker *, struct h2_sess *);
+void h2_req_body(struct req*);
+
+
 
diff --git a/bin/varnishd/http2/cache_http2_proto.c b/bin/varnishd/http2/cache_http2_proto.c
index 3148751..85fb45d 100644
--- a/bin/varnishd/http2/cache_http2_proto.c
+++ b/bin/varnishd/http2/cache_http2_proto.c
@@ -51,6 +51,8 @@
 #undef H2EC2
 #undef H2EC3
 
+typedef h2_error h2_frame_f(struct worker *, struct h2_sess *, struct h2_req *);
+
 enum h2frame {
 #define H2_FRAME(l,u,t,f)	H2F_##u = t,
 #include "tbl/h2_frames.h"
@@ -84,28 +86,9 @@ h2_settingname(enum h2setting h2f)
 #define H2_FRAME_FLAGS(l,u,v)	const uint8_t H2FF_##u = v;
 #include "tbl/h2_frames.h"
 
-static const char h2_resp_101[] =
-	"HTTP/1.1 101 Switching Protocols\r\n"
-	"Connection: Upgrade\r\n"
-	"Upgrade: h2c\r\n"
-	"\r\n";
-
-static const char H2_prism[24] = {
-	0x50, 0x52, 0x49, 0x20, 0x2a, 0x20, 0x48, 0x54,
-	0x54, 0x50, 0x2f, 0x32, 0x2e, 0x30, 0x0d, 0x0a,
-	0x0d, 0x0a, 0x53, 0x4d, 0x0d, 0x0a, 0x0d, 0x0a
-};
-
-static const uint8_t H2_settings[] = {
-	0x00, 0x03,
-	0x00, 0x00, 0x00, 0x64,
-	0x00, 0x04,
-	0x00, 0x00, 0xff, 0xff
-};
-
 /**********************************************************************/
 #define DUMMY_FRAME(l) \
-	h2_error __match_proto__(h2_frame_f) \
+	static h2_error __match_proto__(h2_frame_f) \
 	h2_rx_##l(struct worker *wrk, struct h2_sess *h2, struct h2_req *r2) \
 	__match_proto__(h2_frame_f) \
 	{ (void)wrk; (void)r2; VSLb(h2->vsl, SLT_Debug, "XXX implement " #l); INCOMPL(); }
@@ -115,62 +98,6 @@ DUMMY_FRAME(push_promise)
 DUMMY_FRAME(continuation)
 
 /**********************************************************************
- * The h2_sess struct needs many of the same things as a request,
- * WS, VSL, HTC &c,  but rather than implement all that stuff over, we
- * grab an actual struct req, and mirror the relevant fields into
- * struct h2_sess.
- * To make things really incestuous, we allocate the h2_sess on
- * the WS of that "Session ReQuest".
- */
-
-static struct h2_sess *
-h2_new_sess(const struct worker *wrk, struct sess *sp, struct req *srq)
-{
-	uintptr_t *up;
-	struct h2_sess *h2;
-
-	if (SES_Get_xport_priv(sp, &up)) {
-		/* Already reserved if we came via H1 */
-		SES_Reserve_xport_priv(sp, &up);
-		*up = 0;
-	}
-	if (*up == 0) {
-		if (srq == NULL)
-			srq = Req_New(wrk, sp);
-		AN(srq);
-		h2 = WS_Alloc(srq->ws, sizeof *h2);
-		AN(h2);
-		INIT_OBJ(h2, H2_SESS_MAGIC);
-		h2->srq = srq;
-		h2->htc = srq->htc;
-		h2->ws = srq->ws;
-		h2->vsl = srq->vsl;
-		h2->vsl->wid = sp->vxid;
-		h2->htc->rfd = &sp->fd;
-		h2->sess = sp;
-		VTAILQ_INIT(&h2->streams);
-#define H2_SETTINGS(n,v,d)					\
-		do {						\
-			assert(v < H2_SETTINGS_N);		\
-			h2->their_settings[v] = d;		\
-			h2->our_settings[v] = d;		\
-		} while (0);
-#include "tbl/h2_settings.h"
-#undef H2_SETTINGS
-
-		/* XXX: Lacks a VHT_Fini counterpart. Will leak memory. */
-		AZ(VHT_Init(h2->dectbl,
-			h2->our_settings[H2S_HEADER_TABLE_SIZE]));
-
-		SES_Reserve_xport_priv(sp, &up);
-		*up = (uintptr_t)h2;
-	}
-	AN(up);
-	CAST_OBJ_NOTNULL(h2, (void*)(*up), H2_SESS_MAGIC);
-	return (h2);
-}
-
-/**********************************************************************
  */
 
 static struct h2_req *
@@ -295,7 +222,7 @@ h2_vsl_frame(const struct h2_sess *h2, const void *ptr, size_t len)
 /**********************************************************************
  */
 
-h2_error __match_proto__(h2_frame_f)
+static h2_error __match_proto__(h2_frame_f)
 h2_rx_ping(struct worker *wrk, struct h2_sess *h2, struct h2_req *r2)
 {
 
@@ -313,7 +240,7 @@ h2_rx_ping(struct worker *wrk, struct h2_sess *h2, struct h2_req *r2)
 /**********************************************************************
  */
 
-h2_error __match_proto__(h2_frame_f)
+static h2_error __match_proto__(h2_frame_f)
 h2_rx_goaway(struct worker *wrk, struct h2_sess *h2, struct h2_req *r2)
 {
 	uint32_t	error;
@@ -327,7 +254,7 @@ h2_rx_goaway(struct worker *wrk, struct h2_sess *h2, struct h2_req *r2)
 	return (0);
 }
 
-h2_error __match_proto__(h2_frame_f)
+static h2_error __match_proto__(h2_frame_f)
 h2_rx_window_update(struct worker *wrk, struct h2_sess *h2, struct h2_req *r2)
 {
 	uint32_t wu;
@@ -349,7 +276,7 @@ h2_rx_window_update(struct worker *wrk, struct h2_sess *h2, struct h2_req *r2)
  * Incoming PRIORITY, possibly an ACK of one we sent.
  */
 
-h2_error __match_proto__(h2_frame_f)
+static h2_error __match_proto__(h2_frame_f)
 h2_rx_priority(struct worker *wrk, struct h2_sess *h2, struct h2_req *r2)
 {
 
@@ -363,7 +290,7 @@ h2_rx_priority(struct worker *wrk, struct h2_sess *h2, struct h2_req *r2)
  * Incoming SETTINGS, possibly an ACK of one we sent.
  */
 
-h2_error __match_proto__(h2_frame_f)
+static h2_error __match_proto__(h2_frame_f)
 h2_rx_settings(struct worker *wrk, struct h2_sess *h2, struct h2_req *r2)
 {
 	const uint8_t *p = h2->rxf_data;
@@ -410,7 +337,7 @@ h2_do_req(struct worker *wrk, void *priv)
 	h2_del_req(wrk, r2);
 }
 
-h2_error __match_proto__(h2_frame_f)
+static h2_error __match_proto__(h2_frame_f)
 h2_rx_headers(struct worker *wrk, struct h2_sess *h2, struct h2_req *r2)
 {
 	struct req *req;
@@ -485,7 +412,7 @@ h2_rx_headers(struct worker *wrk, struct h2_sess *h2, struct h2_req *r2)
 
 /**********************************************************************/
 
-h2_error __match_proto__(h2_frame_f)
+static h2_error __match_proto__(h2_frame_f)
 h2_rx_data(struct worker *wrk, struct h2_sess *h2, struct h2_req *r2)
 {
 	(void)wrk;
@@ -542,7 +469,7 @@ static const struct vfp h2_body = {
 	.pull = h2_vfp_body,
 };
 
-static void __match_proto__(vtr_req_body_t)
+void __match_proto__(vtr_req_body_t)
 h2_req_body(struct req *req)
 {
 	struct h2_req *r2;
@@ -557,21 +484,6 @@ h2_req_body(struct req *req)
 
 /**********************************************************************/
 
-enum htc_status_e __match_proto__(htc_complete_f)
-H2_prism_complete(struct http_conn *htc)
-{
-	int l;
-
-	CHECK_OBJ_NOTNULL(htc, HTTP_CONN_MAGIC);
-	l = htc->rxbuf_e - htc->rxbuf_b;
-	if (l >= sizeof(H2_prism) &&
-	    !memcmp(htc->rxbuf_b, H2_prism, sizeof(H2_prism)))
-		return (HTC_S_COMPLETE);
-	if (l < sizeof(H2_prism) && !memcmp(htc->rxbuf_b, H2_prism, l))
-		return (HTC_S_MORE);
-	return (HTC_S_JUNK);
-}
-
 static enum htc_status_e __match_proto__(htc_complete_f)
 h2_frame_complete(struct http_conn *htc)
 {
@@ -664,7 +576,7 @@ h2_procframe(struct worker *wrk, struct h2_sess *h2)
 	return (0);
 }
 
-static int
+int
 h2_rxframe(struct worker *wrk, struct h2_sess *h2)
 {
 	enum htc_status_e hs;
@@ -705,215 +617,3 @@ h2_rxframe(struct worker *wrk, struct h2_sess *h2)
 	Lck_Unlock(&h2->sess->mtx);
 	return (h2e ? 0 : 1);
 }
-
-
-/**********************************************************************
- * Deal with the base64url (NB: ...url!) encoded SETTINGS in the H1 req
- * of a H2C upgrade.
- */
-
-static int
-h2_b64url_settings(struct h2_sess *h2, struct req *req)
-{
-	const char *p, *q;
-	uint8_t u[6], *up;
-	unsigned x;
-	int i, n;
-	static const char s[] =
-	    "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
-	    "abcdefghijklmnopqrstuvwxyz"
-	    "0123456789"
-	    "-_=";
-
-	/*
-	 * If there is trouble with this, we could reject the upgrade
-	 * but putting this on the H1 side is just plain wrong...
-	 */
-	AN(http_GetHdr(req->http, H_HTTP2_Settings, &p));
-	if (p == NULL)
-		return (-1);
-	VSLb(req->vsl, SLT_Debug, "H2CS %s", p);
-
-	n = 0;
-	x = 0;
-	up = u;
-	for (;*p; p++) {
-		q = strchr(s, *p);
-		if (q == NULL)
-			return (-1);
-		i = q - s;
-		assert(i >= 0 && i <= 63);
-		x <<= 6;
-		x |= i;
-		n += 6;
-		if (n < 8)
-			continue;
-		*up++ = (uint8_t)(x >> (n - 8));
-		n -= 8;
-		if (up == u + sizeof u) {
-			AZ(n);
-			h2_setting(h2, (void*)u);
-			up = u;
-		}
-	}
-	if (up != u)
-		return (-1);
-	return (0);
-}
-
-/**********************************************************************/
-
-static int
-h2_new_pu_session(struct worker *wrk, const struct h2_sess *h2)
-{
-	enum htc_status_e hs;
-
-	(void)wrk;
-
-	hs = H2_prism_complete(h2->htc);
-	if (hs == HTC_S_MORE) {
-		VSLb(h2->vsl, SLT_Debug, "Short pu PRISM");
-		return (0);
-	}
-	if (hs != HTC_S_COMPLETE) {
-		VSLb(h2->vsl, SLT_Debug, "Wrong pu PRISM");
-		return (0);
-	}
-	HTC_RxPipeline(h2->htc, h2->htc->rxbuf_b + sizeof(H2_prism));
-	HTC_RxInit(h2->htc, wrk->aws);
-
-	VSLb(h2->vsl, SLT_Debug, "H2: Got pu PRISM");
-	return (1);
-}
-
-/**********************************************************************/
-
-static int
-h2_new_ou_session(struct worker *wrk, struct h2_sess *h2,
-    struct req *req)
-{
-	ssize_t sz;
-	enum htc_status_e hs;
-
-	sz = write(h2->sess->fd, h2_resp_101, strlen(h2_resp_101));
-	assert(sz == strlen(h2_resp_101));
-
-	AZ(h2_b64url_settings(h2, req));
-	http_Unset(req->http, H_Upgrade);
-	http_Unset(req->http, H_HTTP2_Settings);
-
-	/* Steal pipelined read-ahead, if any */
-	h2->htc->pipeline_b = req->htc->pipeline_b;
-	h2->htc->pipeline_e = req->htc->pipeline_e;
-	req->htc->pipeline_b = NULL;
-	req->htc->pipeline_e = NULL;
-	/* XXX: This call may assert on buffer overflow if the pipelined
-	   data exceeds the available space in the aws workspace. What to
-	   do about the overflowing data is an open issue. */
-	HTC_RxInit(h2->htc, wrk->aws);
-
-	/* Start req thread */
-	(void)h2_new_req(wrk, h2, 1, req);
-	req->req_step = R_STP_RECV;
-	req->transport = &H2_transport;
-	req->task.func = h2_do_req;
-	req->task.priv = req;
-	req->err_code = 0;
-	http_SetH(req->http, HTTP_HDR_PROTO, "HTTP/2.0");
-	XXXAZ(Pool_Task(wrk->pool, &req->task, TASK_QUEUE_REQ));
-
-	/* Wait for PRISM response */
-	hs = HTC_RxStuff(h2->htc, H2_prism_complete,
-	    NULL, NULL, NAN, h2->sess->t_idle + cache_param->timeout_idle, 256);
-	if (hs != HTC_S_COMPLETE) {
-		/* XXX clean up req thread */
-		VSLb(h2->vsl, SLT_Debug, "H2: No OU PRISM (hs=%d)", hs);
-		Req_Release(req);
-		Lck_Unlock(&h2->sess->mtx);
-		SES_Delete(h2->sess, SC_RX_JUNK, NAN);
-		return (0);
-	}
-	HTC_RxPipeline(h2->htc, h2->htc->rxbuf_b + sizeof(H2_prism));
-	HTC_RxInit(h2->htc, wrk->aws);
-	VSLb(h2->vsl, SLT_Debug, "H2: Got PRISM");
-	return (1);
-}
-
-static void __match_proto__(task_func_t)
-h2_new_session(struct worker *wrk, void *arg)
-{
-	struct req *req;
-	struct sess *sp;
-	struct h2_sess *h2;
-	struct h2_req *r2, *r22;
-	uintptr_t wsp;
-
-	CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
-	CAST_OBJ_NOTNULL(req, arg, REQ_MAGIC);
-	sp = req->sp;
-	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
-
-	assert(req->transport == &H2_transport);
-
-	wsp = WS_Snapshot(wrk->aws);
-
-	switch(req->err_code) {
-	case 0:
-		/* Direct H2 connection (via Proxy) */
-		h2 = h2_new_sess(wrk, sp, req);
-		Lck_Lock(&h2->sess->mtx);
-		(void)h2_new_req(wrk, h2, 0, NULL);
-		break;
-	case 1:
-		/* Prior Knowledge H1->H2 upgrade */
-		h2 = h2_new_sess(wrk, sp, req);
-		Lck_Lock(&h2->sess->mtx);
-		(void)h2_new_req(wrk, h2, 0, NULL);
-
-		if (!h2_new_pu_session(wrk, h2))
-			return;
-		break;
-	case 2:
-		/* Optimistic H1->H2 upgrade */
-		h2 = h2_new_sess(wrk, sp, NULL);
-		Lck_Lock(&h2->sess->mtx);
-		(void)h2_new_req(wrk, h2, 0, NULL);
-
-		if (!h2_new_ou_session(wrk, h2, req))
-			return;
-		break;
-	default:
-		WRONG("Bad req->err_code");
-	}
-
-	THR_SetRequest(h2->srq);
-
-	H2_Send_Frame(wrk, h2,
-	    H2_FRAME_SETTINGS, H2FF_NONE, sizeof H2_settings, 0, H2_settings);
-
-	/* and off we go... */
-	h2->cond = &wrk->cond;
-	Lck_Unlock(&h2->sess->mtx);
-
-	while (h2_rxframe(wrk, h2)) {
-		WS_Reset(wrk->aws, wsp);
-		HTC_RxInit(h2->htc, wrk->aws);
-	}
-
-	/* Delete all idle streams */
-	VTAILQ_FOREACH_SAFE(r2, &h2->streams, list, r22) {
-		if (r2->state == H2_S_IDLE)
-			h2_del_req(wrk, r2);
-	}
-	h2->cond = NULL;
-}
-
-struct transport H2_transport = {
-	.name =			"H2",
-	.magic =		TRANSPORT_MAGIC,
-	.new_session =		h2_new_session,
-	.sess_panic =		h2_sess_panic,
-	.deliver =		h2_deliver,
-	.req_body =		h2_req_body,
-	.minimal_response =	h2_minimal_response,
-};
diff --git a/bin/varnishd/http2/cache_http2_session.c b/bin/varnishd/http2/cache_http2_session.c
new file mode 100644
index 0000000..f63445c
--- /dev/null
+++ b/bin/varnishd/http2/cache_http2_session.c
@@ -0,0 +1,460 @@
+/*-
+ * Copyright (c) 2016 Varnish Software AS
+ * All rights reserved.
+ *
+ * Author: Poul-Henning Kamp <phk at phk.freebsd.dk>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include "config.h"
+
+#include "cache/cache.h"
+
+#include <stdio.h>
+
+#include "cache/cache_transport.h"
+#include "cache/cache_filter.h"
+#include "http2/cache_http2.h"
+
+#include "vend.h"
+
+static const char *
+h2_settingname(enum h2setting h2f)
+{
+
+	switch(h2f) {
+#define H2_SETTINGS(n,v,d) case H2S_##n: return #n;
+#include "tbl/h2_settings.h"
+#undef H2_SETTINGS
+	default:
+		return (NULL);
+	}
+}
+
+static const char h2_resp_101[] =
+	"HTTP/1.1 101 Switching Protocols\r\n"
+	"Connection: Upgrade\r\n"
+	"Upgrade: h2c\r\n"
+	"\r\n";
+
+static const char H2_prism[24] = {
+	0x50, 0x52, 0x49, 0x20, 0x2a, 0x20, 0x48, 0x54,
+	0x54, 0x50, 0x2f, 0x32, 0x2e, 0x30, 0x0d, 0x0a,
+	0x0d, 0x0a, 0x53, 0x4d, 0x0d, 0x0a, 0x0d, 0x0a
+};
+
+static const uint8_t H2_settings[] = {
+	0x00, 0x03,
+	0x00, 0x00, 0x00, 0x64,
+	0x00, 0x04,
+	0x00, 0x00, 0xff, 0xff
+};
+
+/**********************************************************************
+ * The h2_sess struct needs many of the same things as a request,
+ * WS, VSL, HTC &c,  but rather than implement all that stuff over, we
+ * grab an actual struct req, and mirror the relevant fields into
+ * struct h2_sess.
+ * To make things really incestuous, we allocate the h2_sess on
+ * the WS of that "Session ReQuest".
+ */
+
+static struct h2_sess *
+h2_new_sess(const struct worker *wrk, struct sess *sp, struct req *srq)
+{
+	uintptr_t *up;
+	struct h2_sess *h2;
+
+	if (SES_Get_xport_priv(sp, &up)) {
+		/* Already reserved if we came via H1 */
+		SES_Reserve_xport_priv(sp, &up);
+		*up = 0;
+	}
+	if (*up == 0) {
+		if (srq == NULL)
+			srq = Req_New(wrk, sp);
+		AN(srq);
+		h2 = WS_Alloc(srq->ws, sizeof *h2);
+		AN(h2);
+		INIT_OBJ(h2, H2_SESS_MAGIC);
+		h2->srq = srq;
+		h2->htc = srq->htc;
+		h2->ws = srq->ws;
+		h2->vsl = srq->vsl;
+		h2->vsl->wid = sp->vxid;
+		h2->htc->rfd = &sp->fd;
+		h2->sess = sp;
+		VTAILQ_INIT(&h2->streams);
+#define H2_SETTINGS(n,v,d)					\
+		do {						\
+			assert(v < H2_SETTINGS_N);		\
+			h2->their_settings[v] = d;		\
+			h2->our_settings[v] = d;		\
+		} while (0);
+#include "tbl/h2_settings.h"
+#undef H2_SETTINGS
+
+		/* XXX: Lacks a VHT_Fini counterpart. Will leak memory. */
+		AZ(VHT_Init(h2->dectbl,
+			h2->our_settings[H2S_HEADER_TABLE_SIZE]));
+
+		SES_Reserve_xport_priv(sp, &up);
+		*up = (uintptr_t)h2;
+	}
+	AN(up);
+	CAST_OBJ_NOTNULL(h2, (void*)(*up), H2_SESS_MAGIC);
+	return (h2);
+}
+
+/**********************************************************************
+ */
+
+static struct h2_req *
+h2_new_req(const struct worker *wrk, struct h2_sess *h2,
+    unsigned stream, struct req *req)
+{
+	struct h2_req *r2;
+
+	Lck_AssertHeld(&h2->sess->mtx);
+	if (req == NULL)
+		req = Req_New(wrk, h2->sess);
+	CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
+
+	r2 = WS_Alloc(req->ws, sizeof *r2);
+	AN(r2);
+	INIT_OBJ(r2, H2_REQ_MAGIC);
+	r2->state = H2_S_IDLE;
+	r2->h2sess = h2;
+	r2->stream = stream;
+	r2->req = req;
+	req->transport_priv = r2;
+	// XXX: ordering ?
+	VTAILQ_INSERT_TAIL(&h2->streams, r2, list);
+	h2->refcnt++;
+	return (r2);
+}
+
+static void
+h2_del_req(struct worker *wrk, struct h2_req *r2)
+{
+	struct h2_sess *h2;
+	struct sess *sp;
+	struct req *req;
+	int r;
+
+	h2 = r2->h2sess;
+	sp = h2->sess;
+	Lck_Lock(&sp->mtx);
+	assert(h2->refcnt > 0);
+	r = --h2->refcnt;
+	/* XXX: PRIORITY reshuffle */
+	VTAILQ_REMOVE(&h2->streams, r2, list);
+	Lck_Unlock(&sp->mtx);
+	Req_Cleanup(sp, wrk, r2->req);
+	Req_Release(r2->req);
+	if (r)
+		return;
+
+	/* All streams gone, including stream #0, clean up */
+	req = h2->srq;
+	Req_Cleanup(sp, wrk, req);
+	Req_Release(req);
+	SES_Delete(sp, SC_RX_JUNK, NAN);
+}
+
+/**********************************************************************
+ * Update and VSL a single SETTING rx'ed from the other side
+ * 'd' must point to six bytes.
+ */
+
+static void
+h2_setting(struct h2_sess *h2, const uint8_t *d)
+{
+	uint16_t x;
+	uint32_t y;
+	const char *n;
+	char nb[8];
+
+	x = vbe16dec(d);
+	y = vbe32dec(d + 2);
+	n = h2_settingname((enum h2setting)x);
+	if (n == NULL) {
+		bprintf(nb, "0x%04x", x);
+		n = nb;
+	}
+	VSLb(h2->vsl, SLT_Debug, "H2SETTING %s 0x%08x", n, y);
+	if (x > 0 && x < H2_SETTINGS_N)
+		h2->their_settings[x] = y;
+}
+
+/**********************************************************************
+ * Incoming HEADERS, this is where the partys at...
+ */
+
+static void __match_proto__(task_func_t)
+h2_do_req(struct worker *wrk, void *priv)
+{
+	struct req *req;
+	struct h2_req *r2;
+
+	CAST_OBJ_NOTNULL(req, priv, REQ_MAGIC);
+	CAST_OBJ_NOTNULL(r2, req->transport_priv, H2_REQ_MAGIC);
+	THR_SetRequest(req);
+	if (!CNT_GotReq(wrk, req))
+		assert(CNT_Request(wrk, req) != REQ_FSM_DISEMBARK);
+	THR_SetRequest(NULL);
+	VSL(SLT_Debug, 0, "H2REQ CNT done");
+	/* XXX clean up req */
+	r2->state = H2_S_CLOSED;
+	h2_del_req(wrk, r2);
+}
+
+/**********************************************************************/
+
+enum htc_status_e __match_proto__(htc_complete_f)
+H2_prism_complete(struct http_conn *htc)
+{
+	int l;
+
+	CHECK_OBJ_NOTNULL(htc, HTTP_CONN_MAGIC);
+	l = htc->rxbuf_e - htc->rxbuf_b;
+	if (l >= sizeof(H2_prism) &&
+	    !memcmp(htc->rxbuf_b, H2_prism, sizeof(H2_prism)))
+		return (HTC_S_COMPLETE);
+	if (l < sizeof(H2_prism) && !memcmp(htc->rxbuf_b, H2_prism, l))
+		return (HTC_S_MORE);
+	return (HTC_S_JUNK);
+}
+
+
+/**********************************************************************
+ * Deal with the base64url (NB: ...url!) encoded SETTINGS in the H1 req
+ * of a H2C upgrade.
+ */
+
+static int
+h2_b64url_settings(struct h2_sess *h2, struct req *req)
+{
+	const char *p, *q;
+	uint8_t u[6], *up;
+	unsigned x;
+	int i, n;
+	static const char s[] =
+	    "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+	    "abcdefghijklmnopqrstuvwxyz"
+	    "0123456789"
+	    "-_=";
+
+	/*
+	 * If there is trouble with this, we could reject the upgrade
+	 * but putting this on the H1 side is just plain wrong...
+	 */
+	AN(http_GetHdr(req->http, H_HTTP2_Settings, &p));
+	if (p == NULL)
+		return (-1);
+	VSLb(req->vsl, SLT_Debug, "H2CS %s", p);
+
+	n = 0;
+	x = 0;
+	up = u;
+	for (;*p; p++) {
+		q = strchr(s, *p);
+		if (q == NULL)
+			return (-1);
+		i = q - s;
+		assert(i >= 0 && i <= 63);
+		x <<= 6;
+		x |= i;
+		n += 6;
+		if (n < 8)
+			continue;
+		*up++ = (uint8_t)(x >> (n - 8));
+		n -= 8;
+		if (up == u + sizeof u) {
+			AZ(n);
+			h2_setting(h2, (void*)u);
+			up = u;
+		}
+	}
+	if (up != u)
+		return (-1);
+	return (0);
+}
+
+/**********************************************************************/
+
+static int
+h2_new_pu_session(struct worker *wrk, const struct h2_sess *h2)
+{
+	enum htc_status_e hs;
+
+	(void)wrk;
+
+	hs = H2_prism_complete(h2->htc);
+	if (hs == HTC_S_MORE) {
+		VSLb(h2->vsl, SLT_Debug, "Short pu PRISM");
+		return (0);
+	}
+	if (hs != HTC_S_COMPLETE) {
+		VSLb(h2->vsl, SLT_Debug, "Wrong pu PRISM");
+		return (0);
+	}
+	HTC_RxPipeline(h2->htc, h2->htc->rxbuf_b + sizeof(H2_prism));
+	HTC_RxInit(h2->htc, wrk->aws);
+
+	VSLb(h2->vsl, SLT_Debug, "H2: Got pu PRISM");
+	return (1);
+}
+
+/**********************************************************************/
+
+static int
+h2_new_ou_session(struct worker *wrk, struct h2_sess *h2,
+    struct req *req)
+{
+	ssize_t sz;
+	enum htc_status_e hs;
+
+	sz = write(h2->sess->fd, h2_resp_101, strlen(h2_resp_101));
+	assert(sz == strlen(h2_resp_101));
+
+	AZ(h2_b64url_settings(h2, req));
+	http_Unset(req->http, H_Upgrade);
+	http_Unset(req->http, H_HTTP2_Settings);
+
+	/* Steal pipelined read-ahead, if any */
+	h2->htc->pipeline_b = req->htc->pipeline_b;
+	h2->htc->pipeline_e = req->htc->pipeline_e;
+	req->htc->pipeline_b = NULL;
+	req->htc->pipeline_e = NULL;
+	/* XXX: This call may assert on buffer overflow if the pipelined
+	   data exceeds the available space in the aws workspace. What to
+	   do about the overflowing data is an open issue. */
+	HTC_RxInit(h2->htc, wrk->aws);
+
+	/* Start req thread */
+	(void)h2_new_req(wrk, h2, 1, req);
+	req->req_step = R_STP_RECV;
+	req->transport = &H2_transport;
+	req->task.func = h2_do_req;
+	req->task.priv = req;
+	req->err_code = 0;
+	http_SetH(req->http, HTTP_HDR_PROTO, "HTTP/2.0");
+	XXXAZ(Pool_Task(wrk->pool, &req->task, TASK_QUEUE_REQ));
+
+	/* Wait for PRISM response */
+	hs = HTC_RxStuff(h2->htc, H2_prism_complete,
+	    NULL, NULL, NAN, h2->sess->t_idle + cache_param->timeout_idle, 256);
+	if (hs != HTC_S_COMPLETE) {
+		/* XXX clean up req thread */
+		VSLb(h2->vsl, SLT_Debug, "H2: No OU PRISM (hs=%d)", hs);
+		Req_Release(req);
+		Lck_Unlock(&h2->sess->mtx);
+		SES_Delete(h2->sess, SC_RX_JUNK, NAN);
+		return (0);
+	}
+	HTC_RxPipeline(h2->htc, h2->htc->rxbuf_b + sizeof(H2_prism));
+	HTC_RxInit(h2->htc, wrk->aws);
+	VSLb(h2->vsl, SLT_Debug, "H2: Got PRISM");
+	return (1);
+}
+
+static void __match_proto__(task_func_t)
+h2_new_session(struct worker *wrk, void *arg)
+{
+	struct req *req;
+	struct sess *sp;
+	struct h2_sess *h2;
+	struct h2_req *r2, *r22;
+	uintptr_t wsp;
+
+	CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
+	CAST_OBJ_NOTNULL(req, arg, REQ_MAGIC);
+	sp = req->sp;
+	CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
+
+	assert(req->transport == &H2_transport);
+
+	wsp = WS_Snapshot(wrk->aws);
+
+	switch(req->err_code) {
+	case 0:
+		/* Direct H2 connection (via Proxy) */
+		h2 = h2_new_sess(wrk, sp, req);
+		Lck_Lock(&h2->sess->mtx);
+		(void)h2_new_req(wrk, h2, 0, NULL);
+		break;
+	case 1:
+		/* Prior Knowledge H1->H2 upgrade */
+		h2 = h2_new_sess(wrk, sp, req);
+		Lck_Lock(&h2->sess->mtx);
+		(void)h2_new_req(wrk, h2, 0, NULL);
+
+		if (!h2_new_pu_session(wrk, h2))
+			return;
+		break;
+	case 2:
+		/* Optimistic H1->H2 upgrade */
+		h2 = h2_new_sess(wrk, sp, NULL);
+		Lck_Lock(&h2->sess->mtx);
+		(void)h2_new_req(wrk, h2, 0, NULL);
+
+		if (!h2_new_ou_session(wrk, h2, req))
+			return;
+		break;
+	default:
+		WRONG("Bad req->err_code");
+	}
+
+	THR_SetRequest(h2->srq);
+
+	H2_Send_Frame(wrk, h2,
+	    H2_FRAME_SETTINGS, H2FF_NONE, sizeof H2_settings, 0, H2_settings);
+
+	/* and off we go... */
+	h2->cond = &wrk->cond;
+	Lck_Unlock(&h2->sess->mtx);
+
+	while (h2_rxframe(wrk, h2)) {
+		WS_Reset(wrk->aws, wsp);
+		HTC_RxInit(h2->htc, wrk->aws);
+	}
+
+	/* Delete all idle streams */
+	VTAILQ_FOREACH_SAFE(r2, &h2->streams, list, r22) {
+		if (r2->state == H2_S_IDLE)
+			h2_del_req(wrk, r2);
+	}
+	h2->cond = NULL;
+}
+
+struct transport H2_transport = {
+	.name =			"H2",
+	.magic =		TRANSPORT_MAGIC,
+	.new_session =		h2_new_session,
+	.sess_panic =		h2_sess_panic,
+	.deliver =		h2_deliver,
+	.req_body =		h2_req_body,
+	.minimal_response =	h2_minimal_response,
+};



More information about the varnish-commit mailing list