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