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