vinyl-cache/bin/vinyltest/vtest2/src/vtc_http2.c
0
/*-
1
 * Copyright (c) 2008-2016 Varnish Software AS
2
 * All rights reserved.
3
 *
4
 * Author: Guillaume Quintard <guillaume.quintard@gmail.com>
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
#include "config.h"
31
32
#include <sys/socket.h>
33
34
#include <math.h>
35
#include <poll.h>
36
#include <stdio.h>
37 3863
#include <stdlib.h>
38
#include <unistd.h>
39
#include <string.h>
40
#include <netinet/in.h>
41
42
#include "vtc.h"
43
#include "vtc_http.h"
44
45
#include "vfil.h"
46 3828
#include "hpack.h"
47
#include "vend.h"
48
49
#define ERR_MAX         13
50
#define BUF_SIZE        (1024*2048)
51
52
static const char *const h2_errs[] = {
53
#define H2_ERROR(n,v,sc,g,r,t) [v] = #n,
54
#include <tbl/h2_error.h>
55 3613
        NULL
56
};
57
58
static const char *const h2_types[] = {
59
#define H2_FRAME(l,u,t,f,...) [t] = #u,
60
#include <tbl/h2_frames.h>
61
        NULL
62
};
63
64 3608
static const char * const h2_settings[] = {
65
        [0] = "unknown",
66
#define H2_SETTING(U,l,v,...) [v] = #U,
67
#include <tbl/h2_settings.h>
68
        NULL
69
};
70
71
enum h2_settings_e {
72
#define H2_SETTING(U,l,v,...) SETTINGS_##U = v,
73 3593
#include <tbl/h2_settings.h>
74
        SETTINGS_MAX
75
};
76
77
78
enum h2_type_e {
79
#define H2_FRAME(l,u,t,f,...) TYPE_##u = t,
80
#include <tbl/h2_frames.h>
81
        TYPE_MAX
82 3593
};
83
84
//lint -save -e849      Same enum value
85
enum {
86
        ACK = 0x1,
87
        END_STREAM = 0x1,
88
        PADDED = 0x8,
89
        END_HEADERS = 0x4,
90
        PRIORITY = 0x20,
91 3583
};
92
//lint -restore
93
94
struct stream {
95
        unsigned                magic;
96
#define STREAM_MAGIC            0x63f1fac2
97
        uint32_t                id;
98
        struct vtclog           *vl;
99
        char                    *spec;
100 3563
        char                    *name;
101
        VTAILQ_ENTRY(stream)    list;
102
        unsigned                running;
103
        pthread_cond_t          cond;
104
        struct frame            *frame;
105
        pthread_t               tp;
106
        struct http             *hp;
107
        int64_t                 win_self;
108
        int64_t                 win_peer;
109 3553
        int                     wf;
110
111
        VTAILQ_HEAD(, frame)   fq;
112
113
        char                    *body;
114
        long                    bodylen;
115
        struct hpk_hdr          req[MAX_HDR];
116
        struct hpk_hdr          resp[MAX_HDR];
117
118 3538
        int                     dependency;
119
        int                     weight;
120
};
121
122
static void
123 6060
clean_headers(struct hpk_hdr *h)
124
{
125 6060
        unsigned n = MAX_HDR;
126
127 15368
        while (h->t && n > 0) {
128 5785
                if (h->key.len)
129 5775
                        free(h->key.ptr);
130 5785
                free(h->value.ptr);
131 5785
                memset(h, 0, sizeof(*h));
132 5785
                h++;
133 5785
                n--;
134
        }
135 6060
}
136 3523
137
#define ONLY_H2_CLIENT(hp, av)                                          \
138
        do {                                                            \
139
                if (hp->sfd != NULL)                                    \
140
                        vtc_fatal(s->vl,                                \
141
                            "\"%s\" only possible in client", av[0]);   \
142
        } while (0)
143
144
#define ONLY_H2_SERVER(hp, av)                                          \
145 3488
        do {                                                            \
146
                if (hp->sfd == NULL)                                    \
147
                        vtc_fatal(s->vl,                                \
148
                            "\"%s\" only possible in server", av[0]);   \
149
        } while (0)
150
151
static void
152 285
http_write(const struct http *hp, int lvl,
153
    const char *buf, int s, const char *pfx)
154 3488
{
155
        ssize_t l;
156
157 285
        CHECK_OBJ_NOTNULL(hp, HTTP_MAGIC);
158 285
        AN(buf);
159 285
        AN(pfx);
160
161 285
        vtc_dump(hp->vl, lvl, pfx, buf, s);
162 285
        l = write(*hp->sess->fd, buf, s);
163 285
        if (l != s)
164 0
                vtc_log(hp->vl, hp->fatal, "Write failed: (%zd vs %d) %s",
165 0
                    l, s, strerror(errno));
166 285
}
167
168
static int
169 8600
get_bytes(const struct http *hp, char *buf, size_t n)
170
{
171
        int i;
172
        struct pollfd pfd[1];
173
174 8600
        CHECK_OBJ_NOTNULL(hp, HTTP_MAGIC);
175 8600
        AN(buf);
176
177 17200
        while (n > 0) {
178 8600
                pfd[0].fd = *hp->sess->fd;
179 8600
                pfd[0].events = POLLIN;
180 8600
                pfd[0].revents = 0;
181 8600
                i = poll(pfd, 1, (int)(hp->timeout * 1000));
182 8600
                if (i < 0 && errno == EINTR)
183 0
                        continue;
184 8600
                if (i == 0)
185 0
                        vtc_log(hp->vl, 3,
186
                            "HTTP2 rx timeout (fd:%d %.3fs)",
187 0
                            *hp->sess->fd, hp->timeout);
188 8600
                if (i < 0)
189 0
                        vtc_log(hp->vl, 3,
190
                            "HTTP2 rx failed (fd:%d poll: %s)",
191 0
                            *hp->sess->fd, strerror(errno));
192 8600
                if (i <= 0)
193 0
                        return (i);
194 8600
                i = read(*hp->sess->fd, buf, n);
195 8600
                if (!(pfd[0].revents & POLLIN))
196 0
                        vtc_log(hp->vl, 4,
197
                            "HTTP2 rx poll (fd:%d revents: %x n=%zu, i=%d)",
198 0
                            *hp->sess->fd, pfd[0].revents, n, i);
199 8600
                if (i == 0)
200 0
                        vtc_log(hp->vl, 3,
201
                            "HTTP2 rx EOF (fd:%d read: %s)",
202 0
                            *hp->sess->fd, strerror(errno));
203 8600
                if (i < 0)
204 0
                        vtc_log(hp->vl, 3,
205
                            "HTTP2 rx failed (fd:%d read: %s)",
206 0
                            *hp->sess->fd, strerror(errno));
207 8600
                if (i <= 0)
208 0
                        return (i);
209 8600
                n -= i;
210
        }
211 8600
        return (1);
212
213 8600
}
214
215
VTAILQ_HEAD(fq_head, frame);
216
217
struct frame {
218
        unsigned        magic;
219
#define FRAME_MAGIC     0x5dd3ec4
220
        uint32_t        size;
221
        uint32_t        stid;
222
        uint8_t         type;
223
        uint8_t         flags;
224
        char            *data;
225
226
        VTAILQ_ENTRY(frame)    list;
227
228
        union {
229
                struct {
230
                        uint32_t stream;
231
                        uint8_t  exclusive;
232
                        uint8_t  weight;
233
                }               prio;
234
                uint32_t        rst_err;
235
                double settings[SETTINGS_MAX+1];
236
                struct {
237
                        char data[9];
238
                        int ack;
239
                }               ping;
240
                struct {
241
                        uint32_t err;
242
                        uint32_t stream;
243
                        char     *debug;
244
                }               goaway;
245
                uint32_t        winup_size;
246
                uint32_t        promised;
247
                uint8_t         padded;
248
        } md;
249
};
250
251
static void
252 5028
readFrameHeader(struct frame *f, const char *buf)
253
{
254 5028
        CHECK_OBJ_NOTNULL(f, FRAME_MAGIC);
255 5028
        AN(buf);
256
257 5028
        f->size  = (unsigned char)buf[0] << 16;
258 5028
        f->size += (unsigned char)buf[1] << 8;
259 5028
        f->size += (unsigned char)buf[2];
260
261 5028
        f->type = (unsigned char)buf[3];
262
263 5028
        f->flags = (unsigned char)buf[4];
264
265 5028
        f->stid  = vbe32dec(buf+5);
266 5028
}
267
268
static void
269 10620
writeFrameHeader(char *buf, const struct frame *f)
270
{
271 10620
        CHECK_OBJ_NOTNULL(f, FRAME_MAGIC);
272 10620
        AN(buf);
273 10620
        buf[0] = (f->size >> 16) & 0xff;
274 10620
        buf[1] = (f->size >>  8) & 0xff;
275 10620
        buf[2] = (f->size      ) & 0xff;
276
277 10620
        buf[3] = f->type;
278
279 10620
        buf[4] = f->flags;
280
281 10620
        vbe32enc(buf + 5, f->stid);
282 10620
}
283
284
#define INIT_FRAME(f, ty, sz, id, fl) \
285
do { \
286
        f.magic = FRAME_MAGIC; \
287
        f.type = TYPE_ ## ty; \
288
        f.size = sz; \
289
        f.stid = id; \
290
        f.flags = fl; \
291
        f.data = NULL; \
292
} while(0)
293
294
static void
295 11136
replace_frame(struct frame **fp, struct frame *new)
296
{
297
        struct frame *old;
298
299 11136
        AN(fp);
300 11136
        CHECK_OBJ_ORNULL(new, FRAME_MAGIC);
301
302 11136
        old = *fp;
303 11136
        *fp = new;
304 11136
        if (old == NULL)
305 6110
                return;
306
307 5026
        CHECK_OBJ(old, FRAME_MAGIC);
308 5026
        if (old->type == TYPE_GOAWAY)
309 205
                free(old->md.goaway.debug);
310 5026
        free(old->data);
311 5026
        FREE_OBJ(old);
312 11136
}
313
314
static void
315 7982
clean_frame(struct frame **fp)
316
{
317
318 7982
        replace_frame(fp, NULL);
319 7982
}
320
321
static void
322 10620
write_frame(struct stream *sp, const struct frame *f, const unsigned lock)
323
{
324
        struct http *hp;
325
        ssize_t l;
326
        char hdr[9];
327
328 10620
        CHECK_OBJ_NOTNULL(sp, STREAM_MAGIC);
329 10620
        hp = sp->hp;
330 10620
        CHECK_OBJ_NOTNULL(hp, HTTP_MAGIC);
331 10620
        CHECK_OBJ_NOTNULL(f, FRAME_MAGIC);
332
333 10620
        writeFrameHeader(hdr, f);
334
335 21240
        vtc_log(sp->vl, 3,
336
            "tx: stream: %d, type: %s (%d), flags: 0x%02x, size: %d",
337 10620
            f->stid,
338 10620
            f->type < TYPE_MAX ? h2_types[f->type] : "?",
339 10620
            f->type, f->flags, f->size);
340
341 10620
        if (f->type == TYPE_DATA) {
342 1680
                sp->win_peer -= f->size;
343 1680
                hp->h2_win_peer->size -= f->size;
344 1680
        }
345
346 10620
        if (lock)
347 8540
                PTOK(pthread_mutex_lock(&hp->mtx));
348 10620
        l = write(*hp->sess->fd, hdr, sizeof(hdr));
349 10620
        if (l != sizeof(hdr))
350 9870
                vtc_log(sp->vl, hp->fatal, "Write failed: (%zd vs %zd) %s",
351 4935
                    l, sizeof(hdr), strerror(errno));
352
353 10620
        if (f->size) {
354 8585
                AN(f->data);
355 8585
                l = write(*hp->sess->fd, f->data, f->size);
356 8585
                if (l != f->size)
357 9870
                        vtc_log(sp->vl, hp->fatal,
358
                                        "Write failed: (%zd vs %d) %s",
359 4935
                                        l, f->size, strerror(errno));
360 8585
        }
361 10620
        if (lock)
362 8540
                PTOK(pthread_mutex_unlock(&hp->mtx));
363 10620
}
364
365
static void
366 30
exclusive_stream_dependency(const struct stream *s)
367
{
368
        struct stream *target;
369 30
        struct http *hp = s->hp;
370
371 30
        if (s->id == 0)
372 0
                return;
373
374 140
        VTAILQ_FOREACH(target, &hp->streams, list) {
375 110
                if (target->id != s->id && target->dependency == s->dependency)
376 40
                        target->dependency = s->id;
377 110
        }
378 30
}
379
380
static void
381 5034
explain_flags(uint8_t flags, uint8_t type, struct vtclog *vl)
382
{
383 5034
        if (flags & ACK && (type == TYPE_PING || type == TYPE_SETTINGS)) {
384 1070
                vtc_log(vl, 3, "flag: ACK");
385 5284
        } else if (flags & END_STREAM && (type == TYPE_HEADERS ||
386 250
            type == TYPE_PUSH_PROMISE || type == TYPE_DATA)) {
387 980
                vtc_log(vl, 3, "flag: END_STREAM");
388 3994
        } else if (flags & END_HEADERS && (type == TYPE_HEADERS ||
389 35
            type == TYPE_PUSH_PROMISE || type == TYPE_CONTINUATION)) {
390 295
                vtc_log(vl, 3, "flag: END_TYPE_HEADERS");
391 2984
        } else if (flags & PRIORITY && (type == TYPE_HEADERS ||
392 0
            type == TYPE_PUSH_PROMISE)) {
393 0
                vtc_log(vl, 3, "flag: END_PRIORITY");
394 2689
        } else if (flags & PADDED && (type == TYPE_DATA || type ==
395 0
            TYPE_HEADERS || type == TYPE_PUSH_PROMISE)) {
396 5
                vtc_log(vl, 3, "flag: PADDED");
397 2689
        } else if (flags)
398 0
                vtc_log(vl, 3, "UNKNOWN FLAG(S): 0x%02x", flags);
399 5034
}
400
401
static void
402 990
parse_data(struct stream *s, struct frame *f)
403
{
404
        struct http *hp;
405 990
        uint32_t size = f->size;
406 990
        char *data = f->data;
407
408 990
        CHECK_OBJ_NOTNULL(f, FRAME_MAGIC);
409 990
        CHECK_OBJ_NOTNULL(s, STREAM_MAGIC);
410 990
        CAST_OBJ_NOTNULL(hp, s->hp, HTTP_MAGIC);;
411
412 990
        if (f->flags & PADDED) {
413 20
                f->md.padded = *((uint8_t *)data);
414 20
                if (f->md.padded >= size) {
415 0
                        vtc_log(s->vl, hp->fatal,
416
                                        "invalid padding: %d reported,"
417
                                        "but size is only %d",
418 0
                                        f->md.padded, size);
419 0
                        size = 0;
420 0
                        f->md.padded = 0;
421 0
                }
422 20
                data++;
423 20
                size -= f->md.padded + 1;
424 20
                vtc_log(s->vl, 4, "padding: %3d", f->md.padded);
425 20
        }
426
427 990
        if (s->id)
428 980
                s->win_self -= size;
429
430 990
        s->hp->h2_win_self->size -= size;
431
432 990
        if (!size) {
433 145
                AZ(data);
434 145
                vtc_log(s->vl, 4, "s%u - no data", s->id);
435 145
                return;
436
        }
437
438 845
        s->body = realloc(s->body, s->bodylen + size + 1L);
439 845
        AN(s->body);
440 845
        memcpy(s->body + s->bodylen, data, size);
441 845
        s->bodylen += size;
442 845
        s->body[s->bodylen] = '\0';
443 990
}
444
445
static void
446 1000
decode_hdr(struct http *hp, struct hpk_hdr *h, const struct vsb *vsb)
447
{
448
        struct hpk_iter *iter;
449 1000
        enum hpk_result r = hpk_err;
450
        int n;
451
452 1000
        CHECK_OBJ_NOTNULL(vsb, VSB_MAGIC);
453 1000
        CAST_OBJ_NOTNULL(hp, hp, HTTP_MAGIC);;
454
455 1000
        if (VSB_len(vsb) == 0)
456 0
                return;
457
458 1000
        iter = HPK_NewIter(hp->decctx, VSB_data(vsb), VSB_len(vsb));
459
460 1000
        n = 0;
461 1000
        while (n < MAX_HDR && h[n].t)
462 0
                n++;
463 5785
        while (n < MAX_HDR) {
464 5785
                r = HPK_DecHdr(iter, h + n);
465 5785
                if (r == hpk_err )
466 10
                        break;
467 11550
                vtc_log(hp->vl, 4, "header[%2d]: %s: %s",
468 5775
                    n, h[n].key.ptr, h[n].value.ptr);
469 5775
                n++;
470 5775
                if (r == hpk_done)
471 990
                        break;
472
        }
473
474 1000
        if (r != hpk_done) {
475 20
                vtc_log(hp->vl, hp->fatal ? 4 : 0,
476 10
                    "Header decoding failed (%d) %d", r, hp->fatal);
477 1000
        } else if (n == MAX_HDR) {
478 0
                vtc_log(hp->vl, hp->fatal,
479
                    "Max number of headers reached (%d)", MAX_HDR);
480 0
        }
481
482 1000
        HPK_FreeIter(iter);
483 1000
}
484
485
static void
486 1070
parse_hdr(struct stream *s, struct frame *f, struct vsb *vsb)
487
{
488 1070
        int shift = 0;
489 1070
        int exclusive = 0;
490 1070
        uint32_t size = f->size;
491 1070
        char *data = f->data;
492
        struct http *hp;
493
        uint32_t n;
494
495 1070
        CHECK_OBJ_NOTNULL(f, FRAME_MAGIC);
496 1070
        CHECK_OBJ_NOTNULL(s, STREAM_MAGIC);
497 1070
        CHECK_OBJ_NOTNULL(vsb, VSB_MAGIC);
498 1070
        CAST_OBJ_NOTNULL(hp, s->hp, HTTP_MAGIC);;
499
500 1070
        if (f->flags & PADDED && f->type != TYPE_CONTINUATION) {
501 10
                f->md.padded = *((uint8_t *)data);
502 10
                if (f->md.padded >= size) {
503 0
                        vtc_log(s->vl, hp->fatal,
504
                                        "invalid padding: %d reported,"
505
                                        "but size is only %d",
506 0
                                        f->md.padded, size);
507 0
                        size = 0;
508 0
                        f->md.padded = 0;
509 0
                }
510 10
                shift += 1;
511 10
                size -= f->md.padded;
512 10
                vtc_log(s->vl, 4, "padding: %3d", f->md.padded);
513 10
        }
514
515 1070
        if (f->type == TYPE_HEADERS && f->flags & PRIORITY){
516 10
                shift += 5;
517 10
                n = vbe32dec(f->data);
518 10
                s->dependency = n & ~(1U << 31);
519 10
                exclusive = n >> 31;
520
521 10
                s->weight = f->data[4];
522 10
                if (exclusive)
523 10
                        exclusive_stream_dependency(s);
524
525 10
                vtc_log(s->vl, 4, "stream->dependency: %u", s->dependency);
526 10
                vtc_log(s->vl, 4, "stream->weight: %u", s->weight);
527 1070
        } else if (f->type == TYPE_PUSH_PROMISE){
528 5
                shift += 4;
529 5
                n = vbe32dec(f->data);
530 5
                f->md.promised = n & ~(1U << 31);
531 5
        }
532
533 1070
        AZ(VSB_bcat(vsb, data + shift, size - shift));
534 1070
}
535
536
static void
537 15
parse_prio(struct stream *s, struct frame *f)
538
{
539
        struct http *hp;
540
        char *buf;
541
        uint32_t n;
542
543 15
        CHECK_OBJ_NOTNULL(f, FRAME_MAGIC);
544 15
        CHECK_OBJ_NOTNULL(s, STREAM_MAGIC);
545 15
        CAST_OBJ_NOTNULL(hp, s->hp, HTTP_MAGIC);;
546
547 15
        if (f->size != 5)
548 0
                vtc_fatal(s->vl, "Size should be 5, but isn't (%d)", f->size);
549
550 15
        buf = f->data;
551 15
        AN(buf);
552
553 15
        n = vbe32dec(f->data);
554 15
        f->md.prio.stream = n & ~(1U << 31);
555
556 15
        s->dependency = f->md.prio.stream;
557 15
        if (n >> 31){
558 5
                f->md.prio.exclusive = 1;
559 5
                exclusive_stream_dependency(s);
560 5
        }
561
562 15
        buf += 4;
563 15
        f->md.prio.weight = *buf;
564 15
        s->weight = f->md.prio.weight;
565
566 15
        vtc_log(s->vl, 3, "prio->stream: %u", f->md.prio.stream);
567 15
        vtc_log(s->vl, 3, "prio->weight: %u", f->md.prio.weight);
568 15
}
569
570
static void
571 299
parse_rst(const struct stream *s, struct frame *f)
572
{
573
        struct http *hp;
574
        uint32_t err;
575
        const char *buf;
576 299
        CHECK_OBJ_NOTNULL(f, FRAME_MAGIC);
577 299
        CHECK_OBJ_NOTNULL(s, STREAM_MAGIC);
578 299
        CAST_OBJ_NOTNULL(hp, s->hp, HTTP_MAGIC);;
579
580 299
        if (f->size != 4)
581 0
                vtc_fatal(s->vl, "Size should be 4, but isn't (%d)", f->size);
582
583 299
        err = vbe32dec(f->data);
584 299
        f->md.rst_err = err;
585
586 299
        vtc_log(s->vl, 2, "ouch");
587 299
        if (err <= ERR_MAX)
588 299
                buf = h2_errs[err];
589
        else
590 0
                buf = "unknown";
591 299
        vtc_log(s->vl, 4, "rst->err: %s (%d)", buf, err);
592
593 299
}
594
595
static void
596 2095
parse_settings(const struct stream *s, struct frame *f)
597
{
598
        struct http *hp;
599
        int v;
600
        unsigned u, t;
601
        const char *buf;
602
        enum hpk_result r;
603 2095
        CHECK_OBJ_NOTNULL(f, FRAME_MAGIC);
604 2095
        CHECK_OBJ_NOTNULL(s, STREAM_MAGIC);
605 2095
        CAST_OBJ_NOTNULL(hp, s->hp, HTTP_MAGIC);;
606
607 2095
        if (f->size % 6)
608 0
                vtc_fatal(s->vl,
609 0
                    "Size should be a multiple of 6, but isn't (%d)", f->size);
610
611 2095
        if (s->id != 0)
612 0
                vtc_fatal(s->vl,
613 0
                    "Setting frames should only be on stream 0, but received on stream: %d", s->id);
614
615 39794
        for (u = 0; u <= SETTINGS_MAX; u++)
616 37699
                f->md.settings[u] = NAN;
617
618 4330
        for (u = 0; u < f->size;) {
619 2235
                t = vbe16dec(f->data + u);
620 2235
                u += 2;
621 2235
                v = vbe32dec(f->data + u);
622 2235
                if (t <= SETTINGS_MAX) {
623 2235
                        buf = h2_settings[t];
624 2235
                        f->md.settings[t] = v;
625 2235
                } else
626 0
                        buf = "unknown";
627 2235
                u += 4;
628
629 2235
                if (t == SETTINGS_HEADER_TABLE_SIZE) {
630 25
                        r = HPK_ResizeTbl(s->hp->encctx, v);
631 25
                        assert(r == hpk_done);
632 25
                }
633
634 2235
                vtc_log(s->vl, 4, "settings->%s (%u): %d", buf, t, v);
635
        }
636
637 2095
}
638
639
static void
640 30
parse_ping(const struct stream *s, struct frame *f)
641
{
642
        struct http *hp;
643 30
        CHECK_OBJ_NOTNULL(f, FRAME_MAGIC);
644 30
        CHECK_OBJ_NOTNULL(s, STREAM_MAGIC);
645 30
        CAST_OBJ_NOTNULL(hp, s->hp, HTTP_MAGIC);;
646 30
        if (f->size != 8)
647 0
                vtc_fatal(s->vl, "Size should be 8, but isn't (%d)", f->size);
648 30
        f->md.ping.ack = f->flags & ACK;
649 30
        memcpy(f->md.ping.data, f->data, 8);
650 30
        f->md.ping.data[8] = '\0';
651
652 30
        vtc_log(s->vl, 4, "ping->data: %s", f->md.ping.data);
653
654 30
}
655
656
static void
657 205
parse_goaway(const struct stream *s, struct frame *f)
658
{
659
        struct http *hp;
660
        const char *err_buf;
661
        uint32_t err, stid;
662 205
        CHECK_OBJ_NOTNULL(f, FRAME_MAGIC);
663 205
        CHECK_OBJ_NOTNULL(s, STREAM_MAGIC);
664 205
        CAST_OBJ_NOTNULL(hp, s->hp, HTTP_MAGIC);;
665
666 205
        if (f->size < 8)
667 0
                vtc_fatal(s->vl,
668 0
                    "Size should be at least 8, but isn't (%d)", f->size);
669 205
        if (f->data[0] & (1<<7))
670 0
                vtc_fatal(s->vl,
671
                    "First bit of data is reserved and should be 0");
672
673 205
        stid = vbe32dec(f->data);
674 205
        err = vbe32dec(f->data + 4);
675 205
        f->md.goaway.err = err;
676 205
        f->md.goaway.stream = stid;
677
678 205
        if (err <= ERR_MAX)
679 205
                err_buf = h2_errs[err];
680
        else
681 0
                err_buf = "unknown";
682
683 205
        if (f->size > 8) {
684 10
                f->md.goaway.debug = malloc((f->size - 8) + 1L);
685 10
                AN(f->md.goaway.debug);
686 10
                f->md.goaway.debug[f->size - 8] = '\0';
687
688 10
                memcpy(f->md.goaway.debug, f->data + 8, f->size - 8);
689 10
        }
690
691 205
        vtc_log(s->vl, 3, "goaway->laststream: %d", stid);
692 205
        vtc_log(s->vl, 3, "goaway->err: %s (%d)", err_buf, err);
693 205
        if (f->md.goaway.debug)
694 10
                vtc_log(s->vl, 3, "goaway->debug: %s", f->md.goaway.debug);
695 205
}
696
697
static void
698 330
parse_winup(const struct stream *s, struct frame *f)
699
{
700
        struct http *hp;
701
        uint32_t size;
702 330
        CHECK_OBJ_NOTNULL(f, FRAME_MAGIC);
703 330
        CHECK_OBJ_NOTNULL(s, STREAM_MAGIC);
704 330
        CAST_OBJ_NOTNULL(hp, s->hp, HTTP_MAGIC);;
705
706 330
        if (f->size != 4)
707 0
                vtc_fatal(s->vl, "Size should be 4, but isn't (%d)", f->size);
708 330
        if (f->data[0] & (1<<7))
709 0
                vtc_log(s->vl, s->hp->fatal,
710
                    "First bit of data is reserved and should be 0");
711
712 330
        size = vbe32dec(f->data);
713 330
        f->md.winup_size = size;
714
715 330
        vtc_log(s->vl, 3, "winup->size: %d", size);
716 330
}
717
718
/* read a frame and queue it in the relevant stream, wait if not present yet.
719
 */
