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