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