[master] 59c4ad8 Add gquintar's HTTP2 support for varnishtest.

Poul-Henning Kamp phk at FreeBSD.org
Tue Aug 30 12:41:11 CEST 2016


commit 59c4ad8e8e231c4ad7acbd36d40e966c7038ee2a
Author: Poul-Henning Kamp <phk at FreeBSD.org>
Date:   Tue Aug 30 10:39:40 2016 +0000

    Add gquintar's HTTP2 support for varnishtest.

diff --git a/bin/varnishtest/Makefile.am b/bin/varnishtest/Makefile.am
index d6dbc10..1c519e1 100644
--- a/bin/varnishtest/Makefile.am
+++ b/bin/varnishtest/Makefile.am
@@ -23,6 +23,7 @@ AM_CPPFLAGS = \
 bin_PROGRAMS =	varnishtest
 
 varnishtest_SOURCES = \
+		hpack.h \
 		programs.h \
 		vmods.h \
 		vtc.c \
@@ -30,7 +31,15 @@ varnishtest_SOURCES = \
 		vtc_barrier.c \
 		vtc_client.c \
 		vtc_http.c \
+		vtc_http.h \
 		vtc_log.c \
+		vtc_http2.c \
+		vtc_h2_dectbl.h \
+		vtc_h2_enctbl.h \
+		vtc_h2_hpack.c \
+		vtc_h2_priv.h \
+		vtc_h2_stattbl.h \
+		vtc_h2_tbl.c \
 		vtc_logexp.c \
 		vtc_main.c \
 		vtc_process.c \
diff --git a/bin/varnishtest/flint.lnt b/bin/varnishtest/flint.lnt
index b7306f1..a02b3be 100644
--- a/bin/varnishtest/flint.lnt
+++ b/bin/varnishtest/flint.lnt
@@ -32,3 +32,4 @@
 
 -efile(451, vmods.h)
 -efile(451, programs.h)
+-efile(451, vtc_h2_stattbl.h)
diff --git a/bin/varnishtest/flint.sh b/bin/varnishtest/flint.sh
index 45bbfd8..7f9c759 100755
--- a/bin/varnishtest/flint.sh
+++ b/bin/varnishtest/flint.sh
@@ -1,5 +1,11 @@
 #!/bin/sh
 
+if [ "x$1" = "x-ok" -a -f _.fl ] ; then
+	echo "Saved as reference"
+	mv _.fl _.fl.old
+	exit 0
+fi
+
 flexelint \
 	-DTOP_BUILDDIR='"foo"' \
 	-I/usr/include \
@@ -10,4 +16,14 @@ flexelint \
 	../../flint.lnt \
 	../flint.lnt \
 	flint.lnt \
-	*.c
+	*.c \
+	2>&1 | tee _.fl
+
+if [ -f _.fl.old ] ; then
+	diff -u _.fl.old _.fl
+fi
+
+if [ "x$1" = "x-ok" ] ; then
+	echo "Saved as reference"
+	mv _.fl _.fl.old
+fi
diff --git a/bin/varnishtest/hpack.h b/bin/varnishtest/hpack.h
new file mode 100644
index 0000000..feca984
--- /dev/null
+++ b/bin/varnishtest/hpack.h
@@ -0,0 +1,53 @@
+#include <stdint.h>
+
+enum hpk_result{
+	hpk_more = 0,
+	hpk_done,
+	hpk_err,
+};
+
+enum hpk_indexed {
+	hpk_unset = 0,
+	hpk_idx,
+	hpk_inc,
+	hpk_not,
+	hpk_never,
+};
+
+struct txt {
+	char *ptr;
+	int len;
+	int huff;
+};
+
+struct hpk_hdr {
+	struct txt key;
+	struct txt value;
+	enum hpk_indexed t;
+	int i;
+};
+
+struct hpk_ctx;
+struct hpk_iter;
+
+struct hpk_ctx * HPK_NewCtx(uint32_t tblsize);
+void HPK_FreeCtx(struct hpk_ctx *ctx);
+
+struct hpk_iter * HPK_NewIter(struct hpk_ctx *ctx, char *buf, int size);
+void HPK_FreeIter(struct hpk_iter *iter);
+
+enum hpk_result HPK_DecHdr(struct hpk_iter *iter, struct hpk_hdr *header);
+enum hpk_result HPK_EncHdr(struct hpk_iter *iter, const struct hpk_hdr *header);
+
+int gethpk_iterLen(const struct hpk_iter *iter);
+
+enum hpk_result HPK_ResizeTbl(struct hpk_ctx *ctx, uint32_t num);
+
+const struct hpk_hdr * HPK_GetHdr(const struct hpk_ctx *ctx, uint32_t index);
+
+uint32_t HPK_GetTblSize(const struct hpk_ctx *ctx);
+uint32_t HPK_GetTblMaxSize(const struct hpk_ctx *ctx);
+uint32_t HPK_GetTblLength(const struct hpk_ctx *ctx);
+
+/* DEBUG */
+void dump_dyn_tbl(const struct hpk_ctx *ctx);
diff --git a/bin/varnishtest/tests/README b/bin/varnishtest/tests/README
index 80fd68a..971ee3d 100644
--- a/bin/varnishtest/tests/README
+++ b/bin/varnishtest/tests/README
@@ -29,15 +29,3 @@ Naming scheme
 	id ~ [t] --> sTreaming tests
 	id ~ [u] --> Unusual background processes
 	id ~ [v] --> VCL tests: execute VRT functions
