Commit 66d26826 authored by Poul-Henning Kamp's avatar Poul-Henning Kamp

Add our skeleton HTTP2 implementation, which seems to survive

at least trival traffic in real life.
parent 59c4ad8e
......@@ -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
......@@ -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);
......
......@@ -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)
......
......@@ -241,6 +241,7 @@ child_main(void)
VBE_InitCfg();
Pool_Init();
V1P_Init();
V2D_Init();
EXP_Init();
HSH_Init(heritage.hash);
......
......@@ -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 *);
......@@ -23,6 +23,7 @@ flexelint \
common/*.c \
hash/*.c \
http1/*.c \
http2/*.c \
mgt/*.c \
proxy/*.c \
storage/*.c \
......
/*-
* Copyright (c) 2016 Varnish Software
* All rights reserved.
*
* Author: Martin Blix Grydeland <martin@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);
This diff is collapsed.
/*-
* Copyright (c) 2016 Dridi Boukelmoune
* All rights reserved.
*
* Author: Dridi Boukelmoune <dridi.boukelmoune@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);
}
This diff is collapsed.
......@@ -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.
......
/*-
* Copyright (c) 2016 Varnish Software AS
* All rights reserved.
*
* Author: Poul-Henning Kamp <phk@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"
/*-
* Copyright (c) 2016 Varnish Software AS
* All rights reserved.
*
* Author: Poul-Henning Kamp <phk@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);
}
/*-
* Copyright (c) 2016 Varnish Software AS
* All rights reserved.
*
* Author: Martin Blix Grydeland <martin@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);
}
/*-
* Copyright (c) 2016 Varnish Software AS
* All rights reserved.
*
* Author: Poul-Henning Kamp <phk@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");
}
This diff is collapsed.
/*-
* Copyright (c) 2016 Varnish Software AS
* All rights reserved.
*
* Author: Poul-Henning Kamp <phk@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);
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment