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 6000
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 6000
};
54
55
static size_t
56 6000
h2_enc_settings(const struct h2_settings *h2s, uint8_t *buf, ssize_t n)
57
{
58 6000
        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 6000
                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 6000
#define H2_SETTING(U,l,v,d,...) . l = d,
75
#include "tbl/h2_settings.h"
76
};
77
78
static void
79 6160
h2_local_settings(struct h2_settings *h2s)
80
{
81 6160
        *h2s = H2_proto_settings;
82
#define H2_SETTINGS_PARAM_ONLY
83
#define H2_SETTING(U, l, ...)                   \
84 6000
        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 10457
H2S_Lock_VSLb(const struct h2_sess *h2, enum VSL_tag_e tag, const char *fmt, ...)
92
{
93
        va_list ap;
94 10457
        int held = 0;
95 6000
96 10457
        AN(h2);
97
98 10457
        if (VSL_tag_is_masked(tag))
99 0
                return;
100
101 10457
        if (h2->highest_stream > 0) {
102 9457
                held = 1;
103 9457
                Lck_Lock(&h2->sess->mtx);
104 9457
        }
105 6000
106 10457
        va_start(ap, fmt);
107 10457
        VSLbv(h2->vsl, tag, fmt, ap);
108 10457
        va_end(ap);
109
110 10457
        if (held)
111 9457
                Lck_Unlock(&h2->sess->mtx);
112 10457
}
113
114
/**********************************************************************
115 6000
 * 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 6160
h2_init_sess(struct sess *sp,
123
    struct h2_sess *h2s, struct req *srq, struct h2h_decode *decode)
124
{
125 6000
        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 6160
        AZ(SES_Get_proto_priv(sp, &up));
131 6160
        assert(*up == 0);
132
133 6160
        if (srq == NULL)
134 280
                srq = Req_New(sp, NULL);
135 6160
        AN(srq);
136 6160
        h2 = h2s;
137 6160
        AN(h2);
138 6160
        INIT_OBJ(h2, H2_SESS_MAGIC);
139 6160
        h2->srq = srq;
140 6160
        h2->htc = srq->htc;
141 6160
        h2->ws = srq->ws;
142 6160
        h2->vsl = srq->vsl;
143 6160
        VSL_Flush(h2->vsl, 0);
144 6160
        h2->vsl->wid = sp->vxid;
145 6160
        h2->htc->rfd = &sp->fd;
146 6160
        h2->sess = sp;
147 6160
        h2->rxthr = pthread_self();
148 6160
        PTOK(pthread_cond_init(h2->winupd_cond, NULL));
149 6160
        VTAILQ_INIT(&h2->streams);
150 6160
        VTAILQ_INIT(&h2->txqueue);
151 6160
        h2_local_settings(&h2->local_settings);
152 6160
        h2->remote_settings = H2_proto_settings;
153 6160
        h2->decode = decode;
154
155 6160
        h2->rapid_reset = cache_param->h2_rapid_reset;
156 6160
        h2->rapid_reset_limit = cache_param->h2_rapid_reset_limit;
157 6160
        h2->rapid_reset_period = cache_param->h2_rapid_reset_period;
158
159 6160
        h2->rst_budget = h2->rapid_reset_limit;
160 6160
        h2->last_rst = sp->t_open;
161 6160
        AZ(isnan(h2->last_rst));
162
163 6160
        AZ(VHT_Init(h2->dectbl, h2->local_settings.header_table_size));
164
165 6160
        *up = (uintptr_t)h2;
166
167 6160
        return (h2);
168
}
169
170
static void
171 6112
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 6112
        CHECK_OBJ_NOTNULL(h2, H2_SESS_MAGIC);
177 6112
        AZ(h2->refcnt);
178 6112
        assert(VTAILQ_EMPTY(&h2->streams));
179 6112
        AN(reason);
180
181 6112
        VHT_Fini(h2->dectbl);
182 6112
        PTOK(pthread_cond_destroy(h2->winupd_cond));
183 6112
        TAKE_OBJ_NOTNULL(req, &h2->srq, REQ_MAGIC);
184 6112
        assert(!WS_IsReserved(req->ws));
185 6112
        sp = h2->sess;
186 6112
        Req_Cleanup(sp, wrk, req);
187 6112
        Req_Release(req);
188 6112
        SES_Delete(sp, reason, NAN);
189 6112
}
190
191
/**********************************************************************/
192
193
enum htc_status_e v_matchproto_(htc_complete_f)
194 383525
H2_prism_complete(struct http_conn *htc)
195
{
196
        size_t sz;
197
198 383525
        CHECK_OBJ_NOTNULL(htc, HTTP_CONN_MAGIC);
199 383525
        sz = sizeof(H2_prism);
200 383525
        if (htc->rxbuf_b + sz > htc->rxbuf_e)
201 8263
                sz = htc->rxbuf_e - htc->rxbuf_b;
202 383525
        if (memcmp(htc->rxbuf_b, H2_prism, sz))
203 359127
                return (HTC_S_JUNK);
204 24398
        return (sz == sizeof(H2_prism) ? HTC_S_COMPLETE : HTC_S_MORE);
205 383525
}
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 280
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 280
        if (!http_GetHdr(req->http, H_HTTP2_Settings, &p))
231 40
                return (-1);
232 240
        AN(p);
233 240
        VSLb(req->vsl, SLT_Debug, "H2CS %s", p);
234
235 240
        n = 0;
236 240
        x = 0;
237 240
        up = u;
238 3440
        for (;*p; p++) {
239 3240
                q = strchr(s, *p);
240 3240
                if (q == NULL)
241 40
                        return (-1);
242 3200
                i = q - s;
243 3200
                assert(i >= 0 && i <= 64);
244 3200
                x <<= 6;
245 3200
                x |= i;
246 3200
                n += 6;
247 3200
                if (n < 8)
248 800
                        continue;
249 2400
                *up++ = (uint8_t)(x >> (n - 8));
250 2400
                n -= 8;
251 2400
                if (up == u + sizeof u) {
252 400
                        AZ(n);
253 400
                        if (h2_set_setting(h2, (void*)u))
254 0
                                return (-1);
255 400
                        up = u;
256 400
                }
257 2400
        }
258 200
        if (up != u)
259 0
                return (-1);
260 200
        return (0);
261 280
}
262
263
264
/**********************************************************************/
265
266
static int
267 80
h2_ou_rel(struct worker *wrk, struct req *req)
268
{
269 80
        CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
270 80
        CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
271 80
        AZ(req->vcl);
272 80
        Req_AcctLogCharge(wrk->stats, req);
273 80
        Req_Release(req);
274 80
        return (0);
275
}
276
277
static int
278 280
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 280
        if (h2_b64url_settings(h2, req)) {
286 80
                VSLb(h2->vsl, SLT_Debug, "H2: Bad HTTP-Settings");
287 80
                return (h2_ou_rel(wrk, req));
288
        }
