varnish-cache/bin/varnishd/http2/cache_http2_session.c
1
/*-
2
 * Copyright (c) 2016 Varnish Software AS
3
 * All rights reserved.
4
 *
5
 * Author: Poul-Henning Kamp <phk@phk.freebsd.dk>
6
 *
7
 * Redistribution and use in source and binary forms, with or without
8
 * modification, are permitted provided that the following conditions
9
 * are met:
10
 * 1. Redistributions of source code must retain the above copyright
11
 *    notice, this list of conditions and the following disclaimer.
12
 * 2. Redistributions in binary form must reproduce the above copyright
13
 *    notice, this list of conditions and the following disclaimer in the
14
 *    documentation and/or other materials provided with the distribution.
15
 *
16
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19
 * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
20
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26
 * SUCH DAMAGE.
27
 *
28
 */
29
30
#include "config.h"
31
32
#include "cache/cache_varnishd.h"
33
34
#include <errno.h>
35
#include <stdio.h>
36
37
#include "cache/cache_transport.h"
38
#include "http2/cache_http2.h"
39
40
#include "vend.h"
41
#include "vtim.h"
42
43
static const char h2_resp_101[] =
44
        "HTTP/1.1 101 Switching Protocols\r\n"
45
        "Connection: Upgrade\r\n"
46
        "Upgrade: h2c\r\n"
47
        "\r\n";
48
49
static const char H2_prism[24] = {
50
        0x50, 0x52, 0x49, 0x20, 0x2a, 0x20, 0x48, 0x54,
51
        0x54, 0x50, 0x2f, 0x32, 0x2e, 0x30, 0x0d, 0x0a,
52
        0x0d, 0x0a, 0x53, 0x4d, 0x0d, 0x0a, 0x0d, 0x0a
53
};
54
55
static size_t
56 284
h2_enc_settings(const struct h2_settings *h2s, uint8_t *buf, size_t n)
57
{
58 284
        size_t len = 0;
59
60
#define H2_SETTING(U,l,v,d,...)                         \
61
        if (h2s->l != d) {                              \
62
                len += 6;                               \
63
                assert(len <= n);                       \
64
                vbe16enc(buf, v);                       \
65
                buf += 2;                               \
66
                vbe32enc(buf, h2s->l);                  \
67
                buf += 4;                               \
68
        }
69
#include "tbl/h2_settings.h"
70 284
        return (len);
71
}
72
73
static const struct h2_settings H2_proto_settings = {
74
#define H2_SETTING(U,l,v,d,...) . l = d,
75
#include "tbl/h2_settings.h"
76
};
77
78
static void
79 296
h2_local_settings(struct h2_settings *h2s)
80
{
81 296
        *h2s = H2_proto_settings;
82
#define H2_SETTINGS_PARAM_ONLY
83
#define H2_SETTING(U, l, ...)                   \
84
        h2s->l = cache_param->h2_##l;
85
#include "tbl/h2_settings.h"
86
#undef H2_SETTINGS_PARAM_ONLY
87 296
}
88
89
/**********************************************************************
90
 * The h2_sess struct needs many of the same things as a request,
91
 * WS, VSL, HTC &c,  but rather than implement all that stuff over, we
92
 * grab an actual struct req, and mirror the relevant fields into
93
 * struct h2_sess.
94
 */
95
96
static struct h2_sess *
97 296
h2_init_sess(const struct worker *wrk, struct sess *sp,
98
    struct h2_sess *h2s, struct req *srq, struct h2h_decode *decode)