720
static void *
721 1062
receive_frame(void *priv)
722
{
723
        struct http *hp;
724
        char hdr[9];
725
        struct frame *f;
726
        struct stream *s;
727 1062
        int expect_cont = 0;
728 1062
        struct vsb *vsb = NULL;
729 1062
        struct hpk_hdr *hdrs = NULL;
730
731 1062
        CAST_OBJ_NOTNULL(hp, priv, HTTP_MAGIC);
732
733 1062
        PTOK(pthread_mutex_lock(&hp->mtx));
734 11795
        while (hp->h2) {
735
                /*no wanted frames? */
736 10733
                assert(hp->wf >= 0);
737 10733
                if (hp->wf == 0) {
738 5699
                        PTOK(pthread_cond_wait(&hp->cond, &hp->mtx));
739 5699
                        continue;
740
                }
741 5034
                PTOK(pthread_mutex_unlock(&hp->mtx));
742
743 5034
                if (get_bytes(hp, hdr, sizeof hdr) <= 0) {
744 0
                        PTOK(pthread_mutex_lock(&hp->mtx));
745 0
                        VTAILQ_FOREACH(s, &hp->streams, list)
746 0
                                PTOK(pthread_cond_signal(&s->cond));
747 0
                        PTOK(pthread_mutex_unlock(&hp->mtx));
748 0
                        vtc_log(hp->vl, hp->fatal,
749
                            "could not get frame header");
750 0
                        return (NULL);
751
                }
752 5034
                ALLOC_OBJ(f, FRAME_MAGIC);
753 5034
                AN(f);
754 5034
                readFrameHeader(f, hdr);
755
756 10068
                vtc_log(hp->vl, 3, "rx: stream: %d, type: %s (%d), "
757
                                "flags: 0x%02x, size: %d",
758 5034
                                f->stid,
759 5034
                                f->type < TYPE_MAX ? h2_types[f->type] : "?",
760 5034
                                f->type, f->flags, f->size);
761 5034
                explain_flags(f->flags, f->type, hp->vl);
762
763 5034
                if (f->size) {
764 3574
                        f->data = malloc(f->size + 1L);
765 3574
                        AN(f->data);
766 3574
                        f->data[f->size] = '\0';
767 3574
                        if (get_bytes(hp, f->data, f->size) <= 0) {
768 0
                                PTOK(pthread_mutex_lock(&hp->mtx));
769 0
                                VTAILQ_FOREACH(s, &hp->streams, list)
770 0
                                        PTOK(pthread_cond_signal(&s->cond));
771 0
                                clean_frame(&f);
772 0
                                PTOK(pthread_mutex_unlock(&hp->mtx));
773 0
                                vtc_log(hp->vl, hp->fatal,
774
                                    "could not get frame body");
775 0
                                return (NULL);
776
                        }
777 3574
                }
778
779
                /* is the corresponding stream waiting? */
780 5034
                PTOK(pthread_mutex_lock(&hp->mtx));
781 5034
                s = NULL;
782 10067
                while (!s) {
783 5740
                        VTAILQ_FOREACH(s, &hp->streams, list)
784 5740
                                if (s->id == f->stid)
785 5033
                                        break;
786 5033
                        if (!s)
787 0
                                PTOK(pthread_cond_wait(&hp->cond, &hp->mtx));
788 5033
                        if (!hp->h2) {
789 0
                                clean_frame(&f);
790 0
                                PTOK(pthread_mutex_unlock(&hp->mtx));
791 0
                                return (NULL);
792
                        }
793
                }
794 5034
                PTOK(pthread_mutex_unlock(&hp->mtx));
795
796 5034
                AN(s);
797 5104
                if (expect_cont &&
798 70
                    (f->type != TYPE_CONTINUATION || expect_cont != s->id))
799 0
                        vtc_fatal(s->vl, "Expected CONTINUATION frame for "
800 0
                            "stream %u", expect_cont);
801
802
                /* parse the frame according to it type, and fill the metadata */
803 5034
                switch (f->type) {
804
                        case TYPE_DATA:
805 990
                                parse_data(s, f);
806 990
                                break;
807
                        case TYPE_PUSH_PROMISE:
808 5
                                hdrs = s->req;
809
                                /*FALLTHROUGH*/
810
                        case TYPE_HEADERS:
811 1000
                                if (!hdrs) {
812 995
                                        if (hp->sfd)
813 235
                                                hdrs = s->req;
814
                                        else
815 760
                                                hdrs = s->resp;
816 995
                                }
817 1000
                                clean_headers(hdrs);
818 1000
                                hdrs[0].t = hpk_unset;
819 1000
                                AZ(vsb);
820 1000
                                vsb = VSB_new_auto();
821
                                /*FALLTHROUGH*/
822
                        case TYPE_CONTINUATION:
823 1070
                                AN(hdrs);
824 1070
                                expect_cont = s->id;
825 1070
                                parse_hdr(s, f, vsb);
826 1070
                                if (f->flags & END_HEADERS) {
827 1000
                                        expect_cont = 0;
828 1000
                                        AZ(VSB_finish(vsb));
829 1000
                                        decode_hdr(hp, hdrs, vsb);
830 1000
                                        VSB_destroy(&vsb);
831 1000
                                        hdrs = NULL;
832 1000
                                }
833 1070
                                break;
834
                        case TYPE_PRIORITY:
835 15
                                parse_prio(s, f);
836 15
                                break;
837
                        case TYPE_RST_STREAM:
838 299
                                parse_rst(s, f);
839 299
                                break;
840
                        case TYPE_SETTINGS:
841 2095
                                parse_settings(s, f);
842 2095
                                break;
843
                        case TYPE_PING:
844 30
                                parse_ping(s, f);
845 30
                                break;
846
                        case TYPE_GOAWAY:
847 205
                                parse_goaway(s, f);
848 205
                                break;
849
                        case TYPE_WINDOW_UPDATE:
850 330
                                parse_winup(s, f);
851 330
                                break;
852
                        default:
853 0
                                WRONG("wrong frame type");
854 0
                }
855
856 5034
                PTOK(pthread_mutex_lock(&hp->mtx));
857 5034
                VTAILQ_INSERT_HEAD(&s->fq, f, list);
858 5034
                if (s->wf) {
859 4813
                        assert(hp->wf > 0);
860 4813
                        hp->wf--;
861 4813
                        s->wf = 0;
862 4813
                        PTOK(pthread_cond_signal(&s->cond));
863 4813
                }
864 5034
                continue;
865
        }
866 1062
        PTOK(pthread_mutex_unlock(&hp->mtx));
867 1062
        if (vsb != NULL)
868 0
                VSB_destroy(&vsb);
869 1062
        return (NULL);
870 1062
}
871
872
#define STRTOU32(n, ss, p, v, c)                                        \
873
        do {                                                            \
874
                n = strtoul(ss, &p, 0);                                 \
875
                if (*p != '\0')                                         \
876
                        vtc_fatal(v, "%s takes an integer as argument " \
877
                                "(found %s)", c, ss);                   \
878
        } while (0)