289
290 200
        sz = write(h2->sess->fd, h2_resp_101, strlen(h2_resp_101));
291 200
        VTCP_Assert(sz);
292 200
        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 200
        http_Unset(req->http, H_Upgrade);
299 200
        http_Unset(req->http, H_HTTP2_Settings);
300
301
        /* Steal pipelined read-ahead, if any */
302 200
        h2->htc->pipeline_b = req->htc->pipeline_b;
303 200
        h2->htc->pipeline_e = req->htc->pipeline_e;
304 200
        req->htc->pipeline_b = NULL;
305 200
        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 200
        HTC_RxInit(h2->htc, h2->ws);
310
311
        /* Start req thread */
312 200
        r2 = h2_new_req(h2, 1, req);
313 200
        AZ(h2->highest_stream);
314 200
        h2->highest_stream = r2->stream;
315 200
        req->transport = &HTTP2_transport;
316 200
        assert(req->req_step == R_STP_TRANSPORT);
317 200
        req->task->func = h2_do_req;
318 200
        req->task->priv = req;
319 200
        r2->scheduled = 1;
320 200
        r2->state = H2_S_CLOS_REM; // rfc7540,l,489,491
321 200
        req->err_code = 0;
322 200
        http_SetH(req->http, HTTP_HDR_PROTO, "HTTP/2.0");