99
{
100
        uintptr_t *up;
101
        struct h2_sess *h2;
102
103 296
        if (SES_Get_proto_priv(sp, &up)) {
104
                /* Already reserved if we came via H1 */
105 0
                SES_Reserve_proto_priv(sp, &up);
106 0
                *up = 0;
107
        }
108 296
        if (*up == 0) {
109 296
                if (srq == NULL)
110 20
                        srq = Req_New(wrk, sp);
111 296
                AN(srq);
112 296
                h2 = h2s;
113 296
                AN(h2);
114 296
                INIT_OBJ(h2, H2_SESS_MAGIC);
115 296
                h2->srq = srq;
116 296
                h2->htc = srq->htc;
117 296
                h2->ws = srq->ws;
118 296
                h2->vsl = srq->vsl;
119 296
                VSL_Flush(h2->vsl, 0);
120 296
                h2->vsl->wid = sp->vxid;
121 296
                h2->htc->rfd = &sp->fd;
122 296
                h2->sess = sp;
123 296
                h2->rxthr = pthread_self();
124 296
                AZ(pthread_cond_init(h2->winupd_cond, NULL));
125 296
                VTAILQ_INIT(&h2->streams);
126 296
                VTAILQ_INIT(&h2->txqueue);
127 296
                h2_local_settings(&h2->local_settings);
128 296
                h2->remote_settings = H2_proto_settings;
129 296
                h2->decode = decode;
130
131 296
                AZ(VHT_Init(h2->dectbl,
132
                        h2->local_settings.header_table_size));
133
134 296
                SES_Reserve_proto_priv(sp, &up);
135 296
                *up = (uintptr_t)h2;
136
        }
137 296
        AN(up);
138 296
        CAST_OBJ_NOTNULL(h2, (void*)(*up), H2_SESS_MAGIC);
139 296
        return (h2);
140
}
141
142
static void
143 284
h2_del_sess(struct worker *wrk, struct h2_sess *h2, enum sess_close reason)
144
{
145
        struct sess *sp;
146
        struct req *req;
147
148 284
        CHECK_OBJ_NOTNULL(h2, H2_SESS_MAGIC);
149 284
        AZ(h2->refcnt);
150 284
        assert(VTAILQ_EMPTY(&h2->streams));
151
152 284
        VHT_Fini(h2->dectbl);
153 284
        AZ(pthread_cond_destroy(h2->winupd_cond));
154 284
        req = h2->srq;
155 284
        AZ(req->ws->r);
156 284
        sp = h2->sess;
157 284
        Req_Cleanup(sp, wrk, req);
158 284
        Req_Release(req);
159 284
        SES_Delete(sp, reason, NAN);
160 284
}
161
162
/**********************************************************************/
163
164
enum htc_status_e v_matchproto_(htc_complete_f)
165 26387
H2_prism_complete(struct http_conn *htc)
166
{
167
        int l;
168
169 26387
        CHECK_OBJ_NOTNULL(htc, HTTP_CONN_MAGIC);
170 26387
        l = htc->rxbuf_e - htc->rxbuf_b;
171 52195
        if (l >= sizeof(H2_prism) &&
172 25808
            !memcmp(htc->rxbuf_b, H2_prism, sizeof(H2_prism)))
173 860
                return (HTC_S_COMPLETE);
174 25527
        if (l < sizeof(H2_prism) && !memcmp(htc->rxbuf_b, H2_prism, l))
175 304
                return (HTC_S_MORE);
176 25223
        return (HTC_S_JUNK);
177
}
178
179
180
/**********************************************************************
181
 * Deal with the base64url (NB: ...url!) encoded SETTINGS in the H1 req
182
 * of a H2C upgrade.
183
 */
184
185
static int
186 20
h2_b64url_settings(struct h2_sess *h2, struct req *req)
187
{
188
        const char *p, *q;
189
        uint8_t u[6], *up;
190
        unsigned x;
191
        int i, n;
192
        static const char s[] =
193
            "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
194
            "abcdefghijklmnopqrstuvwxyz"
195
            "0123456789"
196
            "-_=";
197
198
        /*
199
         * If there is trouble with this, we could reject the upgrade
200
         * but putting this on the H1 side is just plain wrong...
201
         */
202 20
        if (!http_GetHdr(req->http, H_HTTP2_Settings, &p))
203 4
                return (-1);
204 16
        AN(p);
205 16
        VSLb(req->vsl, SLT_Debug, "H2CS %s", p);
206
207 16
        n = 0;
208 16
        x = 0;
209 16
        up = u;
210 208
        for (;*p; p++) {
211 196
                q = strchr(s, *p);
212 196
                if (q == NULL)
213 4
                        return (-1);
214 192
                i = q - s;
215 192
                assert(i >= 0 && i <= 64);
216 192
                x <<= 6;
217 192
                x |= i;
218 192
                n += 6;
219 192
                if (n < 8)
220 48
                        continue;
221 144
                *up++ = (uint8_t)(x >> (n - 8));
222 144
                n -= 8;
223 144
                if (up == u + sizeof u) {
224 24
                        AZ(n);
225 24
                        if (h2_set_setting(h2, (void*)u))
226 0
                                return (-1);
227 24
                        up = u;
228
                }
229
        }
230 12
        if (up != u)
231 0
                return (-1);
232 12
        return (0);
233
}
234
235
236
/**********************************************************************/
237
238
static int
239 8
h2_ou_rel(struct worker *wrk, struct req *req)
240
{
241 8
        CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
242 8
        CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
243 8
        AN (req->vcl);
244 8
        VCL_Rel(&req->vcl);
245 8
        Req_AcctLogCharge(wrk->stats, req);
246 8
        Req_Release(req);
247 8
        return (0);
248
}
249
250
static int
251 20
h2_ou_session(struct worker *wrk, struct h2_sess *h2,
252
    struct req *req)
