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