323
324
        /* Wait for PRISM response */
325 400
        hs = HTC_RxStuff(h2->htc, H2_prism_complete,
326 200
            NULL, NULL, NAN, h2->sess->t_idle + cache_param->timeout_idle, NAN,
327
            sizeof H2_prism);
328 200
        if (hs != HTC_S_COMPLETE) {
329 40
                VSLb(h2->vsl, SLT_Debug, "H2: No/Bad OU PRISM (hs=%d)", hs);
330 40
                r2->scheduled = 0;
331 40
                h2_del_req(wrk, r2);
332 40
                return (0);
333
        }
334 160
        if (Pool_Task(wrk->pool, req->task, TASK_QUEUE_REQ)) {
335 40
                r2->scheduled = 0;
336 40
                h2_del_req(wrk, r2);
337 40
                VSLb(h2->vsl, SLT_Debug, "H2: No Worker-threads");
338 40
                return (0);
339
        }
340 120
        return (1);
341 280
}
342
343
/**********************************************************************
344
 */
345
346
#define H2_PU_MARKER    1
347
#define H2_OU_MARKER    2
348
349
void
350 5880
H2_PU_Sess(struct worker *wrk, struct sess *sp, struct req *req)
351
{
352 5880
        VSL(SLT_Debug, sp->vxid, "H2 Prior Knowledge Upgrade");
353 5880
        req->err_code = H2_PU_MARKER;
354 5880
        SES_SetTransport(wrk, sp, req, &HTTP2_transport);
355 5880
}
356
357
void
358 280
H2_OU_Sess(struct worker *wrk, struct sess *sp, struct req *req)
359
{
360 280
        VSL(SLT_Debug, sp->vxid, "H2 Optimistic Upgrade");
361 280
        req->err_code = H2_OU_MARKER;
362 280
        SES_SetTransport(wrk, sp, req, &HTTP2_transport);
363 280
}
364
365
static void v_matchproto_(task_func_t)
366 6160
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 6160
        CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
379 6160
        CAST_OBJ_NOTNULL(req, arg, REQ_MAGIC);
380 6160
        sp = req->sp;
381 6160
        CHECK_OBJ_NOTNULL(sp, SESS_MAGIC);
382
383 6160
        if (wrk->wpriv->vcl)
384 996
                VCL_Rel(&wrk->wpriv->vcl);
385
386 6160
        assert(req->transport == &HTTP2_transport);
387
388 6160
        assert (req->err_code == H2_PU_MARKER || req->err_code == H2_OU_MARKER);
389
390 12320
        h2 = h2_init_sess(sp, &h2s,
391 6160
            req->err_code == H2_PU_MARKER ? req : NULL, &decode);
392 6160
        h2->req0 = h2_new_req(h2, 0, NULL);
393 6160
        AZ(h2->htc->priv);
394 6160
        h2->htc->priv = h2;
395
396 6160
        AZ(wrk->vsl);
397 6160
        wrk->vsl = h2->vsl;
398
399 6160
        if (req->err_code == H2_OU_MARKER && !h2_ou_session(wrk, h2, req)) {
400 160
                assert(h2->refcnt == 1);
401 160
                h2_del_req(wrk, h2->req0);
402 160
                h2_del_sess(wrk, h2, SC_RX_JUNK);
403 160
                wrk->vsl = NULL;
404 160
                return;
405
        }