253
{
254
        ssize_t sz;
255
        enum htc_status_e hs;
256
        struct h2_req *r2;
257
258 20
        if (h2_b64url_settings(h2, req)) {
259 8
                VSLb(h2->vsl, SLT_Debug, "H2: Bad HTTP-Settings");
260 8
                return (h2_ou_rel(wrk, req));
261
        }
262
263 12
        sz = write(h2->sess->fd, h2_resp_101, strlen(h2_resp_101));
264 12
        if (sz != strlen(h2_resp_101)) {
265 0
                VSLb(h2->vsl, SLT_Debug, "H2: Upgrade: Error writing 101"
266 0
                    " response: %s\n", strerror(errno));
267 0
                return (h2_ou_rel(wrk, req));
268
        }
269
270 12
        http_Unset(req->http, H_Upgrade);
271 12
        http_Unset(req->http, H_HTTP2_Settings);
272
273
        /* Steal pipelined read-ahead, if any */
274 12
        h2->htc->pipeline_b = req->htc->pipeline_b;
275 12
        h2->htc->pipeline_e = req->htc->pipeline_e;
276 12
        req->htc->pipeline_b = NULL;
277 12
        req->htc->pipeline_e = NULL;
278
        /* XXX: This call may assert on buffer overflow if the pipelined
279
           data exceeds the available space in the ws workspace. What to
280
           do about the overflowing data is an open issue. */
281 12
        HTC_RxInit(h2->htc, h2->ws);
282
283
        /* Start req thread */
284 12
        r2 = h2_new_req(wrk, h2, 1, req);
285 12
        req->transport = &H2_transport;
286 12
        req->req_step = R_STP_TRANSPORT;
287 12
        req->task.func = h2_do_req;
288 12
        req->task.priv = req;
289 12
        r2->scheduled = 1;
290 12
        r2->state = H2_S_CLOS_REM; // rfc7540,l,489,491
291 12
        req->err_code = 0;
292 12
        http_SetH(req->http, HTTP_HDR_PROTO, "HTTP/2.0");
293
294
        /* Wait for PRISM response */
295 12
        hs = HTC_RxStuff(h2->htc, H2_prism_complete,
296 12
            NULL, NULL, NAN, h2->sess->t_idle + cache_param->timeout_idle,
297
            sizeof H2_prism);
298 12
        if (hs != HTC_S_COMPLETE) {
299 4
                VSLb(h2->vsl, SLT_Debug, "H2: No/Bad OU PRISM (hs=%d)", hs);
300 4
                r2->scheduled = 0;
301 4
                h2_del_req(wrk, r2);
302 4
                return (0);
303
        }
304 8
        XXXAZ(Pool_Task(wrk->pool, &req->task, TASK_QUEUE_REQ));
305 8
        return (1);
306
}
307
308
/**********************************************************************
309
 */
