[master] 66d2682 Add our skeleton HTTP2 implementation, which seems to survive at least trival traffic in real life.
Poul-Henning Kamp
phk at FreeBSD.org
Tue Aug 30 13:45:12 CEST 2016
commit 66d2682647b8fc00c32f69bb4b1856d7d4dcc30c
Author: Poul-Henning Kamp <phk at FreeBSD.org>
Date: Tue Aug 30 11:43:29 2016 +0000
Add our skeleton HTTP2 implementation, which seems to survive
at least trival traffic in real life.
diff --git a/bin/varnishd/Makefile.am b/bin/varnishd/Makefile.am
index 1a328fb..d26d982 100644
--- a/bin/varnishd/Makefile.am
+++ b/bin/varnishd/Makefile.am
@@ -58,6 +58,8 @@ varnishd_SOURCES = \
hash/hash_critbit.c \
hash/mgt_hash.c \
hash/hash_simple_list.c \
+ hpack/vhp_table.c \
+ hpack/vhp_decode.c \
http1/cache_http1_deliver.c \
http1/cache_http1_fetch.c \
http1/cache_http1_fsm.c \
@@ -65,6 +67,11 @@ varnishd_SOURCES = \
http1/cache_http1_pipe.c \
http1/cache_http1_proto.c \
http1/cache_http1_vfp.c \
+ http2/cache_http2_deliver.c \
+ http2/cache_http2_hpack.c \
+ http2/cache_http2_panic.c \
+ http2/cache_http2_proto.c \
+ http2/cache_http2_send.c \
mgt/mgt_acceptor.c \
mgt/mgt_child.c \
mgt/mgt_cli.c \
@@ -111,7 +118,9 @@ noinst_HEADERS = \
cache/cache_transport.h \
common/heritage.h \
hash/hash_slinger.h \
+ hpack/vhp.h \
http1/cache_http1.h \
+ http2/cache_http2.h \
mgt/mgt.h \
mgt/mgt_param.h \
storage/storage.h \
@@ -153,6 +162,26 @@ varnishd_LDADD = \
EXTRA_DIST = builtin.vcl
DISTCLEANFILES = builtin_vcl.h
+noinst_PROGRAMS = vhp_gen_hufdec
+vhp_gen_hufdec_SOURCES = hpack/vhp_gen_hufdec.c
+vhp_gen_hufdec_CFLAGS = -include config.h
+vhp_gen_hufdec_LDADD = \
+ $(top_builddir)/lib/libvarnish/libvarnish.la
+
+noinst_PROGRAMS += vhp_table_test
+vhp_table_test_SOURCES = hpack/vhp_table.c
+vhp_table_test_CFLAGS = -DTABLE_TEST_DRIVER -include config.h
+vhp_table_test_LDADD = \
+ $(top_builddir)/lib/libvarnish/libvarnish.la
+
+noinst_PROGRAMS += vhp_decode_test
+vhp_decode_test_SOURCES = hpack/vhp_decode.c hpack/vhp_table.c
+vhp_decode_test_CFLAGS = -DDECODE_TEST_DRIVER -include config.h
+vhp_decode_test_LDADD = \
+ $(top_builddir)/lib/libvarnish/libvarnish.la
+
+TESTS = vhp_table_test vhp_decode_test
+
#
# Turn the builtin.vcl file into a C-string we can include in the program.
#
@@ -168,5 +197,12 @@ builtin_vcl.h: builtin.vcl
-e 's/$$/\\n"/' \
-e 's/^/ "/' $(srcdir)/builtin.vcl >> $@
+vhp_hufdec.h: vhp_gen_hufdec
+ $(AM_V_GEN) ./vhp_gen_hufdec > vhp_hufdec.h_
+ mv vhp_hufdec.h_ vhp_hufdec.h
+
+BUILT_SOURCES = vhp_hufdec.h
+DISTCLEANFILES = vhp_hufdec.h
+
# Explicitly record dependency
mgt/mgt_vcc.c: builtin_vcl.h
diff --git a/bin/varnishd/cache/cache.h b/bin/varnishd/cache/cache.h
index 923a8d2..57f260c 100644
--- a/bin/varnishd/cache/cache.h
+++ b/bin/varnishd/cache/cache.h
@@ -816,6 +816,9 @@ extern const char H__Status[];
extern const char H__Proto[];
extern const char H__Reason[];
+/* cache_http2_deliver.c */
+void V2D_Init(void);
+
/* cache_main.c */
#define VXID(u) ((u) & VSL_IDENTMASK)
uint32_t VXID_Get(struct worker *, uint32_t marker);
diff --git a/bin/varnishd/cache/cache_acceptor.c b/bin/varnishd/cache/cache_acceptor.c
index 58481d5..d1f001a 100644
--- a/bin/varnishd/cache/cache_acceptor.c
+++ b/bin/varnishd/cache/cache_acceptor.c
@@ -632,6 +632,7 @@ XPORT_Init(void)
VTAILQ_INSERT_TAIL(&transports, &PROXY_transport, list);
VTAILQ_INSERT_TAIL(&transports, &HTTP1_transport, list);
+ VTAILQ_INSERT_TAIL(&transports, &H2_transport, list);
n = 0;
VTAILQ_FOREACH(xp, &transports, list)
diff --git a/bin/varnishd/cache/cache_main.c b/bin/varnishd/cache/cache_main.c
index 6e67968..7c076e2 100644
--- a/bin/varnishd/cache/cache_main.c
+++ b/bin/varnishd/cache/cache_main.c
@@ -241,6 +241,7 @@ child_main(void)
VBE_InitCfg();
Pool_Init();
V1P_Init();
+ V2D_Init();
EXP_Init();
HSH_Init(heritage.hash);
diff --git a/bin/varnishd/cache/cache_transport.h b/bin/varnishd/cache/cache_transport.h
index ef57325..2633cfe 100644
--- a/bin/varnishd/cache/cache_transport.h
+++ b/bin/varnishd/cache/cache_transport.h
@@ -66,6 +66,8 @@ struct transport {
extern struct transport PROXY_transport;
extern struct transport HTTP1_transport;
+extern struct transport H2_transport;
+htc_complete_f H2_prism_complete;
const struct transport *XPORT_ByNumber(uint16_t no);
void VPX_Send_Proxy(int fd, int version, const struct sess *);
diff --git a/bin/varnishd/flint.sh b/bin/varnishd/flint.sh
index f96d0fc..ba15504 100755
--- a/bin/varnishd/flint.sh
+++ b/bin/varnishd/flint.sh
@@ -23,6 +23,7 @@ flexelint \
common/*.c \
hash/*.c \
http1/*.c \
+ http2/*.c \
mgt/*.c \
proxy/*.c \
storage/*.c \
diff --git a/bin/varnishd/hpack/vhp.h b/bin/varnishd/hpack/vhp.h
new file mode 100644
index 0000000..0f9852d
--- /dev/null
+++ b/bin/varnishd/hpack/vhp.h
@@ -0,0 +1,129 @@
+/*-
+ * Copyright (c) 2016 Varnish Software
+ * All rights reserved.
+ *
+ * Author: Martin Blix Grydeland <martin at varnish-software.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 <stdint.h>
+
+/* VHT - Varnish HPACK Table */
+
+#define VHT_ENTRY_SIZE 32U
+
+struct vht_entry {
+ unsigned magic;
+#define VHT_ENTRY_MAGIC 0xc06dd892
+ unsigned offset;
+ unsigned namelen;
+ unsigned valuelen;
+};
+
+struct vht_table {
+ unsigned magic;
+#define VHT_TABLE_MAGIC 0x6bbdc683
+ unsigned n;
+ unsigned size;
+ unsigned maxsize; /* n * 32 + size <= maxsize */
+ unsigned protomax;
+ unsigned bufsize;
+ char *buf;
+};
+
+void VHT_NewEntry(struct vht_table *);
+int VHT_NewEntry_Indexed(struct vht_table *, unsigned);
+void VHT_AppendName(struct vht_table *, const char *, ssize_t);
+void VHT_AppendValue(struct vht_table *, const char *, ssize_t);
+int VHT_SetMaxTableSize(struct vht_table *, size_t);
+int VHT_SetProtoMax(struct vht_table *, size_t);
+const char *VHT_LookupName(const struct vht_table *, unsigned, size_t *);
+const char *VHT_LookupValue(const struct vht_table *, unsigned, size_t *);
+int VHT_Init(struct vht_table *, size_t);
+void VHT_Fini(struct vht_table *);
+
+/* VHD - Varnish HPACK Decoder */
+
+enum vhd_ret_e {
+#define VHD_RET(NAME, VAL, DESC) \
+ VHD_##NAME = VAL,
+#include "tbl/vhd_return.h"
+#undef VHD_RET
+};
+
+struct vhd_int {
+ uint8_t magic;
+#define VHD_INT_MAGIC 0x05
+
+ uint8_t pfx;
+ uint8_t m;
+ unsigned v;
+};
+
+struct vhd_raw {
+ uint8_t magic;
+#define VHD_RAW_MAGIC 0xa0
+
+ unsigned l;
+};
+
+struct vhd_huffman {
+ uint8_t magic;
+#define VHD_HUFFMAN_MAGIC 0x56
+
+ uint8_t blen;
+ uint16_t bits;
+ uint16_t pos;
+ unsigned len;
+};
+
+struct vhd_lookup {
+ uint8_t magic;
+#define VHD_LOOKUP_MAGIC 0x65
+
+ unsigned l;
+};
+
+struct vhd_decode {
+ unsigned magic;
+#define VHD_DECODE_MAGIC 0x9cbc72b2
+
+ unsigned index;
+ uint16_t state;
+ int8_t error;
+ uint8_t first;
+
+ union {
+ struct vhd_int integer[1];
+ struct vhd_lookup lookup[1];
+ struct vhd_raw raw[1];
+ struct vhd_huffman huffman[1];
+ };
+};
+
+void VHD_Init(struct vhd_decode *);
+enum vhd_ret_e VHD_Decode(struct vhd_decode *, struct vht_table *,
+ const uint8_t *in, size_t inlen, size_t *p_inused,
+ char *out, size_t outlen, size_t *p_outused);
+const char *VHD_Error(enum vhd_ret_e);
diff --git a/bin/varnishd/hpack/vhp_decode.c b/bin/varnishd/hpack/vhp_decode.c
new file mode 100644
index 0000000..334db2d
--- /dev/null
+++ b/bin/varnishd/hpack/vhp_decode.c
@@ -0,0 +1,1218 @@
+/*-
+ * Copyright (c) 2016 Varnish Software
+ * All rights reserved.
+ *
+ * Author: Martin Blix Grydeland <martin at varnish-software.com>
+ * Author: Dridi Boukelmoune <dridi.boukelmoune 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 <string.h>
+#include <limits.h>
+#include <stdlib.h>
+
+#include "vdef.h"
+#include "vas.h"
+#include "miniobj.h"
+
+#include "hpack/vhp.h"
+
+#include "vhp_hufdec.h"
+
+struct vhd_ctx {
+ struct vhd_decode *d;
+ struct vht_table *tbl;
+ const uint8_t *in;
+ const uint8_t *in_e;
+ char *out;
+ char *out_e;
+};
+
+typedef enum vhd_ret_e vhd_state_f(struct vhd_ctx *ctx, unsigned first);
+
+/* Function flags */
+#define VHD_INCREMENTAL (1U << 0)
+
+/* Functions */
+enum vhd_func_e {
+#define VHD_FSM_FUNC(NAME, func) \
+ VHD_F_##NAME,
+#include "tbl/vhd_fsm_funcs.h"
+#undef VHD_FSM_FUNC
+ VHD_F__MAX,
+};
+#define VHD_FSM_FUNC(NAME, func) \
+ static vhd_state_f func;
+#include "tbl/vhd_fsm_funcs.h"
+#undef VHD_FSM_FUNC
+
+/* States */
+enum vhd_state_e {
+#define VHD_FSM(STATE, FUNC, arg1, arg2) \
+ VHD_S_##STATE,
+#include "tbl/vhd_fsm.h"
+#undef VHD_FSM
+ VHD_S__MAX,
+};
+static const struct vhd_state {
+ const char *name;
+ enum vhd_func_e func;
+ unsigned arg1;
+ unsigned arg2;
+} vhd_states[VHD_S__MAX] = {
+#define VHD_FSM(STATE, FUNC, arg1, arg2) \
+ [VHD_S_##STATE] = { #STATE, VHD_F_##FUNC, arg1, arg2 },
+#include "tbl/vhd_fsm.h"
+#undef VHD_FSM
+};
+
+/* Utility functions */
+static void
+vhd_set_state(struct vhd_decode *d, enum vhd_state_e state)
+{
+ AN(d);
+ assert(state >= 0 && state < VHD_S__MAX);
+ d->state = state;
+ d->first = 1;
+}
+
+static void
+vhd_next_state(struct vhd_decode *d)
+{
+ AN(d);
+ assert(d->state + 1 < VHD_S__MAX);
+ vhd_set_state(d, d->state + 1);
+}
+
+/* State functions */
+static enum vhd_ret_e __match_proto__(vhd_state_f)
+vhd_skip(struct vhd_ctx *ctx, unsigned first)
+{
+ AN(ctx);
+ AN(first);
+ vhd_next_state(ctx->d);
+ return (VHD_AGAIN);
+}
+
+static enum vhd_ret_e __match_proto__(vhd_state_f)
+vhd_goto(struct vhd_ctx *ctx, unsigned first)
+{
+ const struct vhd_state *s;
+
+ AN(ctx);
+ AN(first);
+ assert(ctx->d->state < VHD_S__MAX);
+ s = &vhd_states[ctx->d->state];
+ assert(s->arg1 < VHD_S__MAX);
+ vhd_set_state(ctx->d, s->arg1);
+ return (VHD_AGAIN);
+}
+
+static enum vhd_ret_e __match_proto__(vhd_state_f)
+vhd_idle(struct vhd_ctx *ctx, unsigned first)
+{
+ uint8_t c;
+
+ AN(ctx);
+ (void)first;
+
+ while (ctx->in < ctx->in_e) {
+ c = *ctx->in;
+ if ((c & 0x80) == 0x80)
+ vhd_set_state(ctx->d, VHD_S_HP61_START);
+ else if ((c & 0xc0) == 0x40)
+ vhd_set_state(ctx->d, VHD_S_HP621_START);
+ else if ((c & 0xf0) == 0x00)
+ vhd_set_state(ctx->d, VHD_S_HP622_START);
+ else if ((c & 0xf0) == 0x10)
+ vhd_set_state(ctx->d, VHD_S_HP623_START);
+ else if ((c & 0xe0) == 0x20)
+ vhd_set_state(ctx->d, VHD_S_HP63_START);
+ else
+ return (VHD_ERR_ARG);
+ return (VHD_AGAIN);
+ }
+
+ return (VHD_OK);
+}
+
+static enum vhd_ret_e __match_proto__(vhd_state_f)
+vhd_integer(struct vhd_ctx *ctx, unsigned first)
+{
+ const struct vhd_state *s;
+ struct vhd_int *i;
+ uint8_t c;
+ unsigned mask;
+
+ assert(UINT_MAX >= UINT32_MAX);
+
+ AN(ctx);
+ assert(ctx->d->state < VHD_S__MAX);
+ s = &vhd_states[ctx->d->state];
+ i = ctx->d->integer;
+
+ if (first) {
+ INIT_OBJ(i, VHD_INT_MAGIC);
+ i->pfx = s->arg1;
+ assert(i->pfx >= 4 && i->pfx <= 7);
+ }
+ CHECK_OBJ_NOTNULL(i, VHD_INT_MAGIC);
+
+ while (ctx->in < ctx->in_e) {
+ c = *ctx->in;
+ ctx->in++;
+ if (i->pfx) {
+ mask = (1U << i->pfx) - 1;
+ i->pfx = 0;
+ i->v = c & mask;
+ if (i->v < mask) {
+ vhd_next_state(ctx->d);
+ return (VHD_AGAIN);
+ }
+ } else {
+ if ((i->m == 28 && (c & 0x78)) || i->m > 28)
+ return (VHD_ERR_INT);
+ i->v += (c & 0x7f) * ((uint32_t)1 << i->m);
+ i->m += 7;
+ if (!(c & 0x80)) {
+ vhd_next_state(ctx->d);
+ return (VHD_AGAIN);
+ }
+ }
+ }
+ return (VHD_MORE);
+}
+
+static enum vhd_ret_e __match_proto__(vhd_state_f)
+vhd_set_max(struct vhd_ctx *ctx, unsigned first)
+{
+ AN(ctx);
+ AN(first);
+ CHECK_OBJ_NOTNULL(ctx->d->integer, VHD_INT_MAGIC);
+ if (ctx->tbl == NULL)
+ return (VHD_ERR_UPD);
+ if (VHT_SetMaxTableSize(ctx->tbl, ctx->d->integer->v))
+ return (VHD_ERR_UPD);
+ vhd_next_state(ctx->d);
+ return (VHD_AGAIN);
+}
+
+static enum vhd_ret_e __match_proto__(vhd_state_f)
+vhd_set_idx(struct vhd_ctx *ctx, unsigned first)
+{
+ AN(ctx);
+ AN(first);
+ CHECK_OBJ_NOTNULL(ctx->d->integer, VHD_INT_MAGIC);
+ ctx->d->index = ctx->d->integer->v;
+ vhd_next_state(ctx->d);
+ return (VHD_AGAIN);
+}
+
+static enum vhd_ret_e __match_proto__(vhd_state_f)
+vhd_lookup(struct vhd_ctx *ctx, unsigned first)
+{
+ const struct vhd_state *s;
+ struct vhd_lookup *lu;
+ const char *p;
+ size_t l;
+
+ AN(ctx);
+ assert(ctx->d->state < VHD_S__MAX);
+ s = &vhd_states[ctx->d->state];
+ lu = ctx->d->lookup;
+
+ if (first)
+ INIT_OBJ(lu, VHD_LOOKUP_MAGIC);
+ CHECK_OBJ_NOTNULL(lu, VHD_LOOKUP_MAGIC);
+
+ switch (s->arg1) {
+ case VHD_NAME:
+ case VHD_NAME_SEC:
+ p = VHT_LookupName(ctx->tbl, ctx->d->index, &l);
+ break;
+ case VHD_VALUE:
+ case VHD_VALUE_SEC:
+ p = VHT_LookupValue(ctx->tbl, ctx->d->index, &l);
+ break;
+ default:
+ WRONG("vhd_lookup wrong arg1");
+ break;
+ }
+ if (first && p == NULL)
+ return (VHD_ERR_IDX);
+ AN(p);
+ assert(l <= UINT_MAX);
+ if (first)
+ lu->l = l;
+
+ assert(lu->l <= l);
+ p += l - lu->l;
+ l = lu->l;
+ if (l > ctx->out_e - ctx->out)
+ l = ctx->out_e - ctx->out;
+ memcpy(ctx->out, p, l);
+ ctx->out += l;
+ lu->l -= l;
+
+ if (lu->l == 0) {
+ vhd_next_state(ctx->d);
+ return (s->arg1);
+ }
+ assert(ctx->out == ctx->out_e);
+ return (VHD_BUF);
+}
+
+static enum vhd_ret_e __match_proto__(vhd_state_f)
+vhd_new(struct vhd_ctx *ctx, unsigned first)
+{
+ AN(ctx);
+ AN(first);
+ if (ctx->tbl != NULL)
+ VHT_NewEntry(ctx->tbl);
+ vhd_next_state(ctx->d);
+ return (VHD_AGAIN);
+}
+
+static enum vhd_ret_e __match_proto__(vhd_state_f)
+vhd_new_idx(struct vhd_ctx *ctx, unsigned first)
+{
+ AN(ctx);
+ AN(first);
+ if (ctx->tbl != NULL) {
+ if (VHT_NewEntry_Indexed(ctx->tbl, ctx->d->index))
+ return (VHD_ERR_IDX);
+ }
+ vhd_next_state(ctx->d);
+ return (VHD_AGAIN);
+}
+
+static enum vhd_ret_e __match_proto__(vhd_state_f)
+vhd_branch_zidx(struct vhd_ctx *ctx, unsigned first)
+{
+ const struct vhd_state *s;
+
+ AN(ctx);
+ (void)first;
+ assert(ctx->d->state < VHD_S__MAX);
+ s = &vhd_states[ctx->d->state];
+ assert(s->arg1 < VHD_S__MAX);
+
+ if (ctx->d->index == 0)
+ vhd_set_state(ctx->d, s->arg1);
+ else
+ vhd_next_state(ctx->d);
+ return (VHD_AGAIN);
+}
+
+static enum vhd_ret_e __match_proto__(vhd_state_f)
+vhd_branch_bit0(struct vhd_ctx *ctx, unsigned first)
+{
+ const struct vhd_state *s;
+
+ AN(ctx);
+ (void)first;
+ assert(ctx->d->state < VHD_S__MAX);
+ s = &vhd_states[ctx->d->state];
+ assert(s->arg1 < VHD_S__MAX);
+
+ if (ctx->in == ctx->in_e)
+ return (VHD_MORE);
+
+ if (*ctx->in & 0x80)
+ vhd_set_state(ctx->d, s->arg1);
+ else
+ vhd_next_state(ctx->d);
+ return (VHD_AGAIN);
+}
+
+static enum vhd_ret_e __match_proto__(vhd_state_f)
+vhd_raw(struct vhd_ctx *ctx, unsigned first)
+{
+ const struct vhd_state *s;
+ struct vhd_raw *raw;
+ size_t l2;
+
+ AN(ctx);
+ assert(ctx->d->state < VHD_S__MAX);
+ s = &vhd_states[ctx->d->state];
+
+ raw = ctx->d->raw;
+ if (first) {
+ CHECK_OBJ_NOTNULL(ctx->d->integer, VHD_INT_MAGIC);
+ l2 = ctx->d->integer->v;
+ INIT_OBJ(raw, VHD_RAW_MAGIC);
+ raw->l = l2;
+ }
+ CHECK_OBJ_NOTNULL(raw, VHD_RAW_MAGIC);
+
+ while (raw->l > 0) {
+ l2 = raw->l;
+ if (l2 > (ctx->in_e - ctx->in))
+ l2 = ctx->in_e - ctx->in;
+ if (l2 == 0)
+ return (VHD_MORE);
+ if (l2 > (ctx->out_e - ctx->out))
+ l2 = ctx->out_e - ctx->out;
+ if (l2 == 0)
+ return (VHD_BUF);
+ memcpy(ctx->out, ctx->in, l2);
+ ctx->in += l2;
+ if (ctx->tbl != NULL && (s->arg2 & VHD_INCREMENTAL)) {
+ switch (s->arg1) {
+ case VHD_NAME:
+ VHT_AppendName(ctx->tbl, ctx->out, l2);
+ break;
+ case VHD_VALUE:
+ VHT_AppendValue(ctx->tbl, ctx->out, l2);
+ break;
+ default:
+ WRONG("vhd_raw wrong arg1");
+ break;
+ }
+ }
+ ctx->out += l2;
+ raw->l -= l2;
+ }
+ vhd_next_state(ctx->d);
+ return (s->arg1);
+}
+
+static enum vhd_ret_e __match_proto__(vhd_state_f)
+vhd_huffman(struct vhd_ctx *ctx, unsigned first)
+{
+ const struct vhd_state *s;
+ struct vhd_huffman *huf;
+ enum vhd_ret_e r;
+ unsigned u, l;
+
+ AN(ctx);
+ assert(ctx->d->state < VHD_S__MAX);
+ s = &vhd_states[ctx->d->state];
+
+ huf = ctx->d->huffman;
+ if (first) {
+ CHECK_OBJ_NOTNULL(ctx->d->integer, VHD_INT_MAGIC);
+ l = ctx->d->integer->v;
+ INIT_OBJ(huf, VHD_HUFFMAN_MAGIC);
+ huf->len = l;
+ }
+ CHECK_OBJ_NOTNULL(huf, VHD_HUFFMAN_MAGIC);
+
+ r = VHD_OK;
+ l = 0;
+ while (1) {
+ assert(huf->pos < HUFDEC_LEN);
+ assert(hufdec[huf->pos].mask > 0);
+ assert(hufdec[huf->pos].mask <= 8);
+
+ if (huf->len > 0 && huf->blen < hufdec[huf->pos].mask) {
+ /* Refill from input */
+ if (ctx->in == ctx->in_e) {
+ r = VHD_MORE;
+ break;
+ }
+ huf->bits = (huf->bits << 8) | *ctx->in;
+ huf->blen += 8;
+ huf->len--;
+ ctx->in++;
+ }
+
+ if (huf->len == 0 && huf->pos == 0 && huf->blen <= 7 &&
+ huf->bits == (1U << huf->blen) - 1U) {
+ /* End of stream */
+ r = s->arg1;
+ vhd_next_state(ctx->d);
+ break;
+ }
+
+ if (ctx->out + l == ctx->out_e) {
+ r = VHD_BUF;
+ break;
+ }
+
+ if (huf->blen >= hufdec[huf->pos].mask)
+ u = huf->bits >> (huf->blen - hufdec[huf->pos].mask);
+ else
+ u = huf->bits << (hufdec[huf->pos].mask - huf->blen);
+ huf->pos += u;
+ assert(huf->pos < HUFDEC_LEN);
+
+ if (hufdec[huf->pos].len == 0 ||
+ hufdec[huf->pos].len > huf->blen) {
+ /* Invalid or incomplete code */
+ r = VHD_ERR_HUF;
+ break;
+ }
+
+ huf->blen -= hufdec[huf->pos].len;
+ huf->bits &= (1U << huf->blen) - 1U;
+
+ if (hufdec[huf->pos].jump) {
+ huf->pos += hufdec[huf->pos].jump;
+ assert(huf->pos < HUFDEC_LEN);
+ } else {
+ ctx->out[l++] = hufdec[huf->pos].chr;
+ huf->pos = 0;
+ }
+ }
+
+ if (l > 0 && ctx->tbl != NULL && (s->arg2 & VHD_INCREMENTAL)) {
+ switch (s->arg1) {
+ case VHD_NAME:
+ VHT_AppendName(ctx->tbl, ctx->out, l);
+ break;
+ case VHD_VALUE:
+ VHT_AppendValue(ctx->tbl, ctx->out, l);
+ break;
+ default:
+ WRONG("vhd_raw wrong arg1");
+ break;
+ }
+ }
+ ctx->out += l;
+
+ assert(r != VHD_OK);
+ return (r);
+}
+
+/* Public interface */
+
+const char *
+VHD_Error(enum vhd_ret_e r)
+{
+ switch (r) {
+#define VHD_RET(NAME, VAL, DESC) \
+ case VHD_##NAME: \
+ return ("VHD_" #NAME " (" DESC ")");
+#include "tbl/vhd_return.h"
+#undef VHD_RET
+ default:
+ return ("VHD_UNKNOWN");
+ }
+}
+
+enum vhd_ret_e
+VHD_Decode(struct vhd_decode *d, struct vht_table *tbl,
+ const uint8_t *in, size_t inlen, size_t *p_inused,
+ char *out, size_t outlen, size_t *p_outused)
+{
+ const struct vhd_state *s;
+ struct vhd_ctx ctx[1];
+ enum vhd_ret_e ret;
+ unsigned first;
+
+ CHECK_OBJ_NOTNULL(d, VHD_DECODE_MAGIC);
+ CHECK_OBJ_ORNULL(tbl, VHT_TABLE_MAGIC);
+ AN(in);
+ AN(p_inused);
+ AN(out);
+ AN(p_outused);
+
+ if (d->error < 0)
+ return (d->error);
+
+ assert(*p_inused <= inlen);
+ assert(*p_outused <= outlen);
+
+ ctx->d = d;
+ ctx->tbl = tbl;
+ ctx->in = in + *p_inused;
+ ctx->in_e = in + inlen;
+ ctx->out = out + *p_outused;
+ ctx->out_e = out + outlen;
+
+ do {
+ first = d->first;
+ d->first = 0;
+ assert(d->state < VHD_S__MAX);
+ s = &vhd_states[d->state];
+ switch (s->func) {
+#define VHD_FSM_FUNC(NAME, func) \
+ case VHD_F_##NAME: \
+ ret = func(ctx, first); \
+ break;
+#include "tbl/vhd_fsm_funcs.h"
+#undef VHD_FSM_FUNC
+ default:
+ WRONG("Undefined vhd function");
+ break;
+ }
+ } while (ret == VHD_AGAIN);
+
+ if (ret < 0)
+ d->error = ret;
+
+ assert(in + *p_inused <= ctx->in);
+ *p_inused += ctx->in - (in + *p_inused);
+ assert(out + *p_outused <= ctx->out);
+ *p_outused += ctx->out - (out + *p_outused);
+
+ return (ret);
+}
+
+void
+VHD_Init(struct vhd_decode *d)
+{
+
+ AN(d);
+ assert(VHD_S__MAX <= UINT16_MAX);
+ assert(HUFDEC_LEN <= UINT16_MAX);
+ INIT_OBJ(d, VHD_DECODE_MAGIC);
+ d->state = VHD_S_IDLE;
+ d->first = 1;
+}
+
+/* Test driver */
+
+#ifdef DECODE_TEST_DRIVER
+
+#include <ctype.h>
+#include <stdarg.h>
+
+static int verbose = 0;
+
+static size_t
+hexbuf(uint8_t *buf, size_t buflen, const char *h)
+{
+ size_t l;
+ uint8_t u;
+
+ AN(h);
+ AN(buf);
+
+ l = 0;
+ for (; *h != '\0'; h++) {
+ if (l == buflen * 2)
+ WRONG("Too small buffer");
+ if (isspace(*h))
+ continue;
+ if (*h >= '0' && *h <= '9')
+ u = *h - '0';
+ else if (*h >= 'a' && *h <= 'f')
+ u = 0xa + *h - 'a';
+ else if (*h >= 'A' && *h <= 'F')
+ u = 0xa + *h - 'A';
+ else
+ WRONG("Bad input character");
+ assert(u <= 0xf);
+ if (l % 2 == 0) {
+ u <<= 4;
+ buf[l / 2] = u;
+ } else {
+ buf[l / 2] |= u;
+ }
+ l++;
+ }
+ AZ(l % 2);
+ return (l / 2);
+}
+
+static int
+match(const char *b, size_t l, ...)
+{
+ va_list ap;
+ const char *e;
+ const char *m;
+ int r = 0;
+
+ va_start(ap, l);
+ e = b + l;
+ while (1) {
+ m = va_arg(ap, const char *);
+ if (m == NULL)
+ break;
+ l = strlen(m);
+ if (e - b <= l || b[l] != '\0' || strncmp(b, m, l)) {
+ printf("%.*s != %s\n", (int)(e - b), b, m);
+ r = -1;
+ break;
+ } else if (verbose) {
+ printf("%s == %s\n", b, m);
+ }
+ b += l + 1;
+ }
+ va_end(ap);
+ return (r);
+}
+
+#define M_1IN (1U << 0)
+#define M_1OUT (1U << 1)
+
+static enum vhd_ret_e
+decode(struct vhd_decode *d, struct vht_table *tbl, uint8_t *in, size_t in_l,
+ char *out, size_t out_l, unsigned m)
+{
+ size_t in_u, out_u;
+ enum vhd_ret_e r;
+
+ CHECK_OBJ_NOTNULL(d, VHD_DECODE_MAGIC);
+ AN(in);
+ AN(out);
+
+ in_u = 0;
+ out_u = 0;
+
+ while (1) {
+ r = VHD_Decode(d, tbl, in,
+ (m & M_1IN ? (in_l > in_u ? in_u + 1 : in_u) : in_l),
+ &in_u,
+ out,
+ (m & M_1OUT ? (out_l > out_u ? out_u + 1 : out_u) : out_l),
+ &out_u);
+ assert(in_u <= in_l);
+ assert(out_u <= out_l);
+ if (r < VHD_OK)
+ return (r);
+
+ switch (r) {
+ case VHD_OK:
+ return (r);
+
+ case VHD_MORE:
+ if (in_u == in_l)
+ return (r);
+ break;
+
+ case VHD_BUF:
+ if (out_u == out_l)
+ return (r);
+ break;
+
+ case VHD_NAME:
+ case VHD_NAME_SEC:
+ assert(out_l - out_u > 0);
+ out[out_u++] = '\0';
+ if (verbose)
+ printf("Name%s: '%s'\n",
+ (r == VHD_NAME_SEC ? " (sec)" : ""),
+ out);
+ out += out_u;
+ out_l -= out_u;
+ out_u = 0;
+ break;
+
+ case VHD_VALUE:
+ case VHD_VALUE_SEC:
+ assert(out_l - out_u > 0);
+ out[out_u++] = '\0';
+ if (verbose)
+ printf("Value%s: '%s'\n",
+ (r == VHD_VALUE_SEC ? " (sec)" : ""),
+ out);
+ out += out_u;
+ out_l -= out_u;
+ out_u = 0;
+ break;
+
+ default:
+ WRONG("Wrong return code");
+ break;
+ }
+ }
+
+ NEEDLESS_RETURN(0);
+}
+
+#define CHECK_RET(r, e) \
+ do { \
+ if (verbose || r != e) { \
+ printf("%s %s %s\n", \
+ VHD_Error(r), \
+ (r == e ? "==" : "!="), \
+ VHD_Error(e)); \
+ } \
+ assert(r == e); \
+ } while (0)
+
+#define CHECK_INT(d, u) \
+ do { \
+ CHECK_OBJ_NOTNULL(d->integer, VHD_INT_MAGIC); \
+ if (verbose || d->integer->v != u) { \
+ printf("%u %s %u\n", d->integer->v, \
+ (d->integer->v == u ? "==" : "!="), \
+ u); \
+ } \
+ assert(d->integer->v == u); \
+ } while (0)
+
+static void
+test_integer(unsigned mode)
+{
+ struct vhd_decode d[1];
+ uint8_t in[128];
+ size_t in_l;
+ char out[128];
+ enum vhd_ret_e r;
+
+ /* Test single byte decoding */
+ VHD_Init(d);
+ vhd_set_state(d, VHD_S_TEST_INT5);
+ in_l = hexbuf(in, sizeof in, "1e");
+ r = decode(d, NULL, in, in_l, out, sizeof out, mode);
+ CHECK_RET(r, VHD_OK);
+ CHECK_INT(d, 30);
+
+ /* Test multibyte decoding */
+ VHD_Init(d);
+ vhd_set_state(d, VHD_S_TEST_INT5);
+ in_l = hexbuf(in, sizeof in, "ff 9a 0a");
+ r = decode(d, NULL, in, in_l, out, sizeof out, mode);
+ CHECK_RET(r, VHD_OK);
+ CHECK_INT(d, 1337);
+
+ /* Test max size we allow */
+ VHD_Init(d);
+ vhd_set_state(d, VHD_S_TEST_INT5);
+ in_l = hexbuf(in, sizeof in, "1f ff ff ff ff 07");
+ r = decode(d, NULL, in, in_l, out, sizeof out, mode);
+ CHECK_RET(r, VHD_OK);
+ CHECK_INT(d, 0x8000001E);
+
+ /* Test overflow */
+ VHD_Init(d);
+ vhd_set_state(d, VHD_S_TEST_INT5);
+ in_l = hexbuf(in, sizeof in, "1f ff ff ff ff 08");
+ r = decode(d, NULL, in, in_l, out, sizeof out, mode);
+ CHECK_RET(r, VHD_ERR_INT);
+}
+
+static void
+test_raw(unsigned mode)
+{
+ struct vhd_decode d[1];
+ uint8_t in[128];
+ size_t in_l;
+ char out[128];
+ enum vhd_ret_e r;
+
+ /* Test raw encoding */
+ VHD_Init(d);
+ vhd_set_state(d, VHD_S_TEST_LITERAL);
+ in_l = hexbuf(in, sizeof in,
+ "0a63 7573 746f 6d2d 6b65 790d 6375 7374 6f6d 2d68 6561 6465 72");
+ r = decode(d, NULL, in, in_l, out, sizeof out, mode);
+ CHECK_RET(r, VHD_OK);
+ AZ(match(out, sizeof out, "custom-key", "custom-header", NULL));
+
+ /* Test too short input */
+ VHD_Init(d);
+ vhd_set_state(d, VHD_S_TEST_LITERAL);
+ in_l = hexbuf(in, sizeof in,
+ "02");
+ r = decode(d, NULL, in, in_l, out, sizeof out, mode);
+ CHECK_RET(r, VHD_MORE);
+}
+
+static void
+test_huffman(unsigned mode)
+{
+ struct vhd_decode d[1];
+ uint8_t in[256];
+ size_t in_l;
+ char out[256];
+ enum vhd_ret_e r;
+
+ /* Decode a huffman encoded value */
+ VHD_Init(d);
+ in_l = hexbuf(in, sizeof in,
+ "0141 8cf1 e3c2 e5f2 3a6b a0ab 90f4 ff");
+ vhd_set_state(d, VHD_S_TEST_LITERAL);
+ r = decode(d, NULL, in, in_l, out, sizeof out, mode);
+ CHECK_RET(r, VHD_OK);
+ AZ(match(out, sizeof out, "A", "www.example.com", NULL));
+
+ /* Decode an incomplete input buffer */
+ VHD_Init(d);
+ in_l = hexbuf(in, sizeof in,
+ "0141 81");
+ vhd_set_state(d, VHD_S_TEST_LITERAL);
+ r = decode(d, NULL, in, in_l, out, sizeof out, mode);
+ CHECK_RET(r, VHD_MORE);
+
+ /* Decode an incomplete huffman code */
+ VHD_Init(d);
+ in_l = hexbuf(in, sizeof in,
+ "0141 81 fe");
+ vhd_set_state(d, VHD_S_TEST_LITERAL);
+ r = decode(d, NULL, in, in_l, out, sizeof out, mode);
+ CHECK_RET(r, VHD_ERR_HUF);
+
+ /* Decode an invalid huffman code */
+ VHD_Init(d);
+ in_l = hexbuf(in, sizeof in,
+ "0141 84 ff ff ff ff");
+ vhd_set_state(d, VHD_S_TEST_LITERAL);
+ r = decode(d, NULL, in, in_l, out, sizeof out, mode);
+ CHECK_RET(r, VHD_ERR_HUF);
+}
+
+static void
+test_c2(unsigned mode)
+{
+ struct vhd_decode d[1];
+ uint8_t in[256];
+ size_t in_l;
+ char out[256];
+ enum vhd_ret_e r;
+
+ /* See RFC 7541 Appendix C.2 */
+
+ VHD_Init(d);
+
+ /* C.2.1 */
+ in_l = hexbuf(in, sizeof in,
+ "400a 6375 7374 6f6d 2d6b 6579 0d63 7573"
+ "746f 6d2d 6865 6164 6572");
+ r = decode(d, NULL, in, in_l, out, sizeof out, mode);
+ CHECK_RET(r, VHD_OK);
+ AZ(match(out, sizeof out,
+ "custom-key", "custom-header",
+ NULL));
+
+ /* C.2.2 */
+ in_l = hexbuf(in, sizeof in,
+ "040c 2f73 616d 706c 652f 7061 7468");
+ r = decode(d, NULL, in, in_l, out, sizeof out, mode);
+ CHECK_RET(r, VHD_OK);
+ AZ(match(out, sizeof out,
+ ":path", "/sample/path",
+ NULL));
+
+ /* C.2.3 */
+ in_l = hexbuf(in, sizeof in,
+ "1008 7061 7373 776f 7264 0673 6563 7265"
+ "74");
+ r = decode(d, NULL, in, in_l, out, sizeof out, mode);
+ CHECK_RET(r, VHD_OK);
+ AZ(match(out, sizeof out,
+ "password", "secret",
+ NULL));
+
+ /* C.2.4 */
+ in_l = hexbuf(in, sizeof in,
+ "82");
+ r = decode(d, NULL, in, in_l, out, sizeof out, mode);
+ CHECK_RET(r, VHD_OK);
+ AZ(match(out, sizeof out,
+ ":method", "GET",
+ NULL));
+}
+
+static void
+test_c3(unsigned mode)
+{
+ struct vht_table t[1];
+ struct vhd_decode d[1];
+ uint8_t in[256];
+ size_t in_l;
+ char out[256];
+ enum vhd_ret_e r;
+
+ /* See RFC 7541 Appendix C.3 */
+
+ AZ(VHT_Init(t, 4096));
+ VHD_Init(d);
+
+ /* C.3.1 */
+ in_l = hexbuf(in, sizeof in,
+ "8286 8441 0f77 7777 2e65 7861 6d70 6c65"
+ "2e63 6f6d");
+ r = decode(d, t, in, in_l, out, sizeof out, mode);
+ CHECK_RET(r, VHD_OK);
+ AZ(match(out, sizeof out,
+ ":method", "GET",
+ ":scheme", "http",
+ ":path", "/",
+ ":authority", "www.example.com",
+ NULL));
+
+ /* C.3.2 */
+ in_l = hexbuf(in, sizeof in,
+ "8286 84be 5808 6e6f 2d63 6163 6865");
+ r = decode(d, t, in, in_l, out, sizeof out, mode);
+ CHECK_RET(r, VHD_OK);
+ AZ(match(out, sizeof out,
+ ":method", "GET",
+ ":scheme", "http",
+ ":path", "/",
+ ":authority", "www.example.com",
+ "cache-control", "no-cache",
+ NULL));
+
+ /* C.3.3 */
+ in_l = hexbuf(in, sizeof in,
+ "8287 85bf 400a 6375 7374 6f6d 2d6b 6579"
+ "0c63 7573 746f 6d2d 7661 6c75 65");
+ r = decode(d, t, in, in_l, out, sizeof out, mode);
+ CHECK_RET(r, VHD_OK);
+ AZ(match(out, sizeof out,
+ ":method", "GET",
+ ":scheme", "https",
+ ":path", "/index.html",
+ ":authority", "www.example.com",
+ "custom-key", "custom-value",
+ NULL));
+
+ VHT_Fini(t);
+}
+
+static void
+test_c4(unsigned mode)
+{
+ struct vht_table t[1];
+ struct vhd_decode d[1];
+ uint8_t in[256];
+ size_t in_l;
+ char out[256];
+ enum vhd_ret_e r;
+
+ /* See RFC 7541 Appendix C.4 */
+
+ AZ(VHT_Init(t, 4096));
+ VHD_Init(d);
+
+ /* C.4.1 */
+ in_l = hexbuf(in, sizeof in,
+ "8286 8441 8cf1 e3c2 e5f2 3a6b a0ab 90f4 ff");
+ r = decode(d, t, in, in_l, out, sizeof out, mode);
+ CHECK_RET(r, VHD_OK);
+ AZ(match(out, sizeof out,
+ ":method", "GET",
+ ":scheme", "http",
+ ":path", "/",
+ ":authority", "www.example.com",
+ NULL));
+
+ /* C.4.2 */
+ in_l = hexbuf(in, sizeof in,
+ "8286 84be 5886 a8eb 1064 9cbf");
+ r = decode(d, t, in, in_l, out, sizeof out, mode);
+ CHECK_RET(r, VHD_OK);
+ AZ(match(out, sizeof out,
+ ":method", "GET",
+ ":scheme", "http",
+ ":path", "/",
+ ":authority", "www.example.com",
+ "cache-control", "no-cache",
+ NULL));
+
+ /* C.4.3 */
+ in_l = hexbuf(in, sizeof in,
+ "8287 85bf 4088 25a8 49e9 5ba9 7d7f 8925"
+ "a849 e95b b8e8 b4bf");
+ r = decode(d, t, in, in_l, out, sizeof out, mode);
+ CHECK_RET(r, VHD_OK);
+ AZ(match(out, sizeof out,
+ ":method", "GET",
+ ":scheme", "https",
+ ":path", "/index.html",
+ ":authority", "www.example.com",
+ "custom-key", "custom-value",
+ NULL));
+
+ VHT_Fini(t);
+}
+
+static void
+test_c5(unsigned mode)
+{
+ struct vht_table t[1];
+ struct vhd_decode d[1];
+ uint8_t in[256];
+ size_t in_l;
+ char out[256];
+ enum vhd_ret_e r;
+
+ /* See RFC 7541 Appendix C.5 */
+
+ AZ(VHT_Init(t, 256));
+ VHD_Init(d);
+
+ /* C.5.1 */
+ in_l = hexbuf(in, sizeof in,
+ "4803 3330 3258 0770 7269 7661 7465 611d"
+ "4d6f 6e2c 2032 3120 4f63 7420 3230 3133"
+ "2032 303a 3133 3a32 3120 474d 546e 1768"
+ "7474 7073 3a2f 2f77 7777 2e65 7861 6d70"
+ "6c65 2e63 6f6d");
+ r = decode(d, t, in, in_l, out, sizeof out, mode);
+ CHECK_RET(r, VHD_OK);
+ AZ(match(out, sizeof out,
+ ":status", "302",
+ "cache-control", "private",
+ "date", "Mon, 21 Oct 2013 20:13:21 GMT",
+ "location", "https://www.example.com",
+ NULL));
+
+ /* C.5.2 */
+ in_l = hexbuf(in, sizeof in,
+ "4803 3330 37c1 c0bf");
+ r = decode(d, t, in, in_l, out, sizeof out, mode);
+ CHECK_RET(r, VHD_OK);
+ AZ(match(out, sizeof out,
+ ":status", "307",
+ "cache-control", "private",
+ "date", "Mon, 21 Oct 2013 20:13:21 GMT",
+ "location", "https://www.example.com",
+ NULL));
+
+ /* C.5.3 */
+ in_l = hexbuf(in, sizeof in,
+ "88c1 611d 4d6f 6e2c 2032 3120 4f63 7420"
+ "3230 3133 2032 303a 3133 3a32 3220 474d"
+ "54c0 5a04 677a 6970 7738 666f 6f3d 4153"
+ "444a 4b48 514b 425a 584f 5157 454f 5049"
+ "5541 5851 5745 4f49 553b 206d 6178 2d61"
+ "6765 3d33 3630 303b 2076 6572 7369 6f6e"
+ "3d31");
+ r = decode(d, t, in, in_l, out, sizeof out, mode);
+ CHECK_RET(r, VHD_OK);
+ AZ(match(out, sizeof out,
+ ":status", "200",
+ "cache-control", "private",
+ "date", "Mon, 21 Oct 2013 20:13:22 GMT",
+ "location", "https://www.example.com",
+ "content-encoding", "gzip",
+ "set-cookie",
+ "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1",
+ NULL));
+
+ VHT_Fini(t);
+}
+
+static void
+test_c6(unsigned mode)
+{
+ struct vht_table t[1];
+ struct vhd_decode d[1];
+ uint8_t in[256];
+ size_t in_l;
+ char out[256];
+ enum vhd_ret_e r;
+
+ /* See RFC 7541 Appendix C.6 */
+
+ AZ(VHT_Init(t, 256));
+ VHD_Init(d);
+
+ /* C.6.1 */
+ in_l = hexbuf(in, sizeof in,
+ "4882 6402 5885 aec3 771a 4b61 96d0 7abe"
+ "9410 54d4 44a8 2005 9504 0b81 66e0 82a6"
+ "2d1b ff6e 919d 29ad 1718 63c7 8f0b 97c8"
+ "e9ae 82ae 43d3");
+ r = decode(d, t, in, in_l, out, sizeof out, mode);
+ CHECK_RET(r, VHD_OK);
+ AZ(match(out, sizeof out,
+ ":status", "302",
+ "cache-control", "private",
+ "date", "Mon, 21 Oct 2013 20:13:21 GMT",
+ "location", "https://www.example.com",
+ NULL));
+
+ /* C.6.2 */
+ in_l = hexbuf(in, sizeof in,
+ "4883 640e ffc1 c0bf");
+ r = decode(d, t, in, in_l, out, sizeof out, mode);
+ CHECK_RET(r, VHD_OK);
+ AZ(match(out, sizeof out,
+ ":status", "307",
+ "cache-control", "private",
+ "date", "Mon, 21 Oct 2013 20:13:21 GMT",
+ "location", "https://www.example.com",
+ NULL));
+
+ /* C.6.3 */
+ in_l = hexbuf(in, sizeof in,
+ "88c1 6196 d07a be94 1054 d444 a820 0595"
+ "040b 8166 e084 a62d 1bff c05a 839b d9ab"
+ "77ad 94e7 821d d7f2 e6c7 b335 dfdf cd5b"
+ "3960 d5af 2708 7f36 72c1 ab27 0fb5 291f"
+ "9587 3160 65c0 03ed 4ee5 b106 3d50 07");
+ r = decode(d, t, in, in_l, out, sizeof out, mode);
+ CHECK_RET(r, VHD_OK);
+ AZ(match(out, sizeof out,
+ ":status", "200",
+ "cache-control", "private",
+ "date", "Mon, 21 Oct 2013 20:13:22 GMT",
+ "location", "https://www.example.com",
+ "content-encoding", "gzip",
+ "set-cookie",
+ "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1",
+ NULL));
+
+ VHT_Fini(t);
+}
+
+#define do_test(name) \
+ do { \
+ printf("Doing test: %s\n", #name); \
+ name(0); \
+ printf("Doing test: %s 1IN\n", #name); \
+ name(M_1IN); \
+ printf("Doing test: %s 1OUT\n", #name); \
+ name(M_1OUT); \
+ printf("Doing test: %s 1IN|1OUT\n", #name); \
+ name(M_1IN|M_1OUT); \
+ printf("Test finished: %s\n\n", #name); \
+ } while (0)
+
+int
+main(int argc, char **argv)
+{
+ if (argc == 2 && !strcmp(argv[1], "-v"))
+ verbose = 1;
+ else if (argc != 1) {
+ fprintf(stderr, "Usage: %s [-v]\n", argv[0]);
+ return (1);
+ }
+
+ if (verbose) {
+ printf("sizeof (struct vhd_int)=%ju\n",
+ sizeof (struct vhd_int));
+ printf("sizeof (struct vhd_lookup)=%ju\n",
+ sizeof (struct vhd_lookup));
+ printf("sizeof (struct vhd_raw)=%ju\n",
+ sizeof (struct vhd_raw));
+ printf("sizeof (struct vhd_huffman)=%ju\n",
+ sizeof (struct vhd_huffman));
+ printf("sizeof (struct vhd_decode)=%ju\n",
+ sizeof (struct vhd_decode));
+ }
+
+ do_test(test_integer);
+ do_test(test_raw);
+ do_test(test_huffman);
+
+ do_test(test_c2);
+ do_test(test_c3);
+ do_test(test_c4);
+ do_test(test_c5);
+ do_test(test_c6);
+
+ return (0);
+}
+
+#endif /* DECODE_TEST_DRIVER */
diff --git a/bin/varnishd/hpack/vhp_gen_hufdec.c b/bin/varnishd/hpack/vhp_gen_hufdec.c
new file mode 100644
index 0000000..7372815
--- /dev/null
+++ b/bin/varnishd/hpack/vhp_gen_hufdec.c
@@ -0,0 +1,255 @@
+/*-
+ * Copyright (c) 2016 Dridi Boukelmoune
+ * All rights reserved.
+ *
+ * Author: Dridi Boukelmoune <dridi.boukelmoune 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 <ctype.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <limits.h>
+
+#include "vdef.h"
+#include "vas.h"
+
+static unsigned minlen = UINT_MAX;
+static unsigned maxlen = 0;
+static unsigned idx = 0;
+
+static const struct {
+ uint32_t code;
+ unsigned blen;
+ char chr;
+} huf[] = {
+#define HPH(c, h, l) { h, l, (char)c },
+#include "tbl/vhp_huffman.h"
+#undef HPH
+};
+
+#define HUF_LEN (sizeof huf / sizeof huf[0])
+
+struct tbl;
+
+struct cod {
+ uint32_t bits;
+ unsigned len;
+ uint8_t chr;
+ struct tbl *next;
+};
+
+struct tbl {
+ unsigned mask;
+ uint32_t code;
+ unsigned masked;
+ unsigned n;
+ unsigned idx;
+ unsigned lvl;
+ unsigned p_idx;
+ struct cod e[];
+};
+
+static struct tbl *
+tbl_new(unsigned mask)
+{
+ unsigned n;
+ size_t size;
+ struct tbl *tbl;
+
+ assert(mask > 0);
+ assert(mask <= 8);
+ n = 1U << mask;
+ size = sizeof (struct tbl) + n * sizeof (struct cod);
+ tbl = calloc(1, size);
+ AN(tbl);
+ memset(tbl, 0, size);
+ tbl->mask = mask;
+ tbl->n = n;
+ tbl->idx = idx;
+ idx += n;
+ return (tbl);
+}
+
+static void
+tbl_add(struct tbl *tbl, uint32_t code, unsigned codelen,
+ uint32_t bits, unsigned len, char chr)
+{
+ uint32_t b;
+ unsigned u;
+
+ AN(tbl);
+ assert(codelen > 0);
+ assert(codelen <= maxlen);
+ assert(len > 0);
+ assert(tbl->mask > 0);
+
+ if (len > tbl->mask) {
+ /* Does not belong in this table */
+ b = bits >> (len - tbl->mask);
+ bits &= (1U << (len - tbl->mask)) - 1;
+ if (tbl->e[b].next == NULL) {
+ tbl->e[b].len = tbl->mask;
+ tbl->e[b].next = tbl_new(len - tbl->mask);
+ AN(tbl->e[b].next);
+
+ tbl->e[b].next->masked = tbl->masked + tbl->mask;
+ tbl->e[b].next->code = code;
+ tbl->e[b].next->lvl = tbl->lvl + 1;
+ tbl->e[b].next->p_idx = tbl->idx + b;
+ }
+ AN(tbl->e[b].next);
+ tbl_add(tbl->e[b].next, code, codelen,
+ bits, len - tbl->mask, chr);
+ return;
+ }
+
+ bits = bits << (tbl->mask - len);
+ for (u = 0; u < (1U << (tbl->mask - len)); u++) {
+ b = bits | u;
+ assert(b < tbl->n);
+ AZ(tbl->e[b].len);
+ AZ(tbl->e[b].next);
+ tbl->e[b].len = len;
+ tbl->e[b].chr = chr;
+ }
+}
+
+static void
+print_lsb(uint32_t c, int l)
+{
+ assert(l <= 32);
+
+ while (l > 0) {
+ if (c & (1U << (l - 1)))
+ printf("1");
+ else
+ printf("0");
+ l--;
+ }
+}
+
+static void
+tbl_print(const struct tbl *tbl)
+{
+ unsigned u;
+
+ printf("/* Table: lvl=%u p_idx=%u n=%u mask=%u masked=%u */\n",
+ tbl->lvl, tbl->p_idx, tbl->n, tbl->mask, tbl->masked);
+ for (u = 0; u < tbl->n; u++) {
+ printf("/* %3u: ", tbl->idx + u);
+ printf("%*s", maxlen - tbl->mask - tbl->masked, "");
+ printf("%*s", tbl->mask - tbl->e[u].len, "");
+
+ if (tbl->masked > 0) {
+ printf("(");
+ print_lsb(tbl->code >> tbl->mask, tbl->masked);
+ printf(") ");
+ } else
+ printf(" ");
+ if (tbl->e[u].len < tbl->mask) {
+ print_lsb(u >> (tbl->mask - tbl->e[u].len),
+ tbl->e[u].len);
+ printf(" (");
+ print_lsb(u, tbl->mask - tbl->e[u].len);
+ printf(")");
+ } else {
+ assert(tbl->e[u].len == tbl->mask);
+ print_lsb(u, tbl->e[u].len);
+ printf(" ");
+ }
+ printf("%*s", 3 - (tbl->mask - tbl->e[u].len), "");
+ printf(" */ ");
+
+ if (tbl->e[u].next) {
+ /* Jump to next table */
+ assert(tbl->e[u].next->idx - (tbl->idx + u)
+ <= UINT8_MAX);
+ printf("{ .len = %u, .jump = %u },",
+ tbl->e[u].len,
+ tbl->e[u].next->idx - (tbl->idx + u));
+ printf(" /* Next: %u */", tbl->e[u].next->idx);
+ } else if (tbl->e[u].len) {
+ printf("{ ");
+ printf(".len = %u", tbl->e[u].len);
+ printf(", .chr = (char)0x%02x", tbl->e[u].chr);
+ if (isgraph(tbl->e[u].chr))
+ printf(" /* '%c' */", tbl->e[u].chr);
+ if (u == 0)
+ /* First in table, set mask */
+ printf(", .mask = %u", tbl->mask);
+ printf(" },");
+ } else
+ printf("{ .len = 0 }, /* invalid */");
+ printf("\n");
+ }
+
+ for (u = 0; u < tbl->n; u++)
+ if (tbl->e[u].next)
+ tbl_print(tbl->e[u].next);
+}
+
+int
+main(int argc, const char **argv)
+{
+ struct tbl *top;
+ unsigned u;
+
+ (void)argc;
+ (void)argv;
+
+ for (u = 0; u < HUF_LEN; u++) {
+ if (maxlen < huf[u].blen)
+ maxlen = huf[u].blen;
+ if (minlen > huf[u].blen)
+ minlen = huf[u].blen;
+ }
+
+ top = tbl_new(8);
+ AN(top);
+
+ for (u = 0; u < HUF_LEN; u++)
+ tbl_add(top, huf[u].code, huf[u].blen,
+ huf[u].code, huf[u].blen, huf[u].chr);
+
+ printf("/*\n");
+ printf(" * NB: This file is machine generated, DO NOT EDIT!\n");
+ printf(" */\n\n");
+
+ printf("#define HUFDEC_LEN %u\n", idx);
+ printf("#define HUFDEC_MIN %u\n", minlen);
+ printf("#define HUFDEC_MAX %u\n\n", maxlen);
+
+ printf("static const struct {\n");
+ printf("\tuint8_t\tmask;\n");
+ printf("\tuint8_t\tlen;\n");
+ printf("\tuint8_t\tjump;\n");
+ printf("\tchar\tchr;\n");
+ printf("} hufdec[HUFDEC_LEN] = {\n");
+ tbl_print(top);
+ printf("};\n");
+
+ return (0);
+}
diff --git a/bin/varnishd/hpack/vhp_table.c b/bin/varnishd/hpack/vhp_table.c
new file mode 100644
index 0000000..26a046b
--- /dev/null
+++ b/bin/varnishd/hpack/vhp_table.c
@@ -0,0 +1,785 @@
+/*-
+ * Copyright (c) 2016 Varnish Software
+ * All rights reserved.
+ *
+ * Author: Martin Blix Grydeland <martin at varnish-software.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.
+ *
+ */
+
+/*
+ * Layout:
+ *
+ * buf [
+ * <x bytes name index n - 1> <x bytes value index n - 1>
+ * <x bytes name index n - 2> <x bytes value index n - 2>
+ * ...
+ * <x bytes name index 0> <x bytes value index 0>
+ *
+ * (padding bytes for pointer alignment)
+ *
+ * <struct vht_entry index 0>
+ * <struct vht_entry index 1>
+ * ...
+ * <struct vht_entry index n - 1>
+ * ]
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <stdint.h>
+#include <limits.h>
+
+#include "vdef.h"
+#include "miniobj.h"
+#include "vas.h"
+
+#include "hpack/vhp.h"
+
+#define VHT_STATIC_MAX 61
+
+struct vht_static {
+ const char *name;
+ unsigned namelen;
+ const char *value;
+ unsigned valuelen;
+};
+
+static const struct vht_static static_table[] = {
+#define HPS(NUM, NAME, VAL) \
+ { NAME, sizeof NAME - 1, VAL, sizeof VAL - 1 },
+#include "tbl/vhp_static.h"
+#undef HPS
+};
+
+#define TBLSIZE(tbl) ((tbl)->size + (tbl)->n * VHT_ENTRY_SIZE)
+#define ENTRIES(buf, bufsize, n) \
+ (((struct vht_entry *)((uintptr_t)(buf) + bufsize)) - (n))
+#define TBLENTRIES(tbl) ENTRIES((tbl)->buf, (tbl)->bufsize, (tbl)->n)
+#define TBLENTRY(tbl, i) (&TBLENTRIES(tbl)[(i)])
+#define ENTRYLEN(e) ((e)->namelen + (e)->valuelen)
+#define ENTRYSIZE(e) (ENTRYLEN(e) + VHT_ENTRY_SIZE)
+
+/****************************************************************************/
+/* Internal interface */
+
+static void
+vht_newentry(struct vht_table *tbl)
+{
+ struct vht_entry *e;
+
+ assert(tbl->maxsize - TBLSIZE(tbl) >= VHT_ENTRY_SIZE);
+ tbl->n++;
+ e = TBLENTRY(tbl, 0);
+ INIT_OBJ(e, VHT_ENTRY_MAGIC);
+ e->offset = tbl->size;
+}
+
+/* Trim elements from the end until the table size is less than max. */
+static void
+vht_trim(struct vht_table *tbl, ssize_t max)
+{
+ unsigned u, v;
+ int i;
+ struct vht_entry *e;
+
+ CHECK_OBJ_NOTNULL(tbl, VHT_TABLE_MAGIC);
+
+ if (max < 0)
+ max = 0;
+ if (TBLSIZE(tbl) <= max)
+ return;
+
+ u = v = 0;
+ for (i = tbl->n - 1; i >= 0; i--) {
+ e = TBLENTRY(tbl, i);
+ CHECK_OBJ_NOTNULL(e, VHT_ENTRY_MAGIC);
+ if (TBLSIZE(tbl) - (u + v * VHT_ENTRY_SIZE) > max) {
+ /* Trim entry */
+ assert(e->offset == u);
+ u += ENTRYLEN(e);
+ v++;
+ e->magic = 0;
+ } else {
+ /* Fixup offset */
+ assert(e->offset >= u);
+ e->offset -= u;
+ }
+ }
+ assert(v <= tbl->n);
+
+ memmove(tbl->buf, tbl->buf + u, tbl->size - u);
+ memmove(TBLENTRY(tbl, v), TBLENTRY(tbl, 0), (tbl->n - v) * sizeof *e);
+ tbl->n -= v;
+ tbl->size -= u;
+}
+
+/* Append len bytes from buf to entry 0 name. Asserts if no space. */
+static void
+vht_appendname(struct vht_table *tbl, const char *buf, size_t len)
+{
+ struct vht_entry *e;
+
+ CHECK_OBJ_NOTNULL(tbl, VHT_TABLE_MAGIC);
+ e = TBLENTRY(tbl, 0);
+ CHECK_OBJ_NOTNULL(e, VHT_ENTRY_MAGIC);
+ AZ(e->valuelen); /* Name needs to be set before value */
+ assert(TBLSIZE(tbl) + len <= tbl->maxsize);
+ assert(e->offset + e->namelen == tbl->size);
+ memcpy(tbl->buf + tbl->size, buf, len);
+ e->namelen += len;
+ tbl->size += len;
+}
+
+/* Append len bytes from buf to entry 0 value. Asserts if no space. */
+static void
+vht_appendvalue(struct vht_table *tbl, const char *buf, size_t len)
+{
+ struct vht_entry *e;
+
+ CHECK_OBJ_NOTNULL(tbl, VHT_TABLE_MAGIC);
+ e = TBLENTRY(tbl, 0);
+ CHECK_OBJ_NOTNULL(e, VHT_ENTRY_MAGIC);
+ assert(TBLSIZE(tbl) + len <= tbl->maxsize);
+ assert(e->offset + e->namelen + e->valuelen == tbl->size);
+ memcpy(tbl->buf + tbl->size, buf, len);
+ e->valuelen += len;
+ tbl->size += len;
+}
+
+/****************************************************************************/
+/* Public interface */
+
+void
+VHT_NewEntry(struct vht_table *tbl)
+{
+
+ CHECK_OBJ_NOTNULL(tbl, VHT_TABLE_MAGIC);
+ assert(tbl->maxsize <= tbl->protomax);
+ vht_trim(tbl, tbl->maxsize - VHT_ENTRY_SIZE);
+ if (tbl->maxsize - TBLSIZE(tbl) < VHT_ENTRY_SIZE) {
+ /* Maxsize less than one entry */
+ assert(tbl->maxsize < VHT_ENTRY_SIZE);
+ return;
+ }
+ vht_newentry(tbl);
+}
+
+int
+VHT_NewEntry_Indexed(struct vht_table *tbl, unsigned idx)
+{
+ struct vht_entry *e, *e2;
+ unsigned l, l2, lbuf, lentry, lname, u;
+ uint8_t buf[48];
+
+ /* Referenced name insertion. This has to be done carefully
+ because the referenced name may be evicted as the result of the
+ insertion (RFC 7541 section 4.4). */
+
+ assert(sizeof buf >= VHT_ENTRY_SIZE);
+
+ CHECK_OBJ_NOTNULL(tbl, VHT_TABLE_MAGIC);
+ assert(tbl->maxsize <= tbl->protomax);
+
+ if (idx == 0)
+ return (-1);
+
+ if (idx <= VHT_STATIC_MAX) {
+ /* Static table reference */
+ VHT_NewEntry(tbl);
+ VHT_AppendName(tbl, static_table[idx - 1].name,
+ static_table[idx - 1].namelen);
+ return (0);
+ }
+ idx -= VHT_STATIC_MAX + 1;
+
+ if (idx >= tbl->n)
+ return (-1); /* No such index */
+ assert(tbl->maxsize >= VHT_ENTRY_SIZE);
+
+ e = TBLENTRY(tbl, idx);
+ CHECK_OBJ_NOTNULL(e, VHT_ENTRY_MAGIC);
+
+ /* Count how many elements we can safely evict to make space
+ without evicting the referenced entry. */
+ l = 0;
+ u = 0;
+ while (tbl->n - 1 - u > idx &&
+ tbl->maxsize - TBLSIZE(tbl) + l < VHT_ENTRY_SIZE + e->namelen) {
+ e2 = TBLENTRY(tbl, tbl->n - 1 - u);
+ CHECK_OBJ_NOTNULL(e2, VHT_ENTRY_MAGIC);
+ l += ENTRYSIZE(e2);
+ u++;
+ }
+ vht_trim(tbl, TBLSIZE(tbl) - l);
+ e += u;
+ assert(e == TBLENTRY(tbl, idx));
+
+ if (tbl->maxsize - TBLSIZE(tbl) >= VHT_ENTRY_SIZE + e->namelen) {
+ /* New entry with name fits */
+ vht_newentry(tbl);
+ idx++;
+ assert(e == TBLENTRY(tbl, idx));
+ CHECK_OBJ_NOTNULL(e, VHT_ENTRY_MAGIC);
+ vht_appendname(tbl, tbl->buf + e->offset, e->namelen);
+ return (0);
+ }
+
+ /* The tricky case: The referenced name will be evicted as a
+ result of the insertion. Move the element data to the end of
+ the buffer through a local buffer. */
+
+ /* Remove the referenced element from the entry list */
+ assert(idx == tbl->n - 1);
+ assert(e->offset == 0);
+ lname = e->namelen;
+ lentry = ENTRYLEN(e);
+ e->magic = 0;
+ memmove(TBLENTRY(tbl, 1), TBLENTRY(tbl, 0), (tbl->n - 1) * sizeof *e);
+ tbl->n--;
+
+ /* Shift the referenced element last in the buffer. Use what space
+ is available in the table buffer and the local buffer to keep
+ memmove operations to a minimum. */
+ l = 0;
+ while (l < lentry) {
+ l2 = lentry - l;
+ if (l2 > tbl->maxsize - TBLSIZE(tbl))
+ l2 = tbl->maxsize - TBLSIZE(tbl);
+ lbuf = lentry - l - l2;
+ if (lbuf > sizeof buf)
+ lbuf = sizeof buf;
+ memcpy(tbl->buf + tbl->size, tbl->buf, l2);
+ memcpy(buf, tbl->buf + l2, lbuf);
+ memmove(tbl->buf, tbl->buf + l2 + lbuf, tbl->size + l2);
+ memcpy(tbl->buf + tbl->size - lbuf, buf, lbuf);
+ l += l2 + lbuf;
+ }
+ assert(l == lentry);
+ tbl->size -= lentry;
+
+ /* Fix up the existing element offsets */
+ for (u = 0; u < tbl->n; u++) {
+ e = TBLENTRY(tbl, u);
+ CHECK_OBJ_NOTNULL(e, VHT_ENTRY_MAGIC);
+ assert(e->offset >= lentry);
+ e->offset -= lentry;
+ assert(e->offset + ENTRYLEN(e) <= tbl->size);
+ }
+
+ /* Insert the new entry with the name now present at the end of
+ the buffer. */
+ assert(tbl->maxsize - TBLSIZE(tbl) >= VHT_ENTRY_SIZE + lname);
+ tbl->n++;
+ e = TBLENTRY(tbl, 0);
+ INIT_OBJ(e, VHT_ENTRY_MAGIC);
+ e->offset = tbl->size;
+ e->namelen = lname;
+ tbl->size += lname;
+
+ return (0);
+}
+
+void
+VHT_AppendName(struct vht_table *tbl, const char *buf, ssize_t len)
+{
+
+ CHECK_OBJ_NOTNULL(tbl, VHT_TABLE_MAGIC);
+ assert(tbl->maxsize <= tbl->protomax);
+ if (len == 0)
+ return;
+ AN(buf);
+ if (len < 0)
+ len = strlen(buf);
+ vht_trim(tbl, tbl->maxsize - len);
+ if (tbl->n == 0)
+ /* Max size exceeded */
+ return;
+ vht_appendname(tbl, buf, len);
+}
+
+void
+VHT_AppendValue(struct vht_table *tbl, const char *buf, ssize_t len)
+{
+
+ CHECK_OBJ_NOTNULL(tbl, VHT_TABLE_MAGIC);
+ assert(tbl->maxsize <= tbl->protomax);
+ if (len == 0)
+ return;
+ AN(buf);
+ if (len < 0)
+ len = strlen(buf);
+ vht_trim(tbl, tbl->maxsize - len);
+ if (tbl->n == 0)
+ /* Max size exceeded */
+ return;
+ vht_appendvalue(tbl, buf, len);
+}
+
+int
+VHT_SetMaxTableSize(struct vht_table *tbl, size_t maxsize)
+{
+
+ CHECK_OBJ_NOTNULL(tbl, VHT_TABLE_MAGIC);
+ assert(tbl->maxsize <= tbl->protomax);
+ if (maxsize > tbl->protomax)
+ return (-1);
+ vht_trim(tbl, maxsize);
+ assert(TBLSIZE(tbl) <= maxsize);
+ tbl->maxsize = maxsize;
+ return (0);
+}
+
+int
+VHT_SetProtoMax(struct vht_table *tbl, size_t protomax)
+{
+ size_t bufsize;
+ char *buf;
+
+ CHECK_OBJ_NOTNULL(tbl, VHT_TABLE_MAGIC);
+ assert(protomax <= UINT_MAX);
+ assert(tbl->maxsize <= tbl->protomax);
+
+ if (protomax == tbl->protomax)
+ return (0);
+
+ if (tbl->maxsize > protomax)
+ tbl->maxsize = protomax;
+ vht_trim(tbl, tbl->maxsize);
+ assert(TBLSIZE(tbl) <= tbl->maxsize);
+
+ bufsize = PRNDUP(protomax);
+ if (bufsize == tbl->bufsize) {
+ tbl->protomax = protomax;
+ return (0);
+ }
+
+ buf = malloc(bufsize);
+ if (buf == NULL)
+ return (-1);
+
+ memcpy(buf, tbl->buf, tbl->size);
+ memcpy(ENTRIES(buf, bufsize, tbl->n), TBLENTRIES(tbl),
+ sizeof (struct vht_entry) * tbl->n);
+ free(tbl->buf);
+ tbl->buf = buf;
+ tbl->bufsize = bufsize;
+ tbl->protomax = protomax;
+ return (0);
+}
+
+const char *
+VHT_LookupName(const struct vht_table *tbl, unsigned idx, size_t *plen)
+{
+ struct vht_entry *e;
+
+ AN(plen);
+ *plen = 0;
+
+ if (idx == 0) {
+ return (NULL);
+ }
+ if (idx <= VHT_STATIC_MAX) {
+ *plen = static_table[idx - 1].namelen;
+ return (static_table[idx - 1].name);
+ }
+
+ if (tbl == NULL)
+ return (NULL);
+ CHECK_OBJ_NOTNULL(tbl, VHT_TABLE_MAGIC);
+
+ idx -= VHT_STATIC_MAX + 1;
+ if (idx >= tbl->n)
+ return (NULL);
+
+ e = TBLENTRY(tbl, idx);
+ CHECK_OBJ_NOTNULL(e, VHT_ENTRY_MAGIC);
+ assert(e->offset + e->namelen <= tbl->size);
+ *plen = e->namelen;
+ return (tbl->buf + e->offset);
+}
+
+const char *
+VHT_LookupValue(const struct vht_table *tbl, unsigned idx, size_t *plen)
+{
+ struct vht_entry *e;
+
+ AN(plen);
+ *plen = 0;
+
+ if (idx == 0) {
+ return (NULL);
+ }
+ if (idx <= VHT_STATIC_MAX) {
+ *plen = static_table[idx - 1].valuelen;
+ return (static_table[idx - 1].value);
+ }
+
+ if (tbl == NULL)
+ return (NULL);
+ CHECK_OBJ_NOTNULL(tbl, VHT_TABLE_MAGIC);
+
+ idx -= VHT_STATIC_MAX + 1;
+ if (idx >= tbl->n)
+ return (NULL);
+
+ e = TBLENTRY(tbl, idx);
+ CHECK_OBJ_NOTNULL(e, VHT_ENTRY_MAGIC);
+ assert(e->offset + e->namelen + e->valuelen <= tbl->size);
+ *plen = e->valuelen;
+ return (tbl->buf + e->offset + e->namelen);
+}
+
+int
+VHT_Init(struct vht_table *tbl, size_t protomax)
+{
+ int r;
+
+ assert(sizeof (struct vht_entry) <= VHT_ENTRY_SIZE);
+
+ AN(tbl);
+ if (protomax > UINT_MAX)
+ return (-1);
+ INIT_OBJ(tbl, VHT_TABLE_MAGIC);
+ r = VHT_SetProtoMax(tbl, protomax);
+ if (r) {
+ tbl->magic = 0;
+ return (r);
+ }
+ tbl->maxsize = tbl->protomax;
+ return (0);
+}
+
+void
+VHT_Fini(struct vht_table *tbl)
+{
+
+ CHECK_OBJ_NOTNULL(tbl, VHT_TABLE_MAGIC);
+ free(tbl->buf);
+ memset(tbl, 0, sizeof *tbl);
+}
+
+/****************************************************************************/
+/* Internal interface */
+
+#ifdef TABLE_TEST_DRIVER
+
+#define VHT_DYNAMIC (VHT_STATIC_MAX + 1)
+
+static int verbose = 0;
+
+static int
+vht_matchtable(struct vht_table *tbl, ...)
+{
+ va_list ap;
+ unsigned u;
+ int r;
+ const char *a, *b;
+ const struct vht_entry *e;
+
+ CHECK_OBJ_NOTNULL(tbl, VHT_TABLE_MAGIC);
+
+ va_start(ap, tbl);
+ r = 0;
+ for (u = 0; u < tbl->n; u++) {
+ a = NULL;
+ b = NULL;
+ if (!r) {
+ a = va_arg(ap, const char *);
+ if (a == NULL) {
+ printf("Too many elements in table\n");
+ r = -1;
+ } else {
+ b = va_arg(ap, const char *);
+ AN(b);
+ }
+ }
+
+ e = TBLENTRY(tbl, u);
+ CHECK_OBJ_NOTNULL(e, VHT_ENTRY_MAGIC);
+
+ if (a) {
+ AN(b);
+ if (e->namelen != strlen(a) ||
+ strncmp(a, tbl->buf + e->offset, e->namelen))
+ r = -1;
+ if (e->valuelen != strlen(b) ||
+ strncmp(b, tbl->buf + e->offset + e->namelen,
+ e->valuelen))
+ r = -1;
+ }
+
+ if (verbose || r)
+ printf("%2u: @%03u (\"%.*s\", \"%.*s\")",
+ u, e->offset, (int)e->namelen, tbl->buf + e->offset,
+ (int)e->valuelen, tbl->buf + e->offset +e->namelen);
+
+ if (a && (verbose || r)) {
+ AN(b);
+ printf(" %s (\"%s\", \"%s\")", (r ? "!=" : "=="), a, b);
+ }
+
+ if (verbose || r)
+ printf("\n");
+ }
+ if (!r) {
+ a = va_arg(ap, const char *);
+ if (a != NULL) {
+ printf("Missing elements in table\n");
+ r = -1;
+ }
+ }
+ va_end(ap);
+
+ if (verbose || r)
+ printf("n=%d, size=%u, tblsz=%u, max=%u, pmax=%u, bufsz=%u\n",
+ tbl->n, tbl->size, TBLSIZE(tbl), tbl->maxsize,
+ tbl->protomax, tbl->bufsize);
+
+ return (r);
+}
+
+static void
+test_1(void)
+{
+ /* Static table */
+
+ const char *p;
+ size_t l;
+
+ if (verbose)
+ printf("Test 1:\n");
+
+ /* 1: ':authority' -> '' */
+ p = VHT_LookupName(NULL, 1, &l);
+ assert(l == strlen(":authority"));
+ AZ(strncmp(p, ":authority", strlen(":authority")));
+ p = VHT_LookupValue(NULL, 1, &l);
+ AN(p);
+ AZ(l);
+
+ /* 5: ':path' -> '/index.html' */
+ p = VHT_LookupValue(NULL, 5, &l);
+ assert(l == strlen("/index.html"));
+ AZ(strncmp(p, "/index.html", strlen("/index.html")));
+
+ /* 61: 'www-authenticate' -> '' */
+ p = VHT_LookupName(NULL, 61, &l);
+ assert(l == strlen("www-authenticate"));
+ AZ(strncmp(p, "www-authenticate", strlen("www-authenticate")));
+ p = VHT_LookupValue(NULL, 61, &l);
+ AN(p);
+ AZ(l);
+
+ /* Test zero index */
+ AZ(VHT_LookupName(NULL, 0, &l));
+ AZ(l);
+ AZ(VHT_LookupValue(NULL, 0, &l));
+ AZ(l);
+
+ printf("Test 1 finished successfully\n");
+ if (verbose)
+ printf("\n");
+}
+
+static void
+test_2(void)
+{
+ /* Test filling and overflow */
+
+ struct vht_table tbl[1];
+
+ if (verbose)
+ printf("Test 2:\n");
+
+ AZ(VHT_Init(tbl, VHT_ENTRY_SIZE + 10));
+
+ VHT_NewEntry(tbl);
+ VHT_AppendName(tbl, "12345", -1);
+ VHT_AppendValue(tbl, "abcde", -1);
+ assert(TBLSIZE(tbl) == VHT_ENTRY_SIZE + 10);
+ /* 0: '12345' -> 'abcde' */
+ AZ(vht_matchtable(tbl, "12345", "abcde", NULL));
+
+ VHT_AppendValue(tbl, "f", -1);
+ AZ(vht_matchtable(tbl, NULL));
+
+ VHT_NewEntry(tbl);
+ AZ(vht_matchtable(tbl, "", "", NULL));
+
+ VHT_Fini(tbl);
+ AZ(tbl->buf);
+
+ printf("Test 2 finished successfully\n");
+ if (verbose)
+ printf("\n");
+}
+
+static void
+test_3(void)
+{
+ /* Test change in proto max size and dynamic max size */
+
+ struct vht_table tbl[1];
+
+ if (verbose)
+ printf("Test 3:\n");
+
+ AZ(VHT_Init(tbl, 4096));
+
+ VHT_NewEntry(tbl);
+ VHT_AppendName(tbl, "a", -1);
+ VHT_AppendValue(tbl, "12345", -1);
+ VHT_NewEntry(tbl);
+ VHT_AppendName(tbl, "b", -1);
+ VHT_AppendValue(tbl, "67890", -1);
+ VHT_NewEntry(tbl);
+ VHT_AppendName(tbl, "c", -1);
+ VHT_AppendValue(tbl, "abcde", -1);
+ AZ(vht_matchtable(tbl, "c", "abcde", "b", "67890", "a", "12345", NULL));
+
+ /* Buffer reallocation */
+ AZ(VHT_SetProtoMax(tbl, VHT_ENTRY_SIZE * 2 + 6 * 2));
+ AZ(vht_matchtable(tbl, "c", "abcde", "b", "67890", NULL));
+
+ /* Increase table size beyond protomax */
+ assert(VHT_SetMaxTableSize(tbl, VHT_ENTRY_SIZE * 2 + 6 * 2 + 1) == -1);
+
+ /* Decrease by one */
+ AZ(VHT_SetMaxTableSize(tbl, VHT_ENTRY_SIZE * 2 + 6 * 2 - 1));
+ AZ(vht_matchtable(tbl, "c", "abcde", NULL));
+
+ /* Increase by one back to protomax */
+ AZ(VHT_SetMaxTableSize(tbl, VHT_ENTRY_SIZE * 2 + 6 * 2));
+ AZ(vht_matchtable(tbl, "c", "abcde", NULL));
+
+ /* Add entry */
+ VHT_NewEntry(tbl);
+ VHT_AppendName(tbl, "d", -1);
+ VHT_AppendValue(tbl, "ABCDE", -1);
+ AZ(vht_matchtable(tbl, "d", "ABCDE", "c", "abcde", NULL));
+
+ /* Set to zero */
+ AZ(VHT_SetMaxTableSize(tbl, 0));
+ AZ(vht_matchtable(tbl, NULL));
+ VHT_NewEntry(tbl);
+ AZ(vht_matchtable(tbl, NULL));
+
+ /* Set protomax to zero */
+ AZ(VHT_SetProtoMax(tbl, 0));
+ AZ(vht_matchtable(tbl, NULL));
+ VHT_NewEntry(tbl);
+ AZ(vht_matchtable(tbl, NULL));
+
+ VHT_Fini(tbl);
+
+ printf("Test 3 finished successfully\n");
+ if (verbose)
+ printf("\n");
+}
+
+static void
+test_4(void)
+{
+ /* Referenced name new entry */
+
+ struct vht_table tbl[1];
+ static const char longname[] =
+ "1234567890"
+ "1234567890"
+ "1234567890"
+ "1234567890"
+ "1234567890"
+ "1"; /* 51 bytes + VHT_ENTRY_SIZE == 83 */
+
+ if (verbose)
+ printf("Test 4:\n");
+
+ AZ(VHT_Init(tbl, VHT_ENTRY_SIZE * 2 + 10 * 2)); /* 84 bytes */
+
+ /* New entry indexed from static table */
+ AZ(VHT_NewEntry_Indexed(tbl, 4));
+ VHT_AppendValue(tbl, "12345", -1);
+ AZ(vht_matchtable(tbl, ":path", "12345", NULL));
+
+ /* New entry indexed from dynamic table */
+ AZ(VHT_NewEntry_Indexed(tbl, VHT_DYNAMIC + 0));
+ VHT_AppendValue(tbl, "abcde", -1);
+ AZ(vht_matchtable(tbl, ":path", "abcde", ":path", "12345", NULL));
+ AZ(tbl->maxsize - TBLSIZE(tbl)); /* No space left */
+
+ /* New entry indexed from dynamic table, no overlap eviction */
+ AZ(VHT_NewEntry_Indexed(tbl, VHT_DYNAMIC + 0));
+ VHT_AppendValue(tbl, "ABCDE", -1);
+ AZ(vht_matchtable(tbl, ":path", "ABCDE", ":path", "abcde", NULL));
+
+ /* New entry indexed from dynamic table, overlap eviction */
+ AZ(VHT_NewEntry_Indexed(tbl, VHT_DYNAMIC + 1));
+ AZ(vht_matchtable(tbl, ":path", "", ":path", "ABCDE", NULL));
+
+ /* New entry indexed from dynamic table, overlap eviction with
+ overlap larger than the copy buffer size */
+ VHT_NewEntry(tbl);
+ VHT_AppendName(tbl, longname, strlen(longname));
+ AZ(vht_matchtable(tbl, longname, "", NULL));
+ AZ(VHT_NewEntry_Indexed(tbl, VHT_DYNAMIC + 0));
+ VHT_AppendValue(tbl, "2", -1);
+ AZ(vht_matchtable(tbl, longname, "2", NULL));
+
+ VHT_Fini(tbl);
+ printf("Test 4 finished successfully\n");
+ if (verbose)
+ printf("\n");
+}
+
+int
+main(int argc, char **argv)
+{
+
+ if (argc == 2 && !strcmp(argv[1], "-v"))
+ verbose = 1;
+ else if (argc != 1) {
+ fprintf(stderr, "Usage: %s [-v]\n", argv[0]);
+ return (1);
+ }
+
+ if (verbose) {
+ printf("sizeof (struct vht_table) == %ju\n",
+ sizeof (struct vht_table));
+ printf("sizeof (struct vht_entry) == %ju\n",
+ sizeof (struct vht_entry));
+ printf("\n");
+ }
+
+ test_1();
+ test_2();
+ test_3();
+ test_4();
+
+ return (0);
+}
+
+#endif /* TABLE_TEST_DRIVER */
diff --git a/bin/varnishd/http1/cache_http1_fsm.c b/bin/varnishd/http1/cache_http1_fsm.c
index 27a5597..2a4a11a 100644
--- a/bin/varnishd/http1/cache_http1_fsm.c
+++ b/bin/varnishd/http1/cache_http1_fsm.c
@@ -392,16 +392,34 @@ HTTP1_Session(struct worker *wrk, struct req *req)
if (hs != HTC_S_COMPLETE)
WRONG("htc_status (nonbad)");
+ if (H2_prism_complete(req->htc) == HTC_S_COMPLETE) {
+ VSLb(req->vsl, SLT_Debug,
+ "H2 Prior Knowledge Upgrade");
+ http1_setstate(sp, NULL);
+ req->err_code = 1;
+ SES_SetTransport(wrk, sp, req, &H2_transport);
+ return;
+ }
+
i = http1_dissect(wrk, req);
req->acct.req_hdrbytes +=
req->htc->rxbuf_e - req->htc->rxbuf_b;
if (i) {
SES_Close(req->sp, req->doclose);
http1_setstate(sp, H1CLEANUP);
- } else {
- req->req_step = R_STP_RECV;
- http1_setstate(sp, H1PROC);
+ continue;
}
+ if (req->htc->body_status == BS_NONE &&
+ http_HdrIs(req->http, H_Upgrade, "h2c")) {
+ VSLb(req->vsl, SLT_Debug,
+ "H2 Optimistic Upgrade");
+ http1_setstate(sp, NULL);
+ req->err_code = 2;
+ SES_SetTransport(wrk, sp, req, &H2_transport);
+ return;
+ }
+ req->req_step = R_STP_RECV;
+ http1_setstate(sp, H1PROC);
} else if (st == H1BUSY) {
/*
* Return from waitinglist.
diff --git a/bin/varnishd/http2/cache_http2.h b/bin/varnishd/http2/cache_http2.h
new file mode 100644
index 0000000..1378ef8
--- /dev/null
+++ b/bin/varnishd/http2/cache_http2.h
@@ -0,0 +1,138 @@
+/*-
+ * Copyright (c) 2016 Varnish Software AS
+ * All rights reserved.
+ *
+ * Author: Poul-Henning Kamp <phk at phk.freebsd.dk>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+struct h2_sess;
+
+#include "hpack/vhp.h"
+
+enum h2_error_e {
+#define H2_ERROR(NAME, val, desc) \
+ H2E_##NAME = val,
+#include "tbl/h2_error.h"
+};
+
+enum h2_frame_e {
+#define H2_FRAME(l,u,t,f) H2_FRAME_##u = t,
+#include "tbl/h2_frames.h"
+};
+
+enum h2_stream_e {
+#define H2_STREAM(U,s,d) H2_S_##U,
+#include "tbl/h2_stream.h"
+};
+
+#define H2_FRAME_FLAGS(l,u,v) extern const uint8_t H2FF_##u;
+#include "tbl/h2_frames.h"
+
+#define H2_SETTINGS_N 7
+
+struct h2_req {
+ unsigned magic;
+#define H2_REQ_MAGIC 0x03411584
+ uint32_t stream;
+ enum h2_stream_e state;
+ struct h2_sess *h2sess;
+ struct req *req;
+ VTAILQ_ENTRY(h2_req) list;
+ int64_t window;
+};
+
+VTAILQ_HEAD(h2_req_s, h2_req);
+
+struct h2_sess {
+ unsigned magic;
+#define H2_SESS_MAGIC 0xa16f7e4b
+
+ struct sess *sess;
+ int refcnt;
+ uint32_t highest_stream;
+
+ struct h2_req_s streams;
+
+ struct req *srq;
+ struct ws *ws;
+ struct http_conn *htc;
+ struct vsl_log *vsl;
+ struct vht_table dectbl[1];
+
+ unsigned rxf_len;
+ unsigned rxf_flags;
+ unsigned rxf_stream;
+ uint8_t *rxf_data;
+
+ uint32_t their_settings[H2_SETTINGS_N];
+ uint32_t our_settings[H2_SETTINGS_N];
+
+ struct req *new_req;
+ int go_away;
+ uint32_t go_away_last_stream;
+};
+
+/* http2/cache_http2_panic.c */
+#ifdef TRANSPORT_MAGIC
+vtr_sess_panic_f h2_sess_panic;
+#endif
+
+/* http2/cache_http2_deliver.c */
+#ifdef TRANSPORT_MAGIC
+vtr_deliver_f h2_deliver;
+#endif /* TRANSPORT_MAGIC */
+
+/* http2/cache_http2_hpack.c */
+struct h2h_decode {
+ unsigned magic;
+#define H2H_DECODE_MAGIC 0xd092bde4
+
+ int error;
+ enum vhd_ret_e vhd_ret;
+ char *out;
+ char *reset;
+ size_t out_l;
+ size_t out_u;
+ size_t namelen;
+ struct vhd_decode vhd[1];
+};
+
+void h2h_decode_init(const struct h2_sess *h2, struct h2h_decode *d);
+int h2h_decode_fini(const struct h2_sess *h2, struct h2h_decode *d);
+int h2h_decode_bytes(struct h2_sess *h2, struct h2h_decode *d,
+ const uint8_t *ptr, size_t len);
+
+int H2_Send_Frame(struct worker *, const struct h2_sess *,
+ enum h2_frame_e type, uint8_t flags, uint32_t len, uint32_t stream,
+ const void *);
+
+int H2_Send(struct worker *, struct h2_req *, int flush,
+ enum h2_frame_e type, uint8_t flags, uint32_t len, const void *);
+
+typedef void h2_frame_f(struct worker *, struct h2_sess *,
+ struct h2_req *);
+#define H2_FRAME(l,u,t,f) h2_frame_f h2_rx_##l ;
+#include "tbl/h2_frames.h"
+
diff --git a/bin/varnishd/http2/cache_http2_deliver.c b/bin/varnishd/http2/cache_http2_deliver.c
new file mode 100644
index 0000000..5d28140
--- /dev/null
+++ b/bin/varnishd/http2/cache_http2_deliver.c
@@ -0,0 +1,227 @@
+/*-
+ * Copyright (c) 2016 Varnish Software AS
+ * All rights reserved.
+ *
+ * Author: Poul-Henning Kamp <phk at phk.freebsd.dk>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include "config.h"
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <netinet/in.h>
+
+#include <ctype.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "../cache/cache.h"
+#include "../cache/cache_filter.h"
+#include "../cache/cache_transport.h"
+
+#include "../http1/cache_http1.h"
+#include "../http2/cache_http2.h"
+
+#include "vct.h"
+#include "vend.h"
+
+/**********************************************************************/
+
+struct hpack_static {
+ uint8_t idx;
+ const char * name;
+ const char * val;
+};
+
+static const struct hpack_static hp_static[] = {
+#define HPS(I,N,V) [I] = { I, N ":", V },
+#include "tbl/vhp_static.h"
+#undef HPS
+ { 0, "\377:", ""} // Terminator
+};
+
+static const struct hpack_static *hp_idx[256];
+
+void
+V2D_Init(void)
+{
+ int i;
+#define HPS(I,N,V) \
+ i = hp_static[I].name[0]; \
+ if (hp_idx[i] == NULL) hp_idx[i] = &hp_static[I];
+#include "tbl/vhp_static.h"
+#undef HPS
+}
+
+/**********************************************************************/
+
+static int __match_proto__(vdp_bytes)
+h2_bytes(struct req *req, enum vdp_action act, void **priv,
+ const void *ptr, ssize_t len)
+{
+ struct h2_req *r2;
+
+ CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
+ CAST_OBJ_NOTNULL(r2, req->transport_priv, H2_REQ_MAGIC);
+ (void)priv;
+ if (act == VDP_INIT || act == VDP_FINI)
+ return (0);
+ AZ(req->vdp_nxt); /* always at the bottom of the pile */
+
+ H2_Send(req->wrk, r2,
+ act == VDP_FLUSH ? 1 : 0,
+ H2_FRAME_DATA, H2FF_NONE, len, ptr);
+
+ return (0);
+}
+
+void __match_proto__(vtr_deliver_f)
+h2_deliver(struct req *req, struct boc *boc, int sendbody)
+{
+ ssize_t sz, sz1;
+ uint8_t *p;
+ unsigned u;
+ const char *r;
+ struct http *hp;
+ struct sess *sp;
+ struct h2_req *r2;
+ int i, err = 0;
+ const struct hpack_static *hps;
+
+ CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
+ CHECK_OBJ_ORNULL(boc, BOC_MAGIC);
+ CHECK_OBJ_NOTNULL(req->objcore, OBJCORE_MAGIC);
+ CAST_OBJ_NOTNULL(r2, req->transport_priv, H2_REQ_MAGIC);
+ sp = req->sp;
+ CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
+ (void)sendbody;
+
+ VSLb(req->vsl, SLT_Debug, "H2: Deliver");
+
+ (void)WS_Reserve(req->ws, 0);
+ p = (void*)req->ws->f;
+
+ switch (req->resp->status) {
+ case 200: *p++ = 0x80 | 8; break;
+ case 204: *p++ = 0x80 | 9; break;
+ case 206: *p++ = 0x80 | 10; break;
+ case 304: *p++ = 0x80 | 11; break;
+ case 400: *p++ = 0x80 | 12; break;
+ case 404: *p++ = 0x80 | 13; break;
+ case 500: *p++ = 0x80 | 14; break;
+ default:
+ *p++ = 0x18;
+ *p++ = 0x03;
+
+ (void)sprintf((char*)p, "%03d", req->resp->status);
+ p += 3;
+ break;
+ }
+
+ hp = req->resp;
+ for (u = HTTP_HDR_FIRST; u < hp->nhd; u++) {
+ assert((char*)p < req->ws->e);
+
+ r = strchr(hp->hd[u].b, ':');
+ AN(r);
+
+ hps = hp_idx[tolower(*hp->hd[u].b)];
+ sz = 1 + r - hp->hd[u].b;
+ assert(sz > 0);
+ while (hps != NULL && hps->idx > 0) {
+ i = strncasecmp(hps->name, hp->hd[u].b, sz);
+ if (i < 0) {
+ hps++;
+ continue;
+ }
+ if (i > 0)
+ hps = NULL;
+ break;
+ }
+ if (hps != NULL) {
+ VSLb(req->vsl, SLT_Debug,
+ "HP {%d, \"%s\", \"%s\"} <%s>",
+ hps->idx, hps->name, hps->val, hp->hd[u].b);
+ if (hps->idx < 15) {
+ *p++ = 0x10 | hps->idx;
+ } else {
+ *p++ = 0x1f;
+ *p++ = hps->idx - 0x0f;
+ }
+ } else {
+
+ *p++ = 0x10;
+ sz--;
+ if (sz < 127) {
+ *p++ = (uint8_t)sz;
+ } else {
+ *p++ = 0x7f;
+ *p++ = (uint8_t)sz - 0x7f;
+ }
+
+ for(sz1 = 0; sz1 < sz; sz1++)
+ *p++ = (uint8_t)tolower(hp->hd[u].b[sz1]);
+
+ }
+
+ while(vct_islws(*++r))
+ continue;
+ sz = hp->hd[u].e - r;
+ assert(sz <= 254);
+ if (sz < 127) {
+ *p++ = (uint8_t)sz;
+ } else if (sz < 127 * 2) {
+ *p++ = 0x7f;
+ *p++ = (uint8_t)sz - 0x7f;
+ }
+
+ memcpy(p, r, sz);
+ p += sz;
+ assert((char*)p < req->ws->e);
+ }
+ sz = (char*)p - req->ws->f;
+
+ /* XXX: Optimize !sendbody case */
+ H2_Send(req->wrk, r2, 1, H2_FRAME_HEADERS, H2FF_HEADERS_END_HEADERS,
+ sz, req->ws->f);
+
+ WS_Release(req->ws, 0);
+
+ if (sendbody && req->resp_len != 0)
+ VDP_push(req, h2_bytes, NULL, 1, "H2");
+
+ AZ(req->wrk->v1l);
+
+ if (sendbody && req->resp_len != 0)
+ err = VDP_DeliverObj(req);
+
+ H2_Send(req->wrk, r2, 1, H2_FRAME_DATA, H2FF_DATA_END_STREAM, 0, NULL);
+
+ AZ(req->wrk->v1l);
+ VDP_close(req);
+}
diff --git a/bin/varnishd/http2/cache_http2_hpack.c b/bin/varnishd/http2/cache_http2_hpack.c
new file mode 100644
index 0000000..a1470d7
--- /dev/null
+++ b/bin/varnishd/http2/cache_http2_hpack.c
@@ -0,0 +1,313 @@
+/*-
+ * Copyright (c) 2016 Varnish Software AS
+ * All rights reserved.
+ *
+ * Author: Martin Blix Grydeland <martin at varnish-software.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 <ctype.h>
+#include <stdio.h>
+
+#include "../cache/cache.h"
+#include "../http2/cache_http2.h"
+#include "vct.h"
+
+static int
+h2h_checkhdr(const struct http *hp, const char *b, size_t namelen, size_t len)
+{
+ const char *p;
+
+ CHECK_OBJ_NOTNULL(hp, HTTP_MAGIC);
+ AN(b);
+ assert(namelen >= 2); /* 2 chars from the ': ' that we added */
+ assert(namelen <= len);
+
+ if (namelen == 2) {
+ VSLb(hp->vsl, SLT_BogoHeader, "Empty name");
+ return (H2E_PROTOCOL_ERROR);
+ }
+
+ for (p = b; p < b + len; p++) {
+ if (p < b + (namelen - 2)) {
+ /* Check valid name characters */
+ if (p == b && *p == ':')
+ continue; /* pseudo-header */
+ if (vct_istchar(*p) && (!isupper(*p)))
+ /* XXX: vct should have a proper class for
+ this avoiding two checks */
+ continue;
+ VSLb(hp->vsl, SLT_BogoHeader,
+ "Illegal header name: %.*s",
+ (int)(len > 20 ? 20 : len), b);
+ return (H2E_PROTOCOL_ERROR);
+ } else if (p < b + namelen) {
+ /* ': ' added by us */
+ assert(*p == ':' || *p == ' ');
+ } else {
+ /* Check valid value characters */
+ if (!vct_isctl(*p) || vct_issp(*p))
+ continue;
+ VSLb(hp->vsl, SLT_BogoHeader,
+ "Illegal header value: %.*s",
+ (int)(len > 20 ? 20 : len), b);
+ return (H2E_PROTOCOL_ERROR);
+ }
+ }
+
+ return (0);
+}
+
+static int
+h2h_addhdr(struct http *hp, char *b, size_t namelen, size_t len)
+{
+ /* XXX: This might belong in cache/cache_http.c */
+ unsigned n;
+
+ CHECK_OBJ_NOTNULL(hp, HTTP_MAGIC);
+ AN(b);
+ assert(namelen >= 2); /* 2 chars from the ': ' that we added */
+ assert(namelen <= len);
+
+ if (len > UINT_MAX) { /* XXX: cache_param max header size */
+ VSLb(hp->vsl, SLT_BogoHeader, "Header too large: %.*s",
+ (int)(len > 20 ? 20 : len), b);
+ return (H2E_ENHANCE_YOUR_CALM);
+ }
+
+ if (b[0] == ':') {
+ /* Match H/2 pseudo headers */
+ /* XXX: Should probably have some include tbl for
+ pseudo-headers */
+ if (!strncmp(b, ":method: ", namelen)) {
+ b += namelen;
+ len -= namelen;
+ n = HTTP_HDR_METHOD;
+ } else if (!strncmp(b, ":path: ", namelen)) {
+ b += namelen;
+ len -= namelen;
+ n = HTTP_HDR_URL;
+ } else if (!strncmp(b, ":scheme: ", namelen)) {
+ /* XXX: What to do about this one? (typically
+ "http" or "https"). For now set it as a normal
+ header, stripping the first ':'. */
+ b++;
+ n = hp->nhd;
+ } else if (!strncmp(b, ":authority: ", namelen)) {
+ b+=6;
+ memcpy(b, "host", 4);
+ n = hp->nhd;
+ } else {
+ /* Unknown pseudo-header */
+ VSLb(hp->vsl, SLT_BogoHeader,
+ "Unknown pseudo-header: %.*s",
+ (int)(len > 20 ? 20 : len), b);
+ return (H2E_PROTOCOL_ERROR);
+ }
+ } else
+ n = hp->nhd;
+
+ if (n < HTTP_HDR_FIRST) {
+ /* Check for duplicate pseudo-header */
+ if (n < HTTP_HDR_FIRST && hp->hd[n].b != NULL) {
+ VSLb(hp->vsl, SLT_BogoHeader,
+ "Duplicate pseudo-header: %.*s",
+ (int)(len > 20 ? 20 : len), b);
+ return (H2E_PROTOCOL_ERROR);
+ }
+ } else {
+ /* Check for space in struct http */
+ if (n >= hp->shd) {
+ VSLb(hp->vsl, SLT_LostHeader, "Too many headers: %.*s",
+ (int)(len > 20 ? 20 : len), b);
+ return (H2E_ENHANCE_YOUR_CALM);
+ }
+ hp->nhd++;
+ }
+
+ hp->hd[n].b = b;
+ hp->hd[n].e = b + len;
+
+ return (0);
+}
+
+void
+h2h_decode_init(const struct h2_sess *h2, struct h2h_decode *d)
+{
+
+ CHECK_OBJ_NOTNULL(h2, H2_SESS_MAGIC);
+ CHECK_OBJ_NOTNULL(h2->new_req, REQ_MAGIC);
+ CHECK_OBJ_NOTNULL(h2->new_req->http, HTTP_MAGIC);
+ AN(d);
+ INIT_OBJ(d, H2H_DECODE_MAGIC);
+ VHD_Init(d->vhd);
+ d->out_l = WS_Reserve(h2->new_req->http->ws, 0);
+ assert(d->out_l > 0); /* Can't do any work without any buffer
+ space. Require non-zero size. */
+ d->out = h2->new_req->http->ws->f;
+ d->reset = d->out;
+}
+
+/* Possible error returns:
+ *
+ * H2E_COMPRESSION_ERROR: Lost compression state due to incomplete header
+ * block. This is a connection level error.
+ *
+ * H2E_ENHANCE_YOUR_CALM: Ran out of workspace or http header space. This
+ * is a stream level error.
+ */
+int
+h2h_decode_fini(const struct h2_sess *h2, struct h2h_decode *d)
+{
+ int ret;
+
+ CHECK_OBJ_NOTNULL(h2, H2_SESS_MAGIC);
+ CHECK_OBJ_NOTNULL(h2->new_req, REQ_MAGIC);
+ CHECK_OBJ_NOTNULL(d, H2H_DECODE_MAGIC);
+ WS_ReleaseP(h2->new_req->http->ws, d->out);
+ if (d->vhd_ret != VHD_OK) {
+ /* HPACK header block didn't finish at an instruction
+ boundary */
+ VSLb(h2->new_req->http->vsl, SLT_BogoHeader,
+ "HPACK compression error (%s)", VHD_Error(d->vhd_ret));
+ ret = H2E_COMPRESSION_ERROR;
+ } else
+ ret = d->error;
+ d->magic = 0;
+ return (ret);
+}
+
+/* Possible error returns:
+ *
+ * H2E_COMPRESSION_ERROR: Lost compression state due to invalid header
+ * block. This is a connection level error.
+ *
+ * H2E_PROTOCOL_ERROR: Malformed header or duplicate pseudo-header.
+ */
+int
+h2h_decode_bytes(struct h2_sess *h2, struct h2h_decode *d,
+ const uint8_t *in, size_t in_l)
+{
+ struct http *hp;
+ size_t in_u = 0;
+
+ CHECK_OBJ_NOTNULL(h2, H2_SESS_MAGIC);
+ CHECK_OBJ_NOTNULL(h2->new_req, REQ_MAGIC);
+ hp = h2->new_req->http;
+ CHECK_OBJ_NOTNULL(hp, HTTP_MAGIC);
+ CHECK_OBJ_NOTNULL(hp->ws, WS_MAGIC);
+ AN(hp->ws->r);
+ CHECK_OBJ_NOTNULL(d, H2H_DECODE_MAGIC);
+
+ /* Only H2E_ENHANCE_YOUR_CALM indicates that we should continue
+ processing. Other errors should have been returned and handled
+ by the caller. */
+ assert(d->error == 0 || d->error == H2E_ENHANCE_YOUR_CALM);
+
+ while (1) {
+ AN(d->out);
+ assert(d->out_u <= d->out_l);
+ d->vhd_ret = VHD_Decode(d->vhd, h2->dectbl, in, in_l, &in_u,
+ d->out, d->out_l, &d->out_u);
+
+ if (d->vhd_ret < 0) {
+ VSLb(hp->vsl, SLT_BogoHeader,
+ "HPACK compression error (%s)",
+ VHD_Error(d->vhd_ret));
+ d->error = H2E_COMPRESSION_ERROR;
+ break;
+ } else if (d->vhd_ret == VHD_OK || d->vhd_ret == VHD_MORE) {
+ assert(in_u == in_l);
+ break;
+ }
+
+ if (d->error == H2E_ENHANCE_YOUR_CALM) {
+ d->out_u = 0;
+ assert(d->out_u < d->out_l);
+ continue;
+ }
+
+ switch (d->vhd_ret) {
+ case VHD_NAME_SEC:
+ /* XXX: header flag for never-indexed header */
+ case VHD_NAME:
+ assert(d->namelen == 0);
+ if (d->out_l - d->out_u < 2) {
+ d->error = H2E_ENHANCE_YOUR_CALM;
+ break;
+ }
+ d->out[d->out_u++] = ':';
+ d->out[d->out_u++] = ' ';
+ d->namelen = d->out_u;
+ break;
+
+ case VHD_VALUE_SEC:
+ /* XXX: header flag for never-indexed header */
+ case VHD_VALUE:
+ assert(d->namelen > 0);
+ if (d->out_l - d->out_u < 1) {
+ d->error = H2E_ENHANCE_YOUR_CALM;
+ break;
+ }
+ d->error = h2h_checkhdr(hp, d->out, d->namelen,
+ d->out_u);
+ if (d->error)
+ break;
+ d->error = h2h_addhdr(hp, d->out, d->namelen, d->out_u);
+ if (d->error)
+ break;
+ d->out[d->out_u++] = '\0'; /* Zero guard */
+ d->out += d->out_u;
+ d->out_l -= d->out_u;
+ d->out_u = 0;
+ d->namelen = 0;
+ break;
+
+ case VHD_BUF:
+ d->error = H2E_ENHANCE_YOUR_CALM;
+ break;
+
+ default:
+ WRONG("Unhandled return value");
+ break;
+ }
+
+ if (d->error == H2E_ENHANCE_YOUR_CALM) {
+ http_Teardown(hp);
+ d->out = d->reset;
+ d->out_l = hp->ws->r - d->out;
+ d->out_u = 0;
+ assert(d->out_u < d->out_l);
+ } else if (d->error)
+ break;
+ }
+
+ if (d->error == H2E_ENHANCE_YOUR_CALM)
+ return (0); /* Stream error, delay reporting until
+ h2h_decode_fini so that we can process the
+ complete header block */
+ return (d->error);
+}
diff --git a/bin/varnishd/http2/cache_http2_panic.c b/bin/varnishd/http2/cache_http2_panic.c
new file mode 100644
index 0000000..085c5c4
--- /dev/null
+++ b/bin/varnishd/http2/cache_http2_panic.c
@@ -0,0 +1,79 @@
+/*-
+ * Copyright (c) 2016 Varnish Software AS
+ * All rights reserved.
+ *
+ * Author: Poul-Henning Kamp <phk at phk.freebsd.dk>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include "config.h"
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <netinet/in.h>
+
+#include <ctype.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "../cache/cache.h"
+#include "../cache/cache_filter.h"
+#include "../cache/cache_transport.h"
+#include "../http2/cache_http2.h"
+
+#include "vend.h"
+#include "vsb.h"
+#include "vtcp.h"
+#include "vtim.h"
+
+void
+h2_sess_panic(struct vsb *vsb, const struct sess *sp)
+{
+ uintptr_t *up;
+ struct h2_sess *h2;
+ struct h2_req *r2;
+
+ AZ(SES_Get_xport_priv(sp, &up));
+
+ h2 = (void*)*up;
+ CHECK_OBJ_NOTNULL(h2, H2_SESS_MAGIC);
+ VSB_printf(vsb, "streams {\n");
+ VSB_indent(vsb, 2);
+ VTAILQ_FOREACH(r2, &h2->streams, list) {
+ VSB_printf(vsb, "0x%08x", r2->stream);
+ switch(r2->state) {
+#define H2_STREAM(U,sd,d) case H2_S_##U: VSB_printf(vsb, " %-6s", sd); break;
+#include <tbl/h2_stream.h>
+ default:
+ VSB_printf(vsb, " State %d", r2->state);
+ break;
+ }
+ VSB_printf(vsb, "\n");
+ }
+ VSB_indent(vsb, -2);
+ VSB_printf(vsb, "}\n");
+}
diff --git a/bin/varnishd/http2/cache_http2_proto.c b/bin/varnishd/http2/cache_http2_proto.c
new file mode 100644
index 0000000..e7884b0
--- /dev/null
+++ b/bin/varnishd/http2/cache_http2_proto.c
@@ -0,0 +1,745 @@
+/*-
+ * Copyright (c) 2016 Varnish Software AS
+ * All rights reserved.
+ *
+ * Author: Poul-Henning Kamp <phk at phk.freebsd.dk>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include "config.h"
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <netinet/in.h>
+
+#include <ctype.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "../cache/cache.h"
+#include "../cache/cache_filter.h"
+#include "../cache/cache_transport.h"
+#include "../http2/cache_http2.h"
+
+#include "vend.h"
+#include "vsb.h"
+#include "vtcp.h"
+#include "vtim.h"
+
+enum h2frame {
+#define H2_FRAME(l,u,t,f) H2F_##u = t,
+#include "tbl/h2_frames.h"
+};
+
+enum h2setting {
+#define H2_SETTINGS(n,v,d) H2S_##n = v,
+#include "tbl/h2_settings.h"
+#undef H2_SETTINGS
+};
+
+static const char *
+h2_framename(enum h2frame h2f)
+{
+
+ switch(h2f) {
+#define H2_FRAME(l,u,t,f) case H2F_##u: return #u;
+#include "tbl/h2_frames.h"
+ default:
+ return (NULL);
+ }
+}
+
+static const char *
+h2_settingname(enum h2setting h2f)
+{
+
+ switch(h2f) {
+#define H2_SETTINGS(n,v,d) case H2S_##n: return #n;
+#include "tbl/h2_settings.h"
+#undef H2_SETTINGS
+ default:
+ return (NULL);
+ }
+}
+
+#define H2_FRAME_FLAGS(l,u,v) const uint8_t H2FF_##u = v;
+#include "tbl/h2_frames.h"
+
+static const char h2_resp_101[] =
+ "HTTP/1.1 101 Switching Protocols\r\n"
+ "Connection: Upgrade\r\n"
+ "Upgrade: h2c\r\n"
+ "\r\n";
+
+static const char H2_prism[24] = {
+ 0x50, 0x52, 0x49, 0x20, 0x2a, 0x20, 0x48, 0x54,
+ 0x54, 0x50, 0x2f, 0x32, 0x2e, 0x30, 0x0d, 0x0a,
+ 0x0d, 0x0a, 0x53, 0x4d, 0x0d, 0x0a, 0x0d, 0x0a
+};
+
+static const uint8_t H2_settings[] = {
+ 0x00, 0x03,
+ 0x00, 0x00, 0x00, 0x64,
+ 0x00, 0x04,
+ 0x00, 0x00, 0xff, 0xff
+};
+
+/**********************************************************************/
+#define DUMMY_FRAME(l) \
+ void \
+ h2_rx_##l(struct worker *wrk, struct h2_sess *h2, struct h2_req *r2) \
+ __match_proto__(h2_frame_f) \
+ { (void)wrk; (void)r2; VSLb(h2->vsl, SLT_Debug, "XXX implement " #l); INCOMPL(); }
+
+DUMMY_FRAME(data)
+DUMMY_FRAME(rst_stream)
+DUMMY_FRAME(ping)
+DUMMY_FRAME(push_promise)
+DUMMY_FRAME(continuation)
+
+/**********************************************************************
+ * The h2_sess struct needs many of the same things as a request,
+ * WS, VSL, HTC &c, but rather than implement all that stuff over, we
+ * grab an actual struct req, and mirror the relevant fields into
+ * struct h2_sess.
+ * To make things really incestuous, we allocate the h2_sess on
+ * the WS of that "Session ReQuest".
+ */
+
+static struct h2_sess *
+h2_new_sess(const struct worker *wrk, struct sess *sp, struct req *srq)
+{
+ uintptr_t *up;
+ struct h2_sess *h2;
+
+ if (SES_Get_xport_priv(sp, &up)) {
+ /* Already reserved if we came via H1 */
+ SES_Reserve_xport_priv(sp, &up);
+ *up = 0;
+ }
+ if (*up == 0) {
+ if (srq == NULL)
+ srq = Req_New(wrk, sp);
+ AN(srq);
+ h2 = WS_Alloc(srq->ws, sizeof *h2);
+ AN(h2);
+ INIT_OBJ(h2, H2_SESS_MAGIC);
+ h2->refcnt = 1;
+ h2->srq = srq;
+ h2->htc = srq->htc;
+ h2->ws = srq->ws;
+ h2->vsl = srq->vsl;
+ h2->vsl->wid = sp->vxid;
+ h2->htc->fd = sp->fd;
+ h2->sess = sp;
+ VTAILQ_INIT(&h2->streams);
+#define H2_SETTINGS(n,v,d) \
+ do { \
+ assert(v < H2_SETTINGS_N); \
+ h2->their_settings[v] = d; \
+ h2->our_settings[v] = d; \
+ } while (0);
+#include "tbl/h2_settings.h"
+#undef H2_SETTINGS
+
+ /* XXX: Lacks a VHT_Fini counterpart. Will leak memory. */
+ AZ(VHT_Init(h2->dectbl,
+ h2->our_settings[H2S_HEADER_TABLE_SIZE]));
+
+ SES_Reserve_xport_priv(sp, &up);
+ *up = (uintptr_t)h2;
+ }
+ AN(up);
+ CAST_OBJ_NOTNULL(h2, (void*)(*up), H2_SESS_MAGIC);
+ return (h2);
+}
+
+/**********************************************************************
+ */
+
+static struct h2_req *
+h2_new_req(const struct worker *wrk, struct h2_sess *h2,
+ unsigned stream, struct req *req)
+{
+ struct h2_req *r2;
+
+ Lck_AssertHeld(&h2->sess->mtx);
+ if (req == NULL)
+ req = Req_New(wrk, h2->sess);
+ CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
+
+ r2 = WS_Alloc(req->ws, sizeof *r2);
+ AN(r2);
+ INIT_OBJ(r2, H2_REQ_MAGIC);
+ r2->h2sess = h2;
+ r2->stream = stream;
+ r2->req = req;
+ req->transport_priv = r2;
+ // XXX: ordering ?
+ VTAILQ_INSERT_TAIL(&h2->streams, r2, list);
+ h2->refcnt++;
+ return (r2);
+}
+
+static void
+h2_del_req(struct worker *wrk, struct h2_req *r2, enum h2_error_e err)
+{
+ struct h2_sess *h2;
+ int r;
+
+ (void)err;
+ h2 = r2->h2sess;
+ Lck_Lock(&h2->sess->mtx);
+ assert(h2->refcnt > 0);
+ r = --h2->refcnt;
+ /* XXX: PRIORITY reshuffle */
+ VTAILQ_REMOVE(&h2->streams, r2, list);
+ Lck_Unlock(&h2->sess->mtx);
+ Req_Cleanup(h2->sess, wrk, r2->req);
+ if (r)
+ return;
+ SES_Delete(h2->sess, SC_RX_JUNK, NAN);
+}
+
+/**********************************************************************
+ * Update and VSL a single SETTING rx'ed from the other side
+ * 'd' must point to six bytes.
+ */
+
+static void
+h2_setting(struct h2_sess *h2, const uint8_t *d)
+{
+ uint16_t x;
+ uint32_t y;
+ const char *n;
+ char nb[8];
+
+ x = vbe16dec(d);
+ y = vbe32dec(d + 2);
+ n = h2_settingname((enum h2setting)x);
+ if (n == NULL) {
+ bprintf(nb, "0x%04x", x);
+ n = nb;
+ }
+ VSLb(h2->vsl, SLT_Debug, "H2SETTING %s 0x%08x", n, y);
+ if (x > 0 && x < H2_SETTINGS_N)
+ h2->their_settings[x] = y;
+}
+
+/**********************************************************************/
+
+static void
+h2_vsl_frame(const struct h2_sess *h2, const void *ptr, size_t len)
+{
+ const uint8_t *b;
+ struct vsb *vsb;
+ const char *p;
+ unsigned u;
+
+ AN(ptr);
+ assert(len >= 9);
+ b = ptr;
+
+ VSLb_bin(h2->vsl, SLT_H2RxHdr, 9, b);
+ if (len > 9)
+ VSLb_bin(h2->vsl, SLT_H2RxBody, len - 9, b + 9);
+
+ u = vbe32dec(b) >> 8;
+
+ vsb = VSB_new_auto();
+ AN(vsb);
+ p = h2_framename((enum h2frame)b[3]);
+ if (p != NULL)
+ VSB_cat(vsb, p);
+ else
+ VSB_quote(vsb, b + 3, 1, VSB_QUOTE_HEX);
+ VSB_printf(vsb, "[%u] ", u);
+ VSB_quote(vsb, b + 4, 1, VSB_QUOTE_HEX);
+ VSB_putc(vsb, ' ');
+ VSB_quote(vsb, b + 5, 4, VSB_QUOTE_HEX);
+ if (u > 0) {
+ VSB_putc(vsb, ' ');
+ VSB_quote(vsb, b + 9, u, VSB_QUOTE_HEX);
+ }
+ AZ(VSB_finish(vsb));
+ VSLb(h2->vsl, SLT_Debug, "H2RXF %s", VSB_data(vsb));
+ VSB_destroy(&vsb);
+}
+
+
+/**********************************************************************
+ */
+
+void __match_proto__(h2_frame_f)
+h2_rx_goaway(struct worker *wrk, struct h2_sess *h2, struct h2_req *r2)
+{
+ uint32_t error;
+
+ (void)wrk;
+ (void)r2;
+ h2->go_away_last_stream = vbe32dec(h2->rxf_data);
+ error = vbe32dec(h2->rxf_data + 4);
+ h2->go_away = 1;
+}
+
+void __match_proto__(h2_frame_f)
+h2_rx_window_update(struct worker *wrk, struct h2_sess *h2, struct h2_req *r2)
+{
+ uint32_t wu;
+
+ (void)wrk;
+ Lck_AssertHeld(&h2->sess->mtx);
+ xxxassert(h2->rxf_len == 4); // conn FRAME_SIZE_ERROR
+ wu = vbe32dec(h2->rxf_data);
+ xxxassert(wu != 0); // stream PROTOCOL_ERROR
+ r2->window += wu;
+ xxxassert(r2->window < (1LLU<<32)); // FLOW_CONTROL_ERROR
+}
+
+/**********************************************************************
+ * Incoming PRIORITY, possibly an ACK of one we sent.
+ */
+
+void __match_proto__(h2_frame_f)
+h2_rx_priority(struct worker *wrk, struct h2_sess *h2, struct h2_req *r2)
+{
+
+ (void)wrk;
+ (void)h2;
+ xxxassert(r2->stream & 1);
+}
+
+/**********************************************************************
+ * Incoming SETTINGS, possibly an ACK of one we sent.
+ */
+
+void __match_proto__(h2_frame_f)
+h2_rx_settings(struct worker *wrk, struct h2_sess *h2, struct h2_req *r2)
+{
+ const uint8_t *p = h2->rxf_data;
+ unsigned l = h2->rxf_len;
+
+ (void)wrk;
+ (void)r2;
+ AZ(h2->rxf_stream);
+ if (h2->rxf_flags == H2FF_SETTINGS_ACK) {
+ XXXAZ(h2->rxf_len);
+ } else if (h2->rxf_flags == 0) {
+ for (;l >= 6; l -= 6, p += 6)
+ h2_setting(h2, p);
+ if (l > 0)
+ VSLb(h2->vsl, SLT_Debug,
+ "NB: SETTINGS had %u dribble-bytes", l);
+ H2_Send_Frame(wrk, h2,
+ H2_FRAME_SETTINGS, H2FF_SETTINGS_ACK, 0, 0, NULL);
+ } else {
+ WRONG("SETTINGS FRAME");
+ }
+}
+
+/**********************************************************************
+ * Incoming HEADERS, this is where the partys at...
+ */
+
+static void __match_proto__(task_func_t)
+h2_do_req(struct worker *wrk, void *priv)
+{
+ struct req *req;
+ struct h2_req *r2;
+
+ CAST_OBJ_NOTNULL(req, priv, REQ_MAGIC);
+ CAST_OBJ_NOTNULL(r2, req->transport_priv, H2_REQ_MAGIC);
+ assert(CNT_Request(wrk, req) != REQ_FSM_DISEMBARK);
+ VSL(SLT_Debug, 0, "H2REQ CNT done");
+ /* XXX clean up req */
+ h2_del_req(wrk, r2, H2E_NO_ERROR);
+}
+
+void __match_proto__(h2_frame_f)
+h2_rx_headers(struct worker *wrk, struct h2_sess *h2, struct h2_req *r2)
+{
+ struct req *req;
+ struct h2h_decode d[1];
+ const uint8_t *p;
+ size_t l;
+ int i;
+
+ /* XXX: This still lacks support for CONTINUATION frames, half
+ * read frames and proper error handling.
+ */
+
+ assert(h2->rxf_stream & 1);
+
+ req = r2->req;
+ CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
+
+ req->vsl->wid = VXID_Get(wrk, VSL_CLIENTMARKER);
+ VSLb(req->vsl, SLT_Begin, "req %u rxreq", VXID(req->sp->vxid));
+ VSL(SLT_Link, req->sp->vxid, "req %u rxreq", VXID(req->vsl->wid));
+
+ h2->new_req = req;
+ req->sp = h2->sess;
+ req->req_step = R_STP_RECV;
+ req->transport = &H2_transport;
+
+ req->t_first = VTIM_real();
+ req->t_req = VTIM_real();
+ req->t_prev = req->t_first;
+ VSLb_ts_req(req, "Start", req->t_first);
+ VCL_Refresh(&wrk->vcl);
+ req->vcl = wrk->vcl;
+ wrk->vcl = NULL;
+
+ HTTP_Setup(req->http, req->ws, req->vsl, SLT_ReqMethod);
+ h2h_decode_init(h2, d);
+ /* XXX: Error handling */
+ p = h2->rxf_data;
+ l = h2->rxf_len;
+ if (h2->rxf_flags & 0x20) {
+ p += 5;
+ l -= 5;
+ }
+ i = h2h_decode_bytes(h2, d, p, l);
+ if (i)
+ VSLb(h2->vsl, SLT_Debug, "H2H_decode_bytes = %d", i);
+ XXXAZ(i);
+ i = h2h_decode_fini(h2, d);
+ if (i)
+ VSLb(h2->vsl, SLT_Debug, "H2H_decode_fini = %d", i);
+ XXXAZ(i);
+ VSLb_ts_req(req, "Req", req->t_req);
+ http_SetH(req->http, HTTP_HDR_PROTO, "HTTP/2.0");
+
+ req->req_body_status = REQ_BODY_NONE;
+ wrk->stats->client_req++;
+ wrk->stats->s_req++;
+ req->ws_req = WS_Snapshot(req->ws);
+ HTTP_Copy(req->http0, req->http);
+
+ req->task.func = h2_do_req;
+ req->task.priv = req;
+ XXXAZ(Pool_Task(wrk->pool, &req->task, TASK_QUEUE_REQ));
+}
+
+/**********************************************************************/
+
+enum htc_status_e __match_proto__(htc_complete_f)
+H2_prism_complete(struct http_conn *htc)
+{
+ int l;
+
+ CHECK_OBJ_NOTNULL(htc, HTTP_CONN_MAGIC);
+ l = htc->rxbuf_e - htc->rxbuf_b;
+ if (l < strlen(H2_prism))
+ return (HTC_S_MORE);
+ if (!memcmp(htc->rxbuf_b, H2_prism, sizeof(H2_prism)))
+ return (HTC_S_COMPLETE);
+ return (HTC_S_JUNK);
+}
+
+static enum htc_status_e __match_proto__(htc_complete_f)
+h2_frame_complete(struct http_conn *htc)
+{
+ int l;
+ unsigned u;
+
+ CHECK_OBJ_NOTNULL(htc, HTTP_CONN_MAGIC);
+ l = htc->rxbuf_e - htc->rxbuf_b;
+ if (l < 9)
+ return (HTC_S_MORE);
+ u = vbe32dec(htc->rxbuf_b) >> 8;
+ VSL(SLT_Debug, 0, "RX %p %d %u", htc->rxbuf_b, l, u);
+ if (l < u + 9) // XXX: Only for !DATA frames
+ return (HTC_S_MORE);
+ return (HTC_S_COMPLETE);
+}
+
+static int
+h2_rxframe(struct worker *wrk, struct h2_sess *h2)
+{
+ enum htc_status_e hs;
+ enum h2frame ft;
+ struct h2_req *r2 = NULL;
+
+ (void)VTCP_blocking(h2->htc->fd);
+ h2->sess->t_idle = VTIM_real();
+ hs = HTC_RxStuff(h2->htc, h2_frame_complete,
+ NULL, NULL, NAN,
+ h2->sess->t_idle + cache_param->timeout_idle + 100,
+ 1024);
+ if (hs != HTC_S_COMPLETE) {
+ Lck_Lock(&h2->sess->mtx);
+ VSLb(h2->vsl, SLT_Debug, "H2: No frame (hs=%d)", hs);
+ Lck_Unlock(&h2->sess->mtx);
+ return (0);
+ }
+
+ h2->rxf_len = vbe32dec(h2->htc->rxbuf_b) >> 8;
+ h2->rxf_flags = h2->htc->rxbuf_b[4];
+ h2->rxf_stream = vbe32dec(h2->htc->rxbuf_b + 5);
+ h2->rxf_data = (void*)(h2->htc->rxbuf_b + 9);
+ /* XXX: later full DATA will not be rx'ed yet. */
+ HTC_RxPipeline(h2->htc, h2->htc->rxbuf_b + h2->rxf_len + 9);
+
+ Lck_Lock(&h2->sess->mtx);
+ VTAILQ_FOREACH(r2, &h2->streams, list)
+ if (r2->stream == h2->rxf_stream)
+ break;
+ if (r2 == NULL) {
+ xxxassert(h2->rxf_stream > h2->highest_stream);
+ h2->highest_stream = h2->rxf_stream;
+ r2 = h2_new_req(wrk, h2, h2->rxf_stream, NULL);
+ }
+
+ h2_vsl_frame(h2, h2->htc->rxbuf_b, 9L + h2->rxf_len);
+
+ ft = (enum h2frame)h2->htc->rxbuf_b[3];
+ switch (ft) {
+#define H2_FRAME(l,u,t,f) \
+ case H2F_##u: \
+ if (!(h2->rxf_flags & ~f)) { \
+ h2_rx_##l(wrk, h2, r2); \
+ } else { \
+ VSLb(h2->vsl, SLT_Debug, \
+ "H2: Bad flags 0x%02x on " #u, \
+ h2->rxf_flags); \
+ Lck_Unlock(&h2->sess->mtx); \
+ return (0); \
+ } \
+ break;
+#include "tbl/h2_frames.h"
+ default:
+ INCOMPL();
+ }
+ Lck_Unlock(&h2->sess->mtx);
+ return (1);
+}
+
+
+/**********************************************************************
+ * Deal with the base64url (NB: ...url!) encoded SETTINGS in the H1 req
+ * of a H2C upgrade.
+ */
+
+static int
+h2_b64url_settings(struct h2_sess *h2, struct req *req)
+{
+ const char *p, *q;
+ uint8_t u[6], *up;
+ unsigned x;
+ int i, n;
+ static const char s[] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "abcdefghijklmnopqrstuvwxyz"
+ "0123456789"
+ "-_=";
+
+ /*
+ * If there is trouble with this, we could reject the upgrade
+ * but putting this on the H1 side is just plain wrong...
+ */
+ AN(http_GetHdr(req->http, H_HTTP2_Settings, &p));
+ if (p == NULL)
+ return (-1);
+ VSLb(req->vsl, SLT_Debug, "H2CS %s", p);
+
+ n = 0;
+ x = 0;
+ up = u;
+ for(;*p; p++) {
+ q = strchr(s, *p);
+ if (q == NULL)
+ return (-1);
+ i = q - s;
+ assert(i >= 0 && i <= 63);
+ x <<= 6;
+ x |= i;
+ n += 6;
+ if (n < 8)
+ continue;
+ *up++ = (uint8_t)(x >> (n - 8));
+ n -= 8;
+ if (up == u + sizeof u) {
+ AZ(n);
+ h2_setting(h2, (void*)u);
+ up = u;
+ }
+ }
+ if (up != u)
+ return (-1);
+ return (0);
+}
+
+/**********************************************************************/
+
+static int
+h2_new_pu_session(struct worker *wrk, const struct h2_sess *h2)
+{
+ enum htc_status_e hs;
+
+ (void)wrk;
+
+ hs = H2_prism_complete(h2->htc);
+ if (hs == HTC_S_MORE) {
+ VSLb(h2->vsl, SLT_Debug, "Short pu PRISM");
+ return (0);
+ }
+ if (hs != HTC_S_COMPLETE) {
+ VSLb(h2->vsl, SLT_Debug, "Wrong pu PRISM");
+ return (0);
+ }
+ HTC_RxPipeline(h2->htc, h2->htc->rxbuf_b + sizeof(H2_prism));
+ HTC_RxInit(h2->htc, wrk->aws);
+
+ VSLb(h2->vsl, SLT_Debug, "H2: Got pu PRISM");
+ return (1);
+}
+
+/**********************************************************************/
+
+static int
+h2_new_ou_session(struct worker *wrk, struct h2_sess *h2,
+ struct req *req)
+{
+ ssize_t sz;
+ enum htc_status_e hs;
+
+ sz = write(h2->sess->fd, h2_resp_101, strlen(h2_resp_101));
+ assert(sz == strlen(h2_resp_101));
+
+ AZ(h2_b64url_settings(h2, req));
+ http_Unset(req->http, H_Upgrade);
+ http_Unset(req->http, H_HTTP2_Settings);
+
+ /* Steal pipelined read-ahead, if any */
+ h2->htc->pipeline_b = req->htc->pipeline_b;
+ h2->htc->pipeline_e = req->htc->pipeline_e;
+ req->htc->pipeline_b = NULL;
+ req->htc->pipeline_e = NULL;
+ HTC_RxInit(h2->htc, wrk->aws);
+
+ /* Start req thread */
+ (void)h2_new_req(wrk, h2, 1, req);
+ req->req_step = R_STP_RECV;
+ req->transport = &H2_transport;
+ req->task.func = h2_do_req;
+ req->task.priv = req;
+ req->err_code = 0;
+ http_SetH(req->http, HTTP_HDR_PROTO, "HTTP/2.0");
+ XXXAZ(Pool_Task(wrk->pool, &req->task, TASK_QUEUE_REQ));
+
+ /* Wait for PRISM response */
+ hs = HTC_RxStuff(h2->htc, H2_prism_complete,
+ NULL, NULL, NAN, h2->sess->t_idle + cache_param->timeout_idle, 256);
+ if (hs != HTC_S_COMPLETE) {
+ /* XXX clean up req thread */
+ VSLb(h2->vsl, SLT_Debug, "H2: No OU PRISM (hs=%d)", hs);
+ Req_Release(req);
+ Lck_Unlock(&h2->sess->mtx);
+ SES_Delete(h2->sess, SC_RX_JUNK, NAN);
+ return (0);
+ }
+ HTC_RxPipeline(h2->htc, h2->htc->rxbuf_b + sizeof(H2_prism));
+ HTC_RxInit(h2->htc, wrk->aws);
+ VSLb(h2->vsl, SLT_Debug, "H2: Got PRISM");
+ return (1);
+}
+
+static void __match_proto__(task_func_t)
+h2_new_session(struct worker *wrk, void *arg)
+{
+ struct req *req;
+ struct sess *sp;
+ struct h2_sess *h2;
+ struct h2_req *r2;
+ char *wsp;
+
+ CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
+ CAST_OBJ_NOTNULL(req, arg, REQ_MAGIC);
+ sp = req->sp;
+ CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
+
+ assert(req->transport == &H2_transport);
+
+ THR_SetRequest(req);
+ wsp = WS_Snapshot(wrk->aws);
+
+ switch(req->err_code) {
+ case 0:
+ /* Direct H2 connection (via Proxy) */
+ h2 = h2_new_sess(wrk, sp, req);
+ Lck_Lock(&h2->sess->mtx);
+ (void)h2_new_req(wrk, h2, 0, NULL);
+ break;
+ case 1:
+ /* Prior Knowledge H1->H2 upgrade */
+ h2 = h2_new_sess(wrk, sp, req);
+ Lck_Lock(&h2->sess->mtx);
+ (void)h2_new_req(wrk, h2, 0, NULL);
+
+ if (!h2_new_pu_session(wrk, h2))
+ return;
+ break;
+ case 2:
+ /* Optimistic H1->H2 upgrade */
+ h2 = h2_new_sess(wrk, sp, NULL);
+ Lck_Lock(&h2->sess->mtx);
+ (void)h2_new_req(wrk, h2, 0, NULL);
+
+ if (!h2_new_ou_session(wrk, h2, req))
+ return;
+ break;
+ default:
+ WRONG("Bad req->err_code");
+ }
+
+ THR_SetRequest(h2->srq);
+
+ H2_Send_Frame(wrk, h2,
+ H2_FRAME_SETTINGS, H2FF_NONE, sizeof H2_settings, 0, H2_settings);
+
+ /* and off we go... */
+ Lck_Unlock(&h2->sess->mtx);
+
+ while (h2_rxframe(wrk, h2)) {
+ WS_Reset(wrk->aws, wsp);
+ HTC_RxInit(h2->htc, wrk->aws);
+ }
+
+ r2 = VTAILQ_FIRST(&h2->streams);
+ CHECK_OBJ_NOTNULL(r2, H2_REQ_MAGIC);
+ assert(r2->stream == 0);
+ h2_del_req(wrk, r2, H2E_NO_ERROR);
+}
+
+struct transport H2_transport = {
+ .name = "H2",
+ .magic = TRANSPORT_MAGIC,
+ .new_session = h2_new_session,
+ .sess_panic = h2_sess_panic,
+ .deliver = h2_deliver,
+};
diff --git a/bin/varnishd/http2/cache_http2_send.c b/bin/varnishd/http2/cache_http2_send.c
new file mode 100644
index 0000000..70f48bf
--- /dev/null
+++ b/bin/varnishd/http2/cache_http2_send.c
@@ -0,0 +1,114 @@
+/*-
+ * Copyright (c) 2016 Varnish Software AS
+ * All rights reserved.
+ *
+ * Author: Poul-Henning Kamp <phk at phk.freebsd.dk>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include "config.h"
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <netinet/in.h>
+
+#include <ctype.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "../cache/cache.h"
+#include "../cache/cache_filter.h"
+#include "../cache/cache_transport.h"
+#include "../http2/cache_http2.h"
+
+#include "vend.h"
+#include "vsb.h"
+
+static void
+h2_mk_hdr(uint8_t *hdr, enum h2_frame_e type, uint8_t flags,
+ uint32_t len, uint32_t stream)
+{
+
+ AN(hdr);
+ assert(len < (1U << 24));
+ vbe32enc(hdr, len << 8);
+ hdr[3] = (uint8_t)type;
+ hdr[4] = flags;
+ vbe32enc(hdr + 5, stream);
+}
+
+/*
+ * This is the "raw" frame sender, all per stream accounting and
+ * prioritization must have happened before this is called, and
+ * the session mtx must be held.
+ */
+
+int
+H2_Send_Frame(struct worker *wrk, const struct h2_sess *h2,
+ enum h2_frame_e type, uint8_t flags,
+ uint32_t len, uint32_t stream, const void *ptr)
+{
+ uint8_t hdr[9];
+
+ Lck_AssertHeld(&h2->sess->mtx);
+ (void)wrk;
+
+ h2_mk_hdr(hdr, type, flags, len, stream);
+ VSLb_bin(h2->vsl, SLT_H2TxHdr, 9, hdr);
+
+ /*XXX*/(void)write(h2->sess->fd, hdr, sizeof hdr);
+ if (len > 0) {
+ /*XXX*/(void)write(h2->sess->fd, ptr, len);
+ VSLb_bin(h2->vsl, SLT_H2TxBody, len, ptr);
+ }
+ return (0);
+}
+
+/*
+ * This is the per-stream frame sender.
+ * XXX: windows
+ * XXX: priority
+ */
+
+int
+H2_Send(struct worker *wrk, struct h2_req *r2, int flush,
+ enum h2_frame_e type, uint8_t flags, uint32_t len, const void *ptr)
+{
+ int retval;
+ struct h2_sess *h2;
+
+ (void)flush;
+
+ CHECK_OBJ_NOTNULL(r2, H2_REQ_MAGIC);
+ h2 = r2->h2sess;
+ CHECK_OBJ_NOTNULL(h2, H2_SESS_MAGIC);
+
+ Lck_Lock(&h2->sess->mtx);
+ retval = H2_Send_Frame(wrk, h2, type, flags, len, r2->stream, ptr);
+ Lck_Unlock(&h2->sess->mtx);
+ return (retval);
+}
More information about the varnish-commit
mailing list