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