310
311
#define H2_PU_MARKER    1
312
#define H2_OU_MARKER    2
313
314
void
315 276
H2_PU_Sess(struct worker *wrk, struct sess *sp, struct req *req)
316
{
317 276
        VSLb(req->vsl, SLT_Debug, "H2 Prior Knowledge Upgrade");
318 276
        req->err_code = H2_PU_MARKER;
319 276
        SES_SetTransport(wrk, sp, req, &H2_transport);
320 276
}
321
322
void
323 20
H2_OU_Sess(struct worker *wrk, struct sess *sp, struct req *req)
324
{
325 20
        VSLb(req->vsl, SLT_Debug, "H2 Optimistic Upgrade");
326 20
        req->err_code = H2_OU_MARKER;
327 20
        SES_SetTransport(wrk, sp, req, &H2_transport);
328 20
}
329
330
static void v_matchproto_(task_func_t)
331 296
h2_new_session(struct worker *wrk, void *arg)
332
{
333
        struct req *req;
334
        struct sess *sp;
335
        struct h2_sess h2s;
336
        struct h2_sess *h2;
337
        struct h2_req *r2, *r22;
338
        int again;
339
        uint8_t settings[48];
340
        struct h2h_decode decode;
341
        size_t l;
342
343 296
        CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
344 296
        CAST_OBJ_NOTNULL(req, arg, REQ_MAGIC);
345 296
        sp = req->sp;
346 296
        CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
347
348 296
        assert(req->transport == &H2_transport);
349
350 296
        assert (req->err_code == H2_PU_MARKER || req->err_code == H2_OU_MARKER);
351
352 296
        h2 = h2_init_sess(wrk, sp, &h2s,
353 296
            req->err_code == H2_PU_MARKER ? req : NULL, &decode);
354 296
        h2->req0 = h2_new_req(wrk, h2, 0, NULL);
355 296
        AZ(h2->htc->priv);
356 296
        h2->htc->priv = h2;
357
358 296
        if (req->err_code == H2_OU_MARKER && !h2_ou_session(wrk, h2, req)) {
359 12
                assert(h2->refcnt == 1);
360 12
                h2_del_req(wrk, h2->req0);
361 12
                h2_del_sess(wrk, h2, SC_RX_JUNK);
362 12
                return;
363
        }
364 284
        assert(HTC_S_COMPLETE == H2_prism_complete(h2->htc));
365 284
        HTC_RxPipeline(h2->htc, h2->htc->rxbuf_b + sizeof(H2_prism));
366 284
        WS_Reset(h2->ws, 0);
367 284
        HTC_RxInit(h2->htc, h2->ws);
368 284
        AN(h2->ws->r);
369 284
        VSLb(h2->vsl, SLT_Debug, "H2: Got pu PRISM");
370
371 284
        THR_SetRequest(h2->srq);
372 283
        AN(h2->ws->r);
373
374 283
        l = h2_enc_settings(&h2->local_settings, settings, sizeof (settings));
375 284
        AN(h2->ws->r);
376 284
        H2_Send_Get(wrk, h2, h2->req0);
377 284
        AN(h2->ws->r);
378 284
        H2_Send_Frame(wrk, h2,
379
            H2_F_SETTINGS, H2FF_NONE, l, 0, settings);
380 284
        AN(h2->ws->r);
381 284
        H2_Send_Rel(h2, h2->req0);
382 284
        AN(h2->ws->r);
383
384
        /* and off we go... */
385 284
        h2->cond = &wrk->cond;
386
387 1628
        while (h2_rxframe(wrk, h2)) {
388 1060
                WS_Reset(h2->ws, 0);
389 1060
                HTC_RxInit(h2->htc, h2->ws);
390 1060
                if (WS_Overflowed(h2->ws)) {
391 0
                        VSLb(h2->vsl, SLT_Debug, "H2: Empty Rx Workspace");
392 0
                        h2->error = H2CE_INTERNAL_ERROR;
393 0
                        break;
394
                }
395 1060
                AN(h2->ws->r);
396
        }
397
398 280
        AN(h2->error);
399
400
        /* Delete all idle streams */
401 280
        VSLb(h2->vsl, SLT_Debug, "H2 CLEANUP %s", h2->error->name);
402 280
        Lck_Lock(&h2->sess->mtx);
403 760
        VTAILQ_FOREACH(r2, &h2->streams, list) {
404 480
                if (r2->error == 0)
405 476
                        r2->error = h2->error;
406 480
                if (r2->cond != NULL)
407 16
                        AZ(pthread_cond_signal(r2->cond));
408
        }
409 280
        AZ(pthread_cond_broadcast(h2->winupd_cond));
410 280
        Lck_Unlock(&h2->sess->mtx);
411
        while (1) {
412 1488
                again = 0;
413 2416
                VTAILQ_FOREACH_SAFE(r2, &h2->streams, list, r22) {
414 1532
                        if (r2 != h2->req0) {
415 648
                                h2_kill_req(wrk, h2, r2, h2->error);
416 648
                                again++;
417
                        }
418
                }
419 884
                if (!again)
420 272
                        break;
421 612
                Lck_Lock(&h2->sess->mtx);
422 1679
                VTAILQ_FOREACH(r2, &h2->streams, list)
423 1067
                        VSLb(h2->vsl, SLT_Debug, "ST %u %d",
424 1067
                            r2->stream, r2->state);
425 612
                (void)Lck_CondWait(h2->cond, &h2->sess->mtx, VTIM_real() + .1);
426 604
                Lck_Unlock(&h2->sess->mtx);
427
        }
428 272
        h2->cond = NULL;
429 272
        assert(h2->refcnt == 1);
430 272
        h2_del_req(wrk, h2->req0);
431
        /* TODO: proper sess close reason */
432 272
        h2_del_sess(wrk, h2, SC_RX_JUNK);
433
}
434
435
struct transport H2_transport = {
436
        .name =                 "H2",
437
        .magic =                TRANSPORT_MAGIC,
438
        .deliver =              h2_deliver,
439
        .minimal_response =     h2_minimal_response,
440
        .new_session =          h2_new_session,
441
        .req_body =             h2_req_body,
442
        .req_fail =             h2_req_fail,
443
        .sess_panic =           h2_sess_panic,
444
};