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