varnish-cache/bin/varnishd/http2/cache_http2_send.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 <sys/uio.h>
33
34
#include "cache/cache_varnishd.h"
35
36
#include "cache/cache_transport.h"
37
#include "http2/cache_http2.h"
38
39
#include "vend.h"
40
#include "vtim.h"
41
42
static void
43 616
h2_send_get(struct worker *wrk, struct h2_sess *h2, struct h2_req *r2)
44
{
45 616
        CHECK_OBJ_NOTNULL(h2, H2_SESS_MAGIC);
46 616
        CHECK_OBJ_NOTNULL(r2, H2_REQ_MAGIC);
47 616
        CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
48
49 616
        Lck_AssertHeld(&h2->sess->mtx);
50 616
        if (&wrk->cond == h2->cond)
51 248
                ASSERT_RXTHR(h2);
52 616
        r2->wrk = wrk;
53 616
        VTAILQ_INSERT_TAIL(&h2->txqueue, r2, tx_list);
54 1238
        while (VTAILQ_FIRST(&h2->txqueue) != r2)
55 6
                AZ(Lck_CondWait(&wrk->cond, &h2->sess->mtx, 0));
56 616
        r2->wrk = NULL;
57 616
}
58
59
void
60 604
H2_Send_Get(struct worker *wrk, struct h2_sess *h2, struct h2_req *r2)
61
{
62 604
        CHECK_OBJ_NOTNULL(h2, H2_SESS_MAGIC);
63 604
        CHECK_OBJ_NOTNULL(r2, H2_REQ_MAGIC);
64 604
        CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
65
66 604
        Lck_Lock(&h2->sess->mtx);
67 604
        h2_send_get(wrk, h2, r2);
68 604
        Lck_Unlock(&h2->sess->mtx);
69 604
}
70
71
static void
72 616
h2_send_rel(struct h2_sess *h2, const struct h2_req *r2)
73
{
74 616
        CHECK_OBJ_NOTNULL(r2, H2_REQ_MAGIC);
75 616
        CHECK_OBJ_NOTNULL(h2, H2_SESS_MAGIC);
76
77 616
        Lck_AssertHeld(&h2->sess->mtx);
78 616
        assert(VTAILQ_FIRST(&h2->txqueue) == r2);
79 616
        VTAILQ_REMOVE(&h2->txqueue, r2, tx_list);
80 616
        r2 = VTAILQ_FIRST(&h2->txqueue);
81 616
        if (r2 != NULL) {
82 6
                CHECK_OBJ_NOTNULL(r2->wrk, WORKER_MAGIC);
83 6
                AZ(pthread_cond_signal(&r2->wrk->cond));
84
        }
85 616
}
86
87
void
88 604
H2_Send_Rel(struct h2_sess *h2, const struct h2_req *r2)
89
{
90 604
        CHECK_OBJ_NOTNULL(h2, H2_SESS_MAGIC);
91 604
        CHECK_OBJ_NOTNULL(r2, H2_REQ_MAGIC);
92
93 604
        Lck_Lock(&h2->sess->mtx);
94 604
        h2_send_rel(h2, r2);
95 604
        Lck_Unlock(&h2->sess->mtx);
96 604
}
97
98
static void
99 620
h2_mk_hdr(uint8_t *hdr, h2_frame ftyp, uint8_t flags,
100
    uint32_t len, uint32_t stream)
101
{
102
103 620
        AN(hdr);
104 620
        assert(len < (1U << 24));
105 620
        vbe32enc(hdr, len << 8);
106 620
        hdr[3] = ftyp->type;
107 620
        hdr[4] = flags;
108 620
        vbe32enc(hdr + 5, stream);
109 620
}
110
111
/*
112
 * This is the "raw" frame sender, all per stream accounting and
113
 * prioritization must have happened before this is called, and
114
 * the session mtx must be held.
115
 */
116
117
void
118 620
H2_Send_Frame(struct worker *wrk, struct h2_sess *h2,
119
    h2_frame ftyp, uint8_t flags,
120
    uint32_t len, uint32_t stream, const void *ptr)