406 6000
        assert(HTC_S_COMPLETE == H2_prism_complete(h2->htc));
407 6000
        HTC_RxPipeline(h2->htc, h2->htc->rxbuf_b + sizeof(H2_prism));
408 6000
        HTC_RxInit(h2->htc, h2->ws);
409 6000
        AN(WS_Reservation(h2->ws));
410 6000
        VSLb(h2->vsl, SLT_Debug, "H2: Got pu PRISM");
411
412 6000
        THR_SetRequest(h2->srq);
413 6000
        AN(WS_Reservation(h2->ws));
414
415 6000
        l = h2_enc_settings(&h2->local_settings, settings, sizeof (settings));
416 6000
        AN(WS_Reservation(h2->ws));
417 6000
        H2_Send_Get(wrk, h2, h2->req0);
418 6000
        AN(WS_Reservation(h2->ws));
419 12000
        H2_Send_Frame(wrk, h2,
420 6000
            H2_F_SETTINGS, H2FF_NONE, l, 0, settings);
421 6000
        AN(WS_Reservation(h2->ws));
422 6000
        H2_Send_Rel(h2, h2->req0);
423 6000
        AN(WS_Reservation(h2->ws));
424
425
        /* and off we go... */
426 6000
        h2->cond = &wrk->cond;
427
428 43706
        while (h2_rxframe(wrk, h2)) {
429 37706
                HTC_RxInit(h2->htc, h2->ws);
430 37706
                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 37706
                AN(WS_Reservation(h2->ws));
436
        }
437
438 6000
        assert(!WS_IsReserved(h2->ws));
439 6000
        AN(h2->error);
440
441
        /* Delete all idle streams */
442 6000
        Lck_Lock(&h2->sess->mtx);
443 6000
        VSLb(h2->vsl, SLT_Debug, "H2 CLEANUP %s", h2->error->name);
444 16459
        VTAILQ_FOREACH(r2, &h2->streams, list) {
445 10459
                if (r2->error == 0)
446 9855
                        r2->error = h2->error;
447 10459
                if (r2->cond != NULL)
448 163
                        PTOK(pthread_cond_signal(r2->cond));
449 10459
        }
450 6000
        PTOK(pthread_cond_broadcast(h2->winupd_cond));
451 6000
        Lck_Unlock(&h2->sess->mtx);
452 10639
        while (1) {
453 10639
                again = 0;
454 27625
                VTAILQ_FOREACH_SAFE(r2, &h2->streams, list, r22) {
455 16986
                        if (r2 != h2->req0) {
456 6395
                                h2_kill_req(wrk, h2, r2, h2->error);
457 6395
                                again++;
458 6395
                        }
459 16986
                }
460 10639
                if (!again)
461 6000
                        break;
462 4639
                Lck_Lock(&h2->sess->mtx);
463 11174
                VTAILQ_FOREACH(r2, &h2->streams, list)
464 13070
                        VSLb(h2->vsl, SLT_Debug, "ST %u %d",
465 6535
                            r2->stream, r2->state);
466 4639
                (void)Lck_CondWaitTimeout(h2->cond, &h2->sess->mtx, .1);
467 4639
                Lck_Unlock(&h2->sess->mtx);
468
        }
469 6000
        h2->cond = NULL;
470 6000
        assert(h2->refcnt == 1);
471 6000
        h2_del_req(wrk, h2->req0);
472 6000
        h2_del_sess(wrk, h2, h2->error->reason);
473 6000
        wrk->vsl = NULL;
474 6160
}
475
476
static int v_matchproto_(vtr_poll_f)
477 18761
h2_poll(struct req *req)
478
{
479
        struct h2_req *r2;
480
481 18761
        CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
482 18761
        CAST_OBJ_NOTNULL(r2, req->transport_priv, H2_REQ_MAGIC);
483 18761
        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
};