Commit e5b99ec6 authored by Poul-Henning Kamp's avatar Poul-Henning Kamp

Split H2 session from protocol/frame processing

parent 428996a3
...@@ -71,6 +71,7 @@ varnishd_SOURCES = \ ...@@ -71,6 +71,7 @@ varnishd_SOURCES = \
http2/cache_http2_hpack.c \ http2/cache_http2_hpack.c \
http2/cache_http2_panic.c \ http2/cache_http2_panic.c \
http2/cache_http2_proto.c \ http2/cache_http2_proto.c \
http2/cache_http2_session.c \
http2/cache_http2_send.c \ http2/cache_http2_send.c \
mgt/mgt_acceptor.c \ mgt/mgt_acceptor.c \
mgt/mgt_child.c \ mgt/mgt_child.c \
......
...@@ -158,8 +158,9 @@ int H2_Send_Frame(struct worker *, const struct h2_sess *, ...@@ -158,8 +158,9 @@ int H2_Send_Frame(struct worker *, const struct h2_sess *,
int H2_Send(struct worker *, struct h2_req *, int flush, int H2_Send(struct worker *, struct h2_req *, int flush,
enum h2_frame_e type, uint8_t flags, uint32_t len, const void *); enum h2_frame_e type, uint8_t flags, uint32_t len, const void *);
typedef h2_error h2_frame_f(struct worker *, struct h2_sess *, /* cache_http2_proto.c */
struct h2_req *); int h2_rxframe(struct worker *, struct h2_sess *);
#define H2_FRAME(l,u,t,f) h2_frame_f h2_rx_##l ; void h2_req_body(struct req*);
#include "tbl/h2_frames.h"
...@@ -51,6 +51,8 @@ ...@@ -51,6 +51,8 @@
#undef H2EC2 #undef H2EC2
#undef H2EC3 #undef H2EC3
typedef h2_error h2_frame_f(struct worker *, struct h2_sess *, struct h2_req *);
enum h2frame { enum h2frame {
#define H2_FRAME(l,u,t,f) H2F_##u = t, #define H2_FRAME(l,u,t,f) H2F_##u = t,
#include "tbl/h2_frames.h" #include "tbl/h2_frames.h"
...@@ -84,28 +86,9 @@ h2_settingname(enum h2setting h2f) ...@@ -84,28 +86,9 @@ h2_settingname(enum h2setting h2f)
#define H2_FRAME_FLAGS(l,u,v) const uint8_t H2FF_##u = v; #define H2_FRAME_FLAGS(l,u,v) const uint8_t H2FF_##u = v;
#include "tbl/h2_frames.h" #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) \ #define DUMMY_FRAME(l) \
h2_error __match_proto__(h2_frame_f) \ static h2_error __match_proto__(h2_frame_f) \
h2_rx_##l(struct worker *wrk, struct h2_sess *h2, struct h2_req *r2) \ h2_rx_##l(struct worker *wrk, struct h2_sess *h2, struct h2_req *r2) \
__match_proto__(h2_frame_f) \ __match_proto__(h2_frame_f) \
{ (void)wrk; (void)r2; VSLb(h2->vsl, SLT_Debug, "XXX implement " #l); INCOMPL(); } { (void)wrk; (void)r2; VSLb(h2->vsl, SLT_Debug, "XXX implement " #l); INCOMPL(); }
...@@ -114,62 +97,6 @@ DUMMY_FRAME(rst_stream) ...@@ -114,62 +97,6 @@ DUMMY_FRAME(rst_stream)
DUMMY_FRAME(push_promise) DUMMY_FRAME(push_promise)
DUMMY_FRAME(continuation) DUMMY_FRAME(continuation)
/**********************************************************************
* The h2_sess struct needs many of the same things as a request,
* WS, VSL, HTC &c, but rather than implement all that stuff over, we
* grab an actual struct req, and mirror the relevant fields into
* struct h2_sess.
* To make things really incestuous, we allocate the h2_sess on
* the WS of that "Session ReQuest".
*/
static struct h2_sess *
h2_new_sess(const struct worker *wrk, struct sess *sp, struct req *srq)
{
uintptr_t *up;
struct h2_sess *h2;
if (SES_Get_xport_priv(sp, &up)) {
/* Already reserved if we came via H1 */
SES_Reserve_xport_priv(sp, &up);
*up = 0;
}
if (*up == 0) {
if (srq == NULL)
srq = Req_New(wrk, sp);
AN(srq);
h2 = WS_Alloc(srq->ws, sizeof *h2);
AN(h2);
INIT_OBJ(h2, H2_SESS_MAGIC);
h2->srq = srq;
h2->htc = srq->htc;
h2->ws = srq->ws;
h2->vsl = srq->vsl;
h2->vsl->wid = sp->vxid;
h2->htc->rfd = &sp->fd;
h2->sess = sp;
VTAILQ_INIT(&h2->streams);
#define H2_SETTINGS(n,v,d) \
do { \
assert(v < H2_SETTINGS_N); \
h2->their_settings[v] = d; \
h2->our_settings[v] = d; \
} while (0);
#include "tbl/h2_settings.h"
#undef H2_SETTINGS
/* XXX: Lacks a VHT_Fini counterpart. Will leak memory. */
AZ(VHT_Init(h2->dectbl,
h2->our_settings[H2S_HEADER_TABLE_SIZE]));
SES_Reserve_xport_priv(sp, &up);
*up = (uintptr_t)h2;
}
AN(up);
CAST_OBJ_NOTNULL(h2, (void*)(*up), H2_SESS_MAGIC);
return (h2);
}
/********************************************************************** /**********************************************************************
*/ */
...@@ -295,7 +222,7 @@ h2_vsl_frame(const struct h2_sess *h2, const void *ptr, size_t len) ...@@ -295,7 +222,7 @@ h2_vsl_frame(const struct h2_sess *h2, const void *ptr, size_t len)
/********************************************************************** /**********************************************************************
*/ */
h2_error __match_proto__(h2_frame_f) static h2_error __match_proto__(h2_frame_f)
h2_rx_ping(struct worker *wrk, struct h2_sess *h2, struct h2_req *r2) h2_rx_ping(struct worker *wrk, struct h2_sess *h2, struct h2_req *r2)
{ {
...@@ -313,7 +240,7 @@ h2_rx_ping(struct worker *wrk, struct h2_sess *h2, struct h2_req *r2) ...@@ -313,7 +240,7 @@ h2_rx_ping(struct worker *wrk, struct h2_sess *h2, struct h2_req *r2)
/********************************************************************** /**********************************************************************
*/ */
h2_error __match_proto__(h2_frame_f) static h2_error __match_proto__(h2_frame_f)
h2_rx_goaway(struct worker *wrk, struct h2_sess *h2, struct h2_req *r2) h2_rx_goaway(struct worker *wrk, struct h2_sess *h2, struct h2_req *r2)
{ {
uint32_t error; uint32_t error;
...@@ -327,7 +254,7 @@ h2_rx_goaway(struct worker *wrk, struct h2_sess *h2, struct h2_req *r2) ...@@ -327,7 +254,7 @@ h2_rx_goaway(struct worker *wrk, struct h2_sess *h2, struct h2_req *r2)
return (0); return (0);
} }
h2_error __match_proto__(h2_frame_f) static h2_error __match_proto__(h2_frame_f)
h2_rx_window_update(struct worker *wrk, struct h2_sess *h2, struct h2_req *r2) h2_rx_window_update(struct worker *wrk, struct h2_sess *h2, struct h2_req *r2)
{ {
uint32_t wu; uint32_t wu;
...@@ -349,7 +276,7 @@ h2_rx_window_update(struct worker *wrk, struct h2_sess *h2, struct h2_req *r2) ...@@ -349,7 +276,7 @@ h2_rx_window_update(struct worker *wrk, struct h2_sess *h2, struct h2_req *r2)
* Incoming PRIORITY, possibly an ACK of one we sent. * Incoming PRIORITY, possibly an ACK of one we sent.
*/ */
h2_error __match_proto__(h2_frame_f) static h2_error __match_proto__(h2_frame_f)
h2_rx_priority(struct worker *wrk, struct h2_sess *h2, struct h2_req *r2) h2_rx_priority(struct worker *wrk, struct h2_sess *h2, struct h2_req *r2)
{ {
...@@ -363,7 +290,7 @@ h2_rx_priority(struct worker *wrk, struct h2_sess *h2, struct h2_req *r2) ...@@ -363,7 +290,7 @@ h2_rx_priority(struct worker *wrk, struct h2_sess *h2, struct h2_req *r2)
* Incoming SETTINGS, possibly an ACK of one we sent. * Incoming SETTINGS, possibly an ACK of one we sent.
*/ */
h2_error __match_proto__(h2_frame_f) static h2_error __match_proto__(h2_frame_f)
h2_rx_settings(struct worker *wrk, struct h2_sess *h2, struct h2_req *r2) h2_rx_settings(struct worker *wrk, struct h2_sess *h2, struct h2_req *r2)
{ {
const uint8_t *p = h2->rxf_data; const uint8_t *p = h2->rxf_data;
...@@ -410,7 +337,7 @@ h2_do_req(struct worker *wrk, void *priv) ...@@ -410,7 +337,7 @@ h2_do_req(struct worker *wrk, void *priv)
h2_del_req(wrk, r2); h2_del_req(wrk, r2);
} }
h2_error __match_proto__(h2_frame_f) static h2_error __match_proto__(h2_frame_f)
h2_rx_headers(struct worker *wrk, struct h2_sess *h2, struct h2_req *r2) h2_rx_headers(struct worker *wrk, struct h2_sess *h2, struct h2_req *r2)
{ {
struct req *req; struct req *req;
...@@ -485,7 +412,7 @@ h2_rx_headers(struct worker *wrk, struct h2_sess *h2, struct h2_req *r2) ...@@ -485,7 +412,7 @@ h2_rx_headers(struct worker *wrk, struct h2_sess *h2, struct h2_req *r2)
/**********************************************************************/ /**********************************************************************/
h2_error __match_proto__(h2_frame_f) static h2_error __match_proto__(h2_frame_f)
h2_rx_data(struct worker *wrk, struct h2_sess *h2, struct h2_req *r2) h2_rx_data(struct worker *wrk, struct h2_sess *h2, struct h2_req *r2)
{ {
(void)wrk; (void)wrk;
...@@ -542,7 +469,7 @@ static const struct vfp h2_body = { ...@@ -542,7 +469,7 @@ static const struct vfp h2_body = {
.pull = h2_vfp_body, .pull = h2_vfp_body,
}; };
static void __match_proto__(vtr_req_body_t) void __match_proto__(vtr_req_body_t)
h2_req_body(struct req *req) h2_req_body(struct req *req)
{ {
struct h2_req *r2; struct h2_req *r2;
...@@ -557,21 +484,6 @@ h2_req_body(struct req *req) ...@@ -557,21 +484,6 @@ h2_req_body(struct req *req)
/**********************************************************************/ /**********************************************************************/
enum htc_status_e __match_proto__(htc_complete_f)
H2_prism_complete(struct http_conn *htc)
{
int l;
CHECK_OBJ_NOTNULL(htc, HTTP_CONN_MAGIC);
l = htc->rxbuf_e - htc->rxbuf_b;
if (l >= sizeof(H2_prism) &&
!memcmp(htc->rxbuf_b, H2_prism, sizeof(H2_prism)))
return (HTC_S_COMPLETE);
if (l < sizeof(H2_prism) && !memcmp(htc->rxbuf_b, H2_prism, l))
return (HTC_S_MORE);
return (HTC_S_JUNK);
}
static enum htc_status_e __match_proto__(htc_complete_f) static enum htc_status_e __match_proto__(htc_complete_f)
h2_frame_complete(struct http_conn *htc) h2_frame_complete(struct http_conn *htc)
{ {
...@@ -664,7 +576,7 @@ h2_procframe(struct worker *wrk, struct h2_sess *h2) ...@@ -664,7 +576,7 @@ h2_procframe(struct worker *wrk, struct h2_sess *h2)
return (0); return (0);
} }
static int int
h2_rxframe(struct worker *wrk, struct h2_sess *h2) h2_rxframe(struct worker *wrk, struct h2_sess *h2)
{ {
enum htc_status_e hs; enum htc_status_e hs;
...@@ -705,215 +617,3 @@ h2_rxframe(struct worker *wrk, struct h2_sess *h2) ...@@ -705,215 +617,3 @@ h2_rxframe(struct worker *wrk, struct h2_sess *h2)
Lck_Unlock(&h2->sess->mtx); Lck_Unlock(&h2->sess->mtx);
return (h2e ? 0 : 1); return (h2e ? 0 : 1);
} }
/**********************************************************************
* Deal with the base64url (NB: ...url!) encoded SETTINGS in the H1 req
* of a H2C upgrade.
*/
static int
h2_b64url_settings(struct h2_sess *h2, struct req *req)
{
const char *p, *q;
uint8_t u[6], *up;
unsigned x;
int i, n;
static const char s[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789"
"-_=";
/*
* If there is trouble with this, we could reject the upgrade
* but putting this on the H1 side is just plain wrong...
*/
AN(http_GetHdr(req->http, H_HTTP2_Settings, &p));
if (p == NULL)
return (-1);
VSLb(req->vsl, SLT_Debug, "H2CS %s", p);
n = 0;
x = 0;
up = u;
for (;*p; p++) {
q = strchr(s, *p);
if (q == NULL)
return (-1);
i = q - s;
assert(i >= 0 && i <= 63);
x <<= 6;
x |= i;
n += 6;
if (n < 8)
continue;
*up++ = (uint8_t)(x >> (n - 8));
n -= 8;
if (up == u + sizeof u) {
AZ(n);
h2_setting(h2, (void*)u);
up = u;
}
}
if (up != u)
return (-1);
return (0);
}
/**********************************************************************/
static int
h2_new_pu_session(struct worker *wrk, const struct h2_sess *h2)
{
enum htc_status_e hs;
(void)wrk;
hs = H2_prism_complete(h2->htc);
if (hs == HTC_S_MORE) {
VSLb(h2->vsl, SLT_Debug, "Short pu PRISM");
return (0);
}
if (hs != HTC_S_COMPLETE) {
VSLb(h2->vsl, SLT_Debug, "Wrong pu PRISM");
return (0);
}
HTC_RxPipeline(h2->htc, h2->htc->rxbuf_b + sizeof(H2_prism));
HTC_RxInit(h2->htc, wrk->aws);
VSLb(h2->vsl, SLT_Debug, "H2: Got pu PRISM");
return (1);
}
/**********************************************************************/
static int
h2_new_ou_session(struct worker *wrk, struct h2_sess *h2,
struct req *req)
{
ssize_t sz;
enum htc_status_e hs;
sz = write(h2->sess->fd, h2_resp_101, strlen(h2_resp_101));
assert(sz == strlen(h2_resp_101));
AZ(h2_b64url_settings(h2, req));
http_Unset(req->http, H_Upgrade);
http_Unset(req->http, H_HTTP2_Settings);
/* Steal pipelined read-ahead, if any */
h2->htc->pipeline_b = req->htc->pipeline_b;
h2->htc->pipeline_e = req->htc->pipeline_e;
req->htc->pipeline_b = NULL;
req->htc->pipeline_e = NULL;
/* XXX: This call may assert on buffer overflow if the pipelined
data exceeds the available space in the aws workspace. What to
do about the overflowing data is an open issue. */
HTC_RxInit(h2->htc, wrk->aws);
/* Start req thread */
(void)h2_new_req(wrk, h2, 1, req);
req->req_step = R_STP_RECV;
req->transport = &H2_transport;
req->task.func = h2_do_req;
req->task.priv = req;
req->err_code = 0;
http_SetH(req->http, HTTP_HDR_PROTO, "HTTP/2.0");
XXXAZ(Pool_Task(wrk->pool, &req->task, TASK_QUEUE_REQ));
/* Wait for PRISM response */
hs = HTC_RxStuff(h2->htc, H2_prism_complete,
NULL, NULL, NAN, h2->sess->t_idle + cache_param->timeout_idle, 256);
if (hs != HTC_S_COMPLETE) {
/* XXX clean up req thread */
VSLb(h2->vsl, SLT_Debug, "H2: No OU PRISM (hs=%d)", hs);
Req_Release(req);
Lck_Unlock(&h2->sess->mtx);
SES_Delete(h2->sess, SC_RX_JUNK, NAN);
return (0);
}
HTC_RxPipeline(h2->htc, h2->htc->rxbuf_b + sizeof(H2_prism));
HTC_RxInit(h2->htc, wrk->aws);
VSLb(h2->vsl, SLT_Debug, "H2: Got PRISM");
return (1);
}
static void __match_proto__(task_func_t)
h2_new_session(struct worker *wrk, void *arg)
{
struct req *req;
struct sess *sp;
struct h2_sess *h2;
struct h2_req *r2, *r22;
uintptr_t wsp;
CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
CAST_OBJ_NOTNULL(req, arg, REQ_MAGIC);
sp = req->sp;
CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
assert(req->transport == &H2_transport);
wsp = WS_Snapshot(wrk->aws);
switch(req->err_code) {
case 0:
/* Direct H2 connection (via Proxy) */
h2 = h2_new_sess(wrk, sp, req);
Lck_Lock(&h2->sess->mtx);
(void)h2_new_req(wrk, h2, 0, NULL);
break;
case 1:
/* Prior Knowledge H1->H2 upgrade */
h2 = h2_new_sess(wrk, sp, req);
Lck_Lock(&h2->sess->mtx);
(void)h2_new_req(wrk, h2, 0, NULL);
if (!h2_new_pu_session(wrk, h2))
return;
break;
case 2:
/* Optimistic H1->H2 upgrade */
h2 = h2_new_sess(wrk, sp, NULL);
Lck_Lock(&h2->sess->mtx);
(void)h2_new_req(wrk, h2, 0, NULL);
if (!h2_new_ou_session(wrk, h2, req))
return;
break;
default:
WRONG("Bad req->err_code");
}
THR_SetRequest(h2->srq);
H2_Send_Frame(wrk, h2,
H2_FRAME_SETTINGS, H2FF_NONE, sizeof H2_settings, 0, H2_settings);
/* and off we go... */
h2->cond = &wrk->cond;
Lck_Unlock(&h2->sess->mtx);
while (h2_rxframe(wrk, h2)) {
WS_Reset(wrk->aws, wsp);
HTC_RxInit(h2->htc, wrk->aws);
}
/* Delete all idle streams */
VTAILQ_FOREACH_SAFE(r2, &h2->streams, list, r22) {
if (r2->state == H2_S_IDLE)
h2_del_req(wrk, r2);
}
h2->cond = NULL;
}
struct transport H2_transport = {
.name = "H2",
.magic = TRANSPORT_MAGIC,
.new_session = h2_new_session,
.sess_panic = h2_sess_panic,
.deliver = h2_deliver,
.req_body = h2_req_body,
.minimal_response = h2_minimal_response,
};
/*-
* 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 "cache/cache.h"
#include <stdio.h>
#include "cache/cache_transport.h"
#include "cache/cache_filter.h"
#include "http2/cache_http2.h"
#include "vend.h"
static const char *
h2_settingname(enum h2setting h2f)
{
switch(h2f) {
#define H2_SETTINGS(n,v,d) case H2S_##n: return #n;
#include "tbl/h2_settings.h"
#undef H2_SETTINGS
default:
return (NULL);
}
}
static const char h2_resp_101[] =
"HTTP/1.1 101 Switching Protocols\r\n"
"Connection: Upgrade\r\n"
"Upgrade: h2c\r\n"
"\r\n";
static const char H2_prism[24] = {
0x50, 0x52, 0x49, 0x20, 0x2a, 0x20, 0x48, 0x54,
0x54, 0x50, 0x2f, 0x32, 0x2e, 0x30, 0x0d, 0x0a,
0x0d, 0x0a, 0x53, 0x4d, 0x0d, 0x0a, 0x0d, 0x0a
};
static const uint8_t H2_settings[] = {
0x00, 0x03,
0x00, 0x00, 0x00, 0x64,
0x00, 0x04,
0x00, 0x00, 0xff, 0xff
};
/**********************************************************************
* The h2_sess struct needs many of the same things as a request,
* WS, VSL, HTC &c, but rather than implement all that stuff over, we
* grab an actual struct req, and mirror the relevant fields into
* struct h2_sess.
* To make things really incestuous, we allocate the h2_sess on
* the WS of that "Session ReQuest".
*/
static struct h2_sess *
h2_new_sess(const struct worker *wrk, struct sess *sp, struct req *srq)
{
uintptr_t *up;
struct h2_sess *h2;
if (SES_Get_xport_priv(sp, &up)) {
/* Already reserved if we came via H1 */
SES_Reserve_xport_priv(sp, &up);
*up = 0;
}
if (*up == 0) {
if (srq == NULL)
srq = Req_New(wrk, sp);
AN(srq);
h2 = WS_Alloc(srq->ws, sizeof *h2);
AN(h2);
INIT_OBJ(h2, H2_SESS_MAGIC);
h2->srq = srq;
h2->htc = srq->htc;
h2->ws = srq->ws;
h2->vsl = srq->vsl;
h2->vsl->wid = sp->vxid;
h2->htc->rfd = &sp->fd;
h2->sess = sp;
VTAILQ_INIT(&h2->streams);
#define H2_SETTINGS(n,v,d) \
do { \
assert(v < H2_SETTINGS_N); \
h2->their_settings[v] = d; \
h2->our_settings[v] = d; \
} while (0);
#include "tbl/h2_settings.h"
#undef H2_SETTINGS
/* XXX: Lacks a VHT_Fini counterpart. Will leak memory. */
AZ(VHT_Init(h2->dectbl,
h2->our_settings[H2S_HEADER_TABLE_SIZE]));
SES_Reserve_xport_priv(sp, &up);
*up = (uintptr_t)h2;
}
AN(up);
CAST_OBJ_NOTNULL(h2, (void*)(*up), H2_SESS_MAGIC);
return (h2);
}
/**********************************************************************
*/
static struct h2_req *
h2_new_req(const struct worker *wrk, struct h2_sess *h2,
unsigned stream, struct req *req)
{
struct h2_req *r2;
Lck_AssertHeld(&h2->sess->mtx);
if (req == NULL)
req = Req_New(wrk, h2->sess);
CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
r2 = WS_Alloc(req->ws, sizeof *r2);
AN(r2);
INIT_OBJ(r2, H2_REQ_MAGIC);
r2->state = H2_S_IDLE;
r2->h2sess = h2;
r2->stream = stream;
r2->req = req;
req->transport_priv = r2;
// XXX: ordering ?
VTAILQ_INSERT_TAIL(&h2->streams, r2, list);
h2->refcnt++;
return (r2);
}
static void
h2_del_req(struct worker *wrk, struct h2_req *r2)
{
struct h2_sess *h2;
struct sess *sp;
struct req *req;
int r;
h2 = r2->h2sess;
sp = h2->sess;
Lck_Lock(&sp->mtx);
assert(h2->refcnt > 0);
r = --h2->refcnt;
/* XXX: PRIORITY reshuffle */
VTAILQ_REMOVE(&h2->streams, r2, list);
Lck_Unlock(&sp->mtx);
Req_Cleanup(sp, wrk, r2->req);
Req_Release(r2->req);
if (r)
return;
/* All streams gone, including stream #0, clean up */
req = h2->srq;
Req_Cleanup(sp, wrk, req);
Req_Release(req);
SES_Delete(sp, SC_RX_JUNK, NAN);
}
/**********************************************************************
* Update and VSL a single SETTING rx'ed from the other side
* 'd' must point to six bytes.
*/
static void
h2_setting(struct h2_sess *h2, const uint8_t *d)
{
uint16_t x;
uint32_t y;
const char *n;
char nb[8];
x = vbe16dec(d);
y = vbe32dec(d + 2);
n = h2_settingname((enum h2setting)x);
if (n == NULL) {
bprintf(nb, "0x%04x", x);
n = nb;
}
VSLb(h2->vsl, SLT_Debug, "H2SETTING %s 0x%08x", n, y);
if (x > 0 && x < H2_SETTINGS_N)
h2->their_settings[x] = y;
}
/**********************************************************************
* Incoming HEADERS, this is where the partys at...
*/
static void __match_proto__(task_func_t)
h2_do_req(struct worker *wrk, void *priv)
{
struct req *req;
struct h2_req *r2;
CAST_OBJ_NOTNULL(req, priv, REQ_MAGIC);
CAST_OBJ_NOTNULL(r2, req->transport_priv, H2_REQ_MAGIC);
THR_SetRequest(req);
if (!CNT_GotReq(wrk, req))
assert(CNT_Request(wrk, req) != REQ_FSM_DISEMBARK);
THR_SetRequest(NULL);
VSL(SLT_Debug, 0, "H2REQ CNT done");
/* XXX clean up req */
r2->state = H2_S_CLOSED;
h2_del_req(wrk, r2);
}
/**********************************************************************/
enum htc_status_e __match_proto__(htc_complete_f)
H2_prism_complete(struct http_conn *htc)
{
int l;
CHECK_OBJ_NOTNULL(htc, HTTP_CONN_MAGIC);
l = htc->rxbuf_e - htc->rxbuf_b;
if (l >= sizeof(H2_prism) &&
!memcmp(htc->rxbuf_b, H2_prism, sizeof(H2_prism)))
return (HTC_S_COMPLETE);
if (l < sizeof(H2_prism) && !memcmp(htc->rxbuf_b, H2_prism, l))
return (HTC_S_MORE);
return (HTC_S_JUNK);
}
/**********************************************************************
* Deal with the base64url (NB: ...url!) encoded SETTINGS in the H1 req
* of a H2C upgrade.
*/
static int
h2_b64url_settings(struct h2_sess *h2, struct req *req)
{
const char *p, *q;
uint8_t u[6], *up;
unsigned x;
int i, n;
static const char s[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789"
"-_=";
/*
* If there is trouble with this, we could reject the upgrade
* but putting this on the H1 side is just plain wrong...
*/
AN(http_GetHdr(req->http, H_HTTP2_Settings, &p));
if (p == NULL)
return (-1);
VSLb(req->vsl, SLT_Debug, "H2CS %s", p);
n = 0;
x = 0;
up = u;
for (;*p; p++) {
q = strchr(s, *p);
if (q == NULL)
return (-1);
i = q - s;
assert(i >= 0 && i <= 63);
x <<= 6;
x |= i;
n += 6;
if (n < 8)
continue;
*up++ = (uint8_t)(x >> (n - 8));
n -= 8;
if (up == u + sizeof u) {
AZ(n);
h2_setting(h2, (void*)u);
up = u;
}
}
if (up != u)
return (-1);
return (0);
}
/**********************************************************************/
static int
h2_new_pu_session(struct worker *wrk, const struct h2_sess *h2)
{
enum htc_status_e hs;
(void)wrk;
hs = H2_prism_complete(h2->htc);
if (hs == HTC_S_MORE) {
VSLb(h2->vsl, SLT_Debug, "Short pu PRISM");
return (0);
}
if (hs != HTC_S_COMPLETE) {
VSLb(h2->vsl, SLT_Debug, "Wrong pu PRISM");
return (0);
}
HTC_RxPipeline(h2->htc, h2->htc->rxbuf_b + sizeof(H2_prism));
HTC_RxInit(h2->htc, wrk->aws);
VSLb(h2->vsl, SLT_Debug, "H2: Got pu PRISM");
return (1);
}
/**********************************************************************/
static int
h2_new_ou_session(struct worker *wrk, struct h2_sess *h2,
struct req *req)
{
ssize_t sz;
enum htc_status_e hs;
sz = write(h2->sess->fd, h2_resp_101, strlen(h2_resp_101));
assert(sz == strlen(h2_resp_101));
AZ(h2_b64url_settings(h2, req));
http_Unset(req->http, H_Upgrade);
http_Unset(req->http, H_HTTP2_Settings);
/* Steal pipelined read-ahead, if any */
h2->htc->pipeline_b = req->htc->pipeline_b;
h2->htc->pipeline_e = req->htc->pipeline_e;
req->htc->pipeline_b = NULL;
req->htc->pipeline_e = NULL;
/* XXX: This call may assert on buffer overflow if the pipelined
data exceeds the available space in the aws workspace. What to
do about the overflowing data is an open issue. */
HTC_RxInit(h2->htc, wrk->aws);
/* Start req thread */
(void)h2_new_req(wrk, h2, 1, req);
req->req_step = R_STP_RECV;
req->transport = &H2_transport;
req->task.func = h2_do_req;
req->task.priv = req;
req->err_code = 0;
http_SetH(req->http, HTTP_HDR_PROTO, "HTTP/2.0");
XXXAZ(Pool_Task(wrk->pool, &req->task, TASK_QUEUE_REQ));
/* Wait for PRISM response */
hs = HTC_RxStuff(h2->htc, H2_prism_complete,
NULL, NULL, NAN, h2->sess->t_idle + cache_param->timeout_idle, 256);
if (hs != HTC_S_COMPLETE) {
/* XXX clean up req thread */
VSLb(h2->vsl, SLT_Debug, "H2: No OU PRISM (hs=%d)", hs);
Req_Release(req);
Lck_Unlock(&h2->sess->mtx);
SES_Delete(h2->sess, SC_RX_JUNK, NAN);
return (0);
}
HTC_RxPipeline(h2->htc, h2->htc->rxbuf_b + sizeof(H2_prism));
HTC_RxInit(h2->htc, wrk->aws);
VSLb(h2->vsl, SLT_Debug, "H2: Got PRISM");
return (1);
}
static void __match_proto__(task_func_t)
h2_new_session(struct worker *wrk, void *arg)
{
struct req *req;
struct sess *sp;
struct h2_sess *h2;
struct h2_req *r2, *r22;
uintptr_t wsp;
CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
CAST_OBJ_NOTNULL(req, arg, REQ_MAGIC);
sp = req->sp;
CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
assert(req->transport == &H2_transport);
wsp = WS_Snapshot(wrk->aws);
switch(req->err_code) {
case 0:
/* Direct H2 connection (via Proxy) */
h2 = h2_new_sess(wrk, sp, req);
Lck_Lock(&h2->sess->mtx);
(void)h2_new_req(wrk, h2, 0, NULL);
break;
case 1:
/* Prior Knowledge H1->H2 upgrade */
h2 = h2_new_sess(wrk, sp, req);
Lck_Lock(&h2->sess->mtx);
(void)h2_new_req(wrk, h2, 0, NULL);
if (!h2_new_pu_session(wrk, h2))
return;
break;
case 2:
/* Optimistic H1->H2 upgrade */
h2 = h2_new_sess(wrk, sp, NULL);
Lck_Lock(&h2->sess->mtx);
(void)h2_new_req(wrk, h2, 0, NULL);
if (!h2_new_ou_session(wrk, h2, req))
return;
break;
default:
WRONG("Bad req->err_code");
}
THR_SetRequest(h2->srq);
H2_Send_Frame(wrk, h2,
H2_FRAME_SETTINGS, H2FF_NONE, sizeof H2_settings, 0, H2_settings);
/* and off we go... */
h2->cond = &wrk->cond;
Lck_Unlock(&h2->sess->mtx);
while (h2_rxframe(wrk, h2)) {
WS_Reset(wrk->aws, wsp);
HTC_RxInit(h2->htc, wrk->aws);
}
/* Delete all idle streams */
VTAILQ_FOREACH_SAFE(r2, &h2->streams, list, r22) {
if (r2->state == H2_S_IDLE)
h2_del_req(wrk, r2);
}
h2->cond = NULL;
}
struct transport H2_transport = {
.name = "H2",
.magic = TRANSPORT_MAGIC,
.new_session = h2_new_session,
.sess_panic = h2_sess_panic,
.deliver = h2_deliver,
.req_body = h2_req_body,
.minimal_response = h2_minimal_response,
};
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