121
{
122
        uint8_t hdr[9];
123
        ssize_t s;
124
        struct iovec iov[2];
125
126
        (void)wrk;
127
128 620
        AN(ftyp);
129 620
        AZ(flags & ~(ftyp->flags));
130 620
        if (stream == 0)
131 364
                AZ(ftyp->act_szero);
132
        else
133 256
                AZ(ftyp->act_snonzero);
134
135 620
        h2_mk_hdr(hdr, ftyp, flags, len, stream);
136 620
        Lck_Lock(&h2->sess->mtx);
137 620
        VSLb_bin(h2->vsl, SLT_H2TxHdr, 9, hdr);
138 620
        h2->srq->acct.resp_hdrbytes += 9;
139 620
        if (ftyp->overhead)
140 408
                h2->srq->acct.resp_bodybytes += len;
141 620
        Lck_Unlock(&h2->sess->mtx);
142
143 620
        memset(iov, 0, sizeof iov);
144 620
        iov[0].iov_base = (void*)hdr;
145 620
        iov[0].iov_len = sizeof hdr;
146 620
        iov[1].iov_base = TRUST_ME(ptr);
147 620
        iov[1].iov_len = len;
148 620
        s = writev(h2->sess->fd, iov, len == 0 ? 1 : 2);
149 620
        if (s != sizeof hdr + len) {
150
                /*
151
                 * There is no point in being nice here, we will be unable
152
                 * to send a GOAWAY once the code unrolls, so go directly
153
                 * to the finale and be done with it.
154
                 */
155 0
                h2->error = H2CE_PROTOCOL_ERROR;
156 620
        } else if (len > 0) {
157 438
                Lck_Lock(&h2->sess->mtx);
158 438
                VSLb_bin(h2->vsl, SLT_H2TxBody, len, ptr);
159 438
                Lck_Unlock(&h2->sess->mtx);
160
        }
161 620
}
162
163
static int64_t
164 68
h2_win_limit(const struct h2_req *r2, const struct h2_sess *h2)
165
{
166
        int64_t m;
167
168 68
        CHECK_OBJ_NOTNULL(r2, H2_REQ_MAGIC);
169 68
        CHECK_OBJ_NOTNULL(h2, H2_SESS_MAGIC);
170 68
        CHECK_OBJ_NOTNULL(h2->req0, H2_REQ_MAGIC);
171
172 68
        Lck_AssertHeld(&h2->sess->mtx);
173 68
        m = r2->t_window;
174 68
        if (m > h2->req0->t_window)
175 6
                m = h2->req0->t_window;
176 68
        return (m);
177
}
178
179
static void
180 68
h2_win_charge(struct h2_req *r2, const struct h2_sess *h2, uint32_t w)
181
{
182 68
        CHECK_OBJ_NOTNULL(r2, H2_REQ_MAGIC);
183 68
        CHECK_OBJ_NOTNULL(h2, H2_SESS_MAGIC);
184 68
        CHECK_OBJ_NOTNULL(h2->req0, H2_REQ_MAGIC);
185
186 68
        Lck_AssertHeld(&h2->sess->mtx);
187 68
        r2->t_window -= w;
188 68
        h2->req0->t_window -= w;
189 68
}
190
191
static h2_error
192 407
h2_errcheck(const struct h2_req *r2, const struct h2_sess *h2)
193
{
194 407
        CHECK_OBJ_NOTNULL(r2, H2_REQ_MAGIC);
195 407
        CHECK_OBJ_NOTNULL(h2, H2_SESS_MAGIC);
196
197 407
        if (r2->error)
198 34
                return (r2->error);
199 373
        if (h2->error && r2->stream > h2->goaway_last_stream)
200 0
                return (h2->error);
201 373
        return (0);
202
}
203
204
static int64_t
205 110
h2_do_window(struct worker *wrk, struct h2_req *r2,
206
    struct h2_sess *h2, int64_t wanted)