879
880
#define STRTOU32_CHECK(n, sp, p, v, c, l)                               \
881
do {                                                                    \
882
        sp++;                                                           \
883
        AN(*sp);                                                        \
884
        STRTOU32(n, *sp, p, v, c);                                      \
885
        if (l && n >= (1U << l))                                        \
886
                vtc_fatal(v,                                            \
887
                    c " must be a %d-bits integer (found %s)", l, *sp); \
888
} while (0)
889
890
#define CHECK_LAST_FRAME(TYPE) \
891
        if (!f || f->type != TYPE_ ## TYPE) {                              \
892
                vtc_fatal(s->vl, "Last frame was not of type " #TYPE); \
893
        }
894
895
#define RETURN_SETTINGS(idx) \
896
do { \
897
        if (isnan(f->md.settings[idx])) { \
898
                return (NULL); \
899
        } \
900
        snprintf(buf, 20, "%.0f", f->md.settings[idx]); \
901
        return (buf); \
902
} while (0)
903
904
#define RETURN_BUFFED(val) \
905
do { \
906
        snprintf(buf, 20, "%ld", (long)val); \
907
        return (buf); \
908
} while (0)
909
910
static char *
911
find_header(const struct hpk_hdr *h, const char *k)
912
{
913
        AN(k);
914
915
        int kl = strlen(k);
916
        while (h->t) {
917
                if (kl == h->key.len  && !strncasecmp(h->key.ptr, k, kl))
918
                        return (h->value.ptr);
919
                h++;
920
        }
921
        return (NULL);
922
}
923 995
/* SECTION: stream.spec.zexpect expect
924
 *
925 995
 * expect in stream works as it does in client or server, except that the
926
 * elements compared will be different.
927 995
 *
928 5315
 * Most of these elements will be frame specific, meaning that the last frame
929 5265
 * received on that stream must of the correct type.
930 945
 *
931 4320
 * Here the list of keywords you can look at.
932
 */
933 50
static const char *
934 995
cmd_var_resolve(const struct stream *s, const char *spec, char *buf)
935
{
936
        uint32_t idx;
937
        int n;
938
        const struct hpk_hdr *h;
939
        struct hpk_ctx *ctx;
940
        struct frame *f = s->frame;
941
942
        CHECK_OBJ_NOTNULL(s, STREAM_MAGIC);
943
        CHECK_OBJ_NOTNULL(s->hp, HTTP_MAGIC);
944
        AN(spec);
945
        AN(buf);
946 7747
947
        n = 0;
948
        /* SECTION: stream.spec.zexpect.ping PING specific
949
         *
950
         * ping.data
951
         *      The 8-bytes string of the PING frame payload.
952 7747
         * ping.ack (PING)
953
         *      "true" if the ACK flag was set, "false" otherwise.
954 7747
         */
955 7747
        if (!strcmp(spec, "ping.data")) {
956 7747
                CHECK_LAST_FRAME(PING);
957 7747
                return (f->md.ping.data);
958
        }
959 7747
        if (!strcmp(spec, "ping.ack")) {
960
                CHECK_LAST_FRAME(PING);
961
                snprintf(buf, 20, (f->flags & ACK) ? "true" : "false");
962
                return (buf);
963
        }
964
        /* SECTION: stream.spec.zexpect.winup WINDOW_UPDATE specific
965
         *
966
         * winup.size
967 7747
         *      The size of the upgrade given by the WINDOW_UPDATE frame.
968 30
         */
969 30
        if (!strcmp(spec, "winup.size")) {
970
                CHECK_LAST_FRAME(WINDOW_UPDATE);
971 7717
                RETURN_BUFFED(f->md.winup_size);
972 20
        }
973 20
        /* SECTION: stream.spec.zexpect.prio PRIORITY specific
974 20
         *
975
         * prio.stream
976
         *      The stream ID announced.
977
         *
978
         * prio.exclusive
979
         *      "true" if the priority is exclusive, else "false".
980
         *
981 7697
         * prio.weight
982 5
         *      The dependency weight.
983 5
         */
984 0
        if (!strcmp(spec, "prio.stream")) {
985
                CHECK_LAST_FRAME(PRIORITY);
986
                RETURN_BUFFED(f->md.prio.stream);
987
        }
988
        if (!strcmp(spec, "prio.exclusive")) {
989
                CHECK_LAST_FRAME(PRIORITY);
990
                snprintf(buf, 20, f->md.prio.exclusive ? "true" : "false");
991
                return (buf);
992
        }
993
        if (!strcmp(spec, "prio.weight")) {
994
                CHECK_LAST_FRAME(PRIORITY);
995
                RETURN_BUFFED(f->md.prio.weight);
996 7692
        }
997 10
        /* SECTION: stream.spec.zexpect.rst RESET_STREAM specific
998 10
         *
999 0
         * rst.err
1000 7682
         *      The error code (as integer) of the RESET_STREAM frame.
1001 0
         */
1002 0
        if (!strcmp(spec, "rst.err")) {
1003 0
                CHECK_LAST_FRAME(RST_STREAM);
1004
                RETURN_BUFFED(f->md.rst_err);
1005 7682
        }
1006 10
        /* SECTION: stream.spec.zexpect.settings SETTINGS specific
1007 10
         *
1008 0
         * settings.ack
1009
         *      "true" if the ACK flag was set, else "false".
1010
         *
1011
         * settings.push
1012
         *      "true" if the push settings was set to yes, "false" if set to
1013
         *      no, and <undef> if not present.
1014 7672
         *
1015 210
         * settings.hdrtbl
1016 210
         *      Value of HEADER_TABLE_SIZE if set, <undef> otherwise.
1017 0
         *
1018
         * settings.maxstreams
1019
         *      Value of MAX_CONCURRENT_STREAMS if set, <undef> otherwise.
1020
         *
1021
         * settings.winsize
1022
         *      Value of INITIAL_WINDOW_SIZE if set, <undef> otherwise.
1023
         *
1024
         * setting.framesize
1025
         *      Value of MAX_FRAME_SIZE if set, <undef> otherwise.
1026
         *
1027
         * settings.hdrsize
1028
         *      Value of MAX_HEADER_LIST_SIZE if set, <undef> otherwise.
1029
         */
1030
        if (!strncmp(spec, "settings.", 9)) {
1031
                CHECK_LAST_FRAME(SETTINGS);
1032
                spec += 9;
1033
                if (!strcmp(spec, "ack")) {
1034
                        snprintf(buf, 20, (f->flags & ACK) ? "true" : "false");
1035
                        return (buf);
1036
                }
1037
                if (!strcmp(spec, "push")) {
1038
                        if (isnan(f->md.settings[SETTINGS_ENABLE_PUSH]))
1039
                                return (NULL);
1040
                        else if (f->md.settings[SETTINGS_ENABLE_PUSH] == 1)
1041
                                snprintf(buf, 20, "true");
1042
                        else
1043
                                snprintf(buf, 20, "false");
1044
                        return (buf);
1045
                }
1046
                if (!strcmp(spec, "hdrtbl"))     { RETURN_SETTINGS(1); }
1047
                if (!strcmp(spec, "maxstreams")) { RETURN_SETTINGS(3); }
1048
                if (!strcmp(spec, "winsize"))    { RETURN_SETTINGS(4); }
1049
                if (!strcmp(spec, "framesize"))  { RETURN_SETTINGS(5); }
1050
                if (!strcmp(spec, "hdrsize"))    { RETURN_SETTINGS(6); }
1051
        }
1052
        /* SECTION: stream.spec.zexpect.push PUSH_PROMISE specific
1053 7462
         *
1054 1159
         * push.id
1055 1159
         *      The id of the promised stream.
1056 1159
         */
1057 1039
        if (!strcmp(spec, "push.id")) {
1058 1039
                CHECK_LAST_FRAME(PUSH_PROMISE);
1059
                RETURN_BUFFED(f->md.promised);
1060 120
        }
1061 110
        /* SECTION: stream.spec.zexpect.goaway GOAWAY specific
1062 90
         *
1063 75
         * goaway.err
1064 60
         *      The error code (as integer) of the GOAWAY frame.
1065 45
         *
1066 35
         * goaway.laststream
1067 25
         *      Last-Stream-ID
1068 10
         *
1069 0
         * goaway.debug
1070
         *      Debug data, if any.
1071
         */
1072
        if (!strncmp(spec, "goaway.", 7)) {
1073
                spec += 7;
1074
                CHECK_LAST_FRAME(GOAWAY);
1075 6303
1076 5
                if (!strcmp(spec, "err"))
1077 5
                        RETURN_BUFFED(f->md.goaway.err);
1078 0
                else if (!strcmp(spec, "laststream"))
1079
                        RETURN_BUFFED(f->md.goaway.stream);
1080
                else if (!strcmp(spec, "debug"))
1081
                        return (f->md.goaway.debug);
1082
        }
1083
        /* SECTION: stream.spec.zexpect.zframe Generic frame
1084
         *
1085
         * frame.data
1086
         *      Payload of the last frame
1087
         *
1088
         * frame.type
1089
         *      Type of the frame, as integer.
1090 6298
         *
1091 340
         * frame.size
1092 340
         *      Size of the frame.
1093
         *
1094 340
         * frame.stream
1095 185
         *      Stream of the frame (correspond to the one you are executing
1096 155
         *      this from, obviously).
1097 145
         *
1098 10
         * frame.padding (for DATA, HEADERS, PUSH_PROMISE frames)
1099 10
         *      Number of padded bytes.
1100 0
         */
1101
        if (!strncmp(spec, "frame.", 6)) {
1102
                spec += 6;
1103
                if (!f)
1104
                        vtc_fatal(s->vl, "No frame received yet.");
1105
                if (!strcmp(spec, "data"))   { return (f->data); }
1106
                else if (!strcmp(spec, "type"))   { RETURN_BUFFED(f->type); }
1107
                else if (!strcmp(spec, "size"))   { RETURN_BUFFED(f->size); }
1108
                else if (!strcmp(spec, "stream")) { RETURN_BUFFED(f->stid); }
1109
                else if (!strcmp(spec, "padding")) {
1110
                        if (f->type != TYPE_DATA &&
1111
                                        f->type != TYPE_HEADERS &&
1112
                                        f->type != TYPE_PUSH_PROMISE)
1113
                                vtc_fatal(s->vl,
1114
                                                "Last frame was not of type "
1115
                                                "DATA, HEADERS or PUSH");
1116
                        RETURN_BUFFED(f->md.padded);
1117
                }
1118
        }
1119 5958
        /* SECTION: stream.spec.zexpect.zstream Stream
1120 40
         *
1121 40
         * stream.window
1122 0
         *      The current local window size of the stream, or, if on stream 0,
1123 40
         *      of the connection.
1124 35
         *
1125 30
         * stream.peer_window
1126 10
         *      The current peer window size of the stream, or, if on stream 0,
1127 5
         *      of the connection.
1128 5
         *
1129 5
         * stream.weight
1130 0
         *      Weight of the stream
1131 0
         *
1132
         * stream.dependency
1133
         *      Id of the stream this one depends on.
1134 5
         */
1135 0
        if (!strcmp(spec, "stream.window")) {
1136 0
                snprintf(buf, 20, "%jd",
1137
                    (intmax_t)(s->id ? s->win_self : s->hp->h2_win_self->size));
1138
                return (buf);
1139
        }
1140
        if (!strcmp(spec, "stream.peer_window")) {
1141
                snprintf(buf, 20, "%jd",
1142
                    (intmax_t)(s->id ? s->win_peer : s->hp->h2_win_peer->size));
1143
                return (buf);
1144
        }
1145
        if (!strcmp(spec, "stream.weight")) {
1146
                if (s->id) {
1147
                        snprintf(buf, 20, "%d", s->weight);
1148
                        return (buf);
1149
                } else
1150
                        return (NULL);
1151
        }
1152
        if (!strcmp(spec, "stream.dependency")) {
1153 5918
                if (s->id) {
1154 130
                        snprintf(buf, 20, "%d", s->dependency);
1155 65
                        return (buf);
1156 65
                } else
1157
                        return (NULL);
1158 5853
        }
1159 160
        /* SECTION: stream.spec.zexpect.ztable Index tables
1160 80
         *
1161 80
         * tbl.dec.size / tbl.enc.size
1162
         *      Size (bytes) of the decoding/encoding table.
1163 5773
         *
1164 40
         * tbl.dec.size / tbl.enc.maxsize
1165 35
         *      Maximum size (bytes) of the decoding/encoding table.
1166 35
         *
1167
         * tbl.dec.length / tbl.enc.length
1168 5
         *      Number of headers in decoding/encoding table.
1169
         *
1170 5733
         * tbl.dec[INT].key / tbl.enc[INT].key
1171 80
         *      Name of the header at index INT of the decoding/encoding
1172 75
         *      table.
1173 75
         *
1174
         * tbl.dec[INT].value / tbl.enc[INT].value
1175 5
         *      Value of the header at index INT of the decoding/encoding
1176
         *      table.
1177
         */
1178
        if (!strncmp(spec, "tbl.dec", 7) || !strncmp(spec, "tbl.enc", 7)) {
1179
                if (spec[4] == 'd')
1180
                        ctx = s->hp->decctx;
1181
                else
1182
                        ctx = s->hp->encctx;
1183
                spec += 7;
1184
1185
                if (1 == sscanf(spec, "[%u].key%n", &idx, &n) &&
1186
                                spec[n] == '\0') {
1187
                        h = HPK_GetHdr(ctx, idx + 61);
1188
                        return (h ? h->key.ptr : NULL);
1189
                }
1190
                else if (1 == sscanf(spec, "[%u].value%n", &idx, &n) &&
1191
                                spec[n] == '\0') {
1192
                        h = HPK_GetHdr(ctx, idx + 61);
1193
                        return (h ? h->value.ptr : NULL);
1194
                }
1195
                else if (!strcmp(spec, ".size"))
1196 5653
                        RETURN_BUFFED(HPK_GetTblSize(ctx));
1197 615
                else if (!strcmp(spec, ".maxsize"))
1198 315
                        RETURN_BUFFED(HPK_GetTblMaxSize(ctx));
1199
                else if (!strcmp(spec, ".length"))
1200 300
                        RETURN_BUFFED(HPK_GetTblLength(ctx));
1201 615
        }
1202
        /* SECTION: stream.spec.zexpect.zre Request and response
1203 615
         *
1204 480
         * Note: it's possible to inspect a request or response while it is
1205 240
         * still being construct (in-between two frames for example).
1206 240
         *
1207
         * req.bodylen / resp.bodylen
1208 375
         *      Length in bytes of the request/response so far.
1209 240
         *
1210 240
         * req.body / resp.body
1211 240
         *      Body of the request/response so far.
1212
         *
1213 135
         * req.http.STRING / resp.http.STRING
1214 130
         *      Value of the header STRING in the request/response.
1215 5
         *
1216 0
         * req.status / resp.status
1217 5
         *      :status pseudo-header's value.
1218 5
         *
1219 0
         * req.url / resp.url
1220
         *      :path pseudo-header's value.
1221
         *
1222
         * req.method / resp.method
1223
         *      :method pseudo-header's value.
1224
         *
1225
         * req.authority / resp.authority
1226
         *      :method pseudo-header's value.
1227
         *
1228
         * req.scheme / resp.scheme
1229
         *      :method pseudo-header's value.
1230
         */
1231
        if (!strncmp(spec, "req.", 4) || !strncmp(spec, "resp.", 5)) {
1232
                if (spec[2] == 'q') {
1233
                        h = s->req;
1234
                        spec += 4;
1235
                } else {
1236
                        h = s->resp;
1237
                        spec += 5;
1238
                }
1239
                if (!strcmp(spec, "body"))
1240
                        return (s->body);
1241
                else if (!strcmp(spec, "bodylen"))
1242
                        RETURN_BUFFED(s->bodylen);
1243
                else if (!strcmp(spec, "status"))
1244
                        return (find_header(h, ":status"));
1245
                else if (!strcmp(spec, "url"))
1246
                        return (find_header(h, ":path"));
1247
                else if (!strcmp(spec, "method"))
1248
                        return (find_header(h, ":method"));
1249 5038
                else if (!strcmp(spec, "authority"))
1250 1175
                        return (find_header(h, ":authority"));
1251 75
                else if (!strcmp(spec, "scheme"))
1252 75
                        return (find_header(h, ":scheme"));
1253 75
                else if (!strncmp(spec, "http.", 5))
1254 1100
                        return (find_header(h, spec + 5));
1255 1100
                else
1256
                        return (NULL);
1257 1175
        }
1258 85
#define H2_ERROR(U,v,sc,g,r,t) \
1259 1090
        if (!strcmp(spec, #U)) { return (#v); }
1260 95
#include "tbl/h2_error.h"
1261 995
        return (spec);
1262 415
}
1263 580
1264 10
/* SECTION: stream.spec.frame_sendhex sendhex
1265 570
 *
1266 10
 * Push bytes directly on the wire. sendhex takes exactly one argument: a string
1267 560
 * describing the bytes, in hex notation, with possible whitespaces between
1268 5
 * them. Here's an example::
1269 555
 *
1270 5
 *      sendhex "00 00 08 00 0900       8d"
1271 550
 */
1272 550
static void
1273
cmd_sendhex(CMD_ARGS)
1274 0
{
1275 0
        struct http *hp;
1276
        struct stream *s;
1277
        struct vsb *vsb;
1278
1279
        (void)vl;
1280
        CAST_OBJ_NOTNULL(s, priv, STREAM_MAGIC);
1281
        CAST_OBJ_NOTNULL(hp, s->hp, HTTP_MAGIC);
1282
        AN(av[1]);
1283
        AZ(av[2]);
1284
        vsb = vtc_hex_to_bin(hp->vl, av[1]);
1285
        assert(VSB_len(vsb) >= 0);
1286
        vtc_hexdump(hp->vl, 4, "sendhex", VSB_data(vsb), VSB_len(vsb));
1287
        PTOK(pthread_mutex_lock(&hp->mtx));
1288
        http_write(hp, 4, VSB_data(vsb), VSB_len(vsb), "sendhex");
1289
        PTOK(pthread_mutex_unlock(&hp->mtx));
1290
        VSB_destroy(&vsb);
1291 285
}
1292
1293
#define ENC(hdr, k, v)                                  \
1294
{                                                       \
1295
        AN(k);                                          \
1296
        hdr.key.ptr = TRUST_ME(k);                      \
1297 285
        hdr.key.len = strlen(k);                        \
1298 285
        AN(v);                                          \
1299 285
        hdr.value.ptr = TRUST_ME(v);                    \
1300 285
        hdr.value.len = strlen(v);                      \
1301 285
        assert(HPK_EncHdr(iter, &hdr) != hpk_err);      \
1302 285
}
1303 285
1304 285
#define STR_ENC(av, field, str)                                                \
1305 285
{                                                                              \
1306 285
        av++;                                                                  \
1307 285
             if (AV_IS("plain")) { hdr.field.huff = 0; }                       \
1308 285
        else if (AV_IS("huf"))   { hdr.field.huff = 1; }                       \
1309 285
        else                                                                   \
1310
                vtc_fatal(vl, str " arg can be huf or plain (got: %s)", *av); \
1311
        av++;                                                                  \
1312
        AN(*av);                                                               \
1313
        hdr.field.ptr = *av;                                                   \
1314
        hdr.field.len = strlen(*av);                                           \
1315
}
1316
1317
1318
/* SECTION: stream.spec.data_0 txreq, txresp, txcont, txpush
1319
 *
1320
 * These four commands are about sending headers. txreq and txresp
1321
 * will send HEADER frames; txcont will send CONTINUATION frames; txpush
1322
 * PUSH frames.
1323
 * The only difference between txreq and txresp are the default headers
1324
 * set by each of them.
1325
 *
1326
 * \-noadd
1327
 *      Do not add default headers. Useful to avoid duplicates when sending
1328
 *      default headers using ``-hdr``, ``-idxHdr`` and ``-litIdxHdr``.
1329
 *
1330
 * \-status INT (txresp)
1331
 *      Set the :status pseudo-header.
1332
 *
1333
 * \-url STRING (txreq, txpush)
1334
 *      Set the :path pseudo-header.
1335
 *
1336
 * \-method STRING (txreq, txpush)
1337
 *      Set the :method pseudo-header.
1338
 *
1339
 * \-req STRING (txreq, txpush)
1340
 *      Alias for -method.
1341
 *
1342
 * \-scheme STRING (txreq, txpush)
1343
 *      Set the :scheme pseudo-header.
1344
 *
1345
 * \-hdr STRING1 STRING2
1346
 *      Insert a header, STRING1 being the name, and STRING2 the value.
1347
 *
1348
 * \-idxHdr INT
1349
 *      Insert an indexed header, using INT as index.
1350
 *
1351
 * \-litIdxHdr inc|not|never INT huf|plain STRING
1352
 *      Insert an literal, indexed header. The first argument specify if the
1353
 *      header should be added to the table, shouldn't, or mustn't be
1354
 *      compressed if/when retransmitted.
1355
 *
1356
 *      INT is the index of the header name to use.
1357
 *
1358
 *      The third argument informs about the Huffman encoding: yes (huf) or
1359
 *      no (plain).
1360
 *
1361
 *      The last term is the literal value of the header.
1362
 *
1363
 * \-litHdr inc|not|never huf|plain STRING1 huf|plain STRING2
1364
 *      Insert a literal header, with the same first argument as
1365
 *      ``-litIdxHdr``.
1366
 *
1367
 *      The second and third terms tell what the name of the header is and if
1368
 *      it should be Huffman-encoded, while the last two do the same
1369
 *      regarding the value.
1370
 *
1371
 * \-body STRING (txreq, txresp)
1372
 *      Specify a body, effectively putting STRING into a DATA frame after
1373
 *      the HEADER frame is sent.
1374
 *
1375
 * \-bodyfrom FILE (txreq, txresp)
1376
 *      Same as ``-body`` but content is read from FILE.
1377
 *
1378
 * \-bodylen INT (txreq, txresp)
1379
 *      Do the same thing as ``-body`` but generate a string of INT length
1380
 *      for you.
1381
 *
1382
 * \-gzipbody STRING (txreq, txresp)
1383
 *      Gzip STRING and send it as body.
1384
 *
1385
 * \-gziplen NUMBER (txreq, txresp)
1386
 *      Combine -bodylen and -gzipbody: generate a string of length NUMBER,
1387
 *      gzip it and send as body.
1388
 *
1389
 * \-nostrend (txreq, txresp)
1390
 *      Don't set the END_STREAM flag automatically, making the peer expect
1391
 *      a body after the headers.
1392
 *
1393
 * \-nohdrend
1394
 *      Don't set the END_HEADERS flag automatically, making the peer expect
1395
 *      more HEADER frames.
1396
 *
1397
 * \-dep INT (txreq, txresp)
1398
 *      Tell the peer that this content depends on the stream with the INT
1399
 *      id.
1400
 *
1401
 * \-ex (txreq, txresp)
1402
 *      Make the dependency exclusive (``-dep`` is still needed).
1403
 *
1404
 * \-weight (txreq, txresp)
1405
 *      Set the weight for the dependency.
1406
 *
1407
 * \-promised INT (txpush)
1408
 *      The id of the promised stream.
1409
 *
1410
 * \-pad STRING / -padlen INT (txreq, txresp, txpush)
1411
 *      Add string as padding to the frame, either the one you provided with
1412
 *      \-pad, or one that is generated for you, of length INT is -padlen
1413
 *      case.
1414
 */
1415
1416
#define cmd_txreq       cmd_tx11obj
1417
#define cmd_txresp      cmd_tx11obj
1418
#define cmd_txpush      cmd_tx11obj
1419
#define cmd_txcont      cmd_tx11obj
1420
1421
static void
1422
cmd_tx11obj(CMD_ARGS)
1423
{
1424
        struct stream *s;
1425
        int i;
1426
        int status_done = 1;
1427
        int method_done = 1;
1428
        int path_done = 1;
1429
        int scheme_done = 1;
1430
        long bodylen = 0;
1431
        ssize_t len;
1432
        uint32_t stid = 0, pstid;
1433
        uint32_t weight = 16;
1434
        uint32_t exclusive = 0;
1435
        char *buf;
1436
        struct hpk_iter *iter;
1437
        struct frame f;
1438
        char *body = NULL, *pad = NULL;
1439
        /*XXX: do we need a better api? yes we do */
1440 6500
        struct hpk_hdr hdr;
1441
        char *cmd_str = *av;
1442
        char *p;
1443
1444 6500
        CAST_OBJ_NOTNULL(s, priv, STREAM_MAGIC);
1445 6500
        INIT_FRAME(f, CONTINUATION, 0, s->id, END_HEADERS);
1446 6500
        buf = malloc(BUF_SIZE);
1447 6500
        AN(buf);
1448 6500
1449
        if (!strcmp(cmd_str, "txreq")) {
1450 6500
                ONLY_H2_CLIENT(s->hp, av);
1451 6500
                f.type = TYPE_HEADERS;
1452 6500
                f.flags |= END_STREAM;
1453
                method_done = 0;
1454
                path_done = 0;
1455
                scheme_done = 0;
1456 6500
        } else if (!strcmp(cmd_str, "txresp")) {
1457
                ONLY_H2_SERVER(s->hp, av);
1458
                f.type = TYPE_HEADERS;
1459 6500
                f.flags |= END_STREAM;
1460
                status_done = 0;
1461
        } else if (!strcmp(cmd_str, "txpush")) {
1462 6500
                ONLY_H2_SERVER(s->hp, av);
1463 6500
                f.type = TYPE_PUSH_PROMISE;
1464 6500
                method_done = 0;
1465 6500
                path_done = 0;
1466
                scheme_done = 0;
1467 6500
        }
1468 1170
1469 1170
        if (f.type == TYPE_PUSH_PROMISE) {
1470 1170
                *buf = 0;
1471 1170
                iter = HPK_NewIter(s->hp->encctx, buf + 4, BUF_SIZE - 4);
1472 1170
        } else
1473 1170
                iter = HPK_NewIter(s->hp->encctx, buf, BUF_SIZE);
1474 6500
1475 240
#define AV_IS(str) !strcmp(*av, str)
1476 240
#define CMD_IS(str) !strcmp(cmd_str, str)
1477 240
        while (*++av) {
1478 240
                memset(&hdr, 0, sizeof(hdr));
1479 5330
                hdr.t = hpk_not;
1480 5
                if (AV_IS("-noadd")) {
1481 5
                        path_done = 1;
1482 5
                        status_done = 1;
1483 5
                        method_done = 1;
1484 5
                        scheme_done = 1;
1485 5
                }
1486
                else if (AV_IS("-status") && CMD_IS("txresp")) {
1487 6500
                        ENC(hdr, ":status", av[1]);
1488 5
                        av++;
1489 5
                        status_done = 1;
1490 5
                }
1491 6495
                else if (AV_IS("-url") &&
1492
                                (CMD_IS("txreq") || CMD_IS("txpush"))) {
1493
                        ENC(hdr, ":path", av[1]);
1494
                        av++;
1495 18905
                        path_done = 1;
1496 12405
                }
1497 12405
                else if ((AV_IS("-method") || AV_IS("-req")) &&
1498 12405
                                (CMD_IS("txreq") || CMD_IS("txpush"))) {
1499 70
                        ENC(hdr, ":method", av[1]);
1500 70
                        av++;
1501 70
                        method_done = 1;
1502 70
                }
1503 70
                else if (AV_IS("-scheme") &&
1504 12335
                                (CMD_IS("txreq") || CMD_IS("txpush"))) {
1505 5
                        ENC(hdr, ":scheme", av[1]);
1506 5
                        av++;
1507 5
                        scheme_done = 1;
1508 5
                }
1509 12335
                else if (AV_IS("-hdr")) {
1510 315
                        if (av[2] == NULL)
1511 315
                                vtc_fatal(vl, "-hdr takes two arguments in http2");
1512 315
                        ENC(hdr, av[1], av[2]);
1513 315
                        av += 2;
1514 315
                }
1515 12015
                else if (AV_IS("-idxHdr")) {
1516 185
                        hdr.t = hpk_idx;
1517 185
                        STRTOU32_CHECK(hdr.i, av, p, vl, "-idxHdr", 0);
1518 185
                        assert(HPK_EncHdr(iter, &hdr) != hpk_err);
1519 185
                }
1520 185
                else if (AV_IS("-litIdxHdr")) {
1521 11830
                        av++;
1522 35
                        if      (AV_IS("inc"))   { hdr.t = hpk_inc;   }
1523 35
                        else if (AV_IS("not"))   { hdr.t = hpk_not;   }
1524 35
                        else if (AV_IS("never")) { hdr.t = hpk_never; }
1525 35
                        else
1526 35
                                vtc_fatal(vl, "first -litidxHdr arg can be "
1527 11795
                                    "inc, not, never (got: %s)", *av);
1528 6050
1529 0
                        STRTOU32_CHECK(hdr.i, av, p, vl,
1530 6050
                            "second -litidxHdr arg", 0);
1531 6050
1532 6050
                        hdr.key.ptr = NULL;
1533 5745
                        hdr.key.len = 0;
1534 210
                        STR_ENC(av, value,   "third -litHdr");
1535 210
                        assert(HPK_EncHdr(iter, &hdr) != hpk_err);
1536 210
                }
1537 210
                else if (AV_IS("-litHdr")) {
1538 5535
                        av++;
1539 80
                        if      (AV_IS("inc"))   { hdr.t = hpk_inc;   }
1540 80
                        else if (AV_IS("not"))   { hdr.t = hpk_not;   }
1541 5
                        else if (AV_IS("never")) { hdr.t = hpk_never; }
1542 5
                        else
1543
                                vtc_fatal(vl, "first -litHdr arg can be inc, "
1544 0
                                    "not, never (got: %s)", *av);
1545 0
1546
                        STR_ENC(av, key,   "second -litHdr");
1547 80
                        STR_ENC(av, value, "fourth -litHdr");
1548
                        assert(HPK_EncHdr(iter, &hdr) != hpk_err);
1549
                }
1550 80
                else if (AV_IS("-nostrend")) {
1551 80
                        f.flags &= ~END_STREAM;
1552 120
                }
1553 80
                else if (AV_IS("-nohdrend")) {
1554 80
                        f.flags &= ~END_HEADERS;
1555 5455
                }
1556 35
                else if (AV_IS("-promised") && CMD_IS("txpush")) {
1557 35
                        STRTOU32_CHECK(pstid, av, p, vl, "-promised", 31);
1558 0
                        vbe32enc(buf, pstid);
1559 0
                }
1560
                else if (AV_IS("-pad") && !CMD_IS("txcont")) {
1561 0
                        AZ(pad);
1562 0
                        av++;
1563
                        AN(*av);
1564 50
                        pad = strdup(*av);
1565 50
                }
1566 35
                else if (AV_IS("-padlen") && !CMD_IS("txcont")) {
1567 35
                        AZ(pad);
1568 5420
                        av++;
1569 195
                        pad = synth_body(*av, 0);
1570 195
                }
1571 5225
                else if (CMD_IS("txreq") || CMD_IS("txresp")) {
1572 5080
                        if (AV_IS("-body")) {
1573 5080
                                AZ(body);
1574 145
                                REPLACE(body, av[1]);
1575 5
                                AN(body);
1576 5
                                bodylen = strlen(body);
1577 5
                                f.flags &= ~END_STREAM;
1578 140
                                av++;
1579 30
                        }
1580 30
                        else if (AV_IS("-bodyfrom")) {
1581 30
                                AZ(body);
1582 30
                                body = VFIL_readfile(NULL, av[1], &len);
1583 30
                                AN(body);
1584 110
                                assert(len < INT_MAX);
1585 0
                                bodylen = len;
1586 0
                                f.flags &= ~END_STREAM;
1587 0
                                av++;
1588 0
                        }
1589 110
                        else if (AV_IS("-bodylen")) {
1590 110
                                AZ(body);
1591 40
                                body = synth_body(av[1], 0);
1592 40
                                bodylen = strlen(body);
1593 40
                                f.flags &= ~END_STREAM;
1594 40
                                av++;
1595 40
                        }
1596 40
                        else if (!strncmp(*av, "-gzip", 5)) {
1597 40
                                i = vtc_gzip_cmd(s->hp, av, &body, &bodylen);
1598 70
                                if (i == 0)
1599 10
                                        break;
1600 10
                                av++;
1601 10
                                if (i > 1) {
1602 10
                                        ENC(hdr, ":content-encoding", "gzip");
1603 10
                                        f.flags &= ~END_STREAM;
1604 10
                                }
1605 10
                        }
1606 10
                        else if (AV_IS("-dep")) {
1607 60
                                STRTOU32_CHECK(stid, av, p, vl, "-dep", 0);
1608 20
                                f.flags |= PRIORITY;
1609 20
                        }
1610 20
                        else if (AV_IS("-ex")) {
1611 20
                                exclusive = 1U << 31;
1612 20
                                f.flags |= PRIORITY;
1613 20
                        }
1614 40
                        else if (AV_IS("-weight")) {
1615 10
                                STRTOU32_CHECK(weight, av, p, vl, "-weight", 8);
1616 10
                                f.flags |= PRIORITY;
1617 0
                        } else
1618 10
                                break;
1619 10
                } else
1620 10
                        break;
1621 10
        }
1622 10
#undef CMD_IS
1623 10
#undef AV_IS
1624 30
        if (*av != NULL)
1625 15
                vtc_fatal(vl, "Unknown %s spec: %s\n", cmd_str, *av);
1626 15
1627 15
        memset(&hdr, 0, sizeof(hdr));
1628 15
        hdr.t = hpk_not;
1629 10
1630 10
        if (!status_done) { ENC(hdr, ":status", "200"); }
1631 10
        if (!path_done)   { ENC(hdr, ":path",   "/"); }
1632 5
        if (!method_done) { ENC(hdr, ":method", "GET"); }
1633 5
        if (!scheme_done) { ENC(hdr, ":scheme", "http"); }
1634 5
1635 5
        f.size = gethpk_iterLen(iter);
1636 0
        if (f.flags & PRIORITY) {
1637 110
                s->weight = weight & 0xff;
1638 0
                s->dependency = stid;
1639
1640
                assert(f.size + 5 < BUF_SIZE);
1641
                memmove(buf + 5, buf, f.size);
1642 6500
                vbe32enc(buf, (stid | exclusive));
1643 0
                buf[4] = s->weight;
1644
                f.size += 5;
1645 6500
1646 6500
                vtc_log(vl, 4, "stream->dependency: %u", s->dependency);
1647
                vtc_log(vl, 4, "stream->weight: %u", s->weight);
1648 6500
                if (exclusive)
1649 6500
                        exclusive_stream_dependency(s);
1650 6500
        }
1651 6500
        if (pad) {
1652
                if (strlen(pad) > 255)
1653 6500
                        vtc_fatal(vl, "Padding is limited to 255 bytes");
1654 6500
                f.flags |= PADDED;
1655 20
                assert(f.size + strlen(pad) < BUF_SIZE);
1656 20
                memmove(buf + 1, buf, f.size);
1657
                buf[0] = strlen(pad);
1658 20
                f.size += 1;
1659 20
                memcpy(buf + f.size, pad, strlen(pad));
1660 20
                f.size += strlen(pad);
1661 20
                free(pad);
1662 20
        }
1663
        if (f.type == TYPE_PUSH_PROMISE)
1664 20
                f.size += 4;
1665 20
        f.data = buf;
1666 20
        HPK_FreeIter(iter);
1667 10
        write_frame(s, &f, 1);
1668 20
        free(buf);
1669 6500
1670 30
        if (!body)
1671 0
                return;
1672 30
1673 30
        INIT_FRAME(f, DATA, bodylen, s->id, END_STREAM);
1674 30
        f.data = body;
1675 30
1676 30
        write_frame(s, &f, 1);
1677 30
        free(body);
1678 30
}
1679 30
1680 30
/* SECTION: stream.spec.data_1 txdata
1681 6500
 *
1682 5
 * By default, data frames are empty. The receiving end will know the whole body
1683 6500
 * has been delivered thanks to the END_STREAM flag set in the last DATA frame,
1684 6500
 * and txdata automatically set it.
1685 6500
 *
1686 6500
 * \-data STRING
1687
 *      Data to be embedded into the frame.
1688 6500
 *
1689 6420
 * \-datalen INT
1690
 *      Generate and INT-bytes long string to be sent in the frame.
1691 80
 *
1692 80
 * \-pad STRING / -padlen INT
1693
 *      Add string as padding to the frame, either the one you provided with
1694 80
 *      \-pad, or one that is generated for you, of length INT is -padlen
1695 80
 *      case.
1696 6500
 *
1697
 * \-nostrend
1698
 *      Don't set the END_STREAM flag, allowing to send more data on this
1699
 *      stream.
1700
 */
1701
static void
1702
cmd_txdata(CMD_ARGS)
1703
{
1704
        struct stream *s;
1705
        char *pad = NULL;
1706
        struct frame f;
1707
        char *body = NULL;
1708
        char *data = NULL;
1709
1710
        CAST_OBJ_NOTNULL(s, priv, STREAM_MAGIC);
1711
1712
        INIT_FRAME(f, DATA, 0, s->id, END_STREAM);
1713
1714
        while (*++av) {
1715
                if (!strcmp(*av, "-data")) {
1716
                        AZ(body);
1717
                        av++;
1718
                        body = strdup(*av);
1719
                } else if (!strcmp(*av, "-datalen")) {
1720 1600
                        AZ(body);
1721
                        av++;
1722
                        body = synth_body(*av, 0);
1723 1600
                } else if (!strcmp(*av, "-pad")) {
1724
                        AZ(pad);
1725 1600
                        av++;
1726 1600
                        AN(*av);
1727
                        pad = strdup(*av);
1728 1600
                } else if (!strcmp(*av, "-padlen")) {
1729
                        AZ(pad);
1730 1600
                        av++;
1731
                        pad = synth_body(*av, 0);
1732 4685
                } else if (!strcmp(*av, "-nostrend"))
1733 3085
                        f.flags &= ~END_STREAM;
1734 125
                else
1735 125
                        break;
1736 125
        }
1737 3085
        if (*av != NULL)
1738 175
                vtc_fatal(vl, "Unknown txdata spec: %s\n", *av);
1739 175
1740 175
        if (!body)
1741 2960
                body = strdup("");
1742 5
1743 5
        if (pad) {
1744 5
                f.flags |= PADDED;
1745 5
                if (strlen(pad) > 255)
1746 2785
                        vtc_fatal(vl, "Padding is limited to 255 bytes");
1747 1340
                data = malloc( 1 + strlen(body) + strlen(pad));
1748 1340
                AN(data);
1749 1340
                *((uint8_t *)data) = strlen(pad);
1750 2780
                f.size = 1;
1751 1440
                memcpy(data + f.size, body, strlen(body));
1752
                f.size += strlen(body);
1753 0
                memcpy(data + f.size, pad, strlen(pad));
1754
                f.size += strlen(pad);
1755 1600
                f.data = data;
1756 0
        } else {
1757
                f.size = strlen(body);
1758 1600
                f.data = body;
1759 1300
        }
1760
        write_frame(s, &f, 1);
1761 1600
        free(body);
1762 1345
        free(pad);
1763 1345
        free(data);
1764 0
}
1765 1345
1766 1345
/* SECTION: stream.spec.reset_txrst txrst
1767 1345
 *
1768 1345
 * Send a RST_STREAM frame. By default, txrst will send a 0 error code
1769 1345
 * (NO_ERROR).
1770 1345
 *
1771 1345
 * \-err STRING|INT
1772 1345
 *      Sets the error code to be sent. The argument can be an integer or a
1773 1345
 *      string describing the error, such as NO_ERROR, or CANCEL (see
1774 1345
 *      rfc7540#11.4 for more strings).
1775 255
 */
1776 255
static void
1777
cmd_txrst(CMD_ARGS)
1778 1600
{
1779 1600
        struct stream *s;
1780 1600
        char *p;
1781 1600
        uint32_t err = 0;
1782 1600
        struct frame f;
1783
1784
        CAST_OBJ_NOTNULL(s, priv, STREAM_MAGIC);
1785
1786
        INIT_FRAME(f, RST_STREAM, 4, s->id, 0);
1787
1788
        while (*++av) {
1789
                if (!strcmp(*av, "-err")) {
1790
                        ++av;
1791
                        for (err = 0; h2_errs[err]; err++) {
1792
                                if (!strcmp(h2_errs[err], *av))
1793
                                        break;
1794
                        }
1795 95
1796
                        if (h2_errs[err])
1797
                                continue;
1798
1799 95
                        STRTOU32(err, *av, p, vl, "-err");
1800
                } else
1801
                        break;
1802 95
        }
1803
        if (*av != NULL)
1804 95
                vtc_fatal(vl, "Unknown txrst spec: %s\n", *av);
1805
1806 135
        err = htonl(err);
1807 40
        f.data = (void *)&err;
1808 40
        write_frame(s, &f, 1);
1809 600
}
1810 560
1811 0
/* SECTION: stream.spec.prio_txprio txprio
1812 560
 *
1813
 * Send a PRIORITY frame
1814 40
 *
1815 0
 * \-stream INT
1816
 *      indicate the id of the stream the sender stream depends on.
1817 40
 *
1818 40
 * \-ex
1819 0
 *      the dependency should be made exclusive (only this streams depends on
1820
 *      the parent stream).
1821 95
 *
1822 0
 * \-weight INT
1823
 *      an 8-bits integer is used to balance priority between streams
1824 95
 *      depending on the same streams.
1825 95
 */
1826 95
static void
1827 95
cmd_txprio(CMD_ARGS)
1828
{
1829
        struct stream *s;
1830
        char *p;
1831
        uint32_t stid = 0;
1832
        struct frame f;
1833
        uint32_t weight = 0;
1834
        uint32_t exclusive = 0;
1835
        uint8_t buf[5];
1836
1837
        CAST_OBJ_NOTNULL(s, priv, STREAM_MAGIC);
1838
1839
        INIT_FRAME(f, PRIORITY, 5, s->id, 0);
1840
        f.data = (void *)buf;
1841
1842
        while (*++av) {
1843
                if (!strcmp(*av, "-stream")) {
1844
                        STRTOU32_CHECK(stid, av, p, vl, "-stream", 0);
1845 65
                } else if (!strcmp(*av, "-ex")) {
1846
                        exclusive = 1U << 31;
1847
                } else if (!strcmp(*av, "-weight")) {
1848
                        STRTOU32_CHECK(weight, av, p, vl, "-weight", 8);
1849 65
                } else
1850
                        break;
1851 65
        }
1852 65
        if (*av != NULL)
1853
                vtc_fatal(vl, "Unknown txprio spec: %s\n", *av);
1854
        s->weight = weight & 0xff;
1855 65
        s->dependency = stid;
1856
1857 65
        if (exclusive)
1858 65
                exclusive_stream_dependency(s);
1859
1860 155
        vbe32enc(buf, (stid | exclusive));
1861 90
        buf[4] = s->weight;
1862 45
        write_frame(s, &f, 1);
1863 90
}
1864 5
1865 45
#define PUT_KV(av, vl, name, val, code) \
1866 40
        do {\
1867 40
                STRTOU32_CHECK(val, av, p, vl, #name, 0);       \
1868 0
                vbe16enc(cursor, code);                         \
1869
                cursor += sizeof(uint16_t);                     \
1870 65
                vbe32enc(cursor, val);                          \
1871 0
                cursor += sizeof(uint32_t);                     \
1872 65
                f.size += 6;                                    \
1873 65
        } while(0)
1874
1875 65
/* SECTION: stream.spec.settings_txsettings txsettings
1876 5
 *
1877
 * SETTINGS frames must be acknowledge, arguments are as follow (most of them
1878 65
 * are from  rfc7540#6.5.2):
1879 65
 *
1880 65
 * \-hdrtbl INT
1881 65
 *      headers table size
1882
 *
1883
 * \-push BOOL
1884
 *      whether push frames are accepted or not
1885
 *
1886
 * \-maxstreams INT
1887
 *      maximum concurrent streams allowed
1888
 *
1889
 * \-winsize INT
1890
 *      sender's initial window size
1891
 *
1892
 * \-framesize INT
1893
 *      largest frame size authorized
1894
 *
1895
 * \-hdrsize INT
1896
 *      maximum size of the header list authorized
1897
 *
1898
 * \-0xHH[HH] INT
1899
 *      tx arbitraty settings with tag xx
1900
 *
1901
 * \-ack
1902
 *      set the ack bit
1903
 */
1904
static void
1905
cmd_txsettings(CMD_ARGS)
1906
{
1907
        struct stream *s, *s2;
1908
        struct http *hp;
1909
        char *p, *e;
1910
        unsigned long u;
1911
        uint32_t val = 0;
1912
        struct frame f;
1913
        //TODO dynamic alloc
1914
        char buf[512];
1915
        char *cursor = buf;
1916
1917
        CAST_OBJ_NOTNULL(s, priv, STREAM_MAGIC);
1918
        CAST_OBJ_NOTNULL(hp, s->hp, HTTP_MAGIC);
1919
1920
        memset(buf, 0, 512);
1921
        INIT_FRAME(f, SETTINGS, 0, s->id, 0);
1922
        f.data = buf;
1923
1924
        PTOK(pthread_mutex_lock(&hp->mtx));
1925
        while (*++av) {
1926
                if (!strcmp(*av, "-push")) {
1927
                        ++av;
1928
                        vbe16enc(cursor, 0x2);
1929
                        cursor += sizeof(uint16_t);
1930
                        if (!strcmp(*av, "false"))
1931
                                vbe32enc(cursor, 0);
1932
                        else if (!strcmp(*av, "true"))
1933
                                vbe32enc(cursor, 1);
1934
                        else
1935
                                vtc_fatal(vl, "Push parameter is either "
1936
                                    "\"true\" or \"false\", not %s", *av);
1937
                        cursor += sizeof(uint32_t);
1938
                        f.size += 6;
1939
                }
1940
                else if (!strcmp(*av, "-hdrtbl")) {
1941
                        PUT_KV(av, vl, hdrtbl, val, 0x1);
1942
                        assert(HPK_ResizeTbl(s->hp->decctx, val) != hpk_err);
1943
                }
1944
                else if (!strcmp(*av, "-maxstreams"))
1945
                        PUT_KV(av, vl, maxstreams, val, 0x3);
1946
                else if (!strcmp(*av, "-winsize"))      {
1947
                        PUT_KV(av, vl, winsize, val, 0x4);
1948
                        VTAILQ_FOREACH(s2, &hp->streams, list)
1949
                                s2->win_self += (val - hp->h2_win_self->init);
1950
                        hp->h2_win_self->init = val;
1951 2080
                }
1952
                else if (!strcmp(*av, "-framesize"))
1953
                        PUT_KV(av, vl, framesize, val, 0x5);
1954
                else if (!strcmp(*av, "-hdrsize"))
1955
                        PUT_KV(av, vl, hdrsize, val, 0x6);
1956
                else if (!strncmp(*av, "-0x", 3)) {
1957 2080
                        p = *av + 3;
1958
                        errno = 0;
1959
                        u = strtoul(p, &e, 16);
1960
                        if (*p == '\0' || *e != '\0' || u > 0xffff || errno != 0)
1961 2080
                                vtc_fatal(vl, "Invalid settings tag %s", p);
1962
                        assert(u <= 0xffff);
1963 2080
                        PUT_KV(av, vl, hdrtbl, val, (uint16_t)u);
1964 2080
                }
1965
                else if (!strcmp(*av, "-ack"))
1966 2080
                        f.flags |= 1;
1967 2080
                else
1968 2080
                        break;
1969
        }
1970 2080
        if (*av != NULL)
1971 3220
                vtc_fatal(vl, "Unknown txsettings spec: %s\n", *av);
1972 1140
1973 10
        AN(s->hp);
1974 1135
        write_frame(s, &f, 0);
1975 25
        PTOK(pthread_mutex_unlock(&hp->mtx));
1976 25
}
1977 25
1978 1110
/* SECTION: stream.spec.ping_txping txping
1979 5
 *
1980 1105
 * Send PING frame.
1981 40
 *
1982 85
 * \-data STRING
1983 45
 *      specify the payload of the frame, with STRING being an 8-char string.
1984 40
 *
1985 40
 * \-ack
1986 1065
 *      set the ACK flag.
1987 15
 */
1988 1050
static void
1989 5
cmd_txping(CMD_ARGS)
1990 1045
{
1991 10
        struct stream *s;
1992 1040
        struct frame f;
1993 10
        char buf[8];
1994 1035
1995 5
        memset(buf, 0, 8);
1996 1030
        CAST_OBJ_NOTNULL(s, priv, STREAM_MAGIC);
1997 0
        INIT_FRAME(f, PING, 8, s->id, 0);
1998 0
1999 0
        while (*++av) {
2000 0
                if (!strcmp(*av, "-data")) {
2001 0
                        av++;
2002 0
                        if (f.data)
2003 0
                                vtc_fatal(vl, "this frame already has data");
2004 0
                        if (strlen(*av) != 8)
2005 1030
                                vtc_fatal(vl, "data must be a 8-char string, found  (%s)", *av);
2006 1030
                        f.data = *av;
2007
                } else if (!strcmp(*av, "-ack"))
2008 0
                        f.flags |= 1;
2009
                else
2010 2080
                        break;
2011 0
        }
2012
        if (*av != NULL)
2013 2080
                vtc_fatal(vl, "Unknown txping spec: %s\n", *av);
2014 2080
        if (!f.data)
2015 2080
                f.data = buf;
2016 2080
        write_frame(s, &f, 1);
2017
}
2018
2019
/*
2020
 * SECTION: stream.spec.goaway_txgoaway txgoaway
2021
 *
2022
 * Possible options include:
2023
 *
2024
 * \-err STRING|INT
2025
 *      set the error code to explain the termination. The second argument
2026
 *      can be a integer or the string version of the error code as found
2027
 *      in rfc7540#7.
2028
 *
2029 35
 * \-laststream INT
2030
 *      the id of the "highest-numbered stream identifier for which the
2031
 *      sender of the GOAWAY frame might have taken some action on or might
2032
 *      yet take action on".
2033
 *
2034
 * \-debug
2035 35
 *      specify the debug data, if any to append to the frame.
2036 35
 */
2037 35
static void
2038
cmd_txgoaway(CMD_ARGS)
2039 70
{
2040 35
        struct stream *s;
2041 25
        char *p;
2042 25
        uint32_t err = 0;
2043 0
        uint32_t ls = 0;
2044 25
        struct frame f;
2045 0
2046 25
        CAST_OBJ_NOTNULL(s, priv, STREAM_MAGIC);
2047 35
2048 10
        INIT_FRAME(f, GOAWAY, 8, s->id, 0);
2049
2050 0
        while (*++av) {
2051
                if (!strcmp(*av, "-err")) {
2052 35
                        ++av;
2053 0
                        for (err = 0; h2_errs[err]; err++)
2054 35
                                if (!strcmp(h2_errs[err], *av))
2055 10
                                        break;
2056 35
2057 35
                        if (h2_errs[err])
2058
                                continue;
2059
2060
                        STRTOU32(err, *av, p, vl, "-err");
2061
                } else if (!strcmp(*av, "-laststream")) {
2062
                        STRTOU32_CHECK(ls, av, p, vl, "-laststream", 31);
2063
                } else if (!strcmp(*av, "-debug")) {
2064
                        ++av;
2065
                        if (f.data)
2066
                                vtc_fatal(vl, "this frame already has debug data");
2067
                        f.size = 8 + strlen(*av);
2068
                        f.data = malloc(f.size);
2069
                        AN(f.data);
2070
                        memcpy(f.data + 8, *av, f.size - 8);
2071
                } else
2072
                        break;
2073
        }
2074
        if (*av != NULL)
2075
                vtc_fatal(vl, "Unknown txgoaway spec: %s\n", *av);
2076
2077
        if (!f.data) {
2078 25
                f.data = malloc(8);
2079
                AN(f.data);
2080
        }
2081
        vbe32enc(f.data, ls);
2082 25
        vbe32enc(f.data + 4, err);
2083 25
        write_frame(s, &f, 1);
2084
        free(f.data);
2085
}
2086 25
2087
/* SECTION: stream.spec.winup_txwinup txwinup
2088 25
 *
2089
 * Transmit a WINDOW_UPDATE frame, increasing the amount of credit of the
2090 70
 * connection (from stream 0) or of the stream (any other stream).
2091 45
 *
2092 25
 * \-size INT
2093 375
 *      give INT credits to the peer.
2094 350
 */
2095 0
static void
2096
cmd_txwinup(CMD_ARGS)
2097 25
{
2098 0
        struct http *hp;
2099
        struct stream *s;
2100 25
        char *p;
2101 45
        struct frame f;
2102 10
        char buf[8];
2103 20
        uint32_t size = 0;
2104 10
2105 10
        CAST_OBJ_NOTNULL(s, priv, STREAM_MAGIC);
2106 0
        CAST_OBJ_NOTNULL(hp, s->hp, HTTP_MAGIC);
2107 10
        memset(buf, 0, 8);
2108 10
2109 10
        AN(av[1]);
2110 10
        AN(av[2]);
2111 10
2112 0
        INIT_FRAME(f, WINDOW_UPDATE, 4, s->id, 0);
2113
        f.data = buf;
2114 25
2115 0
        while (*++av)
2116
                if (!strcmp(*av, "-size")) {
2117 25
                        STRTOU32_CHECK(size, av, p, vl, "-size", 0);
2118 15
                } else
2119 15
                        break;
2120 15
        if (*av != NULL)
2121 25
                vtc_fatal(vl, "Unknown txwinup spec: %s\n", *av);
2122 25
2123 25
        PTOK(pthread_mutex_lock(&hp->mtx));
2124 25
        if (s->id == 0)
2125 25
                hp->h2_win_self->size += size;
2126
        s->win_self += size;
2127
        PTOK(pthread_mutex_unlock(&hp->mtx));
2128
2129
        size = htonl(size);
2130
        f.data = (void *)&size;
2131
        write_frame(s, &f, 1);
2132
}
2133
2134
static struct frame *
2135
rxstuff(struct stream *s)
2136 140
{
2137
        struct frame *f;
2138
2139
        CHECK_OBJ_NOTNULL(s, STREAM_MAGIC);
2140
2141
        PTOK(pthread_mutex_lock(&s->hp->mtx));
2142
        if (VTAILQ_EMPTY(&s->fq)) {
2143 140
                assert(s->hp->wf >= 0);
2144
                s->hp->wf++;
2145 140
                s->wf = 1;
2146 140
                PTOK(pthread_cond_signal(&s->hp->cond));
2147 140
                PTOK(pthread_cond_wait(&s->cond, &s->hp->mtx));
2148
        }
2149 140
        if (VTAILQ_EMPTY(&s->fq)) {
2150 140
                PTOK(pthread_mutex_unlock(&s->hp->mtx));
2151
                return (NULL);
2152 140
        }
2153 140
        clean_frame(&s->frame);
2154
        f = VTAILQ_LAST(&s->fq, fq_head);
2155 280
        CHECK_OBJ_NOTNULL(f, FRAME_MAGIC);
2156 140
        VTAILQ_REMOVE(&s->fq, f, list);
2157 140
        PTOK(pthread_mutex_unlock(&s->hp->mtx));
2158 140
        return (f);
2159 0
}
2160 140
2161 0
#define CHKFRAME(rt, wt, rcv, func) \
2162
        do { \
2163 140
        if (rt != wt) \
2164 140
                vtc_fatal(vl, "Frame #%d for %s was of type %s (%d) " \
2165 50
                    "instead of %s (%d)", \
2166 140
                    rcv, func, \
2167 140
                    rt < TYPE_MAX ? h2_types[rt] : "?", rt, \
2168
                    wt < TYPE_MAX ? h2_types[wt] : "?", wt); \
2169 140
        } while (0);
2170 140
2171 140
/* SECTION: stream.spec.data_11 rxhdrs
2172 140
 *
2173
 * ``rxhdrs`` will expect one HEADER frame, then, depending on the arguments,
2174
 * zero or more CONTINUATION frame.
2175 4835
 *
2176
 * \-all
2177
 *      Keep waiting for CONTINUATION frames until END_HEADERS flag is seen.
2178
 *
2179 4835
 * \-some INT
2180
 *      Retrieve INT - 1 CONTINUATION frames after the HEADER frame.
2181 4835
 *
2182 4835
 */
2183 4812
static void
2184 4812
cmd_rxhdrs(CMD_ARGS)
2185 4812
{
2186 4812
        struct stream *s;
2187 4812
        struct frame *f = NULL;
2188 4812
        char *p;
2189 4835
        int loop = 0;
2190 0
        unsigned long int times = 1;
2191 0
        unsigned rcv = 0;
2192
        enum h2_type_e expect = TYPE_HEADERS;
2193 4835
2194 4835
        CAST_OBJ_NOTNULL(s, priv, STREAM_MAGIC);
2195 4835
2196 4835
        while (*++av) {
2197 4835
                if (!strcmp(*av, "-some")) {
2198 4835
                        STRTOU32_CHECK(times, av, p, vl, "-some", 0);
2199 4835
                        if (!times)
2200
                                vtc_fatal(vl, "-some argument must be more"
2201
                                               "than 0 (found \"%s\")\n", *av);
2202
                } else if (!strcmp(*av, "-all"))
2203
                        loop = 1;
2204
                else
2205
                        break;
2206
        }
2207
        if (*av != NULL)
2208
                vtc_fatal(vl, "Unknown rxhdrs spec: %s\n", *av);
2209
2210
        do {
2211
                replace_frame(&f, rxstuff(s));
2212
                if (f == NULL)
2213
                        break;
2214
                rcv++;
2215
                CHKFRAME(f->type, expect, rcv, "rxhdrs");
2216
                expect = TYPE_CONTINUATION;
2217
        } while (rcv < times || (loop && !(f->flags & END_HEADERS)));
2218
        replace_frame(&s->frame, f);
2219
}
2220
2221
static void
2222
cmd_rxcont(CMD_ARGS)
2223
{
2224 70
        struct stream *s;
2225
        struct frame *f = NULL;
2226
        char *p;
2227 70
        int loop = 0;
2228
        unsigned long int times = 1;
2229 70
        unsigned rcv = 0;
2230 70
2231 70
        (void)av;
2232 70
        CAST_OBJ_NOTNULL(s, priv, STREAM_MAGIC);
2233
2234 70
        while (*++av)
2235
                if (!strcmp(*av, "-some")) {
2236 80
                        STRTOU32(times, *av, p, vl, "-some");
2237 10
                        if (!times)
2238 5
                                vtc_fatal(vl, "-some argument must be more"
2239 5
                                               "than 0 (found \"%s\")\n", *av);
2240 0
                } else if (!strcmp(*av, "-all"))
2241 0
                        loop = 1;
2242 10
                else
2243 5
                        break;
2244
        if (*av != NULL)
2245 0
                vtc_fatal(vl, "Unknown rxcont spec: %s\n", *av);
2246
2247 70
        do {
2248 0
                replace_frame(&f, rxstuff(s));
2249
                if (f == NULL)
2250 70
                        break;
2251 85
                rcv++;
2252 85
                CHKFRAME(f->type, TYPE_CONTINUATION, rcv, "rxcont");
2253 0
        } while (rcv < times || (loop && !(f->flags & END_HEADERS)));
2254 85
        replace_frame(&s->frame, f);
2255 85
}
2256 85
2257 85
2258 70
/* SECTION: stream.spec.data_13 rxdata
2259 70
 *
2260
 * Receiving data is done using the ``rxdata`` keywords and will retrieve one
2261
 * DATA frame, if you wish to receive more, you can use these two convenience
2262 25
 * arguments:
2263
 *
2264
 * \-all
2265 25
 *      keep waiting for DATA frame until one sets the END_STREAM flag
2266
 *
2267 25
 * \-some INT
2268 25
 *      retrieve INT DATA frames.
2269 25
 *
2270
 */
2271 25
static void
2272 25
cmd_rxdata(CMD_ARGS)
2273
{
2274 25
        struct stream *s;
2275 0
        struct frame *f = NULL;
2276 0
        char *p;
2277 0
        int loop = 0;
2278 0
        unsigned long int times = 1;
2279 0
        unsigned rcv = 0;
2280 0
2281 0
        (void)av;
2282
        CAST_OBJ_NOTNULL(s, priv, STREAM_MAGIC);
2283 0
2284 25
        while (*++av)
2285 0
                if (!strcmp(*av, "-some")) {
2286
                        av++;
2287 25
                        STRTOU32(times, *av, p, vl, "-some");
2288 25
                        if (!times)
2289 25
                                vtc_fatal(vl, "-some argument must be more"
2290 0
                                               "than 0 (found \"%s\")\n", *av);
2291 25
                } else if (!strcmp(*av, "-all"))
2292 25
                        loop = 1;
2293 25
                else
2294 25
                        break;
2295 25
        if (*av != NULL)
2296
                vtc_fatal(vl, "Unknown rxdata spec: %s\n", *av);
2297
2298
        do {
2299
                replace_frame(&f, rxstuff(s));
2300
                if (f == NULL)
2301
                        break;
2302
                rcv++;
2303
                CHKFRAME(f->type, TYPE_DATA, rcv, "rxhdata");
2304
        } while (rcv < times || (loop && !(f->flags & END_STREAM)));
2305
        replace_frame(&s->frame, f);
2306
}
2307
2308
/* SECTION: stream.spec.data_10 rxreq, rxresp
2309
 *
2310
 * These are two convenience functions to receive headers and body of an
2311
 * incoming request or response. The only difference is that rxreq can only be
2312 75
 * by a server, and rxresp by a client.
2313
 *
2314
 */
2315 75
2316
#define cmd_rxreq       cmd_rxmsg
2317 75
#define cmd_rxresp      cmd_rxmsg
2318 75
2319 75
static void
2320
cmd_rxmsg(CMD_ARGS)
2321 75
{
2322 75
        struct stream *s;
2323
        struct frame *f = NULL;
2324 80
        int end_stream;
2325 5
        int rcv = 0;
2326 5
2327 5
        CAST_OBJ_NOTNULL(s, priv, STREAM_MAGIC);
2328 5
2329 0
        if (!strcmp(av[0], "rxreq"))
2330 0
                ONLY_H2_SERVER(s->hp, av);
2331 5
        else
2332 0
                ONLY_H2_CLIENT(s->hp, av);
2333
2334 0
        do {
2335 75
                replace_frame(&f, rxstuff(s));
2336 0
                CHECK_OBJ_ORNULL(f, FRAME_MAGIC);
2337
                if (f == NULL)
2338 75
                        return;
2339 85
        } while (f->type == TYPE_WINDOW_UPDATE);
2340 85
2341 0
        rcv++;
2342 85
        CHKFRAME(f->type, TYPE_HEADERS, rcv, *av);
2343 85
2344 85
        end_stream = f->flags & END_STREAM;
2345 75
2346 75
        while (!(f->flags & END_HEADERS)) {
2347
                replace_frame(&f, rxstuff(s));
2348
                CHECK_OBJ_ORNULL(f, FRAME_MAGIC);
2349
                if (f == NULL)
2350
                        return;
2351
                rcv++;
2352
                CHKFRAME(f->type, TYPE_CONTINUATION, rcv, *av);
2353
        }
2354
2355
        while (!end_stream) {
2356
                replace_frame(&f, rxstuff(s));
2357
                CHECK_OBJ_ORNULL(f, FRAME_MAGIC);
2358
                if (f == NULL)
2359
                        break;
2360 925
                rcv++;
2361
                CHKFRAME(f->type, TYPE_DATA, rcv, *av);
2362
                end_stream = f->flags & END_STREAM;
2363 925
        }
2364
        replace_frame(&s->frame, f);
2365 925
}
2366
2367 925
/* SECTION: stream.spec.data_12 rxpush
2368
 *
2369 925
 * This works like ``rxhdrs``, expecting a PUSH frame and then zero or more
2370 235
 * CONTINUATION frames.
2371
 *
2372 690
 * \-all
2373
 *      Keep waiting for CONTINUATION frames until END_HEADERS flag is seen.
2374 925
 *
2375 935
 * \-some INT
2376 935
 *      Retrieve INT - 1 CONTINUATION frames after the PUSH frame.
2377 935
 *
2378 0
 */
2379 935
static void
2380
cmd_rxpush(CMD_ARGS)
2381 925
{
2382 925
        struct stream *s;
2383
        struct frame *f = NULL;
2384 925
        char *p;
2385
        int loop = 0;
2386 955
        unsigned long int times = 1;
2387 30
        unsigned rcv = 0;
2388 30
        enum h2_type_e expect = TYPE_PUSH_PROMISE;
2389 30
2390 0
        CAST_OBJ_NOTNULL(s, priv, STREAM_MAGIC);
2391 30
2392 30
        while (*++av) {
2393
                if (!strcmp(*av, "-some")) {
2394
                        STRTOU32_CHECK(times, av, p, vl, "-some", 0);
2395 1830
                        if (!times)
2396 905
                                vtc_fatal(vl, "-some argument must be more"
2397 905
                                               "than 0 (found \"%s\")\n", *av);
2398 905
                } else if (!strcmp(*av, "-all")) {
2399 0
                        loop = 1;
2400 905
                } else
2401 905
                        break;
2402 905
        }
2403
        if (*av != NULL)
2404 925
                vtc_fatal(vl, "Unknown rxpush spec: %s\n", *av);
2405 925
2406
        do {
2407
                f = rxstuff(s);
2408
                if (!f)
2409
                        return;
2410
                rcv++;
2411
                CHKFRAME(f->type, expect, rcv, "rxpush");
2412
                expect = TYPE_CONTINUATION;
2413
        } while (rcv < times || (loop && !(f->flags & END_HEADERS)));
2414
        s->frame = f;
2415
}
2416
2417
/* SECTION: stream.spec.winup_rxwinup rxwinup
2418
 *
2419
 * Receive a WINDOW_UPDATE frame.
2420 5
 */
2421
static void
2422
cmd_rxwinup(CMD_ARGS)
2423 5
{
2424
        struct stream *s;
2425 5
        struct frame *f;
2426 5
2427 5
        CAST_OBJ_NOTNULL(s, priv, STREAM_MAGIC);
2428 5
        s->frame = rxstuff(s);
2429
        CAST_OBJ_NOTNULL(f, s->frame, FRAME_MAGIC);
2430 5
        CHKFRAME(f->type, TYPE_WINDOW_UPDATE, 0, *av);
2431
        if (s->id == 0)
2432 5
                s->hp->h2_win_peer->size += s->frame->md.winup_size;
2433 0
        s->win_peer += s->frame->md.winup_size;
2434 0
}
2435 0
2436 0
/* SECTION: stream.spec.settings_rxsettings rxsettings
2437 0
 *
2438 0
 * Receive a SETTINGS frame.
2439 0
 */
2440 0
static void
2441 0
cmd_rxsettings(CMD_ARGS)
2442
{
2443 5
        struct stream *s, *s2;
2444 0
        uint32_t val = 0;
2445
        struct http *hp;
2446 5
        struct frame *f;
2447 5
2448 5
        CAST_OBJ_NOTNULL(s, priv, STREAM_MAGIC);
2449 0
        CAST_OBJ_NOTNULL(hp, s->hp, HTTP_MAGIC);
2450 5
        s->frame = rxstuff(s);
2451 5
        CAST_OBJ_NOTNULL(f, s->frame, FRAME_MAGIC);
2452 5
        CHKFRAME(f->type, TYPE_SETTINGS, 0, *av);
2453 5
        if (! isnan(f->md.settings[SETTINGS_INITIAL_WINDOW_SIZE])) {
2454 5
                val = (uint32_t)f->md.settings[SETTINGS_INITIAL_WINDOW_SIZE];
2455 5
                VTAILQ_FOREACH(s2, &hp->streams, list)
2456
                        s2->win_peer += (val - hp->h2_win_peer->init);
2457
                hp->h2_win_peer->init = val;
2458
        }
2459
}
2460
/* SECTION: stream.spec.prio_rxprio rxprio
2461
 *
2462 125
 * Receive a PRIORITY frame.
2463
 */
2464
static void
2465
cmd_rxprio (CMD_ARGS)
2466
{
2467 125
        struct stream *s;
2468 125
        (void)av;
2469 125
        CAST_OBJ_NOTNULL(s, priv, STREAM_MAGIC);
2470 125
        if (s->hp->no_rfc7540_priorities) {
2471 125
                vtc_log(vl, 4, "skipping rxprio: no_rfc7540_priorities is set");
2472 30
                return;
2473 125
        }
2474 125
        s->frame = rxstuff(s);
2475
        if (s->frame != NULL && s->frame->type != TYPE_PRIORITY) {
2476
                vtc_fatal(vl,
2477
                    "Wrong frame type %s (%d) wanted %s",
2478
                    s->frame->type < TYPE_MAX ?
2479
                    h2_types[s->frame->type] : "?",
2480
                    s->frame->type, "PRIORITY");
2481 2094
        }
2482
}
2483
2484 2094
#define RXFUNC(lctype, upctype) \
2485
        static void \
2486
        cmd_rx ## lctype(CMD_ARGS) \
2487
        { \
2488 2094
                struct stream *s; \
2489 2094
                (void)av; \
2490 2094
                CAST_OBJ_NOTNULL(s, priv, STREAM_MAGIC); \
2491 2094
                s->frame = rxstuff(s); \
2492 2094
                if (s->frame != NULL && s->frame->type != TYPE_ ## upctype) { \
2493 2094
                        vtc_fatal(vl, \
2494 685
                            "Wrong frame type %s (%d) wanted %s", \
2495 1405
                            s->frame->type < TYPE_MAX ? \
2496 720
                            h2_types[s->frame->type] : "?", \
2497 685
                            s->frame->type, #upctype); \
2498 685
                } \
2499 2094
        }
2500
2501
/* SECTION: stream.spec.reset_rxrst rxrst
2502
 *
2503
 * Receive a RST_STREAM frame.
2504
 */
2505 45
RXFUNC(rst,     RST_STREAM)
2506
2507
/* SECTION: stream.spec.ping_rxping rxping
2508 45
 *
2509 45
 * Receive a PING frame.
2510 45
 */
2511 30
RXFUNC(ping,    PING)
2512 30
2513
/* SECTION: stream.spec.goaway_rxgoaway rxgoaway
2514 15
 *
2515 15
 * Receive a GOAWAY frame.
2516 0
 */
2517
RXFUNC(goaway,  GOAWAY)
2518 0
2519 0
/* SECTION: stream.spec.frame_rxframe
2520 0
 *
2521
 * Receive a frame, any frame.
2522 45
 */
2523
static void
2524
cmd_rxframe(CMD_ARGS)
2525
{
2526
        struct stream *s;
2527
2528
        (void)vl;
2529
        (void)av;
2530
        CAST_OBJ_NOTNULL(s, priv, STREAM_MAGIC);
2531
        if (rxstuff(s) == NULL)
2532
                vtc_fatal(s->vl, "No frame received");
2533
}
2534
2535
static void
2536
cmd_expect(CMD_ARGS)
2537
{
2538
        struct http *hp;
2539
        struct stream *s;
2540
        const char *lhs;
2541
        char *cmp;
2542
        const char *rhs;
2543
        char buf[20];
2544
2545 295
        CAST_OBJ_NOTNULL(s, priv, STREAM_MAGIC);
2546
        hp = s->hp;
2547
        CHECK_OBJ_NOTNULL(hp, HTTP_MAGIC);
2548
2549
        AZ(strcmp(av[0], "expect"));
2550
        av++;
2551 30
2552
        AN(av[0]);
2553
        AN(av[1]);
2554
        AN(av[2]);
2555
        AZ(av[3]);
2556
        PTOK(pthread_mutex_lock(&hp->mtx));
2557 205
        lhs = cmd_var_resolve(s, av[0], buf);
2558
        cmp = av[1];
2559
        rhs = cmd_var_resolve(s, av[2], buf);
2560
        vtc_expect(vl, av[0], lhs, cmp, av[2], rhs);
2561
        PTOK(pthread_mutex_unlock(&hp->mtx));
2562
}
2563
2564 0
/* SECTION: stream.spec.gunzip gunzip
2565
 *
2566
 * Same as the ``gunzip`` command for HTTP/1.
2567
 */
2568 0
static void
2569 0
cmd_gunzip(CMD_ARGS)
2570 0
{
2571 0
        struct http *hp;
2572 0
        struct stream *s;
2573 0
2574
        (void)av;
2575
        (void)vl;
2576 3875
2577
        CAST_OBJ_NOTNULL(s, priv, STREAM_MAGIC);
2578
        hp = s->hp;
2579
        CHECK_OBJ_NOTNULL(hp, HTTP_MAGIC);
2580
2581
        vtc_gunzip(hp, s->body, &s->bodylen);
2582
}
2583
2584
/* SECTION: stream.spec.write_body
2585 3875
 *
2586 3875
 * write_body STRING
2587 3875
 *      Same as the ``write_body`` command for HTTP/1.
2588
 */
2589 3875
static void
2590 3875
cmd_write_body(CMD_ARGS)
2591 3875
{
2592 3875
        struct stream *s;
2593 3875
2594
        (void)vl;
2595 3875
        CAST_OBJ_NOTNULL(s, priv, STREAM_MAGIC);
2596 3875
        AN(av[0]);
2597 3875
        AN(av[1]);
2598 3875
        AZ(av[2]);
2599 3875
        AZ(strcmp(av[0], "write_body"));
2600 3875
        if (VFIL_writefile(NULL, av[1], s->body, s->bodylen) != 0)
2601 3875
                vtc_fatal(s->vl, "failed to write body: %s (%d)",
2602 3875
                    strerror(errno), errno);
2603
}
2604
2605
/* SECTION: stream.spec Specification
2606
 *
2607
 * The specification of a stream follows the exact same rules as one for a
2608
 * client or a server.
2609 10
 */
2610
static const struct cmds stream_cmds[] = {
2611
#define CMD_STREAM(n) { CMDS_MAGIC, #n, cmd_##n },
2612
        /* spec */
2613
        CMD_STREAM(expect)
2614 10
        CMD_STREAM(gunzip)
2615 10
        CMD_STREAM(rxcont)
2616
        CMD_STREAM(rxdata)
2617 10
        CMD_STREAM(rxframe)
2618 10
        CMD_STREAM(rxgoaway)
2619 10
        CMD_STREAM(rxhdrs)
2620
        CMD_STREAM(rxping)
2621 10
        CMD_STREAM(rxprio)
2622 10
        CMD_STREAM(rxpush)
2623
        CMD_STREAM(rxreq)
2624
        CMD_STREAM(rxresp)
2625
        CMD_STREAM(rxrst)
2626
        CMD_STREAM(rxsettings)
2627
        CMD_STREAM(rxwinup)
2628
        CMD_STREAM(sendhex)
2629
        CMD_STREAM(txcont)
2630 10
        CMD_STREAM(txdata)
2631
        CMD_STREAM(txgoaway)
2632
        CMD_STREAM(txping)
2633
        CMD_STREAM(txprio)
2634 10
        CMD_STREAM(txpush)
2635 10
        CMD_STREAM(txreq)
2636 10
        CMD_STREAM(txresp)
2637 10
        CMD_STREAM(txrst)
2638 10
        CMD_STREAM(txsettings)
2639 10
        CMD_STREAM(txwinup)
2640 10
        CMD_STREAM(write_body)
2641 0
        { CMDS_MAGIC, NULL, NULL }
2642 0
#undef CMD_STREAM
2643 10
};
2644
2645
static void *
2646
stream_thread(void *priv)
2647
{
2648
        struct stream *s;
2649
2650
        CAST_OBJ_NOTNULL(s, priv, STREAM_MAGIC);
2651
        parse_string(s->vl, s, s->spec);
2652
        vtc_log(s->vl, 2, "Ending stream %u", s->id);
2653
        return (NULL);
2654
}
2655
/**********************************************************************
2656
 * Allocate and initialize a stream
2657
 */
2658
2659
static struct stream *
2660
stream_new(const char *name, struct http *h)
2661
{
2662
        char *p, buf[20];
2663
        struct stream *s;
2664
2665
        if (!strcmp("next", name)) {
2666
                if (h->last_stream > 0)
2667
                        bprintf(buf, "%d", h->last_stream + 2);
2668
                else
2669
                        bprintf(buf, "%d", 1);
2670
                name = buf;
2671
        }
2672
2673
        ALLOC_OBJ(s, STREAM_MAGIC);
2674
        AN(s);
2675
        PTOK(pthread_cond_init(&s->cond, NULL));
2676
        REPLACE(s->name, name);
2677
        AN(s->name);
2678
        VTAILQ_INIT(&s->fq);
2679
        s->win_self = h->h2_win_self->init;
2680
        s->win_peer = h->h2_win_peer->init;
2681
        s->vl = vtc_logopen("%s.%s", h->sess->name, name);
2682
        vtc_log_set_cmd(s->vl, stream_cmds);
2683
2684
        s->weight = 16;
2685
        s->dependency = 0;
2686 2948
2687
        STRTOU32(s->id, name, p, s->vl, "stream");
2688
        if (s->id & (1U << 31))
2689
                vtc_fatal(s->vl, "Stream id must be a 31-bits integer "
2690 2948
                    "(found %s)", name);
2691 2948
2692 2948
        CHECK_OBJ_NOTNULL(h, HTTP_MAGIC);
2693 2948
        s->hp = h;
2694
        h->last_stream = s->id;
2695
2696
        //bprintf(s->connect, "%s", "${v1_sock}");
2697
        PTOK(pthread_mutex_lock(&h->mtx));
2698
        VTAILQ_INSERT_HEAD(&h->streams, s, list);
2699
        PTOK(pthread_mutex_unlock(&h->mtx));
2700 2530
        return (s);
2701
}
2702
2703
/**********************************************************************
2704
 * Clean up stream
2705 2530
 */
2706 105
2707 70
static void
2708
stream_delete(struct stream *s)
2709 35
{
2710 105
        struct frame *f, *f2;
2711 105
2712
        CHECK_OBJ_NOTNULL(s, STREAM_MAGIC);
2713 2530
2714 2530
        VTAILQ_FOREACH_SAFE(f, &s->fq, list, f2) {
2715 2530
                VTAILQ_REMOVE(&s->fq, f, list);
2716 2530
                clean_frame(&f);
2717 2530
        }
2718 2530
        vtc_logclose(s->vl);
2719 2530
        clean_headers(s->req);
2720 2530
        clean_headers(s->resp);
2721 2530
        AZ(s->frame);
2722 2530
        free(s->body);
2723
        free(s->spec);
2724 2530
        free(s->name);
2725 2530
        FREE_OBJ(s);
2726
}
2727 2530
2728 2530
/**********************************************************************
2729 0
 * Start the stream thread
2730 0
 */
2731
2732 2530
static void
2733 2530
stream_start(struct stream *s)
2734 2530
{
2735
        CHECK_OBJ_NOTNULL(s, STREAM_MAGIC);
2736
        vtc_log(s->hp->vl, 2, "Starting stream %s (%p)", s->name, s);
2737 2530
        PTOK(pthread_create(&s->tp, NULL, stream_thread, s));
2738 2530
        s->running = 1;
2739 2530
}
2740 2530
2741
/**********************************************************************
2742
 * Wait for stream thread to stop
2743
 */
2744
static void
2745
stream_wait(struct stream *s)
2746
{
2747
        void *res;
2748 2530
        struct frame *f, *f2;
2749
2750
        CHECK_OBJ_NOTNULL(s, STREAM_MAGIC);
2751
        vtc_log(s->hp->vl, 2, "Waiting for stream %u", s->id);
2752 2530
        PTOK(pthread_join(s->tp, &res));
2753
        if (res != NULL)
2754 2695
                vtc_fatal(s->hp->vl, "Stream %u returned \"%s\"", s->id,
2755 165
                    (char *)res);
2756 165
2757 165
        VTAILQ_FOREACH_SAFE(f, &s->fq, list, f2) {
2758 2530
                VTAILQ_REMOVE(&s->fq, f, list);
2759 2530
                clean_frame(&f);
2760 2530
        }
2761 2530
        clean_frame(&s->frame);
2762 2530
        s->tp = 0;
2763 2530
        s->running = 0;
2764 2530
}
2765 2530
2766 2530
/**********************************************************************
2767
 * Run the stream thread
2768
 */
2769
2770
static void
2771
stream_run(struct stream *s)
2772
{
2773 2950
        stream_start(s);
2774
        stream_wait(s);
2775 2950
}
2776 2950
2777 2950
2778 2950
2779 2950
/* SECTION: client-server.spec.stream
2780
 *
2781
 * stream
2782
 *      HTTP/2 introduces the concept of streams, and these come with
2783
 *      their own specification, and as it's quite big, have been moved
2784
 *      to their own chapter.
2785 2950
 *
2786
 * SECTION: stream stream
2787
 *
2788
 * (note: this section is at the top-level for easier navigation, but
2789
 * it's part of the client/server specification)
2790 2950
 *
2791 2950
 * Streams map roughly to a request in HTTP/2, a request is sent on
2792 2950
 * stream N, the response too, then the stream is discarded. The main
2793 2950
 * exception is the first stream, 0, that serves as coordinator.
2794 0
 *
2795 0
 * Stream syntax follow the client/server one::
2796
 *
2797 2984
 *      stream ID [SPEC] [ACTION]
2798 34
 *
2799 34
 * ID is the HTTP/2 stream number, while SPEC describes what will be
2800 34
 * done in that stream. If ID has the value ``next``, the actual stream
2801 2950
 * number is computed based on the last one.
2802 2950
 *
2803 2950
 * Note that, when parsing a stream action, if the entity isn't operating
2804 2950
 * in HTTP/2 mode, these spec is ran before::
2805
 *
2806
 *      txpri/rxpri # client/server
2807
 *      stream 0 {
2808
 *          txsettings
2809
 *          rxsettings
2810
 *          txsettings -ack
2811 2690
 *          rxsettings
2812
 *          expect settings.ack == true
2813 2690
 *      } -run
2814 2690
 *
2815 2690
 * And HTTP/2 mode is then activated before parsing the specification.
2816
 *
2817
 * SECTION: stream.actions Actions
2818
 *
2819
 * \-start
2820
 *      Run the specification in a thread, giving back control immediately.
2821
 *
2822
 * \-wait
2823
 *      Wait for the started thread to finish running the spec.
2824
 *
2825
 * \-run
2826
 *      equivalent to calling ``-start`` then ``-wait``.
2827
 */
2828
2829
void
2830
cmd_stream(CMD_ARGS)
2831
{
2832
        struct stream *s;
2833
        struct http *h;
2834
2835
        (void)vl;
2836
        CAST_OBJ_NOTNULL(h, priv, HTTP_MAGIC);
2837
2838
        AZ(strcmp(av[0], "stream"));
2839
        av++;
2840
2841
        VTAILQ_FOREACH(s, &h->streams, list)
2842
                if (!strcmp(s->name, av[0]))
2843
                        break;
2844
        if (s == NULL)
2845
                s = stream_new(av[0], h);
2846
        av++;
2847
2848
        for (; *av != NULL; av++) {
2849
                if (vtc_error)
2850
                        break;
2851
2852
                if (!strcmp(*av, "-wait")) {
2853
                        stream_wait(s);
2854
                        continue;
2855
                }
2856
2857
                /* Don't muck about with a running client */
2858
                if (s->running)
2859
                        stream_wait(s);
2860
2861
                if (!strcmp(*av, "-start")) {
2862
                        stream_start(s);
2863
                        continue;
2864
                }
2865
                if (!strcmp(*av, "-run")) {
2866
                        stream_run(s);
2867
                        continue;
2868
                }
2869
                if (**av == '-')
2870 3069
                        vtc_fatal(vl, "Unknown stream argument: %s", *av);
2871
                REPLACE(s->spec, *av);
2872
        }
2873
}
2874
2875 3069
void
2876 3069
b64_settings(struct http *hp, const char *s)
2877
{
2878 3069
        uint16_t i;
2879 3069
        uint64_t v, vv;
2880
        const char *buf;
2881 6074
        int shift;
2882 3544
2883 539
        while (*s) {
2884 3069
                v = 0;
2885 2530
                for (shift = 42; shift >= 0; shift -= 6) {
2886 3069
                        if (*s >= 'A' && *s <= 'Z')
2887
                                vv = (*s - 'A');
2888 9094
                        else if (*s >= 'a' && *s <= 'z')
2889 6025
                                vv = (*s - 'a') + 26;
2890 0
                        else if (*s >= '0' && *s <= '9')
2891
                                vv = (*s - '0') + 52;
2892 6025
                        else if (*s == '-')
2893 125
                                vv = 62;
2894 125
                        else if (*s == '_')
2895
                                vv = 63;
2896
                        else
2897
                                vtc_fatal(hp->vl,
2898 5900
                                    "Bad \"HTTP2-Settings\" header");
2899 0
                        v |= vv << shift;
2900
                        s++;
2901 5900
                }
2902 260
                i = v >> 32;
2903 260
                v &= 0xffff;
2904
2905 5640
                if (i <= SETTINGS_MAX)
2906 2690
                        buf = h2_settings[i];
2907 2690
                else
2908
                        buf = "unknown";
2909 2950
2910 0
                if (i == SETTINGS_NO_RFC7540_PRIORITIES) {
2911 2950
                        hp->no_rfc7540_priorities = v;
2912 2950
                }
2913 3069
                if (i == SETTINGS_HEADER_TABLE_SIZE) {
2914
                        enum hpk_result hrs;
2915
                        if (hp->sfd) {
2916 15
                                AN(hp->encctx);
2917
                                hrs = HPK_ResizeTbl(hp->encctx, v);
2918
                        } else {
2919
                                AN(hp->decctx);
2920
                                hrs = HPK_ResizeTbl(hp->decctx, v);
2921
                        }
2922
                        if (hrs != hpk_done)
2923 60
                                vtc_fatal(hp->vl, "HPK resize failed %d\n", hrs);
2924 45
                }
2925 405
2926 360
                vtc_log(hp->vl, 4, "Upgrade: %s (%d): %ju",
2927 300
                    buf, i, (intmax_t)v);
2928 60
        }
2929 30
}
2930 30
2931 0
void
2932 30
start_h2(struct http *hp)
2933 0
{
2934 30
        CHECK_OBJ_NOTNULL(hp, HTTP_MAGIC);
2935 30
        PTOK(pthread_mutex_init(&hp->mtx, NULL));
2936
        PTOK(pthread_cond_init(&hp->cond, NULL));
2937 0
        VTAILQ_INIT(&hp->streams);
2938
        hp->h2_win_self->init = 0xffff;
2939 360
        hp->h2_win_self->size = 0xffff;
2940 360
        hp->h2_win_peer->init = 0xffff;
2941 360
        hp->h2_win_peer->size = 0xffff;
2942 45
        hp->h2 = 1;
2943 45
2944
        hp->decctx = HPK_NewCtx(4096);
2945 45
        hp->encctx = HPK_NewCtx(4096);
2946 45
        PTOK(pthread_create(&hp->tp, NULL, receive_frame, hp));
2947
}
2948 0
2949
void
2950 45
stop_h2(struct http *hp)
2951 15
{
2952 15
        struct stream *s, *s2;
2953 45
2954
        CHECK_OBJ_NOTNULL(hp, HTTP_MAGIC);
2955 0
        VTAILQ_FOREACH_SAFE(s, &hp->streams, list, s2) {
2956 0
                if (s->running)
2957 0
                        stream_wait(s);
2958 0
                PTOK(pthread_mutex_lock(&hp->mtx));
2959 0
                VTAILQ_REMOVE(&hp->streams, s, list);
2960 0
                PTOK(pthread_mutex_unlock(&hp->mtx));
2961
                stream_delete(s);
2962 0
        }
2963 0
2964 0
        PTOK(pthread_mutex_lock(&hp->mtx));
2965
        hp->h2 = 0;
2966 90
        PTOK(pthread_cond_signal(&hp->cond));
2967 45
        PTOK(pthread_mutex_unlock(&hp->mtx));
2968
        PTOK(pthread_join(hp->tp, NULL));
2969 15
2970
        HPK_FreeCtx(hp->decctx);
2971
        HPK_FreeCtx(hp->encctx);
2972 1064
2973
        PTOK(pthread_mutex_destroy(&hp->mtx));
2974 1064
        PTOK(pthread_cond_destroy(&hp->cond));
2975 1064
}