-
-
-Private test scope
-------------------
-
-	Test cases matching
-
-		[id]1%04d.vtc
-
-	are reserved for private tests.
-
-
diff --git a/bin/varnishtest/vtc.c b/bin/varnishtest/vtc.c
index 11ab41d..f3bf538 100644
--- a/bin/varnishtest/vtc.c
+++ b/bin/varnishtest/vtc.c
@@ -262,7 +262,7 @@ parse_string(const char *spec, const struct cmds *cmd, void *priv,
 {
 	char *token_s[MAX_TOKENS], *token_e[MAX_TOKENS];
 	struct vsb *token_exp[MAX_TOKENS];
-	char *p, *q, *f, *buf;
+	char *e, *p, *q, *f, *buf;
 	int nest_brace;
 	int tn;
 	const struct cmds *cp;
@@ -270,7 +270,9 @@ parse_string(const char *spec, const struct cmds *cmd, void *priv,
 	AN(spec);
 	buf = strdup(spec);
 	AN(buf);
-	for (p = buf; *p != '\0'; p++) {
+	e = strchr(buf, '\0');
+	AN(e);
+	for (p = buf; p < e; p++) {
 		if (vtc_error || vtc_stop)
 			break;
 		/* Start of line */
@@ -298,8 +300,9 @@ parse_string(const char *spec, const struct cmds *cmd, void *priv,
 		/* First content on line, collect tokens */
 		tn = 0;
 		f = p;
-		while (*p != '\0') {
+		while (p < e) {
 			assert(tn < MAX_TOKENS);
+			assert(p < e);
 			if (*p == '\n') { /* End on NL */
 				break;
 			}
@@ -315,6 +318,7 @@ parse_string(const char *spec, const struct cmds *cmd, void *priv,
 				token_s[tn] = ++p;
 				q = p;
 				for (; *p != '\0'; p++) {
+					assert(p < e);
 					if (*p == '"')
 						break;
 					if (*p == '\\') {
@@ -334,7 +338,7 @@ parse_string(const char *spec, const struct cmds *cmd, void *priv,
 			} else if (*p == '{') { /* Braces */
 				nest_brace = 0;
 				token_s[tn] = p + 1;
-				for (; *p != '\0'; p++) {
+				for (; p < e; p++) {
 					if (*p == '{')
 						nest_brace++;
 					else if (*p == '}') {
@@ -346,25 +350,26 @@ parse_string(const char *spec, const struct cmds *cmd, void *priv,
 				token_e[tn++] = p++;
 			} else { /* other tokens */
 				token_s[tn] = p;
-				for (; *p != '\0' && !isspace(*p); p++)
-					;
+				for (; p < e && !isspace(*p); p++)
+					continue;
 				token_e[tn++] = p;
 			}
 		}
+
+		assert(p <= e);
 		assert(tn < MAX_TOKENS);
 		token_s[tn] = NULL;
 		for (tn = 0; token_s[tn] != NULL; tn++) {
 			token_exp[tn] = NULL;
 			AN(token_e[tn]);	/*lint !e771 */
 			*token_e[tn] = '\0';	/*lint !e771 */
-			if (NULL == strstr(token_s[tn], "${"))
-				continue;
-			token_exp[tn] = macro_expand(vl, token_s[tn]);
-			if (vtc_error) {
-				return;
+			if (NULL != strstr(token_s[tn], "${")) {
+				token_exp[tn] = macro_expand(vl, token_s[tn]);
+				if (vtc_error)
+					return;
+				token_s[tn] = VSB_data(token_exp[tn]);
+				token_e[tn] = strchr(token_s[tn], '\0');
 			}
-			token_s[tn] = VSB_data(token_exp[tn]);
-			token_e[tn] = strchr(token_s[tn], '\0');
 		}
 
 		for (cp = cmd; cp->name != NULL; cp++)
@@ -507,13 +512,14 @@ cmd_err_shell(CMD_ARGS)
 	VSB_destroy(&vsb);
 }
 
-/* SECTION: delay delay
+/* SECTION: client-server.spec.delay delay
  *
- * This is the equivalent of ``sleep`` in shell: the command takes one argument
- * that is the number of seconds (can be a float) to wait before continuing the
- * test.
+ * Take a float as argument and sleep for that number of seconds.
+ */
+/* SECTION: stream.spec.delay delay
+ *
+ * Take a float as argument and sleep for that number of seconds.
  */
-
 void
 cmd_delay(CMD_ARGS)
 {
diff --git a/bin/varnishtest/vtc.h b/bin/varnishtest/vtc.h
index b872e89..a614cd0 100644
--- a/bin/varnishtest/vtc.h
+++ b/bin/varnishtest/vtc.h
@@ -80,6 +80,10 @@ void init_barrier(void);
 void init_server(void);
 
 int http_process(struct vtclog *vl, const char *spec, int sock, int *sfd);
+int http2_process(struct vtclog *vl, const char *spec, int sock, int *sfd,
+		unsigned nosettings);
+
+char * synth_body(const char *len, int rnd);
 
 void cmd_server_genvcl(struct vsb *vsb);
 
@@ -107,3 +111,10 @@ struct vsb *macro_expand(struct vtclog *vl, const char *text);
 
 void extmacro_def(const char *name, const char *fmt, ...)
     __v_printflike(2, 3);
+
+struct http;
+extern const struct cmds http_cmds[];
+void cmd_stream(CMD_ARGS);
+void start_h2(struct http *hp);
+void stop_h2(struct http *hp);
+void b64_settings(const struct http *hp, const char *s);
diff --git a/bin/varnishtest/vtc_doc_hdr.rst b/bin/varnishtest/vtc_doc_hdr.rst
new file mode 100644
index 0000000..de8541f
--- /dev/null
+++ b/bin/varnishtest/vtc_doc_hdr.rst
@@ -0,0 +1,33 @@
+Syntax RFP for HTTP2 in varnishtest
+===================================
+
+This document tries to document how varnishtest would work this H/2, adapting to
+the introduction of stream. The main idea is the introduction of a new command
+(stream) inside client and server specification that naively maps to RFC7540.
+
+It provides little abstraction, allowwing great control over test scenario,
+while still retaining a familiar logic.
+
+Here's an example of test file::
+
+        server s1 {
+        	non-fatal
+        	stream 1 {
+        		rxreq
+        		expect req.http.foo == <undef>
+        		txgoaway -laststream 0 -err 9 -debug "COMPRESSION_ERROR"
+        	} -run
+        } -start
+        
+        client c1 -connect ${s1_sock} {
+        	stream 1 {
+        		txreq -idxHdr 100 -litHdr inc plain "foo" plain "bar"
+        		rxgoaway
+        		expect goaway.err == 9
+        		expect goaway.laststream == 0
+        		expect goaway.debug == "COMPRESSION_ERROR"
+        	} -run
+        } -run
+
+.. contents::
+
diff --git a/bin/varnishtest/vtc_h2_dectbl.h b/bin/varnishtest/vtc_h2_dectbl.h
new file mode 100644
index 0000000..03a2d73
--- /dev/null
+++ b/bin/varnishtest/vtc_h2_dectbl.h
@@ -0,0 +1,1028 @@
+struct stbl;
+
+struct ssym {
+	uint8_t csm;		/* bits consumed */
+	uint8_t	chr;		/* character */
+	struct stbl *nxt;	/* next table */
+};
+
+struct stbl {
+	int msk;
+	struct ssym syms[];
+};
+
+static struct stbl byte3_pref0110 = {
+	1,
+	{
+		/* idx   0 */ {1, 199, NULL},
+		/* idx   1 */ {1, 207, NULL},
+	}
+};
+
+static struct stbl byte3_pref0111 = {
+	1,
+	{
+		/* idx   0 */ {1, 234, NULL},
+		/* idx   1 */ {1, 235, NULL},
+	}
+};
+
+static struct stbl byte3_pref1000 = {
+	2,
+	{
+		/* idx   0 */ {2, 192, NULL},
+		/* idx   1 */ {2, 193, NULL},
+		/* idx   2 */ {2, 200, NULL},
+		/* idx   3 */ {2, 201, NULL},
+	}
+};
+
+static struct stbl byte3_pref1001 = {
+	2,
+	{
+		/* idx   0 */ {2, 202, NULL},
+		/* idx   1 */ {2, 205, NULL},
+		/* idx   2 */ {2, 210, NULL},
+		/* idx   3 */ {2, 213, NULL},
+	}
+};
+
+static struct stbl byte3_pref1010 = {
+	2,
+	{
+		/* idx   0 */ {2, 218, NULL},
+		/* idx   1 */ {2, 219, NULL},
+		/* idx   2 */ {2, 238, NULL},
+		/* idx   3 */ {2, 240, NULL},
+	}
+};
+
+static struct stbl byte3_pref1011 = {
+	3,
+	{
+		/* idx   0 */ {2, 242, NULL},
+		/* idx   1 */ {2, 242, NULL},
+		/* idx   2 */ {2, 243, NULL},
+		/* idx   3 */ {2, 243, NULL},
+		/* idx   4 */ {2, 255, NULL},
+		/* idx   5 */ {2, 255, NULL},
+		/* idx   6 */ {3, 203, NULL},
+		/* idx   7 */ {3, 204, NULL},
+	}
+};
+
+static struct stbl byte3_pref1100 = {
+	3,
+	{
+		/* idx   0 */ {3, 211, NULL},
+		/* idx   1 */ {3, 212, NULL},
+		/* idx   2 */ {3, 214, NULL},
+		/* idx   3 */ {3, 221, NULL},
+		/* idx   4 */ {3, 222, NULL},
+		/* idx   5 */ {3, 223, NULL},
+		/* idx   6 */ {3, 241, NULL},
+		/* idx   7 */ {3, 244, NULL},
+	}
+};
+
+static struct stbl byte3_pref1101 = {
+	3,
+	{
+		/* idx   0 */ {3, 245, NULL},
+		/* idx   1 */ {3, 246, NULL},
+		/* idx   2 */ {3, 247, NULL},
+		/* idx   3 */ {3, 248, NULL},
+		/* idx   4 */ {3, 250, NULL},
+		/* idx   5 */ {3, 251, NULL},
+		/* idx   6 */ {3, 252, NULL},
+		/* idx   7 */ {3, 253, NULL},
+	}
+};
+
+static struct stbl byte3_pref1110 = {
+	4,
+	{
+		/* idx   0 */ {3, 254, NULL},
+		/* idx   1 */ {3, 254, NULL},
+		/* idx   2 */ {4,   2, NULL},
+		/* idx   3 */ {4,   3, NULL},
+		/* idx   4 */ {4,   4, NULL},
+		/* idx   5 */ {4,   5, NULL},
+		/* idx   6 */ {4,   6, NULL},
+		/* idx   7 */ {4,   7, NULL},
+		/* idx   8 */ {4,   8, NULL},
+		/* idx   9 */ {4,  11, NULL},
+		/* idx  10 */ {4,  12, NULL},
+		/* idx  11 */ {4,  14, NULL},
+		/* idx  12 */ {4,  15, NULL},
+		/* idx  13 */ {4,  16, NULL},
+		/* idx  14 */ {4,  17, NULL},
+		/* idx  15 */ {4,  18, NULL},
+	}
+};
+
+static struct stbl byte3_pref1111 = {
+	6,
+	{
+		/* idx   0 */ {4,  19, NULL},
+		/* idx   1 */ {4,  19, NULL},
+		/* idx   2 */ {4,  19, NULL},
+		/* idx   3 */ {4,  19, NULL},
+		/* idx   4 */ {4,  20, NULL},
+		/* idx   5 */ {4,  20, NULL},
+		/* idx   6 */ {4,  20, NULL},
+		/* idx   7 */ {4,  20, NULL},
+		/* idx   8 */ {4,  21, NULL},
+		/* idx   9 */ {4,  21, NULL},
+		/* idx  10 */ {4,  21, NULL},
+		/* idx  11 */ {4,  21, NULL},
+		/* idx  12 */ {4,  23, NULL},
+		/* idx  13 */ {4,  23, NULL},
+		/* idx  14 */ {4,  23, NULL},
+		/* idx  15 */ {4,  23, NULL},
+		/* idx  16 */ {4,  24, NULL},
+		/* idx  17 */ {4,  24, NULL},
+		/* idx  18 */ {4,  24, NULL},
+		/* idx  19 */ {4,  24, NULL},
+		/* idx  20 */ {4,  25, NULL},
+		/* idx  21 */ {4,  25, NULL},
+		/* idx  22 */ {4,  25, NULL},
+		/* idx  23 */ {4,  25, NULL},
+		/* idx  24 */ {4,  26, NULL},
+		/* idx  25 */ {4,  26, NULL},
+		/* idx  26 */ {4,  26, NULL},
+		/* idx  27 */ {4,  26, NULL},
+		/* idx  28 */ {4,  27, NULL},
+		/* idx  29 */ {4,  27, NULL},
+		/* idx  30 */ {4,  27, NULL},
+		/* idx  31 */ {4,  27, NULL},
+		/* idx  32 */ {4,  28, NULL},
+		/* idx  33 */ {4,  28, NULL},
+		/* idx  34 */ {4,  28, NULL},
+		/* idx  35 */ {4,  28, NULL},
+		/* idx  36 */ {4,  29, NULL},
+		/* idx  37 */ {4,  29, NULL},
+		/* idx  38 */ {4,  29, NULL},
+		/* idx  39 */ {4,  29, NULL},
+		/* idx  40 */ {4,  30, NULL},
+		/* idx  41 */ {4,  30, NULL},
+		/* idx  42 */ {4,  30, NULL},
+		/* idx  43 */ {4,  30, NULL},
+		/* idx  44 */ {4,  31, NULL},
+		/* idx  45 */ {4,  31, NULL},
+		/* idx  46 */ {4,  31, NULL},
+		/* idx  47 */ {4,  31, NULL},
+		/* idx  48 */ {4, 127, NULL},
+		/* idx  49 */ {4, 127, NULL},
+		/* idx  50 */ {4, 127, NULL},
+		/* idx  51 */ {4, 127, NULL},
+		/* idx  52 */ {4, 220, NULL},
+		/* idx  53 */ {4, 220, NULL},
+		/* idx  54 */ {4, 220, NULL},
+		/* idx  55 */ {4, 220, NULL},
+		/* idx  56 */ {4, 249, NULL},
+		/* idx  57 */ {4, 249, NULL},
+		/* idx  58 */ {4, 249, NULL},
+		/* idx  59 */ {4, 249, NULL},
+		/* idx  60 */ {6,  10, NULL},
+		/* idx  61 */ {6,  13, NULL},
+		/* idx  62 */ {6,  22, NULL},
+		/* idx  63 */ {0,   0, NULL}
+	}
+};
+
+static struct stbl byte2_pref0 = {
+	5,
+	{
+		/* idx   0 */ {3,  92, NULL}, /* '\' */
+		/* idx   1 */ {3,  92, NULL}, /* '\' */
+		/* idx   2 */ {3,  92, NULL}, /* '\' */
+		/* idx   3 */ {3,  92, NULL}, /* '\' */
+		/* idx   4 */ {3, 195, NULL}, /* . */
+		/* idx   5 */ {3, 195, NULL}, /* . */
+		/* idx   6 */ {3, 195, NULL}, /* . */
+		/* idx   7 */ {3, 195, NULL}, /* . */
+		/* idx   8 */ {3, 208, NULL}, /* . */
+		/* idx   9 */ {3, 208, NULL}, /* . */
+		/* idx  10 */ {3, 208, NULL}, /* . */
+		/* idx  11 */ {3, 208, NULL}, /* . */
+		/* idx  12 */ {4, 128, NULL}, /* . */
+		/* idx  13 */ {4, 128, NULL}, /* . */
+		/* idx  14 */ {4, 130, NULL}, /* . */
+		/* idx  15 */ {4, 130, NULL}, /* . */
+		/* idx  16 */ {4, 131, NULL}, /* . */
+		/* idx  17 */ {4, 131, NULL}, /* . */
+		/* idx  18 */ {4, 162, NULL}, /* . */
+		/* idx  19 */ {4, 162, NULL}, /* . */
+		/* idx  20 */ {4, 184, NULL}, /* . */
+		/* idx  21 */ {4, 184, NULL}, /* . */
+		/* idx  22 */ {4, 194, NULL}, /* . */
+		/* idx  23 */ {4, 194, NULL}, /* . */
+		/* idx  24 */ {4, 224, NULL}, /* . */
+		/* idx  25 */ {4, 224, NULL}, /* . */
+		/* idx  26 */ {4, 226, NULL}, /* . */
+		/* idx  27 */ {4, 226, NULL}, /* . */
+		/* idx  28 */ {5, 153, NULL}, /* . */
+		/* idx  29 */ {5, 161, NULL}, /* . */
+		/* idx  30 */ {5, 167, NULL}, /* . */
+		/* idx  31 */ {5, 172, NULL} /* . */
+	}
+};
+
+static struct stbl byte2_pref1 = {
+	8,
+	{
+		/* idx   0 */ {5, 176, NULL}, /* . */
+		/* idx   1 */ {5, 176, NULL}, /* . */
+		/* idx   2 */ {5, 176, NULL}, /* . */
+		/* idx   3 */ {5, 176, NULL}, /* . */
+		/* idx   4 */ {5, 176, NULL}, /* . */
+		/* idx   5 */ {5, 176, NULL}, /* . */
+		/* idx   6 */ {5, 176, NULL}, /* . */
+		/* idx   7 */ {5, 176, NULL}, /* . */
+		/* idx   8 */ {5, 177, NULL}, /* . */
+		/* idx   9 */ {5, 177, NULL}, /* . */
+		/* idx  10 */ {5, 177, NULL}, /* . */
+		/* idx  11 */ {5, 177, NULL}, /* . */
+		/* idx  12 */ {5, 177, NULL}, /* . */
+		/* idx  13 */ {5, 177, NULL}, /* . */
+		/* idx  14 */ {5, 177, NULL}, /* . */
+		/* idx  15 */ {5, 177, NULL}, /* . */
+		/* idx  16 */ {5, 179, NULL}, /*  */
+		/* idx  17 */ {5, 179, NULL}, /*  */
+		/* idx  18 */ {5, 179, NULL}, /*  */
+		/* idx  19 */ {5, 179, NULL}, /*  */
+		/* idx  20 */ {5, 179, NULL}, /*  */
+		/* idx  21 */ {5, 179, NULL}, /*  */
+		/* idx  22 */ {5, 179, NULL}, /*  */
+		/* idx  23 */ {5, 179, NULL}, /*  */
+		/* idx  24 */ {5, 209, NULL}, /* . */
+		/* idx  25 */ {5, 209, NULL}, /* . */
+		/* idx  26 */ {5, 209, NULL}, /* . */
+		/* idx  27 */ {5, 209, NULL}, /* . */
+		/* idx  28 */ {5, 209, NULL}, /* . */
+		/* idx  29 */ {5, 209, NULL}, /* . */
+		/* idx  30 */ {5, 209, NULL}, /* . */
+		/* idx  31 */ {5, 209, NULL}, /* . */
+		/* idx  32 */ {5, 216, NULL}, /* . */
+		/* idx  33 */ {5, 216, NULL}, /* . */
+		/* idx  34 */ {5, 216, NULL}, /* . */
+		/* idx  35 */ {5, 216, NULL}, /* . */
+		/* idx  36 */ {5, 216, NULL}, /* . */
+		/* idx  37 */ {5, 216, NULL}, /* . */
+		/* idx  38 */ {5, 216, NULL}, /* . */
+		/* idx  39 */ {5, 216, NULL}, /* . */
+		/* idx  40 */ {5, 217, NULL}, /* . */
+		/* idx  41 */ {5, 217, NULL}, /* . */
+		/* idx  42 */ {5, 217, NULL}, /* . */
+		/* idx  43 */ {5, 217, NULL}, /* . */
+		/* idx  44 */ {5, 217, NULL}, /* . */
+		/* idx  45 */ {5, 217, NULL}, /* . */
+		/* idx  46 */ {5, 217, NULL}, /* . */
+		/* idx  47 */ {5, 217, NULL}, /* . */
+		/* idx  48 */ {5, 227, NULL}, /* . */
+		/* idx  49 */ {5, 227, NULL}, /* . */
+		/* idx  50 */ {5, 227, NULL}, /* . */
+		/* idx  51 */ {5, 227, NULL}, /* . */
+		/* idx  52 */ {5, 227, NULL}, /* . */
+		/* idx  53 */ {5, 227, NULL}, /* . */
+		/* idx  54 */ {5, 227, NULL}, /* . */
+		/* idx  55 */ {5, 227, NULL}, /* . */
+		/* idx  56 */ {5, 229, NULL}, /* . */
+		/* idx  57 */ {5, 229, NULL}, /* . */
+		/* idx  58 */ {5, 229, NULL}, /* . */
+		/* idx  59 */ {5, 229, NULL}, /* . */
+		/* idx  60 */ {5, 229, NULL}, /* . */
+		/* idx  61 */ {5, 229, NULL}, /* . */
+		/* idx  62 */ {5, 229, NULL}, /* . */
+		/* idx  63 */ {5, 229, NULL}, /* . */
+		/* idx  64 */ {5, 230, NULL}, /* . */
+		/* idx  65 */ {5, 230, NULL}, /* . */
+		/* idx  66 */ {5, 230, NULL}, /* . */
+		/* idx  67 */ {5, 230, NULL}, /* . */
+		/* idx  68 */ {5, 230, NULL}, /* . */
+		/* idx  69 */ {5, 230, NULL}, /* . */
+		/* idx  70 */ {5, 230, NULL}, /* . */
+		/* idx  71 */ {5, 230, NULL}, /* . */
+		/* idx  72 */ {6, 129, NULL}, /* . */
+		/* idx  73 */ {6, 129, NULL}, /* . */
+		/* idx  74 */ {6, 129, NULL}, /* . */
+		/* idx  75 */ {6, 129, NULL}, /* . */
+		/* idx  76 */ {6, 132, NULL}, /* . */
+		/* idx  77 */ {6, 132, NULL}, /* . */
+		/* idx  78 */ {6, 132, NULL}, /* . */
+		/* idx  79 */ {6, 132, NULL}, /* . */
+		/* idx  80 */ {6, 133, NULL}, /* . */
+		/* idx  81 */ {6, 133, NULL}, /* . */
+		/* idx  82 */ {6, 133, NULL}, /* . */
+		/* idx  83 */ {6, 133, NULL}, /* . */
+		/* idx  84 */ {6, 134, NULL}, /* . */
+		/* idx  85 */ {6, 134, NULL}, /* . */
+		/* idx  86 */ {6, 134, NULL}, /* . */
+		/* idx  87 */ {6, 134, NULL}, /* . */
+		/* idx  88 */ {6, 136, NULL}, /* . */
+		/* idx  89 */ {6, 136, NULL}, /* . */
+		/* idx  90 */ {6, 136, NULL}, /* . */
+		/* idx  91 */ {6, 136, NULL}, /* . */
+		/* idx  92 */ {6, 146, NULL}, /* . */
+		/* idx  93 */ {6, 146, NULL}, /* . */
+		/* idx  94 */ {6, 146, NULL}, /* . */
+		/* idx  95 */ {6, 146, NULL}, /* . */
+		/* idx  96 */ {6, 154, NULL}, /* . */
+		/* idx  97 */ {6, 154, NULL}, /* . */
+		/* idx  98 */ {6, 154, NULL}, /* . */
+		/* idx  99 */ {6, 154, NULL}, /* . */
+		/* idx 100 */ {6, 156, NULL}, /* . */
+		/* idx 101 */ {6, 156, NULL}, /* . */
+		/* idx 102 */ {6, 156, NULL}, /* . */
+		/* idx 103 */ {6, 156, NULL}, /* . */
+		/* idx 104 */ {6, 160, NULL}, /* . */
+		/* idx 105 */ {6, 160, NULL}, /* . */
+		/* idx 106 */ {6, 160, NULL}, /* . */
+		/* idx 107 */ {6, 160, NULL}, /* . */
+		/* idx 108 */ {6, 163, NULL}, /* . */
+		/* idx 109 */ {6, 163, NULL}, /* . */
+		/* idx 110 */ {6, 163, NULL}, /* . */
+		/* idx 111 */ {6, 163, NULL}, /* . */
+		/* idx 112 */ {6, 164, NULL}, /* . */
+		/* idx 113 */ {6, 164, NULL}, /* . */
+		/* idx 114 */ {6, 164, NULL}, /* . */
+		/* idx 115 */ {6, 164, NULL}, /* . */
+		/* idx 116 */ {6, 169, NULL}, /* . */
+		/* idx 117 */ {6, 169, NULL}, /* . */
+		/* idx 118 */ {6, 169, NULL}, /* . */
+		/* idx 119 */ {6, 169, NULL}, /* . */
+		/* idx 120 */ {6, 170, NULL}, /* . */
+		/* idx 121 */ {6, 170, NULL}, /* . */
+		/* idx 122 */ {6, 170, NULL}, /* . */
+		/* idx 123 */ {6, 170, NULL}, /* . */
+		/* idx 124 */ {6, 173, NULL}, /* . */
+		/* idx 125 */ {6, 173, NULL}, /* . */
+		/* idx 126 */ {6, 173, NULL}, /* . */
+		/* idx 127 */ {6, 173, NULL}, /* . */
+		/* idx 128 */ {6, 176, NULL}, /* . */
+		/* idx 129 */ {6, 176, NULL}, /* . */
+		/* idx 130 */ {6, 176, NULL}, /* . */
+		/* idx 131 */ {6, 176, NULL}, /* . */
+		/* idx 132 */ {6, 181, NULL}, /* . */
+		/* idx 133 */ {6, 181, NULL}, /* . */
+		/* idx 134 */ {6, 181, NULL}, /* . */
+		/* idx 135 */ {6, 181, NULL}, /* . */
+		/* idx 136 */ {6, 185, NULL}, /* . */
+		/* idx 137 */ {6, 185, NULL}, /* . */
+		/* idx 138 */ {6, 185, NULL}, /* . */
+		/* idx 139 */ {6, 185, NULL}, /* . */
+		/* idx 140 */ {6, 186, NULL}, /* . */
+		/* idx 141 */ {6, 186, NULL}, /* . */
+		/* idx 142 */ {6, 186, NULL}, /* . */
+		/* idx 143 */ {6, 186, NULL}, /* . */
+		/* idx 144 */ {6, 187, NULL}, /* . */
+		/* idx 145 */ {6, 187, NULL}, /* . */
+		/* idx 146 */ {6, 187, NULL}, /* . */
+		/* idx 147 */ {6, 187, NULL}, /* . */
+		/* idx 148 */ {6, 189, NULL}, /* . */
+		/* idx 149 */ {6, 189, NULL}, /* . */
+		/* idx 150 */ {6, 189, NULL}, /* . */
+		/* idx 151 */ {6, 189, NULL}, /* . */
+		/* idx 152 */ {6, 190, NULL}, /* . */
+		/* idx 153 */ {6, 190, NULL}, /* . */
+		/* idx 154 */ {6, 190, NULL}, /* . */
+		/* idx 155 */ {6, 190, NULL}, /* . */
+		/* idx 156 */ {6, 196, NULL}, /* . */
+		/* idx 157 */ {6, 196, NULL}, /* . */
+		/* idx 158 */ {6, 196, NULL}, /* . */
+		/* idx 159 */ {6, 196, NULL}, /* . */
+		/* idx 160 */ {6, 198, NULL}, /* . */
+		/* idx 161 */ {6, 198, NULL}, /* . */
+		/* idx 162 */ {6, 198, NULL}, /* . */
+		/* idx 163 */ {6, 198, NULL}, /* . */
+		/* idx 164 */ {6, 228, NULL}, /* . */
+		/* idx 165 */ {6, 228, NULL}, /* . */
+		/* idx 166 */ {6, 228, NULL}, /* . */
+		/* idx 167 */ {6, 228, NULL}, /* . */
+		/* idx 168 */ {6, 232, NULL}, /* . */
+		/* idx 169 */ {6, 232, NULL}, /* . */
+		/* idx 170 */ {6, 232, NULL}, /* . */
+		/* idx 171 */ {6, 232, NULL}, /* . */
+		/* idx 172 */ {6, 233, NULL}, /* . */
+		/* idx 173 */ {6, 233, NULL}, /* . */
+		/* idx 174 */ {6, 233, NULL}, /* . */
+		/* idx 175 */ {6, 233, NULL}, /* . */
+		/* idx 176 */ {7,   1, NULL},
+		/* idx 177 */ {7,   1, NULL},
+		/* idx 178 */ {7, 135, NULL},
+		/* idx 179 */ {7, 135, NULL},
+		/* idx 180 */ {7, 137, NULL},
+		/* idx 181 */ {7, 137, NULL},
+		/* idx 182 */ {7, 138, NULL},
+		/* idx 183 */ {7, 138, NULL},
+		/* idx 184 */ {7, 139, NULL},
+		/* idx 185 */ {7, 139, NULL},
+		/* idx 186 */ {7, 140, NULL},
+		/* idx 187 */ {7, 140, NULL},
+		/* idx 188 */ {7, 141, NULL},
+		/* idx 189 */ {7, 141, NULL},
+		/* idx 190 */ {7, 143, NULL},
+		/* idx 191 */ {7, 143, NULL},
+		/* idx 192 */ {7, 147, NULL},
+		/* idx 193 */ {7, 147, NULL},
+		/* idx 194 */ {7, 149, NULL},
+		/* idx 195 */ {7, 149, NULL},
+		/* idx 196 */ {7, 150, NULL},
+		/* idx 197 */ {7, 150, NULL},
+		/* idx 198 */ {7, 151, NULL},
+		/* idx 199 */ {7, 151, NULL},
+		/* idx 200 */ {7, 152, NULL},
+		/* idx 201 */ {7, 152, NULL},
+		/* idx 202 */ {7, 155, NULL},
+		/* idx 203 */ {7, 155, NULL},
+		/* idx 204 */ {7, 157, NULL},
+		/* idx 205 */ {7, 157, NULL},
+		/* idx 206 */ {7, 158, NULL},
+		/* idx 207 */ {7, 158, NULL},
+		/* idx 208 */ {7, 165, NULL},
+		/* idx 209 */ {7, 165, NULL},
+		/* idx 210 */ {7, 166, NULL},
+		/* idx 211 */ {7, 166, NULL},
+		/* idx 212 */ {7, 168, NULL},
+		/* idx 213 */ {7, 168, NULL},
+		/* idx 214 */ {7, 174, NULL},
+		/* idx 215 */ {7, 174, NULL},
+		/* idx 216 */ {7, 175, NULL},
+		/* idx 217 */ {7, 175, NULL},
+		/* idx 218 */ {7, 180, NULL},
+		/* idx 219 */ {7, 180, NULL},
+		/* idx 220 */ {7, 182, NULL},
+		/* idx 221 */ {7, 182, NULL},
+		/* idx 222 */ {7, 183, NULL},
+		/* idx 223 */ {7, 183, NULL},
+		/* idx 224 */ {7, 188, NULL},
+		/* idx 225 */ {7, 188, NULL},
+		/* idx 226 */ {7, 191, NULL},
+		/* idx 227 */ {7, 191, NULL},
+		/* idx 228 */ {7, 197, NULL},
+		/* idx 229 */ {7, 197, NULL},
+		/* idx 230 */ {7, 231, NULL},
+		/* idx 231 */ {7, 231, NULL},
+		/* idx 232 */ {7, 239, NULL},
+		/* idx 233 */ {7, 239, NULL},
+		/* idx 234 */ {8,   9, NULL},
+		/* idx 235 */ {8, 142, NULL},
+		/* idx 236 */ {8, 144, NULL},
+		/* idx 237 */ {8, 145, NULL},
+		/* idx 238 */ {8, 148, NULL},
+		/* idx 239 */ {8, 159, NULL},
+		/* idx 240 */ {8, 171, NULL},
+		/* idx 241 */ {8, 206, NULL},
+		/* idx 242 */ {8, 215, NULL},
+		/* idx 243 */ {8, 225, NULL},
+		/* idx 244 */ {8, 236, NULL},
+		/* idx 245 */ {8, 237, NULL},
+		/* idx 246 */ {8,   0, &byte3_pref0110 },
+		/* idx 247 */ {8,   0, &byte3_pref0111 },
+		/* idx 248 */ {8,   0, &byte3_pref1000 },
+		/* idx 249 */ {8,   0, &byte3_pref1001 },
+		/* idx 250 */ {8,   0, &byte3_pref1010 },
+		/* idx 251 */ {8,   0, &byte3_pref1011 },
+		/* idx 252 */ {8,   0, &byte3_pref1100 },
+		/* idx 253 */ {8,   0, &byte3_pref1101 },
+		/* idx 254 */ {8,   0, &byte3_pref1110 },
+		/* idx 255 */ {8,   0, &byte3_pref1111 }
+	}
+};
+
+
+static struct stbl byte1_pref0 = {
+	2,
+	{
+	/* idx   0 */ {2, 33, NULL}, /* '!' */
+	/* idx   1 */ {2, 34, NULL}, /* '"' */
+	/* idx   2 */ {2, 40, NULL}, /* '(' */
+	/* idx   3 */ {2, 41, NULL}, /* ')' */
+	}
+};
+
+static struct stbl byte1_pref1 = {
+	8,
+	{
+		/* idx   0 */ {2,  63, NULL}, /* '?' */
+		/* idx   1 */ {2,  63, NULL}, /* '?' */
+		/* idx   2 */ {2,  63, NULL}, /* '?' */
+		/* idx   3 */ {2,  63, NULL}, /* '?' */
+		/* idx   4 */ {2,  63, NULL}, /* '?' */
+		/* idx   5 */ {2,  63, NULL}, /* '?' */
+		/* idx   6 */ {2,  63, NULL}, /* '?' */
+		/* idx   7 */ {2,  63, NULL}, /* '?' */
+		/* idx   8 */ {2,  63, NULL}, /* '?' */
+		/* idx   9 */ {2,  63, NULL}, /* '?' */
+		/* idx  10 */ {2,  63, NULL}, /* '?' */
+		/* idx  11 */ {2,  63, NULL}, /* '?' */
+		/* idx  12 */ {2,  63, NULL}, /* '?' */
+		/* idx  13 */ {2,  63, NULL}, /* '?' */
+		/* idx  14 */ {2,  63, NULL}, /* '?' */
+		/* idx  15 */ {2,  63, NULL}, /* '?' */
+		/* idx  16 */ {2,  63, NULL}, /* '?' */
+		/* idx  17 */ {2,  63, NULL}, /* '?' */
+		/* idx  18 */ {2,  63, NULL}, /* '?' */
+		/* idx  19 */ {2,  63, NULL}, /* '?' */
+		/* idx  20 */ {2,  63, NULL}, /* '?' */
+		/* idx  21 */ {2,  63, NULL}, /* '?' */
+		/* idx  22 */ {2,  63, NULL}, /* '?' */
+		/* idx  23 */ {2,  63, NULL}, /* '?' */
+		/* idx  24 */ {2,  63, NULL}, /* '?' */
+		/* idx  25 */ {2,  63, NULL}, /* '?' */
+		/* idx  26 */ {2,  63, NULL}, /* '?' */
+		/* idx  27 */ {2,  63, NULL}, /* '?' */
+		/* idx  28 */ {2,  63, NULL}, /* '?' */
+		/* idx  29 */ {2,  63, NULL}, /* '?' */
+		/* idx  30 */ {2,  63, NULL}, /* '?' */
+		/* idx  31 */ {2,  63, NULL}, /* '?' */
+		/* idx  32 */ {2,  63, NULL}, /* '?' */
+		/* idx  33 */ {2,  63, NULL}, /* '?' */
+		/* idx  34 */ {2,  63, NULL}, /* '?' */
+		/* idx  35 */ {2,  63, NULL}, /* '?' */
+		/* idx  36 */ {2,  63, NULL}, /* '?' */
+		/* idx  37 */ {2,  63, NULL}, /* '?' */
+		/* idx  38 */ {2,  63, NULL}, /* '?' */
+		/* idx  39 */ {2,  63, NULL}, /* '?' */
+		/* idx  40 */ {2,  63, NULL}, /* '?' */
+		/* idx  41 */ {2,  63, NULL}, /* '?' */
+		/* idx  42 */ {2,  63, NULL}, /* '?' */
+		/* idx  43 */ {2,  63, NULL}, /* '?' */
+		/* idx  44 */ {2,  63, NULL}, /* '?' */
+		/* idx  45 */ {2,  63, NULL}, /* '?' */
+		/* idx  46 */ {2,  63, NULL}, /* '?' */
+		/* idx  47 */ {2,  63, NULL}, /* '?' */
+		/* idx  48 */ {2,  63, NULL}, /* '?' */
+		/* idx  49 */ {2,  63, NULL}, /* '?' */
+		/* idx  50 */ {2,  63, NULL}, /* '?' */
+		/* idx  51 */ {2,  63, NULL}, /* '?' */
+		/* idx  52 */ {2,  63, NULL}, /* '?' */
+		/* idx  53 */ {2,  63, NULL}, /* '?' */
+		/* idx  54 */ {2,  63, NULL}, /* '?' */
+		/* idx  55 */ {2,  63, NULL}, /* '?' */
+		/* idx  56 */ {2,  63, NULL}, /* '?' */
+		/* idx  57 */ {2,  63, NULL}, /* '?' */
+		/* idx  58 */ {2,  63, NULL}, /* '?' */
+		/* idx  59 */ {2,  63, NULL}, /* '?' */
+		/* idx  60 */ {2,  63, NULL}, /* '?' */
+		/* idx  61 */ {2,  63, NULL}, /* '?' */
+		/* idx  62 */ {2,  63, NULL}, /* '?' */
+		/* idx  63 */ {2,  63, NULL}, /* '?' */
+		/* idx  64 */ {3,  39, NULL}, /* ''' */
+		/* idx  65 */ {3,  39, NULL}, /* ''' */
+		/* idx  66 */ {3,  39, NULL}, /* ''' */
+		/* idx  67 */ {3,  39, NULL}, /* ''' */
+		/* idx  68 */ {3,  39, NULL}, /* ''' */
+		/* idx  69 */ {3,  39, NULL}, /* ''' */
+		/* idx  70 */ {3,  39, NULL}, /* ''' */
+		/* idx  71 */ {3,  39, NULL}, /* ''' */
+		/* idx  72 */ {3,  39, NULL}, /* ''' */
+		/* idx  73 */ {3,  39, NULL}, /* ''' */
+		/* idx  74 */ {3,  39, NULL}, /* ''' */
+		/* idx  75 */ {3,  39, NULL}, /* ''' */
+		/* idx  76 */ {3,  39, NULL}, /* ''' */
+		/* idx  77 */ {3,  39, NULL}, /* ''' */
+		/* idx  78 */ {3,  39, NULL}, /* ''' */
+		/* idx  79 */ {3,  39, NULL}, /* ''' */
+		/* idx  80 */ {3,  39, NULL}, /* ''' */
+		/* idx  81 */ {3,  39, NULL}, /* ''' */
+		/* idx  82 */ {3,  39, NULL}, /* ''' */
+		/* idx  83 */ {3,  39, NULL}, /* ''' */
+		/* idx  84 */ {3,  39, NULL}, /* ''' */
+		/* idx  85 */ {3,  39, NULL}, /* ''' */
+		/* idx  86 */ {3,  39, NULL}, /* ''' */
+		/* idx  87 */ {3,  39, NULL}, /* ''' */
+		/* idx  88 */ {3,  39, NULL}, /* ''' */
+		/* idx  89 */ {3,  39, NULL}, /* ''' */
+		/* idx  90 */ {3,  39, NULL}, /* ''' */
+		/* idx  91 */ {3,  39, NULL}, /* ''' */
+		/* idx  92 */ {3,  39, NULL}, /* ''' */
+		/* idx  93 */ {3,  39, NULL}, /* ''' */
+		/* idx  94 */ {3,  39, NULL}, /* ''' */
+		/* idx  95 */ {3,  39, NULL}, /* ''' */
+		/* idx  96 */ {3,  43, NULL}, /* '+' */
+		/* idx  97 */ {3,  43, NULL}, /* '+' */
+		/* idx  98 */ {3,  43, NULL}, /* '+' */
+		/* idx  99 */ {3,  43, NULL}, /* '+' */
+		/* idx 100 */ {3,  43, NULL}, /* '+' */
+		/* idx 101 */ {3,  43, NULL}, /* '+' */
+		/* idx 102 */ {3,  43, NULL}, /* '+' */
+		/* idx 103 */ {3,  43, NULL}, /* '+' */
+		/* idx 104 */ {3,  43, NULL}, /* '+' */
+		/* idx 105 */ {3,  43, NULL}, /* '+' */
+		/* idx 106 */ {3,  43, NULL}, /* '+' */
+		/* idx 107 */ {3,  43, NULL}, /* '+' */
+		/* idx 108 */ {3,  43, NULL}, /* '+' */
+		/* idx 109 */ {3,  43, NULL}, /* '+' */
+		/* idx 110 */ {3,  43, NULL}, /* '+' */
+		/* idx 111 */ {3,  43, NULL}, /* '+' */
+		/* idx 112 */ {3,  43, NULL}, /* '+' */
+		/* idx 113 */ {3,  43, NULL}, /* '+' */
+		/* idx 114 */ {3,  43, NULL}, /* '+' */
+		/* idx 115 */ {3,  43, NULL}, /* '+' */
+		/* idx 116 */ {3,  43, NULL}, /* '+' */
+		/* idx 117 */ {3,  43, NULL}, /* '+' */
+		/* idx 118 */ {3,  43, NULL}, /* '+' */
+		/* idx 119 */ {3,  43, NULL}, /* '+' */
+		/* idx 120 */ {3,  43, NULL}, /* '+' */
+		/* idx 121 */ {3,  43, NULL}, /* '+' */
+		/* idx 122 */ {3,  43, NULL}, /* '+' */
+		/* idx 123 */ {3,  43, NULL}, /* '+' */
+		/* idx 124 */ {3,  43, NULL}, /* '+' */
+		/* idx 125 */ {3,  43, NULL}, /* '+' */
+		/* idx 126 */ {3,  43, NULL}, /* '+' */
+		/* idx 127 */ {3,  43, NULL}, /* '+' */
+		/* idx 128 */ {3, 124, NULL}, /* '|' */
+		/* idx 129 */ {3, 124, NULL}, /* '|' */
+		/* idx 130 */ {3, 124, NULL}, /* '|' */
+		/* idx 131 */ {3, 124, NULL}, /* '|' */
+		/* idx 132 */ {3, 124, NULL}, /* '|' */
+		/* idx 133 */ {3, 124, NULL}, /* '|' */
+		/* idx 134 */ {3, 124, NULL}, /* '|' */
+		/* idx 135 */ {3, 124, NULL}, /* '|' */
+		/* idx 136 */ {3, 124, NULL}, /* '|' */
+		/* idx 137 */ {3, 124, NULL}, /* '|' */
+		/* idx 138 */ {3, 124, NULL}, /* '|' */
+		/* idx 139 */ {3, 124, NULL}, /* '|' */
+		/* idx 140 */ {3, 124, NULL}, /* '|' */
+		/* idx 141 */ {3, 124, NULL}, /* '|' */
+		/* idx 142 */ {3, 124, NULL}, /* '|' */
+		/* idx 143 */ {3, 124, NULL}, /* '|' */
+		/* idx 144 */ {3, 124, NULL}, /* '|' */
+		/* idx 145 */ {3, 124, NULL}, /* '|' */
+		/* idx 146 */ {3, 124, NULL}, /* '|' */
+		/* idx 147 */ {3, 124, NULL}, /* '|' */
+		/* idx 148 */ {3, 124, NULL}, /* '|' */
+		/* idx 149 */ {3, 124, NULL}, /* '|' */
+		/* idx 150 */ {3, 124, NULL}, /* '|' */
+		/* idx 151 */ {3, 124, NULL}, /* '|' */
+		/* idx 152 */ {3, 124, NULL}, /* '|' */
+		/* idx 153 */ {3, 124, NULL}, /* '|' */
+		/* idx 154 */ {3, 124, NULL}, /* '|' */
+		/* idx 155 */ {3, 124, NULL}, /* '|' */
+		/* idx 156 */ {3, 124, NULL}, /* '|' */
+		/* idx 157 */ {3, 124, NULL}, /* '|' */
+		/* idx 158 */ {3, 124, NULL}, /* '|' */
+		/* idx 159 */ {3, 124, NULL}, /* '|' */
+		/* idx 160 */ {4,  35, NULL}, /* '#' */
+		/* idx 161 */ {4,  35, NULL}, /* '#' */
+		/* idx 162 */ {4,  35, NULL}, /* '#' */
+		/* idx 163 */ {4,  35, NULL}, /* '#' */
+		/* idx 164 */ {4,  35, NULL}, /* '#' */
+		/* idx 165 */ {4,  35, NULL}, /* '#' */
+		/* idx 166 */ {4,  35, NULL}, /* '#' */
+		/* idx 167 */ {4,  35, NULL}, /* '#' */
+		/* idx 168 */ {4,  35, NULL}, /* '#' */
+		/* idx 169 */ {4,  35, NULL}, /* '#' */
+		/* idx 170 */ {4,  35, NULL}, /* '#' */
+		/* idx 171 */ {4,  35, NULL}, /* '#' */
+		/* idx 172 */ {4,  35, NULL}, /* '#' */
+		/* idx 173 */ {4,  35, NULL}, /* '#' */
+		/* idx 174 */ {4,  35, NULL}, /* '#' */
+		/* idx 175 */ {4,  35, NULL}, /* '#' */
+		/* idx 176 */ {4,  62, NULL}, /* '>' */
+		/* idx 177 */ {4,  62, NULL}, /* '>' */
+		/* idx 178 */ {4,  62, NULL}, /* '>' */
+		/* idx 179 */ {4,  62, NULL}, /* '>' */
+		/* idx 180 */ {4,  62, NULL}, /* '>' */
+		/* idx 181 */ {4,  62, NULL}, /* '>' */
+		/* idx 182 */ {4,  62, NULL}, /* '>' */
+		/* idx 183 */ {4,  62, NULL}, /* '>' */
+		/* idx 184 */ {4,  62, NULL}, /* '>' */
+		/* idx 185 */ {4,  62, NULL}, /* '>' */
+		/* idx 186 */ {4,  62, NULL}, /* '>' */
+		/* idx 187 */ {4,  62, NULL}, /* '>' */
+		/* idx 188 */ {4,  62, NULL}, /* '>' */
+		/* idx 189 */ {4,  62, NULL}, /* '>' */
+		/* idx 190 */ {4,  62, NULL}, /* '>' */
+		/* idx 191 */ {4,  62, NULL}, /* '>' */
+		/* idx 192 */ {5,   0, NULL}, /* . */
+		/* idx 193 */ {5,   0, NULL}, /* . */
+		/* idx 194 */ {5,   0, NULL}, /* . */
+		/* idx 195 */ {5,   0, NULL}, /* . */
+		/* idx 196 */ {5,   0, NULL}, /* . */
+		/* idx 197 */ {5,   0, NULL}, /* . */
+		/* idx 198 */ {5,   0, NULL}, /* . */
+		/* idx 199 */ {5,   0, NULL}, /* . */
+		/* idx 200 */ {5,  36, NULL}, /* $ */
+		/* idx 201 */ {5,  36, NULL}, /* $ */
+		/* idx 202 */ {5,  36, NULL}, /* $ */
+		/* idx 203 */ {5,  36, NULL}, /* $ */
+		/* idx 204 */ {5,  36, NULL}, /* $ */
+		/* idx 205 */ {5,  36, NULL}, /* $ */
+		/* idx 206 */ {5,  36, NULL}, /* $ */
+		/* idx 207 */ {5,  36, NULL}, /* $ */
+		/* idx 208 */ {5,  64, NULL}, /* '@' */
+		/* idx 209 */ {5,  64, NULL}, /* '@' */
+		/* idx 210 */ {5,  64, NULL}, /* '@' */
+		/* idx 211 */ {5,  64, NULL}, /* '@' */
+		/* idx 212 */ {5,  64, NULL}, /* '@' */
+		/* idx 213 */ {5,  64, NULL}, /* '@' */
+		/* idx 214 */ {5,  64, NULL}, /* '@' */
+		/* idx 215 */ {5,  64, NULL}, /* '@' */
+		/* idx 216 */ {5,  91, NULL}, /* '[' */
+		/* idx 217 */ {5,  91, NULL}, /* '[' */
+		/* idx 218 */ {5,  91, NULL}, /* '[' */
+		/* idx 219 */ {5,  91, NULL}, /* '[' */
+		/* idx 220 */ {5,  91, NULL}, /* '[' */
+		/* idx 221 */ {5,  91, NULL}, /* '[' */
+		/* idx 222 */ {5,  91, NULL}, /* '[' */
+		/* idx 223 */ {5,  91, NULL}, /* '[' */
+		/* idx 224 */ {5,  93, NULL}, /* ']' */
+		/* idx 225 */ {5,  93, NULL}, /* ']' */
+		/* idx 226 */ {5,  93, NULL}, /* ']' */
+		/* idx 227 */ {5,  93, NULL}, /* ']' */
+		/* idx 228 */ {5,  93, NULL}, /* ']' */
+		/* idx 229 */ {5,  93, NULL}, /* ']' */
+		/* idx 230 */ {5,  93, NULL}, /* ']' */
+		/* idx 231 */ {5,  93, NULL}, /* ']' */
+		/* idx 232 */ {5, 126, NULL}, /* '~' */
+		/* idx 233 */ {5, 126, NULL}, /* '~' */
+		/* idx 234 */ {5, 126, NULL}, /* '~' */
+		/* idx 235 */ {5, 126, NULL}, /* '~' */
+		/* idx 236 */ {5, 126, NULL}, /* '~' */
+		/* idx 237 */ {5, 126, NULL}, /* '~' */
+		/* idx 238 */ {5, 126, NULL}, /* '~' */
+		/* idx 239 */ {5, 126, NULL}, /* '~' */
+		/* idx 240 */ {6,  94, NULL}, /* '^' */
+		/* idx 241 */ {6,  94, NULL}, /* '^' */
+		/* idx 242 */ {6,  94, NULL}, /* '^' */
+		/* idx 243 */ {6,  94, NULL}, /* '^' */
+		/* idx 244 */ {6, 125, NULL}, /* '}' */
+		/* idx 245 */ {6, 125, NULL}, /* '}' */
+		/* idx 246 */ {6, 125, NULL}, /* '}' */
+		/* idx 247 */ {6, 125, NULL}, /* '}' */
+		/* idx 248 */ {7,  60, NULL}, /* '<' */
+		/* idx 249 */ {7,  60, NULL}, /* '<' */
+		/* idx 250 */ {7,  96, NULL}, /* '`' */
+		/* idx 251 */ {7,  96, NULL}, /* '`' */
+		/* idx 252 */ {7, 123, NULL}, /* '{' */
+		/* idx 253 */ {7, 123, NULL}, /* '{' */
+		/* idx 254 */ {8,   0, &byte2_pref0}, /* escape */
+		/* idx 255 */ {8,   0, &byte2_pref1} /* escape */
+	}
+};
+
+
+static struct stbl byte0 = {
+	8,
+	{
+		/* idx   0 */ {5,  48, NULL}, /* '0' */
+		/* idx   1 */ {5,  48, NULL}, /* '0' */
+		/* idx   2 */ {5,  48, NULL}, /* '0' */
+		/* idx   3 */ {5,  48, NULL}, /* '0' */
+		/* idx   4 */ {5,  48, NULL}, /* '0' */
+		/* idx   5 */ {5,  48, NULL}, /* '0' */
+		/* idx   6 */ {5,  48, NULL}, /* '0' */
+		/* idx   7 */ {5,  48, NULL}, /* '0' */
+		/* idx   8 */ {5,  49, NULL}, /* '1' */
+		/* idx   9 */ {5,  49, NULL}, /* '1' */
+		/* idx  10 */ {5,  49, NULL}, /* '1' */
+		/* idx  11 */ {5,  49, NULL}, /* '1' */
+		/* idx  12 */ {5,  49, NULL}, /* '1' */
+		/* idx  13 */ {5,  49, NULL}, /* '1' */
+		/* idx  14 */ {5,  49, NULL}, /* '1' */
+		/* idx  15 */ {5,  49, NULL}, /* '1' */
+		/* idx  16 */ {5,  50, NULL}, /* '2' */
+		/* idx  17 */ {5,  50, NULL}, /* '2' */
+		/* idx  18 */ {5,  50, NULL}, /* '2' */
+		/* idx  19 */ {5,  50, NULL}, /* '2' */
+		/* idx  20 */ {5,  50, NULL}, /* '2' */
+		/* idx  21 */ {5,  50, NULL}, /* '2' */
+		/* idx  22 */ {5,  50, NULL}, /* '2' */
+		/* idx  23 */ {5,  50, NULL}, /* '2' */
+		/* idx  24 */ {5,  97, NULL}, /* 'a' */
+		/* idx  25 */ {5,  97, NULL}, /* 'a' */
+		/* idx  26 */ {5,  97, NULL}, /* 'a' */
+		/* idx  27 */ {5,  97, NULL}, /* 'a' */
+		/* idx  28 */ {5,  97, NULL}, /* 'a' */
+		/* idx  29 */ {5,  97, NULL}, /* 'a' */
+		/* idx  30 */ {5,  97, NULL}, /* 'a' */
+		/* idx  31 */ {5,  97, NULL}, /* 'a' */
+		/* idx  32 */ {5,  99, NULL}, /* 'c' */
+		/* idx  33 */ {5,  99, NULL}, /* 'c' */
+		/* idx  34 */ {5,  99, NULL}, /* 'c' */
+		/* idx  35 */ {5,  99, NULL}, /* 'c' */
+		/* idx  36 */ {5,  99, NULL}, /* 'c' */
+		/* idx  37 */ {5,  99, NULL}, /* 'c' */
+		/* idx  38 */ {5,  99, NULL}, /* 'c' */
+		/* idx  39 */ {5,  99, NULL}, /* 'c' */
+		/* idx  40 */ {5, 101, NULL}, /* 'e' */
+		/* idx  41 */ {5, 101, NULL}, /* 'e' */
+		/* idx  42 */ {5, 101, NULL}, /* 'e' */
+		/* idx  43 */ {5, 101, NULL}, /* 'e' */
+		/* idx  44 */ {5, 101, NULL}, /* 'e' */
+		/* idx  45 */ {5, 101, NULL}, /* 'e' */
+		/* idx  46 */ {5, 101, NULL}, /* 'e' */
+		/* idx  47 */ {5, 101, NULL}, /* 'e' */
+		/* idx  48 */ {5, 105, NULL}, /* 'i' */
+		/* idx  49 */ {5, 105, NULL}, /* 'i' */
+		/* idx  50 */ {5, 105, NULL}, /* 'i' */
+		/* idx  51 */ {5, 105, NULL}, /* 'i' */
+		/* idx  52 */ {5, 105, NULL}, /* 'i' */
+		/* idx  53 */ {5, 105, NULL}, /* 'i' */
+		/* idx  54 */ {5, 105, NULL}, /* 'i' */
+		/* idx  55 */ {5, 105, NULL}, /* 'i' */
+		/* idx  56 */ {5, 111, NULL}, /* 'o' */
+		/* idx  57 */ {5, 111, NULL}, /* 'o' */
+		/* idx  58 */ {5, 111, NULL}, /* 'o' */
+		/* idx  59 */ {5, 111, NULL}, /* 'o' */
+		/* idx  60 */ {5, 111, NULL}, /* 'o' */
+		/* idx  61 */ {5, 111, NULL}, /* 'o' */
+		/* idx  62 */ {5, 111, NULL}, /* 'o' */
+		/* idx  63 */ {5, 111, NULL}, /* 'o' */
+		/* idx  64 */ {5, 115, NULL}, /* 's' */
+		/* idx  65 */ {5, 115, NULL}, /* 's' */
+		/* idx  66 */ {5, 115, NULL}, /* 's' */
+		/* idx  67 */ {5, 115, NULL}, /* 's' */
+		/* idx  68 */ {5, 115, NULL}, /* 's' */
+		/* idx  69 */ {5, 115, NULL}, /* 's' */
+		/* idx  70 */ {5, 115, NULL}, /* 's' */
+		/* idx  71 */ {5, 115, NULL}, /* 's' */
+		/* idx  72 */ {5, 116, NULL}, /* 't' */
+		/* idx  73 */ {5, 116, NULL}, /* 't' */
+		/* idx  74 */ {5, 116, NULL}, /* 't' */
+		/* idx  75 */ {5, 116, NULL}, /* 't' */
+		/* idx  76 */ {5, 116, NULL}, /* 't' */
+		/* idx  77 */ {5, 116, NULL}, /* 't' */
+		/* idx  78 */ {5, 116, NULL}, /* 't' */
+		/* idx  79 */ {5, 116, NULL}, /* 't' */
+		/* idx  80 */ {6,  32, NULL}, /* tab */
+		/* idx  81 */ {6,  32, NULL}, /* tab */
+		/* idx  82 */ {6,  32, NULL}, /* tab */
+		/* idx  83 */ {6,  32, NULL}, /* tab */
+		/* idx  84 */ {6,  37, NULL}, /* '%' */
+		/* idx  85 */ {6,  37, NULL}, /* '%' */
+		/* idx  86 */ {6,  37, NULL}, /* '%' */
+		/* idx  87 */ {6,  37, NULL}, /* '%' */
+		/* idx  88 */ {6,  45, NULL}, /* '-' */
+		/* idx  89 */ {6,  45, NULL}, /* '-' */
+		/* idx  90 */ {6,  45, NULL}, /* '-' */
+		/* idx  91 */ {6,  45, NULL}, /* '-' */
+		/* idx  92 */ {6,  46, NULL}, /* '.' */
+		/* idx  93 */ {6,  46, NULL}, /* '.' */
+		/* idx  94 */ {6,  46, NULL}, /* '.' */
+		/* idx  95 */ {6,  46, NULL}, /* '.' */
+		/* idx  96 */ {6,  47, NULL}, /* '/' */
+		/* idx  97 */ {6,  47, NULL}, /* '/' */
+		/* idx  98 */ {6,  47, NULL}, /* '/' */
+		/* idx  99 */ {6,  47, NULL}, /* '/' */
+		/* idx 100 */ {6,  51, NULL}, /* '3' */
+		/* idx 101 */ {6,  51, NULL}, /* '3' */
+		/* idx 102 */ {6,  51, NULL}, /* '3' */
+		/* idx 103 */ {6,  51, NULL}, /* '3' */
+		/* idx 104 */ {6,  52, NULL}, /* '4' */
+		/* idx 105 */ {6,  52, NULL}, /* '4' */
+		/* idx 106 */ {6,  52, NULL}, /* '4' */
+		/* idx 107 */ {6,  52, NULL}, /* '4' */
+		/* idx 108 */ {6,  53, NULL}, /* '5' */
+		/* idx 109 */ {6,  53, NULL}, /* '5' */
+		/* idx 110 */ {6,  53, NULL}, /* '5' */
+		/* idx 111 */ {6,  53, NULL}, /* '5' */
+		/* idx 112 */ {6,  54, NULL}, /* '6' */
+		/* idx 113 */ {6,  54, NULL}, /* '6' */
+		/* idx 114 */ {6,  54, NULL}, /* '6' */
+		/* idx 115 */ {6,  54, NULL}, /* '6' */
+		/* idx 116 */ {6,  55, NULL}, /* '7' */
+		/* idx 117 */ {6,  55, NULL}, /* '7' */
+		/* idx 118 */ {6,  55, NULL}, /* '7' */
+		/* idx 119 */ {6,  55, NULL}, /* '7' */
+		/* idx 120 */ {6,  56, NULL}, /* '8' */
+		/* idx 121 */ {6,  56, NULL}, /* '8' */
+		/* idx 122 */ {6,  56, NULL}, /* '8' */
+		/* idx 123 */ {6,  56, NULL}, /* '8' */
+		/* idx 124 */ {6,  57, NULL}, /* '9' */
+		/* idx 125 */ {6,  57, NULL}, /* '9' */
+		/* idx 126 */ {6,  57, NULL}, /* '9' */
+		/* idx 127 */ {6,  57, NULL}, /* '9' */
+		/* idx 128 */ {6,  61, NULL}, /* '=' */
+		/* idx 129 */ {6,  61, NULL}, /* '=' */
+		/* idx 130 */ {6,  61, NULL}, /* '=' */
+		/* idx 131 */ {6,  61, NULL}, /* '=' */
+		/* idx 132 */ {6,  65, NULL}, /* 'A' */
+		/* idx 133 */ {6,  65, NULL}, /* 'A' */
+		/* idx 134 */ {6,  65, NULL}, /* 'A' */
+		/* idx 135 */ {6,  65, NULL}, /* 'A' */
+		/* idx 136 */ {6,  95, NULL}, /* '_' */
+		/* idx 137 */ {6,  95, NULL}, /* '_' */
+		/* idx 138 */ {6,  95, NULL}, /* '_' */
+		/* idx 139 */ {6,  95, NULL}, /* '_' */
+		/* idx 140 */ {6,  98, NULL}, /* 'b' */
+		/* idx 141 */ {6,  98, NULL}, /* 'b' */
+		/* idx 142 */ {6,  98, NULL}, /* 'b' */
+		/* idx 143 */ {6,  98, NULL}, /* 'b' */
+		/* idx 144 */ {6, 100, NULL}, /* 'd' */
+		/* idx 145 */ {6, 100, NULL}, /* 'd' */
+		/* idx 146 */ {6, 100, NULL}, /* 'd' */
+		/* idx 147 */ {6, 100, NULL}, /* 'd' */
+		/* idx 148 */ {6, 102, NULL}, /* 'f' */
+		/* idx 149 */ {6, 102, NULL}, /* 'f' */
+		/* idx 150 */ {6, 102, NULL}, /* 'f' */
+		/* idx 151 */ {6, 102, NULL}, /* 'f' */
+		/* idx 152 */ {6, 103, NULL}, /* 'g' */
+		/* idx 153 */ {6, 103, NULL}, /* 'g' */
+		/* idx 154 */ {6, 103, NULL}, /* 'g' */
+		/* idx 155 */ {6, 103, NULL}, /* 'g' */
+		/* idx 156 */ {6, 104, NULL}, /* 'h' */
+		/* idx 157 */ {6, 104, NULL}, /* 'h' */
+		/* idx 158 */ {6, 104, NULL}, /* 'h' */
+		/* idx 159 */ {6, 104, NULL}, /* 'h' */
+		/* idx 160 */ {6, 108, NULL}, /* 'l' */
+		/* idx 161 */ {6, 108, NULL}, /* 'l' */
+		/* idx 162 */ {6, 108, NULL}, /* 'l' */
+		/* idx 163 */ {6, 108, NULL}, /* 'l' */
+		/* idx 164 */ {6, 109, NULL}, /* 'm' */
+		/* idx 165 */ {6, 109, NULL}, /* 'm' */
+		/* idx 166 */ {6, 109, NULL}, /* 'm' */
+		/* idx 167 */ {6, 109, NULL}, /* 'm' */
+		/* idx 168 */ {6, 110, NULL}, /* 'n' */
+		/* idx 169 */ {6, 110, NULL}, /* 'n' */
+		/* idx 170 */ {6, 110, NULL}, /* 'n' */
+		/* idx 171 */ {6, 110, NULL}, /* 'n' */
+		/* idx 172 */ {6, 112, NULL}, /* 'p' */
+		/* idx 173 */ {6, 112, NULL}, /* 'p' */
+		/* idx 174 */ {6, 112, NULL}, /* 'p' */
+		/* idx 175 */ {6, 112, NULL}, /* 'p' */
+		/* idx 176 */ {6, 114, NULL}, /* 'r' */
+		/* idx 177 */ {6, 114, NULL}, /* 'r' */
+		/* idx 178 */ {6, 114, NULL}, /* 'r' */
+		/* idx 179 */ {6, 114, NULL}, /* 'r' */
+		/* idx 180 */ {6, 117, NULL}, /* 'u' */
+		/* idx 181 */ {6, 117, NULL}, /* 'u' */
+		/* idx 182 */ {6, 117, NULL}, /* 'u' */
+		/* idx 183 */ {6, 117, NULL}, /* 'u' */
+		/* idx 184 */ {7,  58, NULL}, /* ':' */
+		/* idx 185 */ {7,  58, NULL}, /* ':' */
+		/* idx 186 */ {7,  66, NULL}, /* 'B' */
+		/* idx 187 */ {7,  66, NULL}, /* 'B' */
+		/* idx 188 */ {7,  67, NULL}, /* 'C' */
+		/* idx 189 */ {7,  67, NULL}, /* 'C' */
+		/* idx 190 */ {7,  68, NULL}, /* 'D' */
+		/* idx 191 */ {7,  68, NULL}, /* 'D' */
+		/* idx 192 */ {7,  69, NULL}, /* 'E' */
+		/* idx 193 */ {7,  69, NULL}, /* 'E' */
+		/* idx 194 */ {7,  70, NULL}, /* 'F' */
+		/* idx 195 */ {7,  70, NULL}, /* 'F' */
+		/* idx 196 */ {7,  71, NULL}, /* 'G' */
+		/* idx 197 */ {7,  71, NULL}, /* 'G' */
+		/* idx 198 */ {7,  72, NULL}, /* 'H' */
+		/* idx 199 */ {7,  72, NULL}, /* 'H' */
+		/* idx 200 */ {7,  73, NULL}, /* 'I' */
+		/* idx 201 */ {7,  73, NULL}, /* 'I' */
+		/* idx 202 */ {7,  74, NULL}, /* 'J' */
+		/* idx 203 */ {7,  74, NULL}, /* 'J' */
+		/* idx 204 */ {7,  75, NULL}, /* 'K' */
+		/* idx 205 */ {7,  75, NULL}, /* 'K' */
+		/* idx 206 */ {7,  76, NULL}, /* 'L' */
+		/* idx 207 */ {7,  76, NULL}, /* 'L' */
+		/* idx 208 */ {7,  77, NULL}, /* 'M' */
+		/* idx 209 */ {7,  77, NULL}, /* 'M' */
+		/* idx 210 */ {7,  78, NULL}, /* 'N' */
+		/* idx 211 */ {7,  78, NULL}, /* 'N' */
+		/* idx 212 */ {7,  79, NULL}, /* 'O' */
+		/* idx 213 */ {7,  79, NULL}, /* 'O' */
+		/* idx 214 */ {7,  80, NULL}, /* 'P' */
+		/* idx 215 */ {7,  80, NULL}, /* 'P' */
+		/* idx 216 */ {7,  81, NULL}, /* 'Q' */
+		/* idx 217 */ {7,  81, NULL}, /* 'Q' */
+		/* idx 218 */ {7,  82, NULL}, /* 'R' */
+		/* idx 219 */ {7,  82, NULL}, /* 'R' */
+		/* idx 220 */ {7,  83, NULL}, /* 'S' */
+		/* idx 221 */ {7,  83, NULL}, /* 'S' */
+		/* idx 222 */ {7,  84, NULL}, /* 'T' */
+		/* idx 223 */ {7,  84, NULL}, /* 'T' */
+		/* idx 224 */ {7,  85, NULL}, /* 'U' */
+		/* idx 225 */ {7,  85, NULL}, /* 'U' */
+		/* idx 226 */ {7,  86, NULL}, /* 'V' */
+		/* idx 227 */ {7,  86, NULL}, /* 'V' */
+		/* idx 228 */ {7,  87, NULL}, /* 'W' */
+		/* idx 229 */ {7,  87, NULL}, /* 'W' */
+		/* idx 230 */ {7,  89, NULL}, /* 'Y' */
+		/* idx 231 */ {7,  89, NULL}, /* 'Y' */
+		/* idx 232 */ {7, 106, NULL}, /* 'j' */
+		/* idx 233 */ {7, 106, NULL}, /* 'j' */
+		/* idx 234 */ {7, 107, NULL}, /* 'k' */
+		/* idx 235 */ {7, 107, NULL}, /* 'k' */
+		/* idx 236 */ {7, 113, NULL}, /* 'q' */
+		/* idx 237 */ {7, 113, NULL}, /* 'q' */
+		/* idx 238 */ {7, 118, NULL}, /* 'v' */
+		/* idx 239 */ {7, 118, NULL}, /* 'v' */
+		/* idx 240 */ {7, 119, NULL}, /* 'w' */
+		/* idx 241 */ {7, 119, NULL}, /* 'w' */
+		/* idx 242 */ {7, 120, NULL}, /* 'x' */
+		/* idx 243 */ {7, 120, NULL}, /* 'x' */
+		/* idx 244 */ {7, 121, NULL}, /* 'y' */
+		/* idx 245 */ {7, 121, NULL}, /* 'y' */
+		/* idx 246 */ {7, 122, NULL}, /* 'z' */
+		/* idx 247 */ {7, 122, NULL}, /* 'z' */
+		/* idx 248 */ {8,  38, NULL}, /* '&' */
+		/* idx 249 */ {8,  42, NULL}, /* '*' */
+		/* idx 250 */ {8,  44, NULL}, /* ',' */
+		/* idx 251 */ {8,  59, NULL}, /* ';' */
+		/* idx 252 */ {8,  88, NULL}, /* 'X' */
+		/* idx 253 */ {8,  90, NULL}, /* 'Z' */
+		/* idx 254 */ {8,   0, &byte1_pref0}, /* escape */
+		/* idx 255 */ {8,   0, &byte1_pref1} /* escape */
+	}
+};
diff --git a/bin/varnishtest/vtc_h2_enctbl.h b/bin/varnishtest/vtc_h2_enctbl.h
new file mode 100644
index 0000000..5e69f33
--- /dev/null
+++ b/bin/varnishtest/vtc_h2_enctbl.h
@@ -0,0 +1,257 @@
+HPACK(0,    0x1ff8,      13)
+HPACK(1,    0x7fffd8,    23)
+HPACK(2,    0xfffffe2,   28)
+HPACK(3,    0xfffffe3,   28)
+HPACK(4,    0xfffffe4,   28)
+HPACK(5,    0xfffffe5,   28)
+HPACK(6,    0xfffffe6,   28)
+HPACK(7,    0xfffffe7,   28)
+HPACK(8,    0xfffffe8,   28)
+HPACK(9,    0xffffea,    24)
+HPACK(10,   0x3ffffffc,  30)
+HPACK(11,   0xfffffe9,   28)
+HPACK(12,   0xfffffea,   28)
+HPACK(13,   0x3ffffffd,  30)
+HPACK(14,   0xfffffeb,   28)
+HPACK(15,   0xfffffec,   28)
+HPACK(16,   0xfffffed,   28)
+HPACK(17,   0xfffffee,   28)
+HPACK(18,   0xfffffef,   28)
+HPACK(19,   0xffffff0,   28)
+HPACK(20,   0xffffff1,   28)
+HPACK(21,   0xffffff2,   28)
+HPACK(22,   0x3ffffffe,  30)
+HPACK(23,   0xffffff3,   28)
+HPACK(24,   0xffffff4,   28)
+HPACK(25,   0xffffff5,   28)
+HPACK(26,   0xffffff6,   28)
+HPACK(27,   0xffffff7,   28)
+HPACK(28,   0xffffff8,   28)
+HPACK(29,   0xffffff9,   28)
+HPACK(30,   0xffffffa,   28)
+HPACK(31,   0xffffffb,   28)
+HPACK(32,   0x14,         6)    /* ' ' */
+HPACK(33,   0x3f8,       10)    /* '!' */
+HPACK(34,   0x3f9,       10)    /* '"' */
+HPACK(35,   0xffa,       12)    /* '#' */
+HPACK(36,   0x1ff9,      13)    /* '$' */
+HPACK(37,   0x15,         6)    /* '%' */
+HPACK(38,   0xf8,         8)    /* '&' */
+HPACK(39,   0x7fa,       11)    /* ''' */
+HPACK(40,   0x3fa,       10)    /* '(' */
+HPACK(41,   0x3fb,       10)    /* ')' */
+HPACK(42,   0xf9,         8)    /* '*' */
+HPACK(43,   0x7fb,       11)    /* '+' */
+HPACK(44,   0xfa,         8)    /* ',' */
+HPACK(45,   0x16,         6)    /* '-' */
+HPACK(46,   0x17,         6)    /* '.' */
+HPACK(47,   0x18,         6)    /* '/' */
+HPACK(48,   0x0,          5)    /* '0' */
+HPACK(49,   0x1,          5)    /* '1' */
+HPACK(50,   0x2,          5)    /* '2' */
+HPACK(51,   0x19,         6)    /* '3' */
+HPACK(52,   0x1a,         6)    /* '4' */
+HPACK(53,   0x1b,         6)    /* '5' */
+HPACK(54,   0x1c,         6)    /* '6' */
+HPACK(55,   0x1d,         6)    /* '7' */
+HPACK(56,   0x1e,         6)    /* '8' */
+HPACK(57,   0x1f,         6)    /* '9' */
+HPACK(58,   0x5c,         7)    /* ':' */
+HPACK(59,   0xfb,         8)    /* ';' */
+HPACK(60,   0x7ffc,      15)    /* '<' */
+HPACK(61,   0x20,         6)    /* '=' */
+HPACK(62,   0xffb,       12)    /* '>' */
+HPACK(63,   0x3fc,       10)    /* '?' */
+HPACK(64,   0x1ffa,      13)    /* '@' */
+HPACK(65,   0x21,         6)    /* 'A' */
+HPACK(66,   0x5d,         7)    /* 'B' */
+HPACK(67,   0x5e,         7)    /* 'C' */
+HPACK(68,   0x5f,         7)    /* 'D' */
+HPACK(69,   0x60,         7)    /* 'E' */
+HPACK(70,   0x61,         7)    /* 'F' */
+HPACK(71,   0x62,         7)    /* 'G' */
+HPACK(72,   0x63,         7)    /* 'H' */
+HPACK(73,   0x64,         7)    /* 'I' */
+HPACK(74,   0x65,         7)    /* 'J' */
+HPACK(75,   0x66,         7)    /* 'K' */
+HPACK(76,   0x67,         7)    /* 'L' */
+HPACK(77,   0x68,         7)    /* 'M' */
+HPACK(78,   0x69,         7)    /* 'N' */
+HPACK(79,   0x6a,         7)    /* 'O' */
+HPACK(80,   0x6b,         7)    /* 'P' */
+HPACK(81,   0x6c,         7)    /* 'Q' */
+HPACK(82,   0x6d,         7)    /* 'R' */
+HPACK(83,   0x6e,         7)    /* 'S' */
+HPACK(84,   0x6f,         7)    /* 'T' */
+HPACK(85,   0x70,         7)    /* 'U' */
+HPACK(86,   0x71,         7)    /* 'V' */
+HPACK(87,   0x72,         7)    /* 'W' */
+HPACK(88,   0xfc,         8)    /* 'X' */
+HPACK(89,   0x73,         7)    /* 'Y' */
+HPACK(90,   0xfd,         8)    /* 'Z' */
+HPACK(91,   0x1ffb,      13)    /* '[' */
+HPACK(92,   0x7fff0,     19)    /* '\' */
+HPACK(93,   0x1ffc,      13)    /* ']' */
+HPACK(94,   0x3ffc,      14)    /* '^' */
+HPACK(95,   0x22,         6)    /* '_' */
+HPACK(96,   0x7ffd,      15)    /* '`' */
+HPACK(97,   0x3,          5)    /* 'a' */
+HPACK(98,   0x23,         6)    /* 'b' */
+HPACK(99,   0x4,          5)    /* 'c' */
+HPACK(100,  0x24,         6)    /* 'd' */
+HPACK(101,  0x5,          5)    /* 'e' */
+HPACK(102,  0x25,         6)    /* 'f' */
+HPACK(103,  0x26,         6)    /* 'g' */
+HPACK(104,  0x27,         6)    /* 'h' */
+HPACK(105,  0x6,          5)    /* 'i' */
+HPACK(106,  0x74,         7)    /* 'j' */
+HPACK(107,  0x75,         7)    /* 'k' */
+HPACK(108,  0x28,         6)    /* 'l' */
+HPACK(109,  0x29,         6)    /* 'm' */
+HPACK(110,  0x2a,         6)    /* 'n' */
+HPACK(111,  0x7,          5)    /* 'o' */
+HPACK(112,  0x2b,         6)    /* 'p' */
+HPACK(113,  0x76,         7)    /* 'q' */
+HPACK(114,  0x2c,         6)    /* 'r' */
+HPACK(115,  0x8,          5)    /* 's' */
+HPACK(116,  0x9,          5)    /* 't' */
+HPACK(117,  0x2d,         6)    /* 'u' */
+HPACK(118,  0x77,         7)    /* 'v' */
+HPACK(119,  0x78,         7)    /* 'w' */
+HPACK(120,  0x79,         7)    /* 'x' */
+HPACK(121,  0x7a,         7)    /* 'y' */
+HPACK(122,  0x7b,         7)    /* 'z' */
+HPACK(123,  0x7ffe,      15)    /* '{' */
+HPACK(124,  0x7fc,       11)    /* '|' */
+HPACK(125,  0x3ffd,      14)    /* '}' */
+HPACK(126,  0x1ffd,      13)    /* '~' */
+HPACK(127,  0xffffffc,   28)
+HPACK(128,  0xfffe6,     20)
+HPACK(129,  0x3fffd2,    22)
+HPACK(130,  0xfffe7,     20)
+HPACK(131,  0xfffe8,     20)
+HPACK(132,  0x3fffd3,    22)
+HPACK(133,  0x3fffd4,    22)
+HPACK(134,  0x3fffd5,    22)
+HPACK(135,  0x7fffd9,    23)
+HPACK(136,  0x3fffd6,    22)
+HPACK(137,  0x7fffda,    23)
+HPACK(138,  0x7fffdb,    23)
+HPACK(139,  0x7fffdc,    23)
+HPACK(140,  0x7fffdd,    23)
+HPACK(141,  0x7fffde,    23)
+HPACK(142,  0xffffeb,    24)
+HPACK(143,  0x7fffdf,    23)
+HPACK(144,  0xffffec,    24)
+HPACK(145,  0xffffed,    24)
+HPACK(146,  0x3fffd7,    22)
+HPACK(147,  0x7fffe0,    23)
+HPACK(148,  0xffffee,    24)
+HPACK(149,  0x7fffe1,    23)
+HPACK(150,  0x7fffe2,    23)
+HPACK(151,  0x7fffe3,    23)
+HPACK(152,  0x7fffe4,    23)
+HPACK(153,  0x1fffdc,    21)
+HPACK(154,  0x3fffd8,    22)
+HPACK(155,  0x7fffe5,    23)
+HPACK(156,  0x3fffd9,    22)
+HPACK(157,  0x7fffe6,    23)
+HPACK(158,  0x7fffe7,    23)
+HPACK(159,  0xffffef,    24)
+HPACK(160,  0x3fffda,    22)
+HPACK(161,  0x1fffdd,    21)
+HPACK(162,  0xfffe9,     20)
+HPACK(163,  0x3fffdb,    22)
+HPACK(164,  0x3fffdc,    22)
+HPACK(165,  0x7fffe8,    23)
+HPACK(166,  0x7fffe9,    23)
+HPACK(167,  0x1fffde,    21)
+HPACK(168,  0x7fffea,    23)
+HPACK(169,  0x3fffdd,    22)
+HPACK(170,  0x3fffde,    22)
+HPACK(171,  0xfffff0,    24)
+HPACK(172,  0x1fffdf,    21)
+HPACK(173,  0x3fffdf,    22)
+HPACK(174,  0x7fffeb,    23)
+HPACK(175,  0x7fffec,    23)
+HPACK(176,  0x1fffe0,    21)
+HPACK(177,  0x1fffe1,    21)
+HPACK(178,  0x3fffe0,    22)
+HPACK(179,  0x1fffe2,    21)
+HPACK(180,  0x7fffed,    23)
+HPACK(181,  0x3fffe1,    22)
+HPACK(182,  0x7fffee,    23)
+HPACK(183,  0x7fffef,    23)
+HPACK(184,  0xfffea,     20)
+HPACK(185,  0x3fffe2,    22)
+HPACK(186,  0x3fffe3,    22)
+HPACK(187,  0x3fffe4,    22)
+HPACK(188,  0x7ffff0,    23)
+HPACK(189,  0x3fffe5,    22)
+HPACK(190,  0x3fffe6,    22)
+HPACK(191,  0x7ffff1,    23)
+HPACK(192,  0x3ffffe0,   26)
+HPACK(193,  0x3ffffe1,   26)
+HPACK(194,  0xfffeb,     20)
+HPACK(195,  0x7fff1,     19)
+HPACK(196,  0x3fffe7,    22)
+HPACK(197,  0x7ffff2,    23)
+HPACK(198,  0x3fffe8,    22)
+HPACK(199,  0x1ffffec,   25)
+HPACK(200,  0x3ffffe2,   26)
+HPACK(201,  0x3ffffe3,   26)
+HPACK(202,  0x3ffffe4,   26)
+HPACK(203,  0x7ffffde,   27)
+HPACK(204,  0x7ffffdf,   27)
+HPACK(205,  0x3ffffe5,   26)
+HPACK(206,  0xfffff1,    24)
+HPACK(207,  0x1ffffed,   25)
+HPACK(208,  0x7fff2,     19)
+HPACK(209,  0x1fffe3,    21)
+HPACK(210,  0x3ffffe6,   26)
+HPACK(211,  0x7ffffe0,   27)
+HPACK(212,  0x7ffffe1,   27)
+HPACK(213,  0x3ffffe7,   26)
+HPACK(214,  0x7ffffe2,   27)
+HPACK(215,  0xfffff2,    24)
+HPACK(216,  0x1fffe4,    21)
+HPACK(217,  0x1fffe5,    21)
+HPACK(218,  0x3ffffe8,   26)
+HPACK(219,  0x3ffffe9,   26)
+HPACK(220,  0xffffffd,   28)
+HPACK(221,  0x7ffffe3,   27)
+HPACK(222,  0x7ffffe4,   27)
+HPACK(223,  0x7ffffe5,   27)
+HPACK(224,  0xfffec,     20)
+HPACK(225,  0xfffff3,    24)
+HPACK(226,  0xfffed,     20)
+HPACK(227,  0x1fffe6,    21)
+HPACK(228,  0x3fffe9,    22)
+HPACK(229,  0x1fffe7,    21)
+HPACK(230,  0x1fffe8,    21)
+HPACK(231,  0x7ffff3,    23)
+HPACK(232,  0x3fffea,    22)
+HPACK(233,  0x3fffeb,    22)
+HPACK(234,  0x1ffffee,   25)
+HPACK(235,  0x1ffffef,   25)
+HPACK(236,  0xfffff4,    24)
+HPACK(237,  0xfffff5,    24)
+HPACK(238,  0x3ffffea,   26)
+HPACK(239,  0x7ffff4,    23)
+HPACK(240,  0x3ffffeb,   26)
+HPACK(241,  0x7ffffe6,   27)
+HPACK(242,  0x3ffffec,   26)
+HPACK(243,  0x3ffffed,   26)
+HPACK(244,  0x7ffffe7,   27)
+HPACK(245,  0x7ffffe8,   27)
+HPACK(246,  0x7ffffe9,   27)
+HPACK(247,  0x7ffffea,   27)
+HPACK(248,  0x7ffffeb,   27)
+HPACK(249,  0xffffffe,   28)
+HPACK(250,  0x7ffffec,   27)
+HPACK(251,  0x7ffffed,   27)
+HPACK(252,  0x7ffffee,   27)
+HPACK(253,  0x7ffffef,   27)
+HPACK(254,  0x7fffff0,   27)
+HPACK(255,  0x3ffffee,   26)
+HPACK(0,    0x3fffffff,  30)
diff --git a/bin/varnishtest/vtc_h2_hpack.c b/bin/varnishtest/vtc_h2_hpack.c
new file mode 100644
index 0000000..63034fd
--- /dev/null
+++ b/bin/varnishtest/vtc_h2_hpack.c
@@ -0,0 +1,430 @@
+/*-
+ * Copyright (c) 2008-2016 Varnish Software AS
+ * All rights reserved.
+ *
+ * Author: Guillaume Quintard <guillaume.quintard at gmail.com>
+ *
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <vas.h>
+
+#include "hpack.h"
+#include "vtc_h2_priv.h"
+
+struct symbol {
+	uint32_t	val;
+	uint8_t		size;
+};
+
+static struct symbol coding_table[] = {
+#define HPACK(i, v, l) {v, l},
+#include "vtc_h2_enctbl.h"
+#undef HPACK
+	{0, 0}
+};
+
+#include "vtc_h2_dectbl.h"
+
+struct symbol *EOS = &coding_table[256];
+
+#define MASK(pack, n) (pack >> (64 - n))
+static int
+huff_decode(char *str, int nm, struct hpk_iter *iter, int ilen)
+{
+	int l = 0;
+	uint64_t pack = 0;
+	int pl = 0; /* pack length*/
+	struct stbl *tbl = &byte0;
+	struct ssym *sym;
+
+	(void)nm;
+	while (ilen > 0 || pl != 0) {
+		assert(pl >= 0);
+		/* make sure we have enough data*/
+		if (pl < tbl->msk) {
+			if (ilen == 0) {
+				if (pl == 0 || (MASK(pack, pl) ==
+						(unsigned)((1 << pl) - 1))) {
+					assert(tbl == &byte0);
+					return (l);
+				}
+			}
+			/* fit as many bytes as we can in pack */
+			while (pl <= 56 && ilen > 0) {
+				pack |= (uint64_t)(*iter->buf & 0xff)
+					<< (56 - pl);
+				pl += 8;
+				iter->buf++;
+				ilen--;
+			}
+		}
+		assert(tbl);
+		assert(tbl->msk);
+		sym = &tbl->syms[MASK(pack, tbl->msk)];
+
+		assert(sym->csm <= tbl->msk);
+
+		if (sym->csm == 0 || pl < sym->csm)
+			return (0);
+
+		pack <<= sym->csm;
+		pl -= sym->csm;
+		if (sym->nxt) {
+			tbl = sym->nxt;
+			continue;
+		}
+		str[l++] = sym->chr;
+		tbl = &byte0;
+	}
+	return (l);
+}
+
+/* inspired from Dridi Boukelmoune's cashpack. */
+static int
+huff_encode(struct hpk_iter *iter, const char *str, int len)
+{
+	uint64_t pack = 0;
+	int pl = 0; /* pack length*/
+	uint32_t	v;
+	uint8_t		s;
+	
+	assert(iter->buf < iter->end); 
+
+	while (len--) {
+		v = coding_table[(uint8_t)*str].val;
+		s = coding_table[(uint8_t)*str].size;
+
+		pl += s;
+		pack |= (uint64_t)v << (64 - pl);
+
+		while (pl >= 8) {
+			if (iter->buf == iter->end)
+				return (1);
+			*iter->buf = (char)(pack >> 56);
+			iter->buf++;
+			pack <<= 8;
+			pl -= 8;
+		}
+		str++;
+	}
+
+	/* padding */
+	if (pl) {
+		assert(pl < 8);
+		if (iter->buf == iter->end)
+			return (1);
+		pl += 8;
+		pack |= (uint64_t)0xff << (64 - pl);
+		*iter->buf = (char)(pack >> 56);
+		iter->buf++;
+	}
+
+	return (0);
+}
+
+static int
+huff_simulate(const char *str, int ilen, int huff)
+{
+	int olen = 0;
+	if (!huff || !ilen)
+		return (ilen);
+	while (ilen--) {
+		olen += coding_table[(unsigned char)*str].size;
+		str++;
+	}
+	return ((olen+7)/8);
+}
+
+static enum hpk_result
+num_decode(uint32_t *result, struct hpk_iter *iter, uint8_t prefix)
+{
+	uint8_t shift = 0;
+
+	assert(iter->buf < iter->end);
+	assert(prefix);
+	assert(prefix <= 8);
+
+	*result = 0;
+	*result = *iter->buf & (0xff >> (8-prefix));
+	if (*result < (1 << prefix) - 1) {
+		iter->buf++;
+		return (ITER_DONE(iter));
+	}
+	do {
+		iter->buf++;
+		if (iter->end == iter->buf)
+			return (hpk_err);
+		/* check for overflow */
+		if ((UINT32_MAX - *result) >> shift < (*iter->buf & 0x7f))
+			return (hpk_err);
+
+		*result += (uint32_t)(*iter->buf & 0x7f) << shift;
+		shift += 7;
+	} while (*iter->buf & 0x80);
+	iter->buf++;
+
+	return (ITER_DONE(iter));
+}
+
+static enum hpk_result
+num_encode(struct hpk_iter *iter, uint8_t prefix, uint32_t num)
+{
+	assert(prefix);
+	assert(prefix <= 8);
+	assert(iter->buf < iter->end);
+
+	uint8_t pmax = (1 << prefix) - 1;
+
+	*iter->buf &= 0xff << prefix;
+	if (num <=  pmax) {
+		*iter->buf++ |= num;
+		return (ITER_DONE(iter));
+	} else if (iter->end - iter->buf < 2)
+		return (hpk_err);
+
+	iter->buf[0] |= pmax;
+	num -= pmax;
+	do {
+		iter->buf++;
+		if (iter->end == iter->buf)
+			return (hpk_err);
+		*iter->buf = num % 128;
+		*iter->buf |= 0x80;
+		num /= 128;
+	} while (num);
+	*iter->buf++ &= 127;
+	return (ITER_DONE(iter));
+}
+
+static enum hpk_result
+str_encode(struct hpk_iter *iter, const struct txt *t)
+{
+	int slen = huff_simulate(t->ptr, t->len, t->huff);
+	assert(iter->buf < iter->end);
+	if (t->huff)
+		*iter->buf = 0x80;
+	else
+		*iter->buf = 0;
+
+	if (hpk_err == num_encode(iter, 7, slen))
+		return (hpk_err);
+
+	if (slen > iter->end - iter->buf)
+		return (hpk_err);
+
+	if (t->huff) {
+		return (huff_encode(iter, t->ptr, t->len));
+	} else {
+		memcpy(iter->buf, t->ptr, slen);
+		iter->buf += slen;
+		return (ITER_DONE(iter));
+	}
+}
+
+static enum hpk_result
+str_decode(struct hpk_iter *iter, struct txt *t)
+{
+	uint32_t num;
+	int huff;
+	assert(iter->buf < iter->end);
+	huff = (*iter->buf & 0x80);
+	if (hpk_more != num_decode(&num, iter, 7))
+		return (hpk_err);
+	if (num > iter->end - iter->buf)
+		return (hpk_err);
+	if (huff) { /*Huffman encoding */
+		t->ptr = malloc((num * 8) / 5L + 1L);
+		AN(t->ptr);
+		num = huff_decode(t->ptr, (num * 8) / 5, iter, num);
+		if (!num) {
+			free(t->ptr);
+			return (hpk_err);
+		}
+		t->huff = 1;
+		/* XXX: do we care? */
+		t->ptr = realloc(t->ptr, num + 1L);
+		AN(t->ptr);
+		memcpy(t->ptr, t->ptr, num);
+	} else { /* literal string */
+		t->huff = 0;
+		t->ptr = malloc(num + 1L);
+		AN(t->ptr);
+		memcpy(t->ptr, iter->buf, num);
+		iter->buf += num;
+	}
+
+	t->ptr[num] = '\0';
+	t->len = num;
+	
+	return (ITER_DONE(iter));
+}
+
+static inline void
+txtcpy(struct txt *to, const struct txt *from)
+{
+	//AZ(to->ptr);
+	to->ptr = malloc(from->len + 1L);
+	AN(to->ptr);
+	memcpy(to->ptr, from->ptr, from->len + 1L);
+	to->len = from->len;
+}
+
+int
+gethpk_iterLen(const struct hpk_iter *iter)
+{
+	return (iter->buf - iter->orig);
+}
+
+enum hpk_result
+HPK_DecHdr(struct hpk_iter *iter, struct hpk_hdr *header)
+{
+	int pref = 0;
+	const struct txt *t;
+	uint32_t num;
+	int must_index = 0;
+	assert(iter);
+	assert(iter->buf < iter->end);
+	/* Indexed Header Field */
+	if (*iter->buf & 128) {
+		header->t = hpk_idx;
+		if (hpk_err == num_decode(&num, iter, 7))
+			return (hpk_err);
+
+		if (num) { /* indexed key and value*/
+			t = tbl_get_key(iter->ctx, num);
+			if (!t)
+				return (hpk_err);
+			txtcpy(&header->key, t);
+
+			t = tbl_get_value(iter->ctx, num);
+			if (!t) {
+				free(header->key.ptr);
+				return (hpk_err);
+			}
+
+			txtcpy(&header->value, t);
+
+			if (iter->buf < iter->end)
+				return (hpk_more);
+			else
+				return (hpk_done);
+		} else
+			return (hpk_err);
+
+	}
+	/* Literal Header Field with Incremental Indexing */
+	else if (*iter->buf >> 6 == 1) {
+		header->t = hpk_inc;
+		pref = 6;
+		must_index = 1;
+	}
+	/* Literal Header Field without Indexing */
+	else if (*iter->buf >> 4 == 0) {
+		header->t = hpk_not;
+		pref = 4;
+	}
+	/* Literal Header Field never Indexed */
+	else if (*iter->buf >> 4 == 1) {
+		header->t = hpk_never;
+		pref = 4;
+	}
+	/* Dynamic Table Size Update */
+	/* XXX if under max allowed value */
+	else if (*iter->buf >> 5 == 1) {
+		if (hpk_done != num_decode(&num, iter, 5))
+			return (hpk_err);
+		return HPK_ResizeTbl(iter->ctx, num);
+	} else {
+		return (hpk_err);
+	}
+
+	assert(pref);
+	if (hpk_more != num_decode(&num, iter, pref))
+		return (hpk_err);
+
+	header->i = num;
+	if (num) { /* indexed key */
+		t = tbl_get_key(iter->ctx, num);
+		if (!t)
+			return (hpk_err);
+		txtcpy(&header->key, t);
+	} else {
+		if (hpk_more != str_decode(iter, &header->key))
+			return (hpk_err);
+	}
+
+	if (hpk_err == str_decode(iter, &header->value))
+		return (hpk_err);
+
+	if (must_index)
+		push_header(iter->ctx, header);
+	return (ITER_DONE(iter));
+}
+
+enum hpk_result
+HPK_EncHdr(struct hpk_iter *iter, const struct hpk_hdr *h)
+{
+	int pref;
+	int must_index = 0;
+	enum hpk_result ret;
+	switch (h->t) {
+		case hpk_idx:
+			*iter->buf = 0x80;
+			num_encode(iter, 7, h->i);
+			return (ITER_DONE(iter));
+		case hpk_inc:
+			*iter->buf = 0x40;
+			pref = 6;
+			must_index = 1;
+			break;
+		case hpk_not:
+			*iter->buf = 0x00;
+			pref = 4;
+			break;
+		case hpk_never:
+			*iter->buf = 0x10;
+			pref = 4;
+			break;
+		default:
+			INCOMPL();
+	}
+	if (h->i) {
+		if (hpk_more != num_encode(iter, pref, h->i))
+			return (hpk_err);
+	} else {
+		iter->buf++;
+		if (hpk_more != str_encode(iter, &h->key))
+			return (hpk_err);
+	}
+	ret = str_encode(iter, &h->value);
+	if (ret == hpk_err)
+		return (hpk_err);
+	if (must_index)
+		push_header(iter->ctx, h);
+	return (ret);
+
+}
diff --git a/bin/varnishtest/vtc_h2_priv.h b/bin/varnishtest/vtc_h2_priv.h
new file mode 100644
index 0000000..706ba0c
--- /dev/null
+++ b/bin/varnishtest/vtc_h2_priv.h
@@ -0,0 +1,22 @@
+#include "vqueue.h"
+
+#define ITER_DONE(iter) (iter->buf == iter->end ? hpk_done : hpk_more)
+
+struct dynhdr {
+	struct hpk_hdr header;
+	VTAILQ_ENTRY(dynhdr)      list;
+};
+
+VTAILQ_HEAD(dynamic_table,dynhdr);
+
+struct hpk_iter {
+	struct hpk_ctx *ctx;
+	char *orig;
+	char *buf;
+	char *end;
+};
+
+const struct txt * tbl_get_key(const struct hpk_ctx *ctx, uint32_t index);
+
+const struct txt * tbl_get_value(const struct hpk_ctx *ctx, uint32_t index);
+void push_header (struct hpk_ctx *ctx, const struct hpk_hdr *h);
diff --git a/bin/varnishtest/vtc_h2_stattbl.h b/bin/varnishtest/vtc_h2_stattbl.h
new file mode 100644
index 0000000..1e54862
--- /dev/null
+++ b/bin/varnishtest/vtc_h2_stattbl.h
@@ -0,0 +1,61 @@
+STAT_HDRS(1,   ":authority",                   "")
+STAT_HDRS(2,   ":method",                      "GET")
+STAT_HDRS(3,   ":method",                      "POST")
+STAT_HDRS(4,   ":path",                        "/")
+STAT_HDRS(5,   ":path",                        "/index.html")
+STAT_HDRS(6,   ":scheme",                      "http")
+STAT_HDRS(7,   ":scheme",                      "https")
+STAT_HDRS(8,   ":status",                      "200")
+STAT_HDRS(9,   ":status",                      "204")
+STAT_HDRS(10,  ":status",                      "206")
+STAT_HDRS(11,  ":status",                      "304")
+STAT_HDRS(12,  ":status",                      "400")
+STAT_HDRS(13,  ":status",                      "404")
+STAT_HDRS(14,  ":status",                      "500")
+STAT_HDRS(15,  "accept-charset",               "")
+STAT_HDRS(16,  "accept-encoding",              "gzip,deflate")
+STAT_HDRS(17,  "accept-language",              "")
+STAT_HDRS(18,  "accept-ranges",                "")
+STAT_HDRS(19,  "accept",                       "")
+STAT_HDRS(20,  "access-control-allow-origin",  "")
+STAT_HDRS(21,  "age",                          "")
+STAT_HDRS(22,  "allow",                        "")
+STAT_HDRS(23,  "authorization",                "")
+STAT_HDRS(24,  "cache-control",                "")
+STAT_HDRS(25,  "content-disposition",          "")
+STAT_HDRS(26,  "content-encoding",             "")
+STAT_HDRS(27,  "content-language",             "")
+STAT_HDRS(28,  "content-length",               "")
+STAT_HDRS(29,  "content-location",             "")
+STAT_HDRS(30,  "content-range",                "")
+STAT_HDRS(31,  "content-type",                 "")
+STAT_HDRS(32,  "cookie",                       "")
+STAT_HDRS(33,  "date",                         "")
+STAT_HDRS(34,  "etag",                         "")
+STAT_HDRS(35,  "expect",                       "")
+STAT_HDRS(36,  "expires",                      "")
+STAT_HDRS(37,  "from",                         "")
+STAT_HDRS(38,  "host",                         "")
+STAT_HDRS(39,  "if-match",                     "")
+STAT_HDRS(40,  "if-modified-since",            "")
+STAT_HDRS(41,  "if-none-match",                "")
+STAT_HDRS(42,  "if-range",                     "")
+STAT_HDRS(43,  "if-unmodified-since",          "")
+STAT_HDRS(44,  "last-modified",                "")
+STAT_HDRS(45,  "link",                         "")
+STAT_HDRS(46,  "location",                     "")
+STAT_HDRS(47,  "max-forwards",                 "")
+STAT_HDRS(48,  "proxy-authenticate",           "")
+STAT_HDRS(49,  "proxy-authorization",          "")
+STAT_HDRS(50,  "range",                        "")
+STAT_HDRS(51,  "referer",                      "")
+STAT_HDRS(52,  "refresh",                      "")
+STAT_HDRS(53,  "retry-after",                  "")
+STAT_HDRS(54,  "server",                       "")
+STAT_HDRS(55,  "set-cookie",                   "")
+STAT_HDRS(56,  "strict-transport-security",    "")
+STAT_HDRS(57,  "transfer-encoding",            "")
+STAT_HDRS(58,  "user-agent",                   "")
+STAT_HDRS(59,  "vary",                         "")
+STAT_HDRS(60,  "via",                          "")
+STAT_HDRS(61,  "www-authenticate",             "")
diff --git a/bin/varnishtest/vtc_h2_tbl.c b/bin/varnishtest/vtc_h2_tbl.c
new file mode 100644
index 0000000..0a50d1c
--- /dev/null
+++ b/bin/varnishtest/vtc_h2_tbl.c
@@ -0,0 +1,291 @@
+/*-
+ * Copyright (c) 2008-2016 Varnish Software AS
+ * All rights reserved.
+ *
+ * Author: Guillaume Quintard <guillaume.quintard at gmail.com>
+ *
+ * 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 <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdint.h>
+
+#include <vas.h>
+
+#include "hpack.h"
+#include "vtc_h2_priv.h"
+
+/* TODO: fix that crazy workaround */
+#define STAT_HDRS(i, k, v) \
+	static char key_ ## i[] = k; \
+	static char value_ ## i[] = v;
+#include "vtc_h2_stattbl.h"
+#undef STAT_HDRS
+
+/*lint -save -e778 */
+struct hpk_hdr sttbl[] = {
+	{{NULL, 0, 0}, {NULL, 0, 0}, hpk_idx, 0},
+#define STAT_HDRS(j, k, v) \
+{ \
+	.key = { \
+		.ptr = key_ ## j, \
+		.len = sizeof(k) - 1, \
+		.huff = 0 \
+	}, \
+	.value = { \
+		.ptr = value_ ## j, \
+		.len = sizeof(v) - 1, \
+		.huff = 0 \
+	}, \
+	.t = hpk_idx, \
+	.i = j, \
+},
+#include "vtc_h2_stattbl.h"
+#undef STAT_HDRS
+};
+/*lint -restore */
+
+struct hpk_ctx {
+	const struct hpk_hdr *sttbl;
+	struct dynamic_table      dyntbl;
+	uint32_t maxsize;
+	uint32_t size;
+};
+
+
+struct hpk_iter *
+HPK_NewIter(struct hpk_ctx *ctx, char *buf, int size)
+{
+	struct hpk_iter *iter = malloc(sizeof(*iter));
+	assert(iter);
+	assert(ctx);
+	assert(buf);
+	assert(size);
+	iter->ctx = ctx;
+	iter->orig = buf;
+	iter->buf = buf;
+	iter->end = buf + size;
+	return (iter);
+}
+
+void
+HPK_FreeIter(struct hpk_iter *iter)
+{
+	free(iter);
+}
+
+static void
+pop_header(struct hpk_ctx *ctx)
+{
+	assert(!VTAILQ_EMPTY(&ctx->dyntbl));
+	struct dynhdr *h = VTAILQ_LAST(&ctx->dyntbl, dynamic_table);
+	VTAILQ_REMOVE(&ctx->dyntbl, h, list);
+	ctx->size -= h->header.key.len + h->header.value.len + 32;
+	free(h->header.key.ptr);
+	free(h->header.value.ptr);
+	free(h);
+}
+
+void
+push_header (struct hpk_ctx *ctx, const struct hpk_hdr *oh)
+{
+	const struct hpk_hdr *ih;
+	struct dynhdr *h;
+	uint32_t len;
+
+	assert(ctx->size <= ctx->maxsize);
+	AN(oh);
+
+	if (!ctx->maxsize)
+		return;
+	len = oh->value.len + 32;
+	if (oh->key.ptr)
+		len += oh->key.len;
+	else {
+		AN(oh->i);
+		ih = HPK_GetHdr(ctx, oh->i);
+		AN(ih);
+		len += ih->key.len;
+	}
+
+	while (!VTAILQ_EMPTY(&ctx->dyntbl) && ctx->maxsize - ctx->size < len)
+		pop_header(ctx);
+	if (ctx->maxsize - ctx->size >= len) {
+		h = malloc(sizeof(*h));
+		AN(h);
+		h->header.t = hpk_idx;
+
+		if (oh->key.ptr) {
+			h->header.key.len = oh->key.len;
+			h->header.key.ptr = malloc(oh->key.len + 1L);
+			AN(h->header.key.ptr);
+			memcpy(h->header.key.ptr,
+			    oh->key.ptr, oh->key.len + 1L);
+		} else {
+			AN(oh->i);
+			ih = HPK_GetHdr(ctx, oh->i);
+			AN(ih);
+
+			h->header.key.len = ih->key.len;
+			h->header.key.ptr = malloc(ih->key.len + 1L);
+			AN(h->header.key.ptr);
+			memcpy(h->header.key.ptr,
+			    ih->key.ptr, ih->key.len + 1L);
+		}
+
+		h->header.value.len = oh->value.len;
+		h->header.value.ptr = malloc(oh->value.len + 1L);
+		AN(h->header.value.ptr);
+		memcpy(h->header.value.ptr, oh->value.ptr, oh->value.len + 1L);
+
+		VTAILQ_INSERT_HEAD(&ctx->dyntbl, h, list);
+		ctx->size += len;
+	}
+
+}
+
+enum hpk_result
+HPK_ResizeTbl(struct hpk_ctx *ctx, uint32_t num)
+{
+	ctx->maxsize = num;
+	while (!VTAILQ_EMPTY(&ctx->dyntbl) && ctx->maxsize < ctx->size)
+		pop_header(ctx);
+	return (hpk_done);
+}
+
+static const struct txt *
+tbl_get_field(const struct hpk_ctx *ctx, uint32_t idx, int key)
+{
+	struct dynhdr *dh;
+	assert(ctx);
+	if (idx > 61 + ctx->size)
+		return (NULL);
+	else if (idx <= 61) {
+		if (key)
+			return (&ctx->sttbl[idx].key);
+		else
+			return (&ctx->sttbl[idx].value);
+	}
+
+	idx -= 62;
+	VTAILQ_FOREACH(dh, &ctx->dyntbl, list)
+		if (!idx--)
+			break;
+	if (idx && dh) {
+		if (key)
+			return (&dh->header.key);
+		else
+			return (&dh->header.value);
+	} else
+		return (NULL);
+}
+
+const struct txt *
+tbl_get_key(const struct hpk_ctx *ctx, uint32_t idx)
+{
+	return (tbl_get_field(ctx, idx, 1));
+}
+
+const struct txt *
+tbl_get_value(const struct hpk_ctx *ctx, uint32_t idx)
+{
+	return (tbl_get_field(ctx, idx, 0));
+}
+
+const struct hpk_hdr *
+HPK_GetHdr(const struct hpk_ctx *ctx, uint32_t idx)
+{
+	uint32_t oi = idx;
+	struct dynhdr *dh;
+	assert(ctx);
+	if (idx > 61 + ctx->size)
+		return (NULL);
+	else if (idx <= 61)
+		return (&ctx->sttbl[idx]);
+
+	idx -= 62;
+	VTAILQ_FOREACH(dh, &ctx->dyntbl, list)
+		if (!idx--)
+			break;
+	if (idx && dh) {
+		dh->header.i = oi;
+		return (&dh->header);
+	} else
+		return (NULL);
+}
+
+uint32_t
+HPK_GetTblSize(const struct hpk_ctx *ctx)
+{
+	return (ctx->size);
+}
+
+uint32_t
+HPK_GetTblMaxSize(const struct hpk_ctx *ctx)
+{
+	return (ctx->maxsize);
+}
+
+uint32_t
+HPK_GetTblLength(const struct hpk_ctx *ctx)
+{
+	struct dynhdr *dh;
+	uint32_t l = 0;
+	VTAILQ_FOREACH(dh, &ctx->dyntbl, list)
+		l++;
+	return (l);
+}
+
+void
+dump_dyn_tbl(const struct hpk_ctx *ctx)
+{
+	int i = 0;
+	struct dynhdr *dh;
+	printf("DUMPING %u/%u\n", ctx->size, ctx->maxsize);
+	VTAILQ_FOREACH(dh, &ctx->dyntbl, list) {
+		printf(" (%d) %s: %s\n",
+		    i++, dh->header.key.ptr, dh->header.value.ptr);
+	}
+	printf("DONE\n");
+}
+
+struct hpk_ctx *
+HPK_NewCtx(uint32_t maxsize)
+{
+	struct hpk_ctx *ctx = calloc(1, sizeof(*ctx));
+	assert(ctx);
+	ctx->sttbl = sttbl;
+	ctx->maxsize = maxsize;
+	ctx->size = 0;
+	return (ctx);
+}
+
+void
+HPK_FreeCtx(struct hpk_ctx *ctx)
+{
+
+	while(!VTAILQ_EMPTY(&ctx->dyntbl))
+		pop_header(ctx);
+	free(ctx);
+}
diff --git a/bin/varnishtest/vtc_http.c b/bin/varnishtest/vtc_http.c
index f7e993f..3c85f50 100644
--- a/bin/varnishtest/vtc_http.c
+++ b/bin/varnishtest/vtc_http.c
@@ -40,14 +40,14 @@
 #include <string.h>
 
 #include "vtc.h"
+#include "vtc_http.h"
 
 #include "vct.h"
 #include "vgz.h"
 #include "vnum.h"
 #include "vre.h"
 #include "vtcp.h"
-
-#define MAX_HDR		50
+#include "hpack.h"
 
 /* SECTION: client-server client/server
  *
@@ -120,39 +120,26 @@
  *        is of the form "CLIENTIP:PORT SERVERIP:PORT".
  *
  * SECTION: client-server.spec Specification
+ *
+ * It's a string, either double-quoted "like this", but most of the time
+ * enclosed in curly brackets, allowing multilining. Write a command per line in
+ * it, empty line are ignored, and long line can be wrapped by using a
+ * backslash. For example::
+ *
+ *     client c1 {
+ *         txreq -url /foo \
+ *               -hdr "bar: baz"
+ *
+ *         rxresp
+ *     } -run
  */
 
-struct http {
-	unsigned		magic;
-#define HTTP_MAGIC		0x2f02169c
-	int			fd;
-	int			*sfd;
-	int			timeout;
-	struct vtclog		*vl;
-
-	struct vsb		*vsb;
-
-	int			nrxbuf;
-	char			*rxbuf;
-	char			*rem_ip;
-	char			*rem_port;
-	int			prxbuf;
-	char			*body;
-	unsigned		bodyl;
-	char			bodylen[20];
-	char			chunklen[20];
-
-	char			*req[MAX_HDR];
-	char			*resp[MAX_HDR];
-
-	int			gziplevel;
-	int			gzipresidual;
-
-	int			fatal;
-};
-
 #define ONLY_CLIENT(hp, av)						\
 	do {								\
+		if (hp->h2)						\
+			vtc_log(hp->vl, 0,				\
+			    "\"%s\" only possible before H/2 upgrade",	\
+					av[0]);				\
 		if (hp->sfd != NULL)					\
 			vtc_log(hp->vl, 0,				\
 			    "\"%s\" only possible in client", av[0]);	\
@@ -160,6 +147,10 @@ struct http {
 
 #define ONLY_SERVER(hp, av)						\
 	do {								\
+		if (hp->h2)						\
+			vtc_log(hp->vl, 0,				\
+			    "\"%s\" only possible before H/2 upgrade",	\
+					av[0]);				\
 		if (hp->sfd == NULL)					\
 			vtc_log(hp->vl, 0,				\
 			    "\"%s\" only possible in server", av[0]);	\
@@ -173,7 +164,7 @@ static const char * const nl = "\r\n";
  * Generate a synthetic body
  */
 
-static char *
+char *
 synth_body(const char *len, int rnd)
 {
 	int i, j, k, l;
@@ -336,6 +327,11 @@ cmd_var_resolve(struct http *hp, char *spec)
 	} else if (!strncmp(spec, "resp.http.", 10)) {
 		hh = hp->resp;
 		hdr = spec + 10;
+	} else if (!strcmp(spec, "h2.state")) {
+		if (hp->h2)
+			return ("true");
+		else
+			return ("false");
 	} else
 		return (spec);
 	hdr = http_find_header(hh, hdr);
@@ -544,8 +540,8 @@ http_rxchar(struct http *hp, int n, int eof)
 			return (i);
 		if (i == 0)
 			vtc_log(hp->vl, hp->fatal,
-			    "HTTP rx EOF (fd:%d read: %s)",
-			    hp->fd, strerror(errno));
+			    "HTTP rx EOF (fd:%d read: %s) %d",
+			    hp->fd, strerror(errno), n);
 		if (i < 0)
 			vtc_log(hp->vl, hp->fatal,
 			    "HTTP rx failed (fd:%d read: %s)",
@@ -729,7 +725,7 @@ cmd_http_rxresphdrs(CMD_ARGS)
 	av++;
 
 	for(; *av != NULL; av++)
-		vtc_log(hp->vl, 0, "Unknown http rxreq spec: %s\n", *av);
+		vtc_log(hp->vl, 0, "Unknown http rxresp spec: %s\n", *av);
 	http_rxhdr(hp);
 	http_splitheader(hp, 0);
 	if (http_count_header(hp->resp, "Content-Length") > 1)
@@ -1035,33 +1031,75 @@ cmd_http_txresp(CMD_ARGS)
 	http_write(hp, 4, "txresp");
 }
 
+static void
+cmd_http_upgrade(CMD_ARGS)
+{
+	char *h;
+	struct http *hp;
+
+	CAST_OBJ_NOTNULL(hp, priv, HTTP_MAGIC);
+	ONLY_SERVER(hp, av);
+	AN(hp->sfd);
+
+	h = http_find_header(hp->req, "Upgrade");
+	if (!h || strcmp(h, "h2c"))
+		vtc_log(vl, 0, "Req misses \"Upgrade: h2c\" header");
+
+	h = http_find_header(hp->req, "Connection");
+	if (!h || strcmp(h, "Upgrade, HTTP2-Settings"))
+		vtc_log(vl, 0, "Req misses \"Connection: " 
+			"Upgrade, HTTP2-Settings\" header");
+
+	h = http_find_header(hp->req, "HTTP2-Settings");
+	if (!h)
+		vtc_log(vl, 0, "Req misses \"HTTP2-Settings\" header");
+
+
+	parse_string("txresp -status 101 "
+				"-hdr \"Connection: Upgrade\" "
+				"-hdr \"Upgrade: h2c\"\n", cmd, hp, vl);
+
+	b64_settings(hp, h);
+
+	parse_string("rxpri\n"
+			"stream 0 {\n"
+			"txsettings\n"
+			"rxsettings\n"
+			"txsettings -ack\n"
+			"rxsettings\n"
+			"expect settings.ack == true\n"
+			"} -start\n", cmd, hp, vl);
+}
+
+/**********************************************************************
+ * Receive a request
+ */
+
 /* SECTION: client-server.spec.rxreq
  *
  * rxreq (server only)
  *         Receive and parse a request's headers and body.
  */
-
 static void
 cmd_http_rxreq(CMD_ARGS)
 {
 	struct http *hp;
 
 	(void)cmd;
-	(void)vl;
 	CAST_OBJ_NOTNULL(hp, priv, HTTP_MAGIC);
 	ONLY_SERVER(hp, av);
 	AZ(strcmp(av[0], "rxreq"));
 	av++;
 
 	for(; *av != NULL; av++)
-		vtc_log(hp->vl, 0, "Unknown http rxreq spec: %s\n", *av);
+		vtc_log(vl, 0, "Unknown http rxreq spec: %s\n", *av);
 	http_rxhdr(hp);
 	http_splitheader(hp, 1);
 	if (http_count_header(hp->req, "Content-Length") > 1)
-		vtc_log(hp->vl, 0,
+		vtc_log(vl, 0,
 		    "Multiple Content-Length headers.\n");
 	http_swallow_body(hp, hp->req, 0);
-	vtc_log(hp->vl, 4, "bodylen = %s", hp->bodylen);
+	vtc_log(vl, 4, "bodylen = %s", hp->bodylen);
 }
 
 /* SECTION: client-server.spec.rxreqhdrs
@@ -1133,7 +1171,7 @@ cmd_http_rxrespbody(CMD_ARGS)
 	av++;
 
 	for(; *av != NULL; av++)
-		vtc_log(hp->vl, 0, "Unknown http rxreq spec: %s\n", *av);
+		vtc_log(hp->vl, 0, "Unknown http rxrespbody spec: %s\n", *av);
 	http_swallow_body(hp, hp->resp, 0);
 	vtc_log(hp->vl, 4, "bodylen = %s", hp->bodylen);
 }
@@ -1175,6 +1213,7 @@ cmd_http_txreq(CMD_ARGS)
 	const char *req = "GET";
 	const char *url = "/";
 	const char *proto = "HTTP/1.1";
+	const char *up = NULL;
 
 	(void)cmd;
 	(void)vl;
@@ -1195,15 +1234,39 @@ cmd_http_txreq(CMD_ARGS)
 		} else if (!strcmp(*av, "-req")) {
 			req = av[1];
 			av++;
+		} else if (!hp->sfd && !strcmp(*av, "-up")) {
+			up = av[1];
+			av++;
 		} else
 			break;
 	}
 	VSB_printf(hp->vsb, "%s %s %s%s", req, url, proto, nl);
 
+	if (up)
+		VSB_printf(hp->vsb, "Connection: Upgrade, HTTP2-Settings%s"
+				"Upgrade: h2c%s"
+				"HTTP2-Settings: %s%s", nl, nl, up, nl);
+
 	av = http_tx_parse_args(av, vl, hp, NULL);
 	if (*av != NULL)
 		vtc_log(hp->vl, 0, "Unknown http txreq spec: %s\n", *av);
 	http_write(hp, 4, "txreq");
+
+	if (up) {
+		parse_string("rxresp\n"
+				"expect resp.status == 101\n"
+				"expect resp.http.connection == Upgrade\n"
+				"expect resp.http.upgrade == h2c\n"
+				"txpri\n", http_cmds, hp, vl);
+		b64_settings(hp, up);
+		parse_string("stream 0 {\n"
+				"txsettings\n"
+				"rxsettings\n"
+				"txsettings -ack\n"
+				"rxsettings\n"
+				"expect settings.ack == true"
+			     "} -start\n", http_cmds, hp, vl);
+	}
 }
 
 /* SECTION: client-server.spec.recv
@@ -1453,6 +1516,11 @@ cmd_http_timeout(CMD_ARGS)
  *         Wait for the connected client to close the connection.
  */
 
+/* SECTION: client-server.spec.expect_close expect_close (server)
+ *
+ * Reads from the connection, expecting nothing to read but an EOF.
+ *
+ */
 static void
 cmd_http_expect_close(CMD_ARGS)
 {
@@ -1467,6 +1535,8 @@ cmd_http_expect_close(CMD_ARGS)
 	AZ(av[1]);
 
 	vtc_log(vl, 4, "Expecting close (fd = %d)", hp->fd);
+	if (hp->h2)
+		stop_h2(hp);
 	while (1) {
 		fds[0].fd = hp->fd;
 		fds[0].events = POLLIN | POLLERR;
@@ -1497,6 +1567,12 @@ cmd_http_expect_close(CMD_ARGS)
  *         Close the active TCP connection
  */
 
+/* SECTION: client-server.spec.close close (server)
+ *
+ * Close the connection. Not that if operating in H/2 mode, no extra (GOAWAY)
+ * frame is sent, it's simply a TCP close.
+ *
+ */
 static void
 cmd_http_close(CMD_ARGS)
 {
@@ -1508,6 +1584,8 @@ cmd_http_close(CMD_ARGS)
 	AZ(av[1]);
 	assert(hp->sfd != NULL);
 	assert(*hp->sfd >= 0);
+	if (hp->h2)
+		stop_h2(hp);
 	VTCP_close(&hp->fd);
 	vtc_log(vl, 4, "Closed");
 }
@@ -1518,6 +1596,11 @@ cmd_http_close(CMD_ARGS)
  *         Close the active connection (if any) and accept a new one.
  */
 
+/* SECTION: client-server.spec.accept accept (server)
+ *
+ * Close the potential current connection, and accept a new one. Note that this
+ * new connection is H/1.
+ */
 static void
 cmd_http_accept(CMD_ARGS)
 {
@@ -1529,6 +1612,8 @@ cmd_http_accept(CMD_ARGS)
 	AZ(av[1]);
 	assert(hp->sfd != NULL);
 	assert(*hp->sfd >= 0);
+	if (hp->h2)
+		stop_h2(hp);
 	if (hp->fd >= 0)
 		VTCP_close(&hp->fd);
 	vtc_log(vl, 4, "Accepting");
@@ -1594,11 +1679,113 @@ cmd_http_fatal(CMD_ARGS)
  *	Same as for the top-level barrier
  */
 
+char PREFACE[] = "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n";
+
+/* SECTION: client-server.spec.txpri txpri (client)
+ *
+ * Send an H/2 preface ("PRI * HTTP/2.0\\r\\n\\r\\nSM\\r\\n\\r\\n") and set
+ * client to H/2.
+ */
+static void
+cmd_http_txpri(CMD_ARGS)
+{
+	size_t l;
+	struct http *hp;
+	(void)cmd;
+	CAST_OBJ_NOTNULL(hp, priv, HTTP_MAGIC);
+	ONLY_CLIENT(hp, av);
+
+	vtc_dump(hp->vl, 4, "txpri", PREFACE, sizeof(PREFACE) - 1);
+	l = write(hp->fd, PREFACE, sizeof(PREFACE) - 1);
+	if (l != sizeof(PREFACE) - 1)
+		vtc_log(vl, hp->fatal, "Write failed: (%zd vs %zd) %s",
+		    l, sizeof(PREFACE) - 1, strerror(errno));
+	start_h2(hp);
+	AN(hp->h2);
+}
+
+/* SECTION: client-server.spec.rxpri rxpri (server)
+ *
+ * Receive a preface, and if it matches, sets the server to H/2, aborts
+ * otherwise.
+ */
+static void
+cmd_http_rxpri(CMD_ARGS)
+{
+	struct http *hp;
+	(void)cmd;
+	CAST_OBJ_NOTNULL(hp, priv, HTTP_MAGIC);
+	ONLY_SERVER(hp, av);
+
+	hp->prxbuf = 0;
+	if (!http_rxchar(hp, sizeof(PREFACE) - 1, 0))
+		vtc_log(vl, 0, "Couldn't retrieve connection preface");
+	if (strncmp(hp->rxbuf, PREFACE, sizeof(PREFACE) - 1))
+		vtc_log(vl, 0, "Received invalid preface\n");
+	start_h2(hp);
+	AN(hp->h2);
+}
+
+/* SECTION: client-server.spec.settings
+ *
+ * settings -dectbl INT
+ *         Force internal H/2 settings to certain values. Currently only
+ *         support setting the decoding table size.
+ */
+static void
+cmd_http_settings(CMD_ARGS)
+{
+	uint32_t n;
+	char *p;
+	struct http *hp;
+	CAST_OBJ_NOTNULL(hp, priv, HTTP_MAGIC);
+	(void)cmd;
+
+	if (!hp->h2)
+		vtc_log(hp->vl, 0, "Only possible in H/2 mode");
+
+	CAST_OBJ_NOTNULL(hp, priv, HTTP_MAGIC);
+
+	for(; *av != NULL; av++) {
+		if (!strcmp(*av, "-dectbl")) {
+			n = strtoul(av[1], &p, 0);
+			if (*p != '\0')
+				vtc_log(hp->vl, 0, "-dectbl takes an integer as"
+						" argument (found %s)", av[1]);
+			HPK_ResizeTbl(hp->decctx, n);
+			av++;
+		} else
+			vtc_log(vl, 0, "Unknown settings spec: %s\n", *av);
+	}
+}
+
+static void
+cmd_http_stream(CMD_ARGS)
+{
+	struct http *hp = (struct http *)priv;
+	CAST_OBJ_NOTNULL(hp, priv, HTTP_MAGIC);
+	if (!hp->h2) {
+		vtc_log(hp->vl, 4, "Not in H/2 mode, do what's needed");
+		if (hp->sfd)
+			parse_string("rxpri", http_cmds, hp, vl);
+		else
+			parse_string("txpri", http_cmds, hp, vl);
+		parse_string("stream 0 {\n"
+				"txsettings\n"
+				"rxsettings\n"
+				"txsettings -ack\n"
+				"rxsettings\n"
+				"expect settings.ack == true"
+			     "} -run\n", http_cmds, hp, vl);
+	}
+	cmd_stream(av, hp, cmd, vl);
+}
+
 /**********************************************************************
  * Execute HTTP specifications
  */
 
-static const struct cmds http_cmds[] = {
+const struct cmds http_cmds[] = {
 	{ "timeout",		cmd_http_timeout },
 	{ "txreq",		cmd_http_txreq },
 
@@ -1629,6 +1816,12 @@ static const struct cmds http_cmds[] = {
 	{ "loop",		cmd_http_loop },
 	{ "fatal",		cmd_http_fatal },
 	{ "non-fatal",		cmd_http_fatal },
+
+	{ "rxpri",		cmd_http_rxpri },
+	{ "txpri",		cmd_http_txpri },
+	{ "stream",		cmd_http_stream },
+	{ "settings",		cmd_http_settings },
+	{ "upgrade",		cmd_http_upgrade },
 	{ NULL,			NULL }
 };
 
@@ -1665,6 +1858,8 @@ http_process(struct vtclog *vl, const char *spec, int sock, int *sfd)
 
 	VTCP_hisname(sock, hp->rem_ip, VTCP_ADDRBUFSIZE, hp->rem_port, VTCP_PORTBUFSIZE);
 	parse_string(spec, http_cmds, hp, vl);
+	if (hp->h2)
+		stop_h2(hp);
 	retval = hp->fd;
 	VSB_destroy(&hp->vsb);
 	free(hp->rxbuf);
diff --git a/bin/varnishtest/vtc_http.h b/bin/varnishtest/vtc_http.h
new file mode 100644
index 0000000..f944f36
--- /dev/null
+++ b/bin/varnishtest/vtc_http.h
@@ -0,0 +1,45 @@
+#define MAX_HDR		50
+
+struct http {
+	unsigned		magic;
+#define HTTP_MAGIC		0x2f02169c
+	int			fd;
+	int			*sfd;
+	int			timeout;
+	struct vtclog		*vl;
+
+	struct vsb		*vsb;
+
+	int			nrxbuf;
+	char			*rxbuf;
+	char			*rem_ip;
+	char			*rem_port;
+	int			prxbuf;
+	char			*body;
+	unsigned		bodyl;
+	char			bodylen[20];
+	char			chunklen[20];
+
+	char			*req[MAX_HDR];
+	char			*resp[MAX_HDR];
+
+	int			gziplevel;
+	int			gzipresidual;
+
+	int			fatal;
+
+	/* H/2 */
+	unsigned		h2;
+	int			wf;
+
+	pthread_t		tp;
+	VTAILQ_HEAD(, stream)   streams;
+	pthread_mutex_t		mtx;
+	pthread_cond_t          cond;
+	struct hpk_ctx		*encctx;
+	struct hpk_ctx		*decctx;
+	uint64_t		iws;
+	int64_t			ws;
+};
+
+
diff --git a/bin/varnishtest/vtc_http2.c b/bin/varnishtest/vtc_http2.c
new file mode 100644
index 0000000..4a90d24
--- /dev/null
+++ b/bin/varnishtest/vtc_http2.c
@@ -0,0 +1,2785 @@
+/*-
+ * Copyright (c) 2008-2016 Varnish Software AS
+ * All rights reserved.
+ *
+ * Author: Guillaume Quintard <guillaume.quintard at gmail.com>
+ *
+ * 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 <sys/types.h>
+#include <sys/socket.h>
+
+#include <errno.h>
+#include <math.h>
+#include <poll.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <ctype.h>
+#include <netinet/in.h>
+
+#include "vtc.h"
+#include "vtc_http.h"
+
+#include "vct.h"
+#include "vgz.h"
+#include "vnum.h"
+#include "vre.h"
+#include "hpack.h"
+#include "vend.h"
+
+#define MAX_HDR		50
+#define ERR_MAX		13
+#define BUF_SIZE	(1024*2048)
+
+static const char *const h2_errs[] = {
+#define H2_ERROR(n,v,t)	[v] = #n,
+#include <tbl/h2_error.h>
+	NULL
+};
+
+static const char *const h2_types[] = {
+#define H2_FRAME(l,u,t,f) [t] = #u,
+#include <tbl/h2_frames.h>
+	NULL
+};
+
+#define SETTINGS_MAX 0x06
+
+static const char * const h2_settings[] = {
+	[0] = "unknown",
+#define H2_SETTINGS(U,v,d) [v] = #U,
+#include <tbl/h2_settings.h>
+	NULL
+};
+
+enum h2_type {
+#define H2_FRAME(l,u,t,f) TYPE_##u = t,
+#include <tbl/h2_frames.h>
+	TYPE_MAX
+};
+
+enum {
+	ACK = 0x1,
+	END_STREAM = 0x1,
+	PADDED = 0x8,
+	END_HEADERS = 0x4,
+	PRIORITY = 0x20,
+};
+
+struct stream {
+	unsigned		magic;
+#define STREAM_MAGIC		0x63f1fac2
+	uint32_t		id;
+	char			*spec;
+	char			*name;
+	VTAILQ_ENTRY(stream)    list;
+	unsigned		running;
+	pthread_cond_t          cond;
+	struct frame		*frame;
+	pthread_t		tp;
+	unsigned		reading;
+	struct http		*hp;
+	int64_t			ws;
+
+	VTAILQ_HEAD(, frame)   fq;
+
+	char			*body;
+	int			bodylen;
+	struct hpk_hdr		req[MAX_HDR];
+	struct hpk_hdr		resp[MAX_HDR];
+
+	int			dependency;
+	int			weight;
+};
+
+static void
+clean_headers(struct hpk_hdr *h)
+{
+	while (h->t) {
+		if (h->key.len)
+			free(h->key.ptr);
+		if (h->value.len)
+			free(h->value.ptr);
+		memset(h, 0, sizeof(*h));
+		h++;
+	}
+}
+
+#define ONLY_CLIENT(hp, av)						\
+	do {								\
+		if (hp->sfd != NULL)					\
+			vtc_log(hp->vl, 0,				\
+			    "\"%s\" only possible in client", av[0]);	\
+	} while (0)
+
+#define ONLY_SERVER(hp, av)						\
+	do {								\
+		if (hp->sfd == NULL)					\
+			vtc_log(hp->vl, 0,				\
+			    "\"%s\" only possible in server", av[0]);	\
+	} while (0)
+
+static void
+http_write(const struct http *hp, int lvl,
+    const char *buf, int s, const char *pfx)
+{
+	ssize_t l;
+
+	CHECK_OBJ_NOTNULL(hp, HTTP_MAGIC);
+	AN(buf);
+	AN(pfx);
+
+	vtc_dump(hp->vl, lvl, pfx, buf, s);
+	l = write(hp->fd, buf, s);
+	if (l != s)
+		vtc_log(hp->vl, hp->fatal, "Write failed: (%zd vs %d) %s",
+		    l, s, strerror(errno));
+}
+
+static int
+get_bytes(const struct http *hp, char *buf, int n)
+{
+	int i;
+	struct pollfd pfd[1];
+
+	CHECK_OBJ_NOTNULL(hp, HTTP_MAGIC);
+	AN(buf);
+
+	while (n > 0) {
+		pfd[0].fd = hp->fd;
+		pfd[0].events = POLLIN;
+		pfd[0].revents = 0;
+		i = poll(pfd, 1, hp->timeout);
+		if (i < 0 && errno == EINTR)
+			continue;
+		if (i == 0)
+			vtc_log(hp->vl, hp->fatal,
+			    "HTTP2 rx timeout (fd:%d %u ms)",
+			    hp->fd, hp->timeout);
+		if (i < 0)
+			vtc_log(hp->vl, hp->fatal,
+			    "HTTP2 rx failed (fd:%d poll: %s)",
+			    hp->fd, strerror(errno));
+		assert(i > 0);
+		i = read(hp->fd, buf, n);
+		if (!(pfd[0].revents & POLLIN))
+			vtc_log(hp->vl, 4,
+			    "HTTP2 rx poll (fd:%d revents: %x n=%d, i=%d)",
+			    hp->fd, pfd[0].revents, n, i);
+		if (i == 0)
+			vtc_log(hp->vl, hp->fatal,
+			    "HTTP2 rx EOF (fd:%d read: %s)",
+			    hp->fd, strerror(errno));
+		if (i < 0)
+			vtc_log(hp->vl, hp->fatal,
+			    "HTTP2 rx failed (fd:%d read: %s)",
+			    hp->fd, strerror(errno));
+		n -= i;
+	}
+	return (1);
+
+}
+
+VTAILQ_HEAD(fq_head, frame);
+
+struct frame {
+	unsigned	magic;
+#define	FRAME_MAGIC	0x5dd3ec4
+	uint32_t        size;
+	uint32_t	stid;
+	uint8_t         type;
+	uint8_t         flags;
+	char		*data;
+
+	VTAILQ_ENTRY(frame)    list;
+
+	union {
+		struct {
+			uint32_t stream;
+			uint8_t  exclusive;
+			uint8_t  weight;
+		}		prio;
+		uint32_t	rst_err;
+		double settings[SETTINGS_MAX+1];
+		struct {
+			char data[9];
+			int ack;
+		}		ping;
+		struct {
+			uint32_t err;
+			uint32_t stream;
+			char	 *debug;
+		}		goaway;
+		uint32_t	winup_size;
+		uint32_t	promised;
+		uint8_t		padded;
+	} md;
+};
+
+static void
+readFrameHeader(struct frame *f, const char *buf)
+{
+	CHECK_OBJ_NOTNULL(f, FRAME_MAGIC);
+	AN(buf);
+
+	f->size  = (unsigned char)buf[0] << 16;
+	f->size += (unsigned char)buf[1] << 8;
+	f->size += (unsigned char)buf[2];
+
+	f->type = (unsigned char)buf[3];
+
+	f->flags = (unsigned char)buf[4];
+
+	f->stid  = (0xff & (unsigned char)buf[5]) << 24;
+	f->stid += (0xff & (unsigned char)buf[6]) << 16;
+	f->stid += (0xff & (unsigned char)buf[7]) <<  8;
+	f->stid += (0xff & (unsigned char)buf[8]);
+};
+
+static void
+writeFrameHeader(char *buf, const struct frame *f)
+{
+	CHECK_OBJ_NOTNULL(f, FRAME_MAGIC);
+	AN(buf);
+	buf[0] = (f->size >> 16) & 0xff;
+	buf[1] = (f->size >>  8) & 0xff;
+	buf[2] = (f->size      ) & 0xff;
+
+	buf[3] = f->type;
+
+	buf[4] = f->flags;
+
+	buf[5] = (f->stid >> 24) & 0xff;
+	buf[6] = (f->stid >> 16) & 0xff;
+	buf[7] = (f->stid >>  8) & 0xff;
+	buf[8] = (f->stid      ) & 0xff;
+}
+
+#define INIT_FRAME(f, ty, sz, id, fl) \
+do { \
+	f.magic = FRAME_MAGIC; \
+	f.type = TYPE_ ## ty; \
+	f.size = sz; \
+	f.stid = id; \
+	f.flags = fl; \
+	f.data = NULL; \
+} while(0)
+
+static void
+clean_frame(struct frame **f)
+{
+	AN(f);
+	if (!*f)
+		return;
+
+	CHECK_OBJ_NOTNULL(*f, FRAME_MAGIC);
+
+	if ((*f)->type == TYPE_GOAWAY)
+		free((*f)->md.goaway.debug);
+	free((*f)->data);
+	free(*f);
+	*f = NULL;
+}
+
+static void
+write_frame(struct http *hp, const struct frame *f, const unsigned lock)
+{
+	ssize_t l;
+	char hdr[9];
+
+	CHECK_OBJ_NOTNULL(hp, HTTP_MAGIC);
+	CHECK_OBJ_NOTNULL(f, FRAME_MAGIC);
+
+	writeFrameHeader(hdr, f);
+
+	vtc_log(hp->vl, 3, "tx: stream: %d, type: %s (%d), "
+			"flags: 0x%02x, size: %d",
+			f->stid,
+			f->type < TYPE_MAX ? h2_types[f->type] : "?",
+			f->type, f->flags, f->size);
+
+	if (lock)
+		AZ(pthread_mutex_lock(&hp->mtx));
+	l = write(hp->fd, hdr, sizeof(hdr));
+	if (l != sizeof(hdr))
+		vtc_log(hp->vl, hp->fatal, "Write failed: (%zd vs %zd) %s",
+		    l, sizeof(hdr), strerror(errno));
+
+	if (f->size) {
+		AN(f->data);
+		l = write(hp->fd, f->data, f->size);
+		if (l != f->size)
+			vtc_log(hp->vl, hp->fatal,
+					"Write failed: (%zd vs %d) %s",
+					l, f->size, strerror(errno));
+	}
+	if (lock)
+		AZ(pthread_mutex_unlock(&hp->mtx));
+}
+
+static void
+exclusive_stream_dependency(const struct stream *s)
+{
+	struct stream *target = NULL;
+	struct http *hp = s->hp;
+	
+	if (s->id == 0)
+		return;
+	
+	VTAILQ_FOREACH(target, &hp->streams, list) {
+		if (target->id != s->id && target->dependency == s->dependency)
+			target->dependency = s->id;
+	}
+}
+
+static void
+explain_flags(uint8_t flags, uint8_t type, struct vtclog *vl)
+{
+	if (flags & ACK && (type == TYPE_PING || type == TYPE_SETTINGS)) {
+		vtc_log(vl, 3, "flag: ACK");
+		flags &= ~ACK;
+	} else if (flags & END_STREAM &&
+			(type == TYPE_HEADERS ||
+			 type == TYPE_PUSH_PROMISE ||
+			 type == TYPE_DATA)) {
+		vtc_log(vl, 3, "flag: END_STREAM");
+		flags &= ~END_STREAM;
+	} else if (flags & END_HEADERS &&
+			(type == TYPE_HEADERS ||
+			 type == TYPE_PUSH_PROMISE ||
+			 type == TYPE_CONTINUATION)) {
+		vtc_log(vl, 3, "flag: END_TYPE_HEADERS");
+		flags &= ~END_HEADERS;
+	} else if (flags & PRIORITY &&
+			(type == TYPE_HEADERS ||
+			 type == TYPE_PUSH_PROMISE)) {
+		vtc_log(vl, 3, "flag: END_PRIORITY");
+		flags &= ~PRIORITY;
+	} else if (flags & PADDED &&
+			(type == TYPE_DATA ||
+			 type == TYPE_HEADERS ||
+			 type == TYPE_PUSH_PROMISE)) {
+		vtc_log(vl, 3, "flag: PADDED");
+		flags &= ~PADDED;
+	} else if (flags)
+		vtc_log(vl, 3, "UNKNOWN FLAG(S): 0x%02x", flags);
+}
+
+static void
+parse_data(struct stream *s, struct frame *f)
+{
+	struct http *hp;
+	uint32_t size = f->size;
+	char *data = f->data;
+	CHECK_OBJ_NOTNULL(f, FRAME_MAGIC);
+	CHECK_OBJ_NOTNULL(s, STREAM_MAGIC);
+	CAST_OBJ_NOTNULL(hp, s->hp, HTTP_MAGIC);;
+
+	if (f->flags & PADDED) {
+		f->md.padded = *((uint8_t *)data);
+		if (f->md.padded >= size) {
+			vtc_log(hp->vl, hp->fatal,
+					"invalid padding: %d reported,"
+					"but size is only %d",
+					f->md.padded, size);
+			size = 0;
+			f->md.padded = 0;
+		}
+		data++;
+		size -= f->md.padded + 1;
+		vtc_log(hp->vl, 4, "padding: %3d", f->md.padded);
+	}
+
+	if (!size)
+		vtc_log(hp->vl, 4, "s%u - no data", s->id);
+
+	if (s->id)
+		s->ws -= size;
+	s->hp->ws -= size;
+
+	if (s->body) {
+		s->body = realloc(s->body, s->bodylen + size + 1L);
+	} else {
+		AZ(s->bodylen);
+		s->body = malloc(size + 1L);
+	}
+	AN(s->body);
+	memcpy(s->body + s->bodylen, data, size);
+	s->bodylen += size;
+	s->body[s->bodylen] = '\0';
+}
+
+static void
+decode_hdr(struct http *hp, struct hpk_hdr *h, const struct vsb *vsb)
+{
+	struct hpk_iter *iter;
+	enum hpk_result r = hpk_err;
+	int n;
+
+	CHECK_OBJ_NOTNULL(vsb, VSB_MAGIC);
+	CAST_OBJ_NOTNULL(hp, hp, HTTP_MAGIC);;
+
+	if (VSB_len(vsb) == 0)
+		return;
+
+	iter = HPK_NewIter(hp->decctx, VSB_data(vsb), VSB_len(vsb));
+
+	n = 0;
+	while (n < MAX_HDR && h[n].t)
+		n++;
+	while (n < MAX_HDR) {
+		r = HPK_DecHdr(iter, h + n);
+		if (r == hpk_err )
+			break;
+		vtc_log(hp->vl, 4,
+				"header[%2d]: %s : %s",
+				n,
+				h[n].key.ptr,
+				h[n].value.ptr);
+		n++;
+		if (r == hpk_done)
+			break;
+	}
+
+	if (r != hpk_done)
+		vtc_log(hp->vl, hp->fatal ? 4 : 0,
+				"Header decoding failed (%d) %d", r, hp->fatal);
+	else if (n == MAX_HDR)
+		vtc_log(hp->vl, hp->fatal,
+				"Max number of headers reached (%d)", MAX_HDR);
+
+	HPK_FreeIter(iter);
+}
+
+static void
+parse_hdr(struct stream *s, struct frame *f, struct vsb *vsb)
+{
+	int shift = 0;
+	int exclusive = 0;
+	uint32_t size = f->size;
+	char *data = f->data;
+	struct http *hp;
+	uint32_t n;
+
+	CHECK_OBJ_NOTNULL(f, FRAME_MAGIC);
+	CHECK_OBJ_NOTNULL(s, STREAM_MAGIC);
+	CHECK_OBJ_NOTNULL(vsb, VSB_MAGIC);
+	CAST_OBJ_NOTNULL(hp, s->hp, HTTP_MAGIC);;
+
+	if (f->flags & PADDED && f->type != TYPE_CONTINUATION) {
+		f->md.padded = *((uint8_t *)data);
+		if (f->md.padded >= size) {
+			vtc_log(hp->vl, hp->fatal,
+					"invalid padding: %d reported,"
+					"but size is only %d",
+					f->md.padded, size);
+			size = 0;
+			f->md.padded = 0;
+		}
+		shift += 1;
+		size -= f->md.padded;
+		vtc_log(hp->vl, 4, "padding: %3d", f->md.padded);
+	}
+
+	if (f->type == TYPE_HEADERS && f->flags & PRIORITY){
+		shift += 5;
+		n = vbe32dec(f->data);
+		s->dependency = n & ~(1 << 31);
+		exclusive = n >> 31;
+
+		s->weight = f->data[4];
+		if (exclusive)
+			exclusive_stream_dependency(s);
+
+		vtc_log(hp->vl, 4, "stream->dependency: %u", s->dependency);
+		vtc_log(hp->vl, 4, "stream->weight: %u", s->weight);
+	} else if (f->type == TYPE_PUSH_PROMISE){
+		shift += 4;
+		n = vbe32dec(f->data);
+		f->md.promised = n & ~(1 << 31);
+	}
+
+	AZ(VSB_bcat(vsb, data + shift, size - shift));
+}
+
+static void
+parse_prio(struct stream *s, struct frame *f)
+{
+	struct http *hp;
+	char *buf;
+	uint32_t n;
+
+	CHECK_OBJ_NOTNULL(f, FRAME_MAGIC);
+	CHECK_OBJ_NOTNULL(s, STREAM_MAGIC);
+	CAST_OBJ_NOTNULL(hp, s->hp, HTTP_MAGIC);;
+
+	if (f->size != 5)
+		vtc_log(hp->vl, 0, "Size should be 5, but isn't (%d)", f->size);
+
+	buf = f->data;
+	AN(buf);
+
+	n = vbe32dec(f->data);
+	f->md.prio.stream = n & ~(1 << 31);
+
+	s->dependency = f->md.prio.stream;
+	if (n >> 31){
+		f->md.prio.exclusive = 1;
+		exclusive_stream_dependency(s);
+	}
+
+	buf += 4;
+	f->md.prio.weight = *buf;
+	s->weight = f->md.prio.weight;
+
+	vtc_log(hp->vl, 3, "prio->stream: %u", f->md.prio.stream);
+	vtc_log(hp->vl, 3, "prio->weight: %u", f->md.prio.weight);
+}
+
+static void
+parse_rst(const struct stream *s, struct frame *f) {
+	struct http *hp;
+	uint32_t err;
+	const char *buf;
+	CHECK_OBJ_NOTNULL(f, FRAME_MAGIC);
+	CHECK_OBJ_NOTNULL(s, STREAM_MAGIC);
+	CAST_OBJ_NOTNULL(hp, s->hp, HTTP_MAGIC);;
+
+	if (f->size != 4)
+		vtc_log(hp->vl, 0, "Size should be 4, but isn't (%d)", f->size);
+
+	err = vbe32dec(f->data);
+	f->md.rst_err = err;
+
+	vtc_log(hp->vl, 2, "ouch");
+	if (err <= ERR_MAX)
+		buf = h2_errs[err];
+	else
+		buf = "unknown";
+	vtc_log(hp->vl, 4, "rst->err: %s (%d)", buf, err);
+
+}
+
+static void
+parse_settings(const struct stream *s, struct frame *f)
+{
+	struct http *hp;
+	int i, t, v;
+	const char *buf;
+	CHECK_OBJ_NOTNULL(f, FRAME_MAGIC);
+	CHECK_OBJ_NOTNULL(s, STREAM_MAGIC);
+	CAST_OBJ_NOTNULL(hp, s->hp, HTTP_MAGIC);;
+
+	if (f->size % 6)
+		vtc_log(hp->vl, 0, "Size should be a multiple of 6, but isn't (%d)", f->size);
+
+	for (i = 0; i <= SETTINGS_MAX; i++)
+		f->md.settings[i] = NAN;
+
+	for (i = 0; i < f->size;) {
+		t = vbe16dec(f->data + i);
+		i += 2;
+		v = vbe32dec(f->data + i);
+		if (t <= SETTINGS_MAX) {
+			buf = h2_settings[t];
+			f->md.settings[t] = v;
+		} else
+			buf = "unknown";
+		i += 4;
+
+		if (t == 1 )
+			HPK_ResizeTbl(s->hp->encctx, v);
+
+		vtc_log(hp->vl, 4, "settings->%s (%d): %d", buf, t, v);
+	}
+
+}
+
+static void
+parse_ping(const struct stream *s, struct frame *f)
+{
+	struct http *hp;
+	CHECK_OBJ_NOTNULL(f, FRAME_MAGIC);
+	CHECK_OBJ_NOTNULL(s, STREAM_MAGIC);
+	CAST_OBJ_NOTNULL(hp, s->hp, HTTP_MAGIC);;
+	if (f->size != 8)
+		vtc_log(hp->vl, 0, "Size should be 8, but isn't (%d)", f->size);
+	f->md.ping.ack = f->flags & ACK;
+	memcpy(f->md.ping.data, f->data, 8);
+	f->md.ping.data[8] = '\0';
+
+	vtc_log(hp->vl, 4, "ping->data: %s", f->md.ping.data);
+
+}
+
+static void
+parse_goaway(const struct stream *s, struct frame *f)
+{
+	struct http *hp;
+	const char *err_buf;
+	uint32_t err, stid;
+	CHECK_OBJ_NOTNULL(f, FRAME_MAGIC);
+	CHECK_OBJ_NOTNULL(s, STREAM_MAGIC);
+	CAST_OBJ_NOTNULL(hp, s->hp, HTTP_MAGIC);;
+
+	if (f->size < 8)
+		vtc_log(hp->vl, 0, "Size should be at least 8, but isn't (%d)", f->size);
+	if (f->data[0] & (1<<7))
+		vtc_log(hp->vl, 0, "First bit of data is reserved and should be 0");
+
+	stid = vbe32dec(f->data);
+	err = vbe32dec(f->data + 4);
+	f->md.goaway.err = err;
+	f->md.goaway.stream = stid;
+
+	if (err <= ERR_MAX)
+		err_buf = h2_errs[err];
+	else
+		err_buf = "unknown";
+
+	if (f->size > 8) {
+		f->md.goaway.debug = malloc(f->size - 8 + 1L);
+		AN(f->md.goaway.debug);
+		f->md.goaway.debug[f->size - 8] = '\0';
+
+		memcpy(f->md.goaway.debug, f->data + 8, f->size - 8);
+	}
+
+	vtc_log(hp->vl, 3, "goaway->laststream: %d", stid);
+	vtc_log(hp->vl, 3, "goaway->err: %s (%d)", err_buf, err);
+	if (f->md.goaway.debug)
+		vtc_log(hp->vl, 3, "goaway->debug: %s", f->md.goaway.debug);
+}
+
+static void
+parse_winup(const struct stream *s, struct frame *f)
+{
+	struct http *hp;
+	uint32_t size;
+	CHECK_OBJ_NOTNULL(f, FRAME_MAGIC);
+	CHECK_OBJ_NOTNULL(s, STREAM_MAGIC);
+	CAST_OBJ_NOTNULL(hp, s->hp, HTTP_MAGIC);;
+
+	if (f->size != 4)
+		vtc_log(hp->vl, 0, "Size should be 4, but isn't (%d)", f->size);
+	if (f->data[0] & (1<<7))
+		vtc_log(hp->vl, s->hp->fatal, "First bit of data is reserved and should be 0");
+
+	size = vbe32dec(f->data);
+	f->md.winup_size = size;
+
+	vtc_log(hp->vl, 3, "winup->size: %d", size);
+}
+
+/* read a frame and queue it in the relevant stream, wait if not present yet.
+ */
+static void *
+receive_frame(void *priv)
+{
+	struct http *hp;
+	char hdr[9];
+	struct frame *f;
+	struct stream *s;
+	int expect_cont = 0;
+	struct vsb *vsb = NULL;
+	struct hpk_hdr *hdrs = NULL;
+
+	CAST_OBJ_NOTNULL(hp, priv, HTTP_MAGIC);
+
+	AZ(pthread_mutex_lock(&hp->mtx));
+	while (hp->h2) {
+		/*no wanted frames? */
+		if (hp->wf == 0) {
+			AZ(pthread_cond_wait(&hp->cond, &hp->mtx));
+			continue;
+		}
+		AZ(pthread_mutex_unlock(&hp->mtx));
+
+		if (!get_bytes(hp, hdr, 9)) {
+			vtc_log(hp->vl, 1, "could not get header");
+			return (NULL);
+		}
+		ALLOC_OBJ(f, FRAME_MAGIC);
+		AN(f);
+		readFrameHeader(f, hdr);
+
+		vtc_log(hp->vl, 3, "rx: stream: %d, type: %s (%d), "
+				"flags: 0x%02x, size: %d",
+				f->stid,
+				f->type < TYPE_MAX ? h2_types[f->type] : "?",
+				f->type, f->flags, f->size);
+		explain_flags(f->flags, f->type, hp->vl);
+
+		if (f->size) {
+			f->data = malloc(f->size + 1L);
+			AN(f->data);
+			f->data[f->size] = '\0';
+			get_bytes(hp, f->data, f->size);
+		}
+
+		/* is the corresponding stream waiting? */
+		AZ(pthread_mutex_lock(&hp->mtx));
+		s = NULL;
+		while (!s) {
+			VTAILQ_FOREACH(s, &hp->streams, list) {
+				if (s->id == f->stid)
+					break;
+			}
+			if (!s)
+				AZ(pthread_cond_wait(&hp->cond, &hp->mtx));
+			if (!hp->h2) {
+				clean_frame(&f);
+				AZ(pthread_mutex_unlock(&hp->mtx));
+				return (NULL);
+			}
+		}
+		AZ(pthread_mutex_unlock(&hp->mtx));
+
+		if (expect_cont && (f->type != TYPE_CONTINUATION ||
+					expect_cont != s->id))
+			vtc_log(hp->vl, 0, "Expected CONTINUATION frame for"
+					" stream %u", expect_cont);
+
+		/* parse the frame according to it type, and fill the metada */
+		switch (f->type) {
+			case TYPE_DATA:
+				parse_data(s, f);
+				break;
+			case TYPE_PUSH_PROMISE:
+				hdrs = s->req;
+				/*FALLTHROUGH*/
+			case TYPE_HEADERS:
+				if (!hdrs) {
+					if (hp->sfd)
+						hdrs = s->req;
+					else
+						hdrs = s->resp;
+				}
+				AZ(vsb);
+				vsb = VSB_new_auto();
+				/*FALLTHROUGH*/
+			case TYPE_CONTINUATION:
+				AN(hdrs);
+				expect_cont = s->id;
+				parse_hdr(s, f, vsb);
+				if (f->flags & END_HEADERS) {
+					expect_cont = 0;
+					VSB_finish(vsb);
+					decode_hdr(hp, hdrs, vsb);
+					VSB_destroy(&vsb);
+					hdrs = NULL;
+				}
+				break;
+			case TYPE_PRIORITY:
+				parse_prio(s, f);
+				break;
+			case TYPE_RST_STREAM:
+				parse_rst(s, f);
+				break;
+			case TYPE_SETTINGS:
+				parse_settings(s, f);
+				break;
+			case TYPE_PING:
+				parse_ping(s, f);
+				break;
+			case TYPE_GOAWAY:
+				parse_goaway(s, f);
+				break;
+			case TYPE_WINDOW_UPDATE:
+				parse_winup(s, f);
+				break;
+			default:
+				WRONG("wrong frame type");
+		}
+
+		AZ(pthread_mutex_lock(&hp->mtx));
+		VTAILQ_INSERT_HEAD(&s->fq, f, list);
+		hp->wf--;
+		AZ(pthread_cond_signal(&s->cond));
+		continue;
+	}
+	AZ(pthread_mutex_unlock(&hp->mtx));
+	AZ(vsb);
+	return (NULL);
+}
+
+#define STRTOU32(n, s, p, v, c) \
+	do { \
+	n = strtoul(s, &p, 0); \
+	if (*p != '\0') { \
+		vtc_log(v, 0, "%s takes an integer as argument" \
+			"(found %s)", c, s); \
+		WRONG("Couldn't convert to integer");\
+	} \
+	} while (0)
+
+#define STRTOU32_CHECK(n, sp, p, v, c, l)				       \
+do {									       \
+	sp++;								       \
+	AN(*sp);							       \
+	STRTOU32(n, *sp, p, v, c);					       \
+	if (l && n >= (1 << l))						       \
+		vtc_log(v, 0, c " must be a %d-bits integer (found %s)",       \
+			       l, *sp);				       \
+} while (0)
+
+#define CHECK_LAST_FRAME(TYPE) \
+	if (!f || f->type != TYPE_ ## TYPE) { \
+		vtc_log(s->hp->vl, 0, "Last frame was not of type " #TYPE); \
+	}
+
+#define RETURN_SETTINGS(idx) \
+do { \
+	if (isnan(f->md.settings[idx])) { \
+		return (NULL); \
+	} \
+	snprintf(buf, 20, "%.0f", f->md.settings[idx]); \
+	return (buf); \
+} while (0);
+
+#define RETURN_BUFFED(val) \
+do { \
+	snprintf(buf, 20, "%d", val); \
+	return (buf); \
+} while (0)
+
+static char *
+find_header(const struct hpk_hdr *h, const char *k)
+{
+	AN(k);
+
+	int kl = strlen(k);
+	while (h->t) {
+		if (kl == h->key.len  && !memcmp(h->key.ptr, k, kl))
+			return h->value.ptr;
+		h++;
+	}
+	return (NULL);
+}
+/* SECTION: stream.spec.zexpect expect
+ *
+ * expect in stream works as it does in client or server, except that the
+ * elements compared will be different.
+ *
+ * Most of these elements will be frame specific, meaning that the last frame
+ * received on that stream must of the correct type.
+ *
+ * Here the list of keywords you can look at.
+ */
+static const char *
+cmd_var_resolve(const struct stream *s, const char *spec, char *buf)
+{
+	uint32_t idx;
+	int n;
+	const struct hpk_hdr *h;
+	struct hpk_ctx *ctx;
+	struct frame *f = s->frame;
+
+	CHECK_OBJ_NOTNULL(s, STREAM_MAGIC);
+	CHECK_OBJ_NOTNULL(s->hp, HTTP_MAGIC);
+	AN(spec);
+	AN(buf);
+
+	n = 0;
+	/* SECTION: stream.spec.zexpect.ping PING specific
+	 * ping.data
+	 *         The 8-bytes string of the PING frame payload.
+	 * ping.ack (PING)
+	 *         "true" if the ACK flag was set, "false" otherwise.
+	 */
+	if (!strcmp(spec, "ping.data")) {
+		CHECK_LAST_FRAME(PING);
+		return (f->md.ping.data);
+	}
+	else if (!strcmp(spec, "ping.ack")) {
+		CHECK_LAST_FRAME(PING);
+		snprintf(buf, 20, (f->flags & ACK) ? "true" : "false");
+		return (buf);
+	}
+	/* SECTION: stream.spec.zexpect.winup WINDOW_UPDATE specific
+	 * winup.size
+	 *         The size of the upgrade given by the WINDOW_UPDATE frame.
+	 */
+	else if (!strcmp(spec, "winup.size")) {
+		CHECK_LAST_FRAME(WINDOW_UPDATE);
+		RETURN_BUFFED(f->md.winup_size);
+	}
+	/* SECTION: stream.spec.zexpect.prio PRIORITY specific
+	 * prio.stream
+	 *         The stream ID announced.
+	 *
+	 * prio.exclusive
+	 *         "true" if the priority is exclusive, else "false".
+	 *
+	 * prio.weight
+	 *         The dependency weight.
+	 */
+	else if (!strcmp(spec, "prio.stream")) {
+		CHECK_LAST_FRAME(PRIORITY);
+		RETURN_BUFFED(f->md.prio.stream);
+	}
+	else if (!strcmp(spec, "prio.exclusive")) {
+		CHECK_LAST_FRAME(PRIORITY);
+		snprintf(buf, 20, f->md.prio.exclusive ? "true" : "false");
+		return (buf);
+	}
+	else if (!strcmp(spec, "prio.weight")) {
+		CHECK_LAST_FRAME(PRIORITY);
+		RETURN_BUFFED(f->md.prio.weight);
+	}
+	/* SECTION: stream.spec.zexpect.rst RESET_STREAM specific
+	 * rst.err
+	 *         The error code (as integer) of the RESET_STREAM frame.
+	 */
+	else if (!strcmp(spec, "rst.err")) {
+		CHECK_LAST_FRAME(RST_STREAM);
+		RETURN_BUFFED(f->md.rst_err);
+	}
+	/* SECTION: stream.spec.zexpect.settings SETTINGS specific
+	 *
+	 * settings.ack
+	 *         "true" if the ACK flag was set, else ""false.
+	 *
+	 * settings.push
+	 *         "true" if the push settings was set to yes, "false" if set to
+	 *         no, and <undef> if not present.
+	 *
+	 * settings.hdrtbl
+	 *         Value of HEADER_TABLE_SIZE if set, <undef> otherwise.
+	 *
+	 * settings.maxstreams
+	 *         Value of MAX_CONCURRENT_STREAMS if set, <undef> otherwise.
+	 *
+	 * settings.winsize
+	 *         Value of INITIAL_WINDOW_SIZE if set, <undef> otherwise.
+	 *
+	 * setting.framesize
+	 *         Value of MAX_FRAME_SIZE if set, <undef> otherwise.
+	 *
+	 * settings.hdrsize
+	 *         Value of MAX_HEADER_LIST_SIZE if set, <undef> otherwise.
+	 */
+	else if (!strncmp(spec, "settings.", 9)) {
+		CHECK_LAST_FRAME(SETTINGS);
+		spec += 9;
+		if (!strcmp(spec, "ack")) {
+			snprintf(buf, 20, (f->flags & ACK) ? "true" : "false");
+			return (buf);
+		}
+		else if (!strcmp(spec, "push")) {
+			if (isnan(f->md.settings[2]))
+				return (NULL);
+			else if (f->md.settings[2] == 1)
+				snprintf(buf, 20, "true");
+			else
+				snprintf(buf, 20, "false");
+			return (buf);
+		}
+		else if (!strcmp(spec, "hdrtbl"))     { RETURN_SETTINGS(1); }
+		else if (!strcmp(spec, "maxstreams")) { RETURN_SETTINGS(3); }
+		else if (!strcmp(spec, "winsize"))    { RETURN_SETTINGS(4); }
+		else if (!strcmp(spec, "framesize"))  { RETURN_SETTINGS(5); }
+		else if (!strcmp(spec, "hdrsize"))    { RETURN_SETTINGS(6); }
+	}
+	/* SECTION: stream.spec.zexpect.push PUSH_PROMISE specific
+	 * push.id
+	 *         The id of the promised stream.
+	 */
+	else if (!strcmp(spec, "push.id")) {
+		CHECK_LAST_FRAME(PUSH_PROMISE);
+		RETURN_BUFFED(f->md.promised);
+	}
+	/* SECTION: stream.spec.zexpect.goaway GOAWAY specific
+	 * goaway.err
+	 *         The error code (as integer) of the GOAWAY frame.
+	 *
+	 * goaway.laststream
+	 *         Last-Stream-ID
+	 *
+	 * goaway.debug
+	 *         Debug data, if any.
+	 */
+	else if (!strncmp(spec, "goaway.", 7)) {
+		spec += 7;
+		CHECK_LAST_FRAME(GOAWAY);
+
+		if (!strcmp(spec, "err")) {
+			RETURN_BUFFED(f->md.goaway.err);
+		}
+		else if (!strcmp(spec, "laststream")) {
+			RETURN_BUFFED(f->md.goaway.stream);
+		}
+		else if (!strcmp(spec, "debug")) {
+			return (f->md.goaway.debug);
+		}
+	}
+	/* SECTION: stream.spec.zexpect.zframe Generic frame
+	 * frame.data
+	 *         Payload of the last frame
+	 *
+	 * frame.type
+	 *         Type of the frame, as integer.
+	 *
+	 * frame.size
+	 *         Size of the frame.
+	 *
+	 * frame.stream
+	 *         Stream of the frame (correspond to the one you are executing
+	 *         this from, obviously).
+	 *
+	 * frame.padding (for DATA, HEADERS, PUSH_PROMISE frames)
+	 *         Number of padded bytes.
+	 */
+	else if (!strncmp(spec, "frame.", 6)) {
+		spec += 6;
+		if (!f)
+			vtc_log(s->hp->vl, 0, "No frame received yet.");
+		if (!strcmp(spec, "data"))   { return (f->data); }
+		else if (!strcmp(spec, "type"))   { RETURN_BUFFED(f->type); }
+		else if (!strcmp(spec, "size"))	  { RETURN_BUFFED(f->size); }
+		else if (!strcmp(spec, "stream")) { RETURN_BUFFED(f->stid); }
+		else if (!strcmp(spec, "padding")) {
+			if (f->type != TYPE_DATA &&
+					f->type != TYPE_HEADERS &&
+					f->type != TYPE_PUSH_PROMISE)
+				vtc_log(s->hp->vl, 0,
+						"Last frame was not of type "
+						"DATA, HEADERS or PUSH");
+			RETURN_BUFFED(f->md.padded);
+		}
+	}
+	/* SECTION: stream.spec.zexpect.zstream Stream
+	 * stream.window
+	 *         The current window size of the stream, or, if on stream 0,
+	 *         of the connection.
+	 *
+	 * stream.weight
+	 *         Weight of the stream
+	 *
+	 * stream.dependency
+	 *         Id of the stream this one depends on.
+	 */
+	else if (!strcmp(spec, "stream.window")) {
+		snprintf(buf, 20, "%ld", s->id ? s->ws : s->hp->ws);
+		return (buf);
+	}
+	else if (!strcmp(spec, "stream.weight")) {
+		if (s->id) {
+			snprintf(buf, 20, "%d", s->weight);
+			return (buf);
+		} else {
+			return NULL;
+		}
+	}
+	else if (!strcmp(spec, "stream.dependency")) {
+		if (s->id) {
+			snprintf(buf, 20, "%d", s->dependency);
+			return (buf);
+		} else {
+			return NULL;
+		}
+	}
+	/* SECTION: stream.spec.zexpect.ztable Index tables
+	 * tbl.dec.size / tbl.enc.size
+	 *         Size (bytes) of the decoding/encoding table.
+	 *
+	 * tbl.dec.size / tbl.enc.maxsize
+	 *         Maximum size (bytes) of the decoding/encoding table.
+	 *
+	 * tbl.dec.length / tbl.enc.length
+	 *         Number of headers in decoding/encoding table.
+	 *
+	 * tbl.dec[INT].key / tbl.enc[INT].key
+	 *         Name of the header at index INT of the decoding/encoding
+	 *         table.
+	 *
+	 * tbl.dec[INT].value / tbl.enc[INT].value
+	 *         Value of the header at index INT of the decoding/encoding
+	 *         table.
+	 */
+	else if (!strncmp(spec, "tbl.dec", 7) ||
+			!strncmp(spec, "tbl.enc", 7)) {
+		if (spec[4] == 'd')
+			ctx = s->hp->decctx;
+		else
+			ctx = s->hp->encctx;
+		spec += 7;
+
+		if (1 == sscanf(spec, "[%u].key%n", &idx, &n) &&
+				spec[n] == '\0') {
+			h = HPK_GetHdr(ctx, idx + 61);
+			return (h ? h->key.ptr : NULL);
+		}
+		else if (1 == sscanf(spec, "[%u].value%n", &idx, &n) &&
+				spec[n] == '\0') {
+			h = HPK_GetHdr(ctx, idx + 61);
+			return (h ? h->value.ptr : NULL);
+		}
+		else if (!strcmp(spec, ".size")) {
+			RETURN_BUFFED(HPK_GetTblSize(ctx));
+		}
+		else if (!strcmp(spec, ".maxsize")) {
+			RETURN_BUFFED(HPK_GetTblMaxSize(ctx));
+		}
+		else if (!strcmp(spec, ".length")) {
+			RETURN_BUFFED(HPK_GetTblLength(ctx));
+		}
+	}
+	/* SECTION: stream.spec.zexpect.zre Request and response
+	 *
+	 * Note: it's possible to inspect a request or response while it is
+	 * still being construct (in-between two frames for example).
+	 *
+	 * req.bodylen / resp.bodylen
+	 *         Length in bytes of the request/response so far.
+	 *
+	 * req.body / resp.body
+	 *         Body of the request/response so far.
+	 *
+	 * req.http.STRING / resp.http.STRING
+	 *         Value of the header STRING in the request/response.
+	 *
+	 * req.status / resp.status
+	 *         :status pseudo-header's value.
+	 *
+	 * req.url / resp.url
+	 *         :path pseudo-header's value.
+	 *
+	 * req.method / resp.method
+	 *         :method pseudo-header's value.
+	 *
+	 * req.authority / resp.authority
+	 *         :method pseudo-header's value.
+	 *
+	 * req.scheme / resp.scheme
+	 *         :method pseudo-header's value.
+	 */
+	else if (!strncmp(spec, "req.", 4) || !strncmp(spec, "resp.", 5)) {
+		if (spec[2] == 'q') {
+			h = s->req;
+			spec += 4;
+		} else {
+			h = s->resp;
+			spec += 5;
+		}
+		if (!strcmp(spec, "body"))
+			return (s->body);
+		else if (!strcmp(spec, "bodylen"))
+			RETURN_BUFFED(s->bodylen);
+		else if (!strcmp(spec, "status"))
+			return (find_header(h, ":status"));
+		else if (!strcmp(spec, "url"))
+			return (find_header(h, ":path"));
+		else if (!strcmp(spec, "method"))
+			return (find_header(h, ":method"));
+		else if (!strcmp(spec, "authority"))
+			return (find_header(h, ":authority"));
+		else if (!strcmp(spec, "scheme"))
+			return (find_header(h, ":scheme"));
+		else if (!strncmp(spec, "http.", 5))
+			return (find_header(h, spec + 5));
+		else
+			return (NULL);
+	}
+	else
+		return (spec);
+	return(NULL);
+}
+
+/* SECTION: stream.spec.frame_sendhex sendhex
+ *
+ * Push bytes directly on the wire. sendhex takes exactly one argument: a string
+ * describing the bytes, in hex notation, will possible whitespaces between
+ * them. Here's an example::
+ *
+ *         sendhex "00 00 08 00 0900       8d"
+ */
+static void
+cmd_sendhex(CMD_ARGS)
+{
+	struct http *hp;
+	struct stream *s;
+	char *q;
+	char *buf;
+	char tmp[3];
+	int i;
+	unsigned size = 0;
+	(void)cmd;
+
+	CAST_OBJ_NOTNULL(s, priv, STREAM_MAGIC);
+	CAST_OBJ_NOTNULL(hp, s->hp, HTTP_MAGIC);
+	AN(av[1]);
+	AZ(av[2]);
+
+	q = av[1];
+	size = strlen(q)/2;
+	buf = malloc(size);
+	AN(buf);
+	for (i = 0; i < size; i++) {
+		while (vct_issp(*q))
+			q++;
+		if (*q == '\0')
+			break;
+		memcpy(tmp, q, 2);
+		q += 2;
+		tmp[2] = '\0';
+		if (!vct_ishex(tmp[0]) || !vct_ishex(tmp[1]))
+			vtc_log(vl, 0, "Illegal Hex char \"%c%c\"",
+					tmp[0], tmp[1]);
+		buf[i] = (uint8_t)strtoul(tmp, NULL, 16);
+	}
+	AZ(pthread_mutex_lock(&hp->mtx));
+	http_write(hp, 4, buf, i, "sendhex");
+
+	AZ(pthread_mutex_unlock(&hp->mtx));
+	vtc_hexdump(vl, 4, "sendhex", (void *)buf, size);
+	free(buf);
+}
+
+#define ENC(hdr, k, v)			\
+{					\
+	AN(k);				\
+	hdr.key.ptr = strdup(k);	\
+	AN(hdr.key.ptr);		\
+	hdr.key.len = strlen(k);	\
+	AN(v);				\
+	hdr.value.ptr = strdup(v);	\
+	AN(hdr.value.ptr);		\
+	hdr.value.len = strlen(v);	\
+	HPK_EncHdr(iter, &hdr);		\
+	free(hdr.key.ptr);		\
+	free(hdr.value.ptr);		\
+}
+
+#define STR_ENC(av, field, str)						       \
+{									       \
+	av++;								       \
+	     if (AV_IS("plain")) { hdr.field.huff = 0; }		       \
+	else if (AV_IS("huf"))   { hdr.field.huff = 1; }		       \
+	else								       \
+		vtc_log(vl, 0, str " arg can be huf or plain (got: %s)", *av); \
+	av++;								       \
+	AN(*av);							       \
+	hdr.field.ptr = *av;						       \
+	hdr.field.len = strlen(*av);					       \
+}
+
+
+/* SECTION: stream.spec.data_0 txreq, txresp, txcont, txpush
+ *
+ * These four commands are about sending headers. txreq,  txresp will send
+ * HEADER frames, txcont will send CONTINUATION frames, and txpush PUSH frames.
+ * The only difference between txreq and txresp are the default headers set by
+ * each of them.
+ *
+ * \-noadd
+ *         Do not add default headers. Useful to avoid duplicates when sending
+ *         default headers using ``-hdr``, ``-idxHdr`` and ``-litIdxHdr``.
+ *
+ * \-status INT (txresp)
+ *         Set the :status pseudo-header.
+ *
+ * \-url STRING (txreq, txpush)
+ *         Set the :path pseudo-header.
+ *
+ * \-req STRING (txreq, txpush)
+ *         Set the :method pseudo-header.
+ *
+ * \-scheme STRING (txreq, txpush)
+ *         Set the :scheme pseudo-header.
+ *
+ * \-hdr STRING1 STRING2
+ *         Insert a header, STRING1 being the name, and STRING2 the value.
+ *
+ * \-idxHdr INT
+ *         Insert an indexed header, using INT as index.
+ *
+ * \-litIdxHdr inc|not|never INT huf|plain STRING
+ *         Insert an literal, indexed header. The first argument specify if the
+ *         header should be added to the table, shouldn't, or mustn't be
+ *         compressed if/when retransmitted.
+ *
+ *         INT is the idex of the header name to use.
+ *
+ *         The third argument informs about the Huffman encoding: yes (huf) or
+ *         no (plain).
+ *
+ *         The last term is the literal value of the header.
+ *
+ * \-litHdr inc|not|never huf|plain STRING1 huf|plain STRING2
+ *         Insert a literal header, with the same first argument as
+ *         ``-litIdxHdr``.
+ *
+ *         The second and third terms tell what the name of the header is and if
+ *         it should be Huffman-encoded, while the last two do the same
+ *         regarding the value.
+ *
+ * \-body STRING (txreq, txresp)
+ *         Specify a body, effectively putting STRING into a DATA frame after
+ *         the HEADER frame is sent.
+ *
+ * \-bodylen INT (txreq, txresp)
+ *         Do the same thing as ``-body`` but generate an string of INT length
+ *         for you.
+ *
+ * \-nostrend (txreq, txresp)
+ *         Don't set the END_STREAM flag automatically, making the peer expect
+ *         a body after the headers.
+ *
+ * \-nohdrend
+ *         Don't set the END_HEADERS flag automatically, making the peer expect
+ *         more HEADER frames.
+ *
+ * \-dep INT (txreq, txresp)
+ *         Tell the peer that this content depends on the stream with the INT
+ *         id.
+ *
+ * \-ex (txreq, txresp)
+ *         Make the dependency exclusive (``-dep`` is still needed).
+ *
+ * \-weight (txreq, txresp)
+ *         Set the weight for the dependency.
+ *
+ * \-promised INT (txpush)
+ *         The id of the promised stream.
+ *
+ * \-pad STRING / -padlen INT (txreq, txresp, txpush)
+ *         Add string as padding to the frame, either the one you provided with
+ *         \-pad, or one that is generated for you, of length INT is -padlen
+ *         case.
+ */
+static void
+cmd_tx11obj(CMD_ARGS)
+{
+	struct stream *s;
+	int status_done = 1;
+	int method_done = 1;
+	int path_done = 1;
+	int scheme_done = 1;
+	uint32_t stid = 0, pstid;
+	uint32_t weight = 16;
+	int exclusive = 0;
+	char *buf;
+	struct hpk_iter *iter;
+	struct frame f;
+	char *body = NULL, *pad = NULL;
+	/*XXX: do we need a better api? yes we do */
+	struct hpk_hdr hdr;
+	char *cmd_str = *av;
+	char *p;
+	(void)cmd;
+
+	CAST_OBJ_NOTNULL(s, priv, STREAM_MAGIC);
+	INIT_FRAME(f, CONTINUATION, 0, s->id, END_HEADERS);
+	buf = malloc(BUF_SIZE);
+	AN(buf);
+
+	if (!strcmp(cmd_str, "txreq")) {
+		ONLY_CLIENT(s->hp, av);
+		f.type = TYPE_HEADERS;
+		f.flags |= END_STREAM;
+		method_done = 0;
+		path_done = 0;
+		scheme_done = 0;
+	} else if (!strcmp(cmd_str, "txresp")) {
+		ONLY_SERVER(s->hp, av);
+		f.type = TYPE_HEADERS;
+		f.flags |= END_STREAM;
+		status_done = 0;
+	} else if (!strcmp(cmd_str, "txpush")) {
+		ONLY_SERVER(s->hp, av);
+		f.type = TYPE_PUSH_PROMISE;
+		method_done = 0;
+		path_done = 0;
+		scheme_done = 0;
+	}
+
+	if (f.type == TYPE_PUSH_PROMISE) {
+		*buf = 0;
+		iter = HPK_NewIter(s->hp->encctx, buf + 4, BUF_SIZE - 4);
+	} else
+		iter = HPK_NewIter(s->hp->encctx, buf, BUF_SIZE);
+
+#define AV_IS(str) !strcmp(*av, str)
+#define CMD_IS(str) !strcmp(cmd_str, str)
+	while (*++av) {
+		memset(&hdr, 0, sizeof(hdr));
+		hdr.t = hpk_not;
+		if (AV_IS("-noadd")) {
+			path_done = 1;
+			status_done = 1;
+			method_done = 1;
+			scheme_done = 1;
+		}
+		else if (AV_IS("-status") && CMD_IS("txresp")) {
+			ENC(hdr, ":status", av[1]);
+			av++;
+			status_done = 1;
+		}
+		else if (AV_IS("-url") &&
+				(CMD_IS("txreq") || CMD_IS("txpush"))) {
+			ENC(hdr, ":path", av[1]);
+			av++;
+			path_done = 1;
+		}
+		else if (AV_IS("-req") &&
+				(CMD_IS("txreq") || CMD_IS("txpush"))) {
+			ENC(hdr, ":method", av[1]);
+			av++;
+			method_done = 1;
+		}
+		else if (AV_IS("-scheme") &&
+				(CMD_IS("txreq") || CMD_IS("txpush"))) {
+			ENC(hdr, ":scheme", av[1]);
+			av++;
+			scheme_done = 1;
+		}
+		else if (AV_IS("-hdr")) {
+			ENC(hdr, av[1], av[2]);
+			av += 2;
+		}
+		else if (AV_IS("-idxHdr")) {
+			hdr.t = hpk_idx;
+			STRTOU32_CHECK(hdr.i, av, p, vl, "-idxHdr", 0);
+			HPK_EncHdr(iter, &hdr);
+		}
+		else if (AV_IS("-litIdxHdr")) {
+			av++;
+			     if (AV_IS("inc"))   { hdr.t = hpk_inc;   }
+			else if (AV_IS("not"))   { hdr.t = hpk_not;   }
+			else if (AV_IS("never")) { hdr.t = hpk_never; }
+			else
+				vtc_log(vl, 0, "first -litidxHdr arg can be "
+						"inc, not, never (got: %s)",
+						*av);
+
+			STRTOU32_CHECK(hdr.i, av, p, vl,
+					"second -litidxHdr arg", 0);
+
+			hdr.key.ptr = NULL;
+			hdr.key.len = 0;
+			STR_ENC(av, value,   "third -litHdr");
+			HPK_EncHdr(iter, &hdr);
+		}
+		else if (AV_IS("-litHdr")) {
+			av++;
+			     if (AV_IS("inc"))   { hdr.t = hpk_inc;   }
+			else if (AV_IS("not"))   { hdr.t = hpk_not;   }
+			else if (AV_IS("never")) { hdr.t = hpk_never; }
+			else
+				vtc_log(vl, 0, "first -litHdr arg can be inc, "
+						"not, never (got: %s)", *av);
+
+			STR_ENC(av, key,   "second -litHdr");
+			STR_ENC(av, value, "fourth -litHdr");
+			HPK_EncHdr(iter, &hdr);
+		}
+		else if (AV_IS("-nostrend")) {
+			f.flags &= ~END_STREAM;
+		}
+		else if (AV_IS("-nohdrend")) {
+			f.flags &= ~END_HEADERS;
+		}
+		else if (AV_IS("-promised") && CMD_IS("txpush")) {
+			STRTOU32_CHECK(pstid, av, p, vl, "-promised", 31);
+			vbe32enc(buf, pstid);
+		}
+		else if (AV_IS("-pad") && !CMD_IS("txcont")) {
+			AZ(pad);
+			av++;
+			AN(*av);
+			pad = strdup(*av);
+		}
+		else if (AV_IS("-padlen") && !CMD_IS("txcont")) {
+			AZ(pad);
+			av++;
+			pad = synth_body(*av, 0);
+		}
+		else if (CMD_IS("txreq") || CMD_IS("txresp")) {
+			if (AV_IS("-body")) {
+				AZ(body);
+				REPLACE(body, av[1]);
+				f.flags &= ~END_STREAM;
+				av++;
+			}
+			else if (AV_IS("-bodylen")) {
+				AZ(body);
+				body = synth_body(av[1], 0);
+				f.flags &= ~END_STREAM;
+				av++;
+			}else if (AV_IS("-dep")) {
+				STRTOU32_CHECK(stid, av, p, vl, "-dep", 0);
+				f.flags |= PRIORITY;
+			}
+			else if (AV_IS("-ex")) {
+				exclusive = 1 << 31;
+				f.flags |= PRIORITY;
+			}
+			else if (AV_IS("-weight")) {
+				STRTOU32_CHECK(weight, av, p, vl, "-weight", 8);
+				f.flags |= PRIORITY;
+			} else
+				break;
+		} else
+			break;
+	}
+#undef CMD_IS
+#undef AV_IS
+	if (*av != NULL)
+		vtc_log(vl, 0, "Unknown %s spec: %s\n", cmd_str, *av);
+
+	memset(&hdr, 0, sizeof(hdr));
+	hdr.t = hpk_not;
+
+	if (!status_done) { ENC(hdr, ":status", "200"); }
+	if (!path_done)   { ENC(hdr, ":path",	"/"); }
+	if (!method_done) { ENC(hdr, ":method", "GET"); }
+	if (!scheme_done) { ENC(hdr, ":scheme", "http"); }
+
+	f.size = gethpk_iterLen(iter);
+	if (f.flags & PRIORITY) {
+		s->weight = weight & 0xff;
+		s->dependency = stid;
+
+		assert(f.size + 5 < BUF_SIZE);
+	        memmove(buf + 5, buf, f.size);
+		vbe32enc(buf, (stid | exclusive));
+		buf[4] = s->weight;
+		f.size += 5;
+
+		vtc_log(vl, 4, "stream->dependency: %u", s->dependency);
+		vtc_log(vl, 4, "stream->weight: %u", s->weight);
+		if (exclusive)
+			exclusive_stream_dependency(s);
+	}
+	if (pad) {
+		if (strlen(pad) >= 128)
+			vtc_log(vl, 0, "Padding is limited to 128 bytes");
+		f.flags |= PADDED;
+		assert(f.size + strlen(pad) < BUF_SIZE);
+		memmove(buf + 1, buf, f.size);
+		buf[0] = strlen(pad);
+		f.size += 1;
+		memcpy(buf + f.size, pad, strlen(pad));
+		f.size += strlen(pad);
+		free(pad);
+	}
+	if (f.type == TYPE_PUSH_PROMISE)
+		f.size += 4;
+	f.data = buf;	
+	HPK_FreeIter(iter);
+	write_frame(s->hp, &f, 1);
+	free(buf);
+
+	if (!body)
+		return;
+
+	INIT_FRAME(f, DATA, strlen(body), s->id, END_STREAM);
+	f.data = body;
+
+	write_frame(s->hp, &f, 1);
+	free(body);
+}
+
+/* SECTION: stream.spec.data_1 txdata
+ *
+ * By default, data frames are empty. The receiving end will know the whole body
+ * has been delivered thanks to the END_STREAM flag set in the last DATA frame,
+ * and txdata automatically set it.
+ *
+ * \-data STRING
+ *         Data to be embedded into the frame.
+ *
+ * \-datalen INT
+ *         Generate and INT-bytes long string to be sent in the frame.
+ *
+ * \-pad STRING / -padlen INT
+ *         Add string as padding to the frame, either the one you provided with
+ *         \-pad, or one that is generated for you, of length INT is -padlen
+ *         case.
+ *
+ * \-nostrend
+ *         Don't set the END_STREAM flag, allowing to send more data on this
+ *         stream.
+ */
+static void
+cmd_txdata(CMD_ARGS)
+{
+	struct stream *s;
+	char *pad = NULL;
+	struct frame f;
+	char *body = NULL;
+	char *data = NULL;
+	(void)cmd;
+
+	CAST_OBJ_NOTNULL(s, priv, STREAM_MAGIC);
+
+	INIT_FRAME(f, DATA, 0, s->id, END_STREAM);
+
+	while (*++av) {
+		if (!strcmp(*av, "-data")) {
+			AZ(body);
+			av++;
+			body = strdup(*av);
+		} else if (!strcmp(*av, "-datalen")) {
+			AZ(data);
+			av++;
+			body = synth_body(*av, 0);
+		} else if (!strcmp(*av, "-pad")) {
+			AZ(pad);
+			av++;
+			AN(*av);
+			pad = strdup(*av);
+		} else if (!strcmp(*av, "-padlen")) {
+			AZ(pad);
+			av++;
+			pad = synth_body(*av, 0);
+		} else if (!strcmp(*av, "-nostrend"))
+			f.flags &= ~END_STREAM;
+		else
+			break;
+	}
+	if (*av != NULL)
+		vtc_log(vl, 0, "Unknown txdata spec: %s\n", *av);
+
+	if (!body)
+		body = strdup("");
+
+	if (pad) {
+		f.flags |= PADDED;
+		if (strlen(pad) >= 128)
+			vtc_log(vl, 0, "Padding is limited to 128 bytes");
+		data = malloc( 1 + strlen(body) + strlen(pad));
+		AN(data);
+		*((uint8_t *)data) = strlen(pad);
+		f.size = 1;
+		memcpy(data + f.size, body, strlen(body));
+		f.size += strlen(body);
+		memcpy(data + f.size, pad, strlen(pad));
+		f.size += strlen(pad);
+		f.data = data;
+	} else {
+		f.size = strlen(body);
+		f.data = body;
+	}
+	write_frame(s->hp, &f, 1);
+	free(body);
+	free(pad);
+	free(data);
+}
+
+/* SECTION: stream.spec.reset_txrst txrst
+ *
+ * Send a RST_STREAM frame. By default, txrst will send a 0 error code
+ * (NO_ERROR).
+ *
+ * \-err STRING|INT
+ *         Sets the error code to be sent. The argument can be an integer or a
+ *         string describing the error, such as NO_ERROR, or CANCEL (see
+ *         rfc7540#11.4 for more strings).
+ */
+static void
+cmd_txrst(CMD_ARGS)
+{
+	struct stream *s;
+	char *p;
+	uint32_t err;
+	struct frame f;
+	(void)cmd;
+	CAST_OBJ_NOTNULL(s, priv, STREAM_MAGIC);
+
+	INIT_FRAME(f, RST_STREAM, 4, s->id, 0);
+
+	while (*++av) {
+		if (!strcmp(*av, "-err")) {
+			++av;
+			for (err=0; h2_errs[err]; err++) {
+				if (!strcmp(h2_errs[err], *av))
+					break;
+			}
+
+			if (h2_errs[err])
+				continue;
+
+			STRTOU32(err, *av, p, vl, "-err");
+		} else
+			break;	
+	}
+	if (*av != NULL)
+		vtc_log(vl, 0, "Unknown txrst spec: %s\n", *av);
+
+	err = htonl(err);
+	f.data = (void *)&err;
+	write_frame(s->hp, &f, 1);
+}
+
+/* SECTION: stream.spec.prio_txprio txprio
+ *
+ * Send a PRIORITY frame
+ *
+ * \-stream INT
+ *         indicate the id of the stream the sender stream depends on.
+ *
+ * \-ex
+ *         the dependency should be made exclusive (only this streams depends on
+ *         the parent stream).
+ *
+ * \-weight INT
+ *         an 8-bits integer is used to balance priority between streams
+ *         depending on the same streams. 
+ */
+static void
+cmd_txprio(CMD_ARGS)
+{
+	struct stream *s;
+	char *p;
+	uint32_t stid = 0;
+	struct frame f;
+	uint32_t weight = 0;
+	uint32_t exclusive = 0;
+	uint8_t buf[5];
+
+	(void)cmd;
+	CAST_OBJ_NOTNULL(s, priv, STREAM_MAGIC);
+
+	INIT_FRAME(f, PRIORITY, 5, s->id, 0);
+	f.data = (void *)buf;
+
+	while (*++av) {
+		if (!strcmp(*av, "-stream")) {
+			STRTOU32_CHECK(stid, av, p, vl, "-stream", 0);
+		} else if (!strcmp(*av, "-ex")) {
+			exclusive = 1U << 31;
+		} else if (!strcmp(*av, "-weight")) {
+			STRTOU32_CHECK(weight, av, p, vl, "-weight", 8);
+		} else
+			break;	
+	}
+	if (*av != NULL)
+		vtc_log(vl, 0, "Unknown txprio spec: %s\n", *av);
+	s->weight = weight & 0xff;
+	s->dependency = stid;
+
+	if(exclusive)
+		exclusive_stream_dependency(s);
+
+	vbe32enc(buf, (stid | exclusive));
+	buf[4] = s->weight;
+	write_frame(s->hp, &f, 1);
+}
+
+#define PUT_KV(av, vl, name, val, code) \
+	do {\
+		STRTOU32_CHECK(val, av, p, vl, #name, 0);	\
+		vbe16enc(cursor, code);				\
+		cursor += sizeof(uint16_t);			\
+		vbe32enc(cursor, val);				\
+		cursor += sizeof(uint32_t);			\
+		f.size += 6;					\
+	} while(0)
+
+/* SECTION: stream.spec.settings_txsettings txsettings
+ *
+ * SETTINGS frames must be acknowledge, arguments are as follow (most of them
+ * are from  rfc7540#6.5.2):
+ *
+ * \-hdrtbl INT
+ *         headers table size
+ *
+ * \-push BOOL
+ *         whether push frames are accepted or not
+ *
+ * \-maxstreams INT
+ *         maximum concurrent streams allowed
+ *
+ * \-winsize INT
+ *         sender's initial window size
+ *
+ * \-framesize INT
+ *         largest frame size authorized
+ *
+ * \-hdrsize INT
+ *         maximum size of the header list authorized
+ *
+ * \-ack
+ *         set the ack bit
+ */
+static void
+cmd_txsettings(CMD_ARGS)
+{
+	struct stream *s, *_s;
+	struct http *hp;
+	char *p;
+	uint32_t val = 0;
+	struct frame f;
+	//TODO dynamic alloc
+	char buf[512];
+	char *cursor = buf;
+
+	(void)cmd;
+	CAST_OBJ_NOTNULL(s, priv, STREAM_MAGIC);
+	CAST_OBJ_NOTNULL(hp, s->hp, HTTP_MAGIC);
+
+	memset(buf, 0, 512);
+	INIT_FRAME(f, SETTINGS, 0, s->id, 0);
+	f.data = buf;
+
+	AZ(pthread_mutex_lock(&hp->mtx));
+	while (*++av) {
+		if (!strcmp(*av, "-push")) {
+			++av;
+			vbe16enc(cursor, 0x2);
+			cursor += sizeof(uint16_t);
+			if (!strcmp(*av, "false"))
+				vbe32enc(cursor, 0);
+			else if (!strcmp(*av, "true"))
+				vbe32enc(cursor, 1);
+			else
+				vtc_log(vl, 0, "Push parameter is either "
+						"\"true\" or \"false\", not %s",
+						*av);
+			cursor += sizeof(uint32_t);
+			f.size += 6;
+		}
+		else if (!strcmp(*av, "-hdrtbl")) {
+			PUT_KV(av, vl, hdrtbl, val, 0x1);
+			HPK_ResizeTbl(s->hp->decctx, val);
+		}
+		else if (!strcmp(*av, "-maxstreams")) {
+			PUT_KV(av, vl, maxstreams, val, 0x3);
+		}
+		else if (!strcmp(*av, "-winsize"))	{
+			PUT_KV(av, vl, winsize, val, 0x4);
+			VTAILQ_FOREACH(_s, &hp->streams, list)
+				_s->ws += (val - hp->iws);
+			hp->iws = val;
+		}
+		else if (!strcmp(*av, "-framesize"))	{
+			PUT_KV(av, vl, framesize, val, 0x5);
+		}
+		else if (!strcmp(*av, "-hdrsize")){
+			PUT_KV(av, vl, hdrsize, val, 0x6);
+		}
+		else if (!strcmp(*av, "-ack")) {
+			f.flags |= 1;
+		} else
+			break;
+	}
+	if (*av != NULL)
+		vtc_log(vl, 0, "Unknown txsettings spec: %s\n", *av);
+
+	write_frame(hp, &f, 0);
+	AZ(pthread_mutex_unlock(&hp->mtx));
+}
+
+/* SECTION: stream.spec.ping_txping txping
+ *
+ * Send PING frame.
+ *
+ * \-data STRING
+ *         specify the payload of the frame, with STRING being an 8-char string.
+ *
+ * \-ack
+ *         set the ACK flag.
+ */
+static void
+cmd_txping(CMD_ARGS)
+{
+	struct stream *s;
+	struct frame f;
+	char buf[8];
+
+	(void)cmd;
+	memset(buf, 0, 8);
+	CAST_OBJ_NOTNULL(s, priv, STREAM_MAGIC);
+	INIT_FRAME(f, PING, 8, s->id, 0);
+
+	while (*++av) {
+		if (!strcmp(*av, "-data")) {
+			av++;
+			if (f.data)
+				vtc_log(vl, 0, "this frame already has data");
+			if (strlen(*av) != 8)
+				vtc_log(vl, 0, "data must be a 8-char string, found  (%s)", *av);
+			f.data = *av;
+		} else if (!strcmp(*av, "-ack")) {
+			f.flags |= 1;
+		} else
+			break;
+	}
+	if (*av != NULL)
+		vtc_log(vl, 0, "Unknown txping spec: %s\n", *av);
+	if (!f.data)
+		f.data = buf;
+	write_frame(s->hp, &f, 1);
+}
+
+/*
+ * SECTION: stream.spec.goaway_txgoaway rxgoaway
+ *
+ * Possible options include:
+ *
+ * \-err STRING|INT
+ *         set the error code to eplain the termination. The second argument
+ *         can be a integer or the string version of the error code as found
+ *         in rfc7540#7.
+ *
+ * \-laststream INT
+ *         the id of the "highest-numbered stream identifier for which the
+ *         sender of the GOAWAY frame might have taken some action on or might
+ *         yet take action on".
+ *
+ * \-debug
+ *         specify the debug data, if any to append to the frame.
+ */
+static void
+cmd_txgoaway(CMD_ARGS)
+{
+	struct stream *s;
+	char *p;
+	uint32_t err = 0;
+	uint32_t ls = 0;
+	struct frame f;
+	char buf[8];
+
+	(void)cmd;
+	CAST_OBJ_NOTNULL(s, priv, STREAM_MAGIC);
+	memset(buf, 0, 8);
+
+	INIT_FRAME(f, GOAWAY, 8, s->id, 0);
+
+	while (*++av) {
+		if (!strcmp(*av, "-err")) {
+			++av;
+			for (err=0; h2_errs[err]; err++) {
+				if (!strcmp(h2_errs[err], *av))
+					break;
+			}
+
+			if (h2_errs[err])
+				continue;
+
+			STRTOU32(err, *av, p, vl, "-err");
+		} else if (!strcmp(*av, "-laststream")) {
+			STRTOU32_CHECK(ls, av, p, vl, "-laststream", 31);
+		} else if (!strcmp(*av, "-debug")) {
+			++av;
+			if (f.data)
+				vtc_log(vl, 0, "this frame already has debug data");
+			f.size = 8 + strlen(*av);
+			f.data = malloc(f.size);
+			memcpy(f.data + 8, *av, f.size - 8);
+		} else
+			break;
+	}
+	if (*av != NULL)
+		vtc_log(vl, 0, "Unknown txgoaway spec: %s\n", *av);
+
+	if (!f.data)
+		f.data = malloc(2);
+	vbe32enc(f.data, ls);
+	vbe32enc(f.data + 4, err);
+	write_frame(s->hp, &f, 1);
+	free(f.data);
+}
+
+/* SECTION: stream.spec.winup_txwinup txwinup
+ *
+ * Transmit a WINDOW_UPDATE frame, increasing the amount of credit of the
+ * connection (from stream 0) or of the stream (any other stream).
+ *
+ * \-size INT
+ *         give INT credits to the peer.
+ */
+static void
+cmd_txwinup(CMD_ARGS)
+{
+	struct http *hp;
+	struct stream *s;
+	char *p;
+	struct frame f;
+	char buf[8];
+	uint32_t size = 0; 
+
+	(void)cmd;
+	CAST_OBJ_NOTNULL(s, priv, STREAM_MAGIC);
+	CAST_OBJ_NOTNULL(hp, s->hp, HTTP_MAGIC);
+	memset(buf, 0, 8);
+
+	AN(av[1]);
+	AN(av[2]);
+
+	INIT_FRAME(f, WINDOW_UPDATE, 4, s->id, 0);
+	f.data = buf;
+
+	while (*++av) {
+		if (!strcmp(*av, "-size")) {
+			STRTOU32_CHECK(size, av, p, vl, "-size", 0);
+		} else
+			break;	
+	}
+	if (*av != NULL)
+		vtc_log(vl, 0, "Unknown txwinup spec: %s\n", *av);
+
+	AZ(pthread_mutex_lock(&hp->mtx));
+	if (s->id == 0)
+		hp->ws += size;
+	s->ws += size;
+	AZ(pthread_mutex_unlock(&hp->mtx));
+
+	size = htonl(size);
+	f.data = (void *)&size;
+	write_frame(hp, &f, 1);
+}
+
+static struct frame *
+rxstuff(struct stream *s) {
+	struct frame *f;
+
+	CHECK_OBJ_NOTNULL(s, STREAM_MAGIC);
+
+	AZ(pthread_mutex_lock(&s->hp->mtx));
+	s->hp->wf++;
+	if (VTAILQ_EMPTY(&s->fq)) {
+		AZ(pthread_cond_signal(&s->hp->cond));
+		AZ(pthread_cond_wait(&s->cond, &s->hp->mtx));
+	}
+	if (VTAILQ_EMPTY(&s->fq)) {
+		AZ(pthread_mutex_unlock(&s->hp->mtx));
+		return (NULL);
+	}
+	clean_frame(&s->frame);
+	f = VTAILQ_LAST(&s->fq, fq_head);
+	VTAILQ_REMOVE(&s->fq, f, list);
+	AZ(pthread_mutex_unlock(&s->hp->mtx));
+
+	CHECK_OBJ_NOTNULL(f, FRAME_MAGIC);
+	return (f);
+}
+
+#define CHKFRAME(rt, wt, rcv, func) \
+	do { \
+	if (rt != wt) \
+		vtc_log(vl, 0, "Frame #%d for %s was of type %s (%d) " \
+				"instead of %s (%d)", \
+				rcv, func, \
+				rt < TYPE_MAX ? h2_types[rt] : "?", rt, \
+			       	wt < TYPE_MAX ? h2_types[wt] : "?", wt); \
+	} while (0);
+
+/* SECTION: stream.spec.data_12 rxhdrs
+ *
+ * ``rxhdrs`` will expect one HEADER frame, then, depending on the arguments,
+ * zero or more CONTINUATION frame.
+ *
+ * \-all
+ *         Keep waiting for CONTINUATION frames until END_HEADERS flag is seen.
+ *
+ * \-some INT
+ *         Retrieve INT - 1 CONTINUATION frames after the HEADER frame.
+ *
+ */
+static void
+cmd_rxhdrs(CMD_ARGS)
+{
+	struct stream *s;
+	struct frame *f = NULL;
+	char *p;
+	int loop = 0;
+	int times = 1;
+	int rcv = 0;
+	enum h2_type expect = TYPE_HEADERS;
+
+	(void)cmd;
+	CAST_OBJ_NOTNULL(s, priv, STREAM_MAGIC);
+
+	while (*++av) {
+		if (!strcmp(*av, "-some")) {
+			STRTOU32_CHECK(times, av, p, vl, "-some", 0);
+			AN(times);
+		} else if (!strcmp(*av, "-all")) {
+			loop = 1;
+		} else
+			break;	
+	}
+	if (*av != NULL)
+		vtc_log(vl, 0, "Unknown rxhdrs spec: %s\n", *av);
+
+	while (rcv++ < times || (loop && !(f->flags & END_HEADERS))) {
+		f = rxstuff(s);
+		if (!f)
+			return;
+		CHKFRAME(f->type, expect, rcv, "rxhdrs");
+		expect = TYPE_CONTINUATION;
+	}
+	s->frame = f;
+}
+
+static void
+cmd_rxcont(CMD_ARGS)
+{
+	struct stream *s;
+	struct frame *f = NULL;
+	char *p;
+	int loop = 0;
+	int times = 1;
+	int rcv = 0;
+
+	(void)cmd;
+	(void)av;
+	CAST_OBJ_NOTNULL(s, priv, STREAM_MAGIC);
+
+	while (*++av) {
+		if (!strcmp(*av, "-some")) {
+			STRTOU32_CHECK(times, av, p, vl, "-some", 0);
+		} else if (!strcmp(*av, "-all")) {
+			loop = 1;
+		} else
+			break;	
+	}
+	if (*av != NULL)
+		vtc_log(vl, 0, "Unknown rxcont spec: %s\n", *av);
+
+	while (rcv++ < times || (loop && !(f->flags & END_HEADERS))) {
+		f = rxstuff(s);
+		if (!f)
+			return;
+		CHKFRAME(f->type, TYPE_CONTINUATION, rcv, "rxcont");
+	}
+	s->frame = f;
+}
+
+
+/* SECTION: stream.spec.data_13 rxdata
+ *
+ * Receiving data is done using the ``rxdata`` keywords and will retrieve one
+ * DATA frame, if you wish to receive more, you can use these two convenience
+ * arguments:
+ *
+ * \-all
+ *         keep waiting for DATA frame until one sets the END_STREAM flag
+ *
+ * \-some INT
+ *         retrieve INT DATA frames.
+ *
+ */
+static void
+cmd_rxdata(CMD_ARGS)
+{
+	struct stream *s;
+	struct frame *f = NULL;
+	char *p;
+	int loop = 0;
+	int times = 1;
+	int rcv = 0;
+
+	(void)cmd;
+	(void)av;
+	CAST_OBJ_NOTNULL(s, priv, STREAM_MAGIC);
+
+	while (*++av) {
+		if (!strcmp(*av, "-some")) {
+			STRTOU32_CHECK(times, av, p, vl, "-some", 0);
+		} else if (!strcmp(*av, "-all")) {
+			loop = 1;
+		} else
+			break;	
+	}
+	if (*av != NULL)
+		vtc_log(vl, 0, "Unknown rxdata spec: %s\n", *av);
+
+	while (rcv++ < times || (loop && !(f->flags & END_STREAM))) {
+		f = rxstuff(s);
+		if (!f)
+			return;
+		CHKFRAME(f->type, TYPE_DATA, rcv, "rxhdata");
+	}
+	s->frame = f;
+}
+
+/* SECTION: stream.spec.data_10 rxreq, rxresp
+ *
+ * These are two convenience functions to receive headers and body of an
+ * incoming request or response. The only difference is that rxreq can only be
+ * by a server, and rxresp by a client.
+ *
+ */
+static void
+cmd_rxreqsp(CMD_ARGS)
+{
+	struct stream *s;
+	struct frame *f;
+	int end_stream;
+	int rcv = 0;
+
+	(void)cmd;
+	CAST_OBJ_NOTNULL(s, priv, STREAM_MAGIC);
+
+	if (!strcmp(av[0], "rxreq"))
+		ONLY_SERVER(s->hp, av);
+	else
+		ONLY_CLIENT(s->hp, av);
+
+	f = rxstuff(s);
+	if (!f)
+		return;
+
+	rcv++;
+	CHKFRAME(f->type, TYPE_HEADERS, rcv, *av);
+
+	end_stream = f->flags & END_STREAM;
+
+	while (!(f->flags & END_HEADERS)) {
+		f = rxstuff(s);
+		if (!f)
+			return;
+		rcv++;
+		CHKFRAME(f->type, TYPE_CONTINUATION, rcv, *av);
+	}
+
+	while (!end_stream && (f = rxstuff(s))) {
+		rcv++;
+		CHKFRAME(f->type, TYPE_DATA, rcv, *av);
+		end_stream = f->flags & END_STREAM;
+	}
+	s->frame = f;
+}
+
+/* SECTION: stream.spec.data_11 rxpush
+ *
+ * This works like ``rxhdrs``, expecting a PUSH frame and then zero or more
+ * CONTINUATION frames.
+ *
+ * \-all
+ *         Keep waiting for CONTINUATION frames until END_HEADERS flag is seen.
+ *
+ * \-some INT
+ *         Retrieve INT - 1 CONTINUATION frames after the PUSH frame.
+ *
+ */
+static void
+cmd_rxpush(CMD_ARGS) {
+	struct stream *s;
+	struct frame *f = NULL;
+	char *p;
+	int loop = 0;
+	int times = 1;
+	int rcv = 0;
+	enum h2_type expect = TYPE_PUSH_PROMISE;
+
+	(void)cmd;
+	CAST_OBJ_NOTNULL(s, priv, STREAM_MAGIC);
+
+	while (*++av) {
+		if (!strcmp(*av, "-some")) {
+			STRTOU32_CHECK(times, av, p, vl, "-some", 0);
+			AN(times);
+		} else if (!strcmp(*av, "-all")) {
+			loop = 1;
+		} else
+			break;
+	}
+	if (*av != NULL)
+		vtc_log(vl, 0, "Unknown rxpush spec: %s\n", *av);
+
+	while (rcv++ < times || (loop && !(f->flags & END_HEADERS))) {
+		f = rxstuff(s);
+		if (!f)
+			return;
+		CHKFRAME(f->type, expect, rcv, "rxpush");
+		expect = TYPE_CONTINUATION;
+	}
+	s->frame = f;
+}
+
+#define RXFUNC(lctype, upctype) \
+	static void \
+	cmd_rx ## lctype(CMD_ARGS) { \
+		struct stream *s; \
+		(void)cmd; \
+		(void)av; \
+		CAST_OBJ_NOTNULL(s, priv, STREAM_MAGIC); \
+		if ((s->frame = rxstuff(s))) \
+				return; \
+		if (s->frame->type != TYPE_ ## upctype) \
+			vtc_log(vl, 0, "Received frame of type %d " \
+					"is invalid for %s", \
+					s->frame->type, "rx ## lctype"); \
+	}
+
+/* SECTION: stream.spec.prio_rxprio rxprio
+ *
+ * Receive a PRIORITY frame
+ */
+RXFUNC(prio,	PRIORITY)
+
+/* SECTION: stream.spec.reset_rxrst rxrst
+ *
+ * Receive a RST_STREAM frame
+ */
+RXFUNC(rst,	RST_STREAM)
+
+/* SECTION: stream.spec.settings_rxsettings rxsettings
+ *
+ * Receive a SETTINGS frame
+ */
+RXFUNC(settings,SETTINGS)
+
+/* SECTION: stream.spec.ping_rxping rxping
+ *
+ * Receive a PING frame
+ */
+RXFUNC(ping,	PING)
+
+/* SECTION: stream.spec.goaway_rxgoaway rxgoaway
+ *
+ * Receive a GOAWAY frame
+ */
+RXFUNC(goaway,	GOAWAY)
+
+/* SECTION: stream.spec.winup_rxwinup rxwinup
+ *
+ * Receive a WINDOW_UPDATE frame
+ */
+RXFUNC(winup,	WINDOW_UPDATE)
+
+/* SECTION: stream.spec.frame_rxframe
+ *
+ * Receive a frame, any frame.
+ */
+static void
+cmd_rxframe(CMD_ARGS) {
+	struct stream *s;
+	(void)cmd;
+	(void)vl;
+	(void)av;
+	CAST_OBJ_NOTNULL(s, priv, STREAM_MAGIC);
+	rxstuff(s);
+}
+
+
+
+static void
+cmd_http_expect(CMD_ARGS)
+{
+	struct http *hp;
+	struct stream *s;
+	const char *lhs, *clhs;
+	char *cmp;
+	const char *rhs, *crhs;
+	vre_t *vre;
+	const char *error;
+	int erroroffset;
+	int i, retval = -1;
+	char buf[20];
+
+	(void)cmd;
+	CAST_OBJ_NOTNULL(s, priv, STREAM_MAGIC);
+	hp = s->hp;
+	CHECK_OBJ_NOTNULL(hp, HTTP_MAGIC);
+
+	AZ(strcmp(av[0], "expect"));
+	av++;
+
+	AN(av[0]);
+	AN(av[1]);
+	AN(av[2]);
+	AZ(av[3]);
+	AZ(pthread_mutex_lock(&s->hp->mtx));
+	lhs = cmd_var_resolve(s, av[0], buf);
+	cmp = av[1];
+	rhs = cmd_var_resolve(s, av[2], buf);
+
+	clhs = lhs ? lhs : "<undef>";
+	crhs = rhs ? rhs : "<undef>";
+
+	if (!strcmp(cmp, "~") || !strcmp(cmp, "!~")) {
+		vre = VRE_compile(crhs, 0, &error, &erroroffset);
+		if (vre == NULL)
+			vtc_log(vl, 0, "REGEXP error: %s (@%d) (%s)",
+			    error, erroroffset, crhs);
+		i = VRE_exec(vre, clhs, strlen(clhs), 0, 0, NULL, 0, 0);
+		retval = (i >= 0 && *cmp == '~') || (i < 0 && *cmp == '!');
+		VRE_free(&vre);
+	} else if (!strcmp(cmp, "==")) {
+		retval = strcmp(clhs, crhs) == 0;
+	} else if (!strcmp(cmp, "!=")) {
+		retval = strcmp(clhs, crhs) != 0;
+	} else if (lhs == NULL || rhs == NULL) {
+		// fail inequality comparisons if either side is undef'ed
+		retval = 0;
+	} else if (!strcmp(cmp, "<")) {
+		retval = isless(VNUM(lhs), VNUM(rhs));
+	} else if (!strcmp(cmp, ">")) {
+		retval = isgreater(VNUM(lhs), VNUM(rhs));
+	} else if (!strcmp(cmp, "<=")) {
+		retval = islessequal(VNUM(lhs), VNUM(rhs));
+	} else if (!strcmp(cmp, ">=")) {
+		retval = isgreaterequal(VNUM(lhs), VNUM(rhs));
+	}
+
+	if (retval == -1)
+		vtc_log(vl, 0,
+		    "EXPECT %s (%s) %s %s (%s) test not implemented",
+		    av[0], clhs, av[1], av[2], crhs);
+	else
+		vtc_log(vl, retval ? 4 : 0, "(s%d) EXPECT %s (%s) %s \"%s\" %s",
+		    s->id, av[0], clhs, cmp, crhs, retval ? "match" : "failed");
+	AZ(pthread_mutex_unlock(&s->hp->mtx));
+}
+
+/* SECTION: stream.spec Specification
+ *
+ * The specification of a stream follows the exact same rules as one for a
+ * client or a server.
+ */
+static const struct cmds stream_cmds[] = {
+	{ "expect",		cmd_http_expect },
+	{ "sendhex",		cmd_sendhex },
+	{ "rxframe",		cmd_rxframe },
+	{ "txdata",		cmd_txdata },
+	{ "rxdata",		cmd_rxdata },
+	{ "rxhdrs",		cmd_rxhdrs },
+	{ "txreq",		cmd_tx11obj },
+	{ "rxreq",		cmd_rxreqsp },
+	{ "txresp",		cmd_tx11obj },
+	{ "rxresp",		cmd_rxreqsp },
+	{ "txprio",		cmd_txprio },
+	{ "rxprio",		cmd_rxprio },
+	{ "txrst",		cmd_txrst },
+	{ "rxrst",		cmd_rxrst },
+	{ "txsettings",		cmd_txsettings },
+	{ "rxsettings",		cmd_rxsettings },
+	{ "txpush",		cmd_tx11obj },
+	{ "rxpush",		cmd_rxpush },
+	{ "txping",		cmd_txping },
+	{ "rxping",		cmd_rxping },
+	{ "txgoaway",		cmd_txgoaway },
+	{ "rxgoaway",		cmd_rxgoaway },
+	{ "txwinup",		cmd_txwinup },
+	{ "rxwinup",		cmd_rxwinup },
+	{ "txcont",		cmd_tx11obj },
+	{ "rxcont",		cmd_rxcont },
+	{ "delay",		cmd_delay },
+	{ "barrier",		cmd_barrier },
+	{ NULL,			NULL }
+};
+
+static void *
+stream_thread(void *priv)
+{
+	struct stream *s;
+
+	CAST_OBJ_NOTNULL(s, priv, STREAM_MAGIC);
+
+	parse_string(s->spec, stream_cmds, s, s->hp->vl);
+
+	clean_headers(s->req);
+	clean_headers(s->resp);
+	vtc_log(s->hp->vl, 2, "Ending stream %u", s->id);
+	return (NULL);
+}
+/**********************************************************************
+ * Allocate and initialize a stream
+ */
+
+static struct stream *
+stream_new(const char *name, struct http *h)
+{
+	char *p;
+	struct stream *s;
+
+	ALLOC_OBJ(s, STREAM_MAGIC);
+	AN(s);
+	pthread_cond_init(&s->cond, NULL);
+	REPLACE(s->name, name);
+	AN(name);
+	VTAILQ_INIT(&s->fq);
+	s->ws = h->iws;
+
+	s->weight = 16;
+	s->dependency = 0;
+
+	STRTOU32(s->id, name, p, h->vl, "stream");
+	if (s->id & (1 << 31))
+		vtc_log(h->vl, 0, "Stream id must be a 31-bits integer "
+				"(found %s)", name);
+
+	CHECK_OBJ_NOTNULL(h, HTTP_MAGIC);
+	s->hp = h;
+
+	//bprintf(s->connect, "%s", "${v1_sock}");
+	AZ(pthread_mutex_lock(&h->mtx));
+	VTAILQ_INSERT_HEAD(&h->streams, s, list);
+	AZ(pthread_mutex_unlock(&h->mtx));
+	return (s);
+}
+
+/**********************************************************************
+ * Clean up stream
+ */
+
+static void
+stream_delete(struct stream *s)
+{
+	CHECK_OBJ_NOTNULL(s, STREAM_MAGIC);
+	free(s->spec);
+	free(s->name);
+	FREE_OBJ(s);
+}
+
+/**********************************************************************
+ * Start the stream thread
+ */
+
+static void
+stream_start(struct stream *s)
+{
+	CHECK_OBJ_NOTNULL(s, STREAM_MAGIC);
+	vtc_log(s->hp->vl, 2, "Starting stream %p", s);
+	AZ(pthread_create(&s->tp, NULL, stream_thread, s));
+	s->running = 1;
+}
+
+/**********************************************************************
+ * Wait for stream thread to stop
+ */
+static void
+stream_wait(struct stream *s)
+{
+	void *res;
+	struct frame *f, *f2;
+
+	CHECK_OBJ_NOTNULL(s, STREAM_MAGIC);
+	vtc_log(s->hp->vl, 2, "Waiting for stream %u", s->id);
+	AZ(pthread_join(s->tp, &res));
+	if (res != NULL)
+		vtc_log(s->hp->vl, 0, "Stream %u returned \"%s\"", s->id,
+				(char *)res);
+
+	VTAILQ_FOREACH_SAFE(f, &s->fq, list, f2)
+		clean_frame(&f);
+	clean_frame(&s->frame);
+	s->tp = 0;
+	s->running = 0;
+}
+
+/**********************************************************************
+ * Run the stream thread
+ */
+
+static void
+stream_run(struct stream *s)
+{
+	stream_start(s);
+	stream_wait(s);
+}
+
+
+
+/* SECTION: client-server.spec.zstream stream
+ *
+ * H/2 introduces the concept of streams, and these come with their own
+ * specification, and as it's quite big, have bee move to their own chapter.
+ *
+ * SECTION: stream stream
+ *
+ * (note: this section is at the top-level for easier navigation, but it's part
+ * of the client/server specification)
+ *
+ * Streams map roughly to a request in H/2, a request is sent on stream N,
+ * the response too, then the stream is discarded. The main exception is the
+ * first stream, 0, that serves as coordinator.
+ *
+ * Stream syntax follow the client/server one::
+ *
+ *         stream ID [SPEC] [ACTION]
+ *
+ * ID is the H/2 stream number, while SPEC describes what will be done in that
+ * stream.
+ *
+ * Note that, when parsing a stream action, if the entity isn't operating in H/2
+ * mode, these spec is ran before::
+ *
+ *         txpri/rxpri # client/server
+ *         stream 0 {
+ *             txsettings
+ *             rxsettings
+ *             txsettings -ack
+ *             rxsettings
+ *             expect settings.ack == true
+ *         } -run
+ *
+ * And H/2 mode is then activated before parsing the specification.
+ *
+ * SECTION: stream.actions Actions
+ *
+ * \-start
+ *         Run the specification in a thread, giving back control immediately.
+ *
+ * \-wait
+ *         Wait for the started thread to finish running the spec.
+ *
+ * \-run
+ *         equivalent to calling ``-start`` then ``-wait``. 
+ */
+
+void
+cmd_stream(CMD_ARGS)
+{
+	struct stream *s;
+	struct http *h;
+
+	(void)cmd;
+	(void)vl;
+	CAST_OBJ_NOTNULL(h, priv, HTTP_MAGIC);
+
+	AZ(strcmp(av[0], "stream"));
+	av++;
+
+	VTAILQ_FOREACH(s, &h->streams, list)
+		if (!strcmp(s->name, av[0]))
+			break;
+	if (s == NULL)
+		s = stream_new(av[0], h);
+	av++;
+
+	for (; *av != NULL; av++) {
+		if (vtc_error)
+			break;
+
+		if (!strcmp(*av, "-wait")) {
+			stream_wait(s);
+			continue;
+		}
+
+		/* Don't muck about with a running client */
+		if (s->running)
+			stream_wait(s);
+
+		if (!strcmp(*av, "-start")) {
+			stream_start(s);
+			continue;
+		}
+		if (!strcmp(*av, "-run")) {
+			stream_run(s);
+			continue;
+		}
+		if (**av == '-')
+			vtc_log(vl, 0, "Unknown client argument: %s", *av);
+		REPLACE(s->spec, *av);
+	}
+}
+
+void
+b64_settings(const struct http *hp, const char *s)
+{
+	uint16_t i;
+	uint64_t v;
+	const char *buf;
+	int shift;
+	while (*s) {
+		v = 0;
+		for (shift = 42; shift >= 0; shift -= 6) {
+			if (*s >= 'A' && *s <= 'Z') {
+				v |= (uint64_t)(*s - 'A') << shift;
+			} else if (*s >= 'a' && *s <= 'z') {
+				v |= (uint64_t)(*s - 'a' + 26) << shift;
+			} else if (*s >= '0' && *s <= '9') {
+				v |= (uint64_t)(*s - '0' + 52) << shift;
+			} else if (*s == '-')
+				v |= (uint64_t)62 << shift;
+			else if (*s == '_') {
+				v |= (uint64_t)63 << shift;
+			} else
+				vtc_log(hp->vl, 0, "Bad \"HTTP2-Settings\" "
+						"header");
+			s++;
+		}
+		i = v >> 32;
+		v &= 0xffff;		
+
+		if (i <= SETTINGS_MAX) {
+			buf = h2_settings[i];
+		} else
+			buf = "unknown";
+
+		if (v == 1 ) {
+			if (hp->sfd)
+				HPK_ResizeTbl(hp->encctx, v);
+			else
+				HPK_ResizeTbl(hp->decctx, v);
+		}
+
+		vtc_log(hp->vl, 4, "Upgrade: %s (%d): %ld", buf, i, v);
+	}
+}
+
+void
+start_h2(struct http *hp)
+{
+	CHECK_OBJ_NOTNULL(hp, HTTP_MAGIC);
+	AZ(pthread_mutex_init(&hp->mtx, NULL));
+	AZ(pthread_cond_init(&hp->cond, NULL));
+	VTAILQ_INIT(&hp->streams);
+	hp->iws = 0xffff;
+	hp->ws = 0xffff;
+
+	hp->h2 = 1;
+
+	hp->decctx = HPK_NewCtx(4096);
+	hp->encctx = HPK_NewCtx(4096);
+	AZ(pthread_create(&hp->tp, NULL, receive_frame, hp));
+}
+
+void
+stop_h2(struct http *hp)
+{
+	struct stream *s, *s2;
+
+	CHECK_OBJ_NOTNULL(hp, HTTP_MAGIC);
+	VTAILQ_FOREACH_SAFE(s, &hp->streams, list, s2) {
+		if (s->running)
+			stream_wait(s);
+		AZ(pthread_mutex_lock(&hp->mtx));
+		VTAILQ_REMOVE(&hp->streams, s, list);
+		AZ(pthread_mutex_unlock(&hp->mtx));
+		stream_delete(s);
+	}
+
+	AZ(pthread_mutex_lock(&hp->mtx));
+	hp->h2 = 0;
+	AZ(pthread_cond_signal(&hp->cond));
+	AZ(pthread_mutex_unlock(&hp->mtx));
+	AZ(pthread_join(hp->tp, NULL));
+
+	HPK_FreeCtx(hp->decctx);
+	HPK_FreeCtx(hp->encctx);
+
+	AZ(pthread_mutex_destroy(&hp->mtx));
+	AZ(pthread_cond_destroy(&hp->cond));
+}
diff --git a/bin/varnishtest/vtc_server.c b/bin/varnishtest/vtc_server.c
index 21fe298..22b9849 100644
--- a/bin/varnishtest/vtc_server.c
+++ b/bin/varnishtest/vtc_server.c
@@ -38,7 +38,6 @@
 
 #include "vtc.h"
 
-#include "vss.h"
 #include "vtcp.h"
 
 struct server {
diff --git a/doc/sphinx/Makefile.am b/doc/sphinx/Makefile.am
index f929a6f..c88b46b 100644
--- a/doc/sphinx/Makefile.am
+++ b/doc/sphinx/Makefile.am
@@ -179,6 +179,7 @@ BUILT_SOURCES += include/vsl-tags.rst
 VTCSYN_SRC = $(top_srcdir)/bin/varnishtest/vtc.c \
 	     $(top_srcdir)/bin/varnishtest/vtc_barrier.c \
 	     $(top_srcdir)/bin/varnishtest/vtc_http.c \
+	     $(top_srcdir)/bin/varnishtest/vtc_http2.c \
 	     $(top_srcdir)/bin/varnishtest/vtc_logexp.c \
 	     $(top_srcdir)/bin/varnishtest/vtc_varnish.c
 include/vtc-syntax.rst: vtc-syntax.py $(VTCSYN_SRC)
diff --git a/doc/sphinx/vtc-syntax.py b/doc/sphinx/vtc-syntax.py
index 45a5102..298f4bb 100644
--- a/doc/sphinx/vtc-syntax.py
+++ b/doc/sphinx/vtc-syntax.py
@@ -34,13 +34,10 @@ import sys
 import re
 
 
-def parse_file(fn):
+def parse_file(fn, cl, tl, sl):
 	p = False
 	section = ""
 	resec = re.compile("[ /]\* SECTION: ")
-	cl = {}
-	tl = {}
-	sl = []
 
 	f = open(fn, "r")
 
@@ -63,6 +60,12 @@ def parse_file(fn):
 			cl[section].append(re.sub(r"^ \* ?", "", l))
 	f.close()
 
+if __name__ == "__main__":
+	cl = {}
+	tl = {}
+	sl = []
+	for fn in sys.argv[1:]:
+		parse_file(fn, cl, tl, sl)
 	sl.sort()
 	for section in sl:
 		print(tl[section], end="")
@@ -72,11 +75,10 @@ def parse_file(fn):
 			r = "-"
 		elif c == 1:
 			r = "~"
-		else:
+		elif c == 2:
 			r = "."
+		else:
+			r = "*"
 		print(re.sub(r".", r, tl[section]), end="")
 		print("".join(cl[section]))
 
-if __name__ == "__main__":
-	for fn in sys.argv[1:]:
-		parse_file(fn)



More information about the varnish-commit mailing list