207
{
208 110
        int64_t w = 0;
209
210 110
        CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
211 110
        CHECK_OBJ_NOTNULL(r2, H2_REQ_MAGIC);
212 110
        CHECK_OBJ_NOTNULL(h2, H2_SESS_MAGIC);
213
214 110
        if (wanted == 0)
215 42
                return (0);
216
217 68
        Lck_Lock(&h2->sess->mtx);
218 68
        if (r2->t_window <= 0 || h2->req0->t_window <= 0) {
219 12
                r2->t_winupd = VTIM_real();
220 12
                h2_send_rel(h2, r2);
221 36
                while (r2->t_window <= 0 && h2_errcheck(r2, h2) == 0) {
222 12
                        r2->cond = &wrk->cond;
223 12
                        AZ(Lck_CondWait(r2->cond, &h2->sess->mtx, 0));
224 12
                        r2->cond = NULL;
225
                }
226 27
                while (h2->req0->t_window <= 0 && h2_errcheck(r2, h2) == 0) {
227 3
                        AZ(Lck_CondWait(h2->winupd_cond, &h2->sess->mtx, 0));
228
                }
229
230 12
                if (h2_errcheck(r2, h2) == 0) {
231 12
                        w = h2_win_limit(r2, h2);
232 12
                        if (w > wanted)
233 4
                                w = wanted;
234 12
                        h2_win_charge(r2, h2, w);
235 12
                        assert (w > 0);
236
                }
237 12
                h2_send_get(wrk, h2, r2);
238
        }
239
240 68
        if (w == 0 && h2_errcheck(r2, h2) == 0) {
241 56
                assert(r2->t_window > 0);
242 56
                assert(h2->req0->t_window > 0);
243 56
                w = h2_win_limit(r2, h2);
244 56
                if (w > wanted)
245 52
                        w = wanted;
246 56
                h2_win_charge(r2, h2, w);
247 56
                assert (w > 0);
248
        }
249 68
        r2->t_winupd = 0;
250 68
        Lck_Unlock(&h2->sess->mtx);
251 68
        return (w);
252
}
253
254
/*
255
 * This is the per-stream frame sender.
256
 * XXX: priority
257
 */
258
259
void
260 214
H2_Send(struct worker *wrk, struct h2_req *r2,
261
    h2_frame ftyp, uint8_t flags, uint32_t len, const void *ptr)
262
{
263
        struct h2_sess *h2;
264
        uint32_t mfs, tf;
265
        const char *p;
266
        uint8_t final_flags;
267
268 214
        CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
269 214
        CHECK_OBJ_NOTNULL(r2, H2_REQ_MAGIC);
270 214
        h2 = r2->h2sess;
271 214
        CHECK_OBJ_NOTNULL(h2, H2_SESS_MAGIC);
272 214
        assert(len == 0 || ptr != NULL);
273
274 214
        assert(VTAILQ_FIRST(&h2->txqueue) == r2);
275
276 214
        if (h2_errcheck(r2, h2))
277 34
                return;
278
279 180
        AN(ftyp);
280 180
        AZ(flags & ~(ftyp->flags));
281 180
        if (r2->stream == 0)
282 0
                AZ(ftyp->act_szero);
283
        else
284 180
                AZ(ftyp->act_snonzero);
285
286 180
        Lck_Lock(&h2->sess->mtx);
287 180
        mfs = h2->remote_settings.max_frame_size;
288 180
        Lck_Unlock(&h2->sess->mtx);
289
290 180
        if (ftyp->respect_window) {
291 84
                tf = h2_do_window(wrk, r2, h2,
292 84
                                  (len > mfs) ? mfs : len);
293 84
                if (h2_errcheck(r2, h2))
294 0
                        return;
295 84
                assert(VTAILQ_FIRST(&h2->txqueue) == r2);
296
        } else
297 96
                tf = mfs;
298
299 180
        if (len <= tf) {
300 172
                H2_Send_Frame(wrk, h2, ftyp, flags, len, r2->stream, ptr);
301
        } else {
302 8
                AN(ptr);
303 8
                p = ptr;
304 8
                final_flags = ftyp->final_flags & flags;
305 8
                flags &= ~ftyp->final_flags;
306
                do {
307 40
                        AN(ftyp->continuation);
308 40
                        if (!ftyp->respect_window)
309 8
                                tf = mfs;
310 40
                        if (ftyp->respect_window && p != ptr) {
311 26
                                tf = h2_do_window(wrk, r2, h2,
312 26
                                                  (len > mfs) ? mfs : len);
313 26
                                if (h2_errcheck(r2, h2))
314 0
                                        return;
315 26
                                assert(VTAILQ_FIRST(&h2->txqueue) == r2);
316
                        }
317 40
                        if (tf < len) {
318 32
                                H2_Send_Frame(wrk, h2, ftyp,
319
                                    flags, tf, r2->stream, p);
320
                        } else {
321 8
                                if (ftyp->respect_window)
322 6
                                        assert(tf == len);
323 8
                                tf = len;
324 8
                                H2_Send_Frame(wrk, h2, ftyp,
325
                                    final_flags, tf, r2->stream, p);
326 8
                                flags = 0;
327
                        }
328 40
                        p += tf;
329 40
                        len -= tf;
330 40
                        ftyp = ftyp->continuation;
331 40
                } while (!h2->error && len > 0);
332
        }
333
}