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 16220
#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 16073
#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 15170
        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 15149
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 15086
#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 15086
};
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 15044
};
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 14960
        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 14918
        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 14855
        int                     dependency;
119
        int                     weight;
120
};
121
122
static void
123 25452
clean_headers(struct hpk_hdr *h)
124
{
125 25452
        unsigned n = MAX_HDR;
126
127 64541
        while (h->t && n > 0) {
128 24297
                if (h->key.len)
129 24255
                        free(h->key.ptr);
130 24297
                free(h->value.ptr);
131 24297
                memset(h, 0, sizeof(*h));
132 24297
                h++;
133 24297
                n--;
134
        }
135 25452
}
136 14792
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 14645
        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 1197
http_write(const struct http *hp, int lvl,
153
    const char *buf, int s, const char *pfx)
154 14645
{
155
        ssize_t l;
156
157 1197
        CHECK_OBJ_NOTNULL(hp, HTTP_MAGIC);
158 1197
        AN(buf);
159 1197
        AN(pfx);
160
161 1197
        vtc_dump(hp->vl, lvl, pfx, buf, s);
162 1197
        l = write(*hp->sess->fd, buf, s);
163 1197
        if (l != s)
164 0
                vtc_log(hp->vl, hp->fatal, "Write failed: (%zd vs %d) %s",
165 0
                    l, s, strerror(errno));
166 1197
}
167
168
static int
169 36269
get_bytes(const struct http *hp, char *buf, size_t n)
170
{
171
        int i;
172
        struct pollfd pfd[1];
173
174 36269
        CHECK_OBJ_NOTNULL(hp, HTTP_MAGIC);
175 36269
        AN(buf);
176
177 72538
        while (n > 0) {
178 36269
                pfd[0].fd = *hp->sess->fd;
179 36269
                pfd[0].events = POLLIN;
180 36269
                pfd[0].revents = 0;
181 36269
                i = poll(pfd, 1, (int)(hp->timeout * 1000));
182 36269
                if (i < 0 && errno == EINTR)
183 0
                        continue;
184 36269
                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 36269
                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 36269
                if (i <= 0)
193 0
                        return (i);
194 36269
                i = read(*hp->sess->fd, buf, n);
195 36269
                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 36269
                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 36269
                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 36269
                if (i <= 0)
208 0
                        return (i);
209 36269
                n -= i;
210
        }
211 36269
        return (1);
212
213 36269
}
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 21193
readFrameHeader(struct frame *f, const char *buf)
253
{
254 21193
        CHECK_OBJ_NOTNULL(f, FRAME_MAGIC);
255 21193
        AN(buf);
256
257 21193
        f->size  = (unsigned char)buf[0] << 16;
258 21193
        f->size += (unsigned char)buf[1] << 8;
259 21193
        f->size += (unsigned char)buf[2];
260
261 21193
        f->type = (unsigned char)buf[3];
262
263 21193
        f->flags = (unsigned char)buf[4];
264
265 21193
        f->stid  = vbe32dec(buf+5);
266 21193
}
267
268
static void
269 44625
writeFrameHeader(char *buf, const struct frame *f)
270
{
271 44625
        CHECK_OBJ_NOTNULL(f, FRAME_MAGIC);
272 44625
        AN(buf);
273 44625
        buf[0] = (f->size >> 16) & 0xff;
274 44625
        buf[1] = (f->size >>  8) & 0xff;
275 44625
        buf[2] = (f->size      ) & 0xff;
276
277 44625
        buf[3] = f->type;
278
279 44625
        buf[4] = f->flags;
280
281 44625
        vbe32enc(buf + 5, f->stid);
282 44625
}
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 46861
replace_frame(struct frame **fp, struct frame *new)
296
{
297
        struct frame *old;
298
299 46861
        AN(fp);
300 46861
        CHECK_OBJ_ORNULL(new, FRAME_MAGIC);
301
302 46861
        old = *fp;
303 46861
        *fp = new;
304 46861
        if (old == NULL)
305 25662
                return;
306
307 21199
        CHECK_OBJ(old, FRAME_MAGIC);
308 21199
        if (old->type == TYPE_GOAWAY)
309 861
                free(old->md.goaway.debug);
310 21199
        free(old->data);
311 21199
        FREE_OBJ(old);
312 46861
}
313
314
static void
315 33598
clean_frame(struct frame **fp)
316
{
317
318 33598
        replace_frame(fp, NULL);
319 33598
}
320
321
static void
322 44625
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 44625
        CHECK_OBJ_NOTNULL(sp, STREAM_MAGIC);
329 44625
        hp = sp->hp;
330 44625
        CHECK_OBJ_NOTNULL(hp, HTTP_MAGIC);
331 44625
        CHECK_OBJ_NOTNULL(f, FRAME_MAGIC);
332
333 44625
        writeFrameHeader(hdr, f);
334
335 89250
        vtc_log(sp->vl, 3,
336
            "tx: stream: %d, type: %s (%d), flags: 0x%02x, size: %d",
337 44625
            f->stid,
338 44625
            f->type < TYPE_MAX ? h2_types[f->type] : "?",
339 44625
            f->type, f->flags, f->size);
340
341 44625
        if (f->type == TYPE_DATA) {
342 7077
                sp->win_peer -= f->size;
343 7077
                hp->h2_win_peer->size -= f->size;
344 7077
        }
345
346 44625
        if (lock)
347 35889
                PTOK(pthread_mutex_lock(&hp->mtx));
348 44625
        l = write(*hp->sess->fd, hdr, sizeof(hdr));
349 44625
        if (l != sizeof(hdr))
350 41450
                vtc_log(sp->vl, hp->fatal, "Write failed: (%zd vs %zd) %s",
351 20725
                    l, sizeof(hdr), strerror(errno));
352
353 44625
        if (f->size) {
354 36078
                AN(f->data);
355 36078
                l = write(*hp->sess->fd, f->data, f->size);
356 36078
                if (l != f->size)
357 41450
                        vtc_log(sp->vl, hp->fatal,
358
                                        "Write failed: (%zd vs %d) %s",
359 20725
                                        l, f->size, strerror(errno));
360 36078
        }
361 44625
        if (lock)
362 35889
                PTOK(pthread_mutex_unlock(&hp->mtx));
363 44625
}
364
365
static void
366 126
exclusive_stream_dependency(const struct stream *s)
367
{
368
        struct stream *target;
369 126
        struct http *hp = s->hp;
370
371 126
        if (s->id == 0)
372 0
                return;
373
374 588
        VTAILQ_FOREACH(target, &hp->streams, list) {
375 462
                if (target->id != s->id && target->dependency == s->dependency)
376 168
                        target->dependency = s->id;
377 462
        }
378 126
}
379
380
static void
381 21210
explain_flags(uint8_t flags, uint8_t type, struct vtclog *vl)
382
{
383 21210
        if (flags & ACK && (type == TYPE_PING || type == TYPE_SETTINGS)) {
384 4494
                vtc_log(vl, 3, "flag: ACK");
385 22260
        } else if (flags & END_STREAM && (type == TYPE_HEADERS ||
386 1050
            type == TYPE_PUSH_PROMISE || type == TYPE_DATA)) {
387 4116
                vtc_log(vl, 3, "flag: END_STREAM");
388 16842
        } else if (flags & END_HEADERS && (type == TYPE_HEADERS ||
389 147
            type == TYPE_PUSH_PROMISE || type == TYPE_CONTINUATION)) {
390 1239
                vtc_log(vl, 3, "flag: END_TYPE_HEADERS");
391 12600
        } else if (flags & PRIORITY && (type == TYPE_HEADERS ||
392 0
            type == TYPE_PUSH_PROMISE)) {
393 0
                vtc_log(vl, 3, "flag: END_PRIORITY");
394 11361
        } else if (flags & PADDED && (type == TYPE_DATA || type ==
395 0
            TYPE_HEADERS || type == TYPE_PUSH_PROMISE)) {
396 21
                vtc_log(vl, 3, "flag: PADDED");
397 11361
        } else if (flags)
398 0
                vtc_log(vl, 3, "UNKNOWN FLAG(S): 0x%02x", flags);
399 21210
}
400
401
static void
402 4158
parse_data(struct stream *s, struct frame *f)
403
{
404
        struct http *hp;
405 4158
        uint32_t size = f->size;
406 4158
        char *data = f->data;
407
408 4158
        CHECK_OBJ_NOTNULL(f, FRAME_MAGIC);
409 4158
        CHECK_OBJ_NOTNULL(s, STREAM_MAGIC);
410 4158
        CAST_OBJ_NOTNULL(hp, s->hp, HTTP_MAGIC);;
411
412 4158
        if (f->flags & PADDED) {
413 84
                f->md.padded = *((uint8_t *)data);
414 84
                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 84
                data++;
423 84
                size -= f->md.padded + 1;
424 84
                vtc_log(s->vl, 4, "padding: %3d", f->md.padded);
425 84
        }
426
427 4158
        if (s->id)
428 4116
                s->win_self -= size;
429
430 4158
        s->hp->h2_win_self->size -= size;
431
432 4158
        if (!size) {
433 609
                AZ(data);
434 609
                vtc_log(s->vl, 4, "s%u - no data", s->id);
435 609
                return;
436
        }
437
438 3549
        s->body = realloc(s->body, s->bodylen + size + 1L);
439 3549
        AN(s->body);
440 3549
        memcpy(s->body + s->bodylen, data, size);
441 3549
        s->bodylen += size;
442 3549
        s->body[s->bodylen] = '\0';
443 4158
}
444
445
static void
446 4200
decode_hdr(struct http *hp, struct hpk_hdr *h, const struct vsb *vsb)
447
{
448
        struct hpk_iter *iter;
449 4200
        enum hpk_result r = hpk_err;
450
        int n;
451
452 4200
        CHECK_OBJ_NOTNULL(vsb, VSB_MAGIC);
453 4200
        CAST_OBJ_NOTNULL(hp, hp, HTTP_MAGIC);;
454
455 4200
        if (VSB_len(vsb) == 0)
456 0
                return;
457
458 4200
        iter = HPK_NewIter(hp->decctx, VSB_data(vsb), VSB_len(vsb));
459
460 4200
        n = 0;
461 4200
        while (n < MAX_HDR && h[n].t)
462 0
                n++;
463 24297
        while (n < MAX_HDR) {
464 24297
                r = HPK_DecHdr(iter, h + n);
465 24297
                if (r == hpk_err )
466 42
                        break;
467 48510
                vtc_log(hp->vl, 4, "header[%2d]: %s: %s",
468 24255
                    n, h[n].key.ptr, h[n].value.ptr);
469 24255
                n++;
470 24255
                if (r == hpk_done)
471 4158
                        break;
472
        }
473
474 4200
        if (r != hpk_done) {
475 84
                vtc_log(hp->vl, hp->fatal ? 4 : 0,
476 42
                    "Header decoding failed (%d) %d", r, hp->fatal);
477 4200
        } 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 4200
        HPK_FreeIter(iter);
483 4200
}
484
485
static void
486 4494
parse_hdr(struct stream *s, struct frame *f, struct vsb *vsb)
487
{
488 4494
        int shift = 0;
489 4494
        int exclusive = 0;
490 4494
        uint32_t size = f->size;
491 4494
        char *data = f->data;
492
        struct http *hp;
493
        uint32_t n;
494
495 4494
        CHECK_OBJ_NOTNULL(f, FRAME_MAGIC);
496 4494
        CHECK_OBJ_NOTNULL(s, STREAM_MAGIC);
497 4494
        CHECK_OBJ_NOTNULL(vsb, VSB_MAGIC);
498 4494
        CAST_OBJ_NOTNULL(hp, s->hp, HTTP_MAGIC);;
499
500 4494
        if (f->flags & PADDED && f->type != TYPE_CONTINUATION) {
501 42
                f->md.padded = *((uint8_t *)data);
502 42
                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 42
                shift += 1;
511 42
                size -= f->md.padded;
512 42
                vtc_log(s->vl, 4, "padding: %3d", f->md.padded);
513 42
        }
514
515 4494
        if (f->type == TYPE_HEADERS && f->flags & PRIORITY){
516 42
                shift += 5;
517 42
                n = vbe32dec(f->data);
518 42
                s->dependency = n & ~(1U << 31);
519 42
                exclusive = n >> 31;
520
521 42
                s->weight = f->data[4];
522 42
                if (exclusive)
523 42
                        exclusive_stream_dependency(s);
524
525 42
                vtc_log(s->vl, 4, "stream->dependency: %u", s->dependency);
526 42
                vtc_log(s->vl, 4, "stream->weight: %u", s->weight);
527 4494
        } else if (f->type == TYPE_PUSH_PROMISE){
528 21
                shift += 4;
529 21
                n = vbe32dec(f->data);
530 21
                f->md.promised = n & ~(1U << 31);
531 21
        }
532
533 4494
        AZ(VSB_bcat(vsb, data + shift, size - shift));
534 4494
}
535
536
static void
537 63
parse_prio(struct stream *s, struct frame *f)
538
{
539
        struct http *hp;
540
        char *buf;
541
        uint32_t n;
542
543 63
        CHECK_OBJ_NOTNULL(f, FRAME_MAGIC);
544 63
        CHECK_OBJ_NOTNULL(s, STREAM_MAGIC);
545 63
        CAST_OBJ_NOTNULL(hp, s->hp, HTTP_MAGIC);;
546
547 63
        if (f->size != 5)
548 0
                vtc_fatal(s->vl, "Size should be 5, but isn't (%d)", f->size);
549
550 63
        buf = f->data;
551 63
        AN(buf);
552
553 63
        n = vbe32dec(f->data);
554 63
        f->md.prio.stream = n & ~(1U << 31);
555
556 63
        s->dependency = f->md.prio.stream;
557 63
        if (n >> 31){
558 21
                f->md.prio.exclusive = 1;
559 21
                exclusive_stream_dependency(s);
560 21
        }
561
562 63
        buf += 4;
563 63
        f->md.prio.weight = *buf;
564 63
        s->weight = f->md.prio.weight;
565
566 63
        vtc_log(s->vl, 3, "prio->stream: %u", f->md.prio.stream);
567 63
        vtc_log(s->vl, 3, "prio->weight: %u", f->md.prio.weight);
568 63
}
569
570
static void
571 1323
parse_rst(const struct stream *s, struct frame *f)
572
{
573
        struct http *hp;
574
        uint32_t err;
575
        const char *buf;
576 1323
        CHECK_OBJ_NOTNULL(f, FRAME_MAGIC);
577 1323
        CHECK_OBJ_NOTNULL(s, STREAM_MAGIC);
578 1323
        CAST_OBJ_NOTNULL(hp, s->hp, HTTP_MAGIC);;
579
580 1323
        if (f->size != 4)
581 0
                vtc_fatal(s->vl, "Size should be 4, but isn't (%d)", f->size);
582
583 1323
        err = vbe32dec(f->data);
584 1323
        f->md.rst_err = err;
585
586 1323
        vtc_log(s->vl, 2, "ouch");
587 1323
        if (err <= ERR_MAX)
588 1323
                buf = h2_errs[err];
589
        else
590 0
                buf = "unknown";
591 1323
        vtc_log(s->vl, 4, "rst->err: %s (%d)", buf, err);
592
593 1323
}
594
595
static void
596 8798
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 8798
        CHECK_OBJ_NOTNULL(f, FRAME_MAGIC);
604 8798
        CHECK_OBJ_NOTNULL(s, STREAM_MAGIC);
605 8798
        CAST_OBJ_NOTNULL(hp, s->hp, HTTP_MAGIC);;
606
607 8798
        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 8798
        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 167169
        for (u = 0; u <= SETTINGS_MAX; u++)
616 158371
                f->md.settings[u] = NAN;
617
618 18185
        for (u = 0; u < f->size;) {
619 9387
                t = vbe16dec(f->data + u);
620 9387
                u += 2;
621 9387
                v = vbe32dec(f->data + u);
622 9387
                if (t <= SETTINGS_MAX) {
623 9387
                        buf = h2_settings[t];
624 9387
                        f->md.settings[t] = v;
625 9387
                } else
626 0
                        buf = "unknown";
627 9387
                u += 4;
628
629 9387
                if (t == SETTINGS_HEADER_TABLE_SIZE) {
630 105
                        r = HPK_ResizeTbl(s->hp->encctx, v);
631 105
                        assert(r == hpk_done);
632 105
                }
633
634 9387
                vtc_log(s->vl, 4, "settings->%s (%u): %d", buf, t, v);
635
        }
636
637 8798
}
638
639
static void
640 126
parse_ping(const struct stream *s, struct frame *f)
641
{
642
        struct http *hp;
643 126
        CHECK_OBJ_NOTNULL(f, FRAME_MAGIC);
644 126
        CHECK_OBJ_NOTNULL(s, STREAM_MAGIC);
645 126
        CAST_OBJ_NOTNULL(hp, s->hp, HTTP_MAGIC);;
646 126
        if (f->size != 8)
647 0
                vtc_fatal(s->vl, "Size should be 8, but isn't (%d)", f->size);
648 126
        f->md.ping.ack = f->flags & ACK;
649 126
        memcpy(f->md.ping.data, f->data, 8);
650 126
        f->md.ping.data[8] = '\0';
651
652 126
        vtc_log(s->vl, 4, "ping->data: %s", f->md.ping.data);
653
654 126
}
655
656
static void
657 861
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 861
        CHECK_OBJ_NOTNULL(f, FRAME_MAGIC);
663 861
        CHECK_OBJ_NOTNULL(s, STREAM_MAGIC);
664 861
        CAST_OBJ_NOTNULL(hp, s->hp, HTTP_MAGIC);;
665
666 861
        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 861
        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 861
        stid = vbe32dec(f->data);
674 861
        err = vbe32dec(f->data + 4);
675 861
        f->md.goaway.err = err;
676 861
        f->md.goaway.stream = stid;
677
678 861
        if (err <= ERR_MAX)
679 861
                err_buf = h2_errs[err];
680
        else
681 0
                err_buf = "unknown";
682
683 861
        if (f->size > 8) {
684 42
                f->md.goaway.debug = malloc((f->size - 8) + 1L);
685 42
                AN(f->md.goaway.debug);
686 42
                f->md.goaway.debug[f->size - 8] = '\0';
687
688 42
                memcpy(f->md.goaway.debug, f->data + 8, f->size - 8);
689 42
        }
690
691 861
        vtc_log(s->vl, 3, "goaway->laststream: %d", stid);
692 861
        vtc_log(s->vl, 3, "goaway->err: %s (%d)", err_buf, err);
693 861
        if (f->md.goaway.debug)
694 42
                vtc_log(s->vl, 3, "goaway->debug: %s", f->md.goaway.debug);
695 861
}
696
697
static void
698 1386
parse_winup(const struct stream *s, struct frame *f)
699
{
700
        struct http *hp;
701
        uint32_t size;
702 1386
        CHECK_OBJ_NOTNULL(f, FRAME_MAGIC);
703 1386
        CHECK_OBJ_NOTNULL(s, STREAM_MAGIC);
704 1386
        CAST_OBJ_NOTNULL(hp, s->hp, HTTP_MAGIC);;
705
706 1386
        if (f->size != 4)
707 0
                vtc_fatal(s->vl, "Size should be 4, but isn't (%d)", f->size);
708 1386
        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 1386
        size = vbe32dec(f->data);
713 1386
        f->md.winup_size = size;
714
715 1386
        vtc_log(s->vl, 3, "winup->size: %d", size);
716 1386
}
717
718
/* read a frame and queue it in the relevant stream, wait if not present yet.
719
 */
720
static void *
721 4471
receive_frame(void *priv)
722
{
723
        struct http *hp;
724
        char hdr[9];
725
        struct frame *f;
726
        struct stream *s;
727 4471
        int expect_cont = 0;
728 4471
        struct vsb *vsb = NULL;
729 4471
        struct hpk_hdr *hdrs = NULL;
730
731 4471
        CAST_OBJ_NOTNULL(hp, priv, HTTP_MAGIC);
732
733 4471
        PTOK(pthread_mutex_lock(&hp->mtx));
734 49639
        while (hp->h2) {
735
                /*no wanted frames? */
736 45168
                assert(hp->wf >= 0);
737 45168
                if (hp->wf == 0) {
738 23959
                        PTOK(pthread_cond_wait(&hp->cond, &hp->mtx));
739 23959
                        continue;
740
                }
741 21209
                PTOK(pthread_mutex_unlock(&hp->mtx));
742
743 21209
                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 21209
                ALLOC_OBJ(f, FRAME_MAGIC);
753 21209
                AN(f);
754 21209
                readFrameHeader(f, hdr);
755
756 42418
                vtc_log(hp->vl, 3, "rx: stream: %d, type: %s (%d), "
757
                                "flags: 0x%02x, size: %d",
758 21209
                                f->stid,
759 21209
                                f->type < TYPE_MAX ? h2_types[f->type] : "?",
760 21209
                                f->type, f->flags, f->size);
761 21209
                explain_flags(f->flags, f->type, hp->vl);
762
763 21209
                if (f->size) {
764 15078
                        f->data = malloc(f->size + 1L);
765 15078
                        AN(f->data);
766 15078
                        f->data[f->size] = '\0';
767 15078
                        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 15078
                }
778
779
                /* is the corresponding stream waiting? */
780 21209
                PTOK(pthread_mutex_lock(&hp->mtx));
781 21209
                s = NULL;
782 42415
                while (!s) {
783 24293
                        VTAILQ_FOREACH(s, &hp->streams, list)
784 24293
                                if (s->id == f->stid)
785 21206
                                        break;
786 21206
                        if (!s)
787 0
                                PTOK(pthread_cond_wait(&hp->cond, &hp->mtx));
788 21206
                        if (!hp->h2) {
789 0
                                clean_frame(&f);
790 0
                                PTOK(pthread_mutex_unlock(&hp->mtx));
791 0
                                return (NULL);
792
                        }
793
                }
794 21209
                PTOK(pthread_mutex_unlock(&hp->mtx));
795
796 21209
                AN(s);
797 21503
                if (expect_cont &&
798 294
                    (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 21209
                switch (f->type) {
804
                        case TYPE_DATA:
805 4158
                                parse_data(s, f);
806 4158
                                break;
807
                        case TYPE_PUSH_PROMISE:
808 21
                                hdrs = s->req;
809
                                /*FALLTHROUGH*/
810
                        case TYPE_HEADERS:
811 4200
                                if (!hdrs) {
812 4179
                                        if (hp->sfd)
813 987
                                                hdrs = s->req;
814
                                        else
815 3192
                                                hdrs = s->resp;
816 4179
                                }
817 4200
                                clean_headers(hdrs);
818 4200
                                hdrs[0].t = hpk_unset;
819 4200
                                AZ(vsb);
820 4200
                                vsb = VSB_new_auto();
821
                                /*FALLTHROUGH*/
822
                        case TYPE_CONTINUATION:
823 4494
                                AN(hdrs);
824 4494
                                expect_cont = s->id;
825 4494
                                parse_hdr(s, f, vsb);
826 4494
                                if (f->flags & END_HEADERS) {
827 4200
                                        expect_cont = 0;
828 4200
                                        AZ(VSB_finish(vsb));
829 4200
                                        decode_hdr(hp, hdrs, vsb);
830 4200
                                        VSB_destroy(&vsb);
831 4200
                                        hdrs = NULL;
832 4200
                                }
833 4494
                                break;
834
                        case TYPE_PRIORITY:
835 63
                                parse_prio(s, f);
836 63
                                break;
837
                        case TYPE_RST_STREAM:
838 1323
                                parse_rst(s, f);
839 1323
                                break;
840
                        case TYPE_SETTINGS:
841 8798
                                parse_settings(s, f);
842 8798
                                break;
843
                        case TYPE_PING:
844 126
                                parse_ping(s, f);
845 126
                                break;
846
                        case TYPE_GOAWAY:
847 861
                                parse_goaway(s, f);
848 861
                                break;
849
                        case TYPE_WINDOW_UPDATE:
850 1386
                                parse_winup(s, f);
851 1386
                                break;
852
                        default:
853 0
                                WRONG("wrong frame type");
854 0
                }
855
856 21209
                PTOK(pthread_mutex_lock(&hp->mtx));
857 21209
                VTAILQ_INSERT_HEAD(&s->fq, f, list);
858 21209
                if (s->wf) {
859 20198
                        assert(hp->wf > 0);
860 20198
                        hp->wf--;
861 20198
                        s->wf = 0;
862 20198
                        PTOK(pthread_cond_signal(&s->cond));
863 20198
                }
864 21209
                continue;
865
        }
866 4471
        PTOK(pthread_mutex_unlock(&hp->mtx));
867 4471
        if (vsb != NULL)
868 0
                VSB_destroy(&vsb);
869 4471
        return (NULL);
870 4471
}
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 4179
/* SECTION: stream.spec.zexpect expect
924
 *
925 4179
 * expect in stream works as it does in client or server, except that the
926
 * elements compared will be different.
927 4179
 *
928 22323
 * Most of these elements will be frame specific, meaning that the last frame
929 22113
 * received on that stream must of the correct type.
930 3969
 *
931 18144
 * Here the list of keywords you can look at.
932
 */
933 210
static const char *
934 4179
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 32535
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 32535
         * ping.ack (PING)
953
         *      "true" if the ACK flag was set, "false" otherwise.
954 32535
         */
955 32535
        if (!strcmp(spec, "ping.data")) {
956 32535
                CHECK_LAST_FRAME(PING);
957 32535
                return (f->md.ping.data);
958
        }
959 32535
        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 32535
         *      The size of the upgrade given by the WINDOW_UPDATE frame.
968 126
         */
969 126
        if (!strcmp(spec, "winup.size")) {
970
                CHECK_LAST_FRAME(WINDOW_UPDATE);
971 32409
                RETURN_BUFFED(f->md.winup_size);
972 84
        }
973 84
        /* SECTION: stream.spec.zexpect.prio PRIORITY specific
974 84
         *
975
         * prio.stream
976
         *      The stream ID announced.
977
         *
978
         * prio.exclusive
979
         *      "true" if the priority is exclusive, else "false".
980
         *
981 32325
         * prio.weight
982 21
         *      The dependency weight.
983 21
         */
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 32304
        }
997 42
        /* SECTION: stream.spec.zexpect.rst RESET_STREAM specific
998 42
         *
999 0
         * rst.err
1000 32262
         *      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 32262
        }
1006 42
        /* SECTION: stream.spec.zexpect.settings SETTINGS specific
1007 42
         *
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 32220
         *
1015 882
         * settings.hdrtbl
1016 882
         *      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 31338
         *
1054 4870
         * push.id
1055 4870
         *      The id of the promised stream.
1056 4870
         */
1057 4366
        if (!strcmp(spec, "push.id")) {
1058 4366
                CHECK_LAST_FRAME(PUSH_PROMISE);
1059
                RETURN_BUFFED(f->md.promised);
1060 504
        }
1061 462
        /* SECTION: stream.spec.zexpect.goaway GOAWAY specific
1062 378
         *
1063 315
         * goaway.err
1064 252
         *      The error code (as integer) of the GOAWAY frame.
1065 189
         *
1066 147
         * goaway.laststream
1067 105
         *      Last-Stream-ID
1068 42
         *
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 26468
1076 21
                if (!strcmp(spec, "err"))
1077 21
                        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 26447
         *
1091 1428
         * frame.size
1092 1428
         *      Size of the frame.
1093
         *
1094 1428
         * frame.stream
1095 777
         *      Stream of the frame (correspond to the one you are executing
1096 651
         *      this from, obviously).
1097 609
         *
1098 42
         * frame.padding (for DATA, HEADERS, PUSH_PROMISE frames)
1099 42
         *      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 25019
        /* SECTION: stream.spec.zexpect.zstream Stream
1120 168
         *
1121 168
         * stream.window
1122 0
         *      The current local window size of the stream, or, if on stream 0,
1123 168
         *      of the connection.
1124 147
         *
1125 126
         * stream.peer_window
1126 42
         *      The current peer window size of the stream, or, if on stream 0,
1127 21
         *      of the connection.
1128 21
         *
1129 21
         * stream.weight
1130 0
         *      Weight of the stream
1131 0
         *
1132
         * stream.dependency
1133
         *      Id of the stream this one depends on.
1134 21
         */
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 24851
                if (s->id) {
1154 546
                        snprintf(buf, 20, "%d", s->dependency);
1155 273
                        return (buf);
1156 273
                } else
1157
                        return (NULL);
1158 24578
        }
1159 672
        /* SECTION: stream.spec.zexpect.ztable Index tables
1160 336
         *
1161 336
         * tbl.dec.size / tbl.enc.size
1162
         *      Size (bytes) of the decoding/encoding table.
1163 24242
         *
1164 168
         * tbl.dec.size / tbl.enc.maxsize
1165 147
         *      Maximum size (bytes) of the decoding/encoding table.
1166 147
         *
1167
         * tbl.dec.length / tbl.enc.length
1168 21
         *      Number of headers in decoding/encoding table.
1169
         *
1170 24074
         * tbl.dec[INT].key / tbl.enc[INT].key
1171 336
         *      Name of the header at index INT of the decoding/encoding
1172 315
         *      table.
1173 315
         *
1174
         * tbl.dec[INT].value / tbl.enc[INT].value
1175 21
         *      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 23738
                        RETURN_BUFFED(HPK_GetTblSize(ctx));
1197 2583
                else if (!strcmp(spec, ".maxsize"))
1198 1323
                        RETURN_BUFFED(HPK_GetTblMaxSize(ctx));
1199
                else if (!strcmp(spec, ".length"))
1200 1260
                        RETURN_BUFFED(HPK_GetTblLength(ctx));
1201 2583
        }
1202
        /* SECTION: stream.spec.zexpect.zre Request and response
1203 2583
         *
1204 2016
         * Note: it's possible to inspect a request or response while it is
1205 1008
         * still being construct (in-between two frames for example).
1206 1008
         *
1207
         * req.bodylen / resp.bodylen
1208 1575
         *      Length in bytes of the request/response so far.
1209 1008
         *
1210 1008
         * req.body / resp.body
1211 1008
         *      Body of the request/response so far.
1212
         *
1213 567
         * req.http.STRING / resp.http.STRING
1214 546
         *      Value of the header STRING in the request/response.
1215 21
         *
1216 0
         * req.status / resp.status
1217 21
         *      :status pseudo-header's value.
1218 21
         *
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 21155
                else if (!strcmp(spec, "authority"))
1250 4935
                        return (find_header(h, ":authority"));
1251 315
                else if (!strcmp(spec, "scheme"))
1252 315
                        return (find_header(h, ":scheme"));
1253 315
                else if (!strncmp(spec, "http.", 5))
1254 4620
                        return (find_header(h, spec + 5));
1255 4620
                else
1256
                        return (NULL);
1257 4935
        }
1258 357
#define H2_ERROR(U,v,sc,g,r,t) \
1259 4578
        if (!strcmp(spec, #U)) { return (#v); }
1260 399
#include "tbl/h2_error.h"
1261 4179
        return (spec);
1262 1743
}
1263 2436
1264 42
/* SECTION: stream.spec.frame_sendhex sendhex
1265 2394
 *
1266 42
 * Push bytes directly on the wire. sendhex takes exactly one argument: a string
1267 2352
 * describing the bytes, in hex notation, with possible whitespaces between
1268 21
 * them. Here's an example::
1269 2331
 *
1270 21
 *      sendhex "00 00 08 00 0900       8d"
1271 2310
 */
1272 2310
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 1197
}
1292
1293
#define ENC(hdr, k, v)                                  \
1294
{                                                       \
1295
        AN(k);                                          \
1296
        hdr.key.ptr = TRUST_ME(k);                      \
1297 1197
        hdr.key.len = strlen(k);                        \
1298 1197
        AN(v);                                          \
1299 1197
        hdr.value.ptr = TRUST_ME(v);                    \
1300 1197
        hdr.value.len = strlen(v);                      \
1301 1197
        assert(HPK_EncHdr(iter, &hdr) != hpk_err);      \
1302 1197
}
1303 1197
1304 1197
#define STR_ENC(av, field, str)                                                \
1305 1197
{                                                                              \
1306 1197
        av++;                                                                  \
1307 1197
             if (AV_IS("plain")) { hdr.field.huff = 0; }                       \
1308 1197
        else if (AV_IS("huf"))   { hdr.field.huff = 1; }                       \
1309 1197
        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 27300
        struct hpk_hdr hdr;
1441
        char *cmd_str = *av;
1442
        char *p;
1443
1444 27300
        CAST_OBJ_NOTNULL(s, priv, STREAM_MAGIC);
1445 27300
        INIT_FRAME(f, CONTINUATION, 0, s->id, END_HEADERS);
1446 27300
        buf = malloc(BUF_SIZE);
1447 27300
        AN(buf);
1448 27300
1449
        if (!strcmp(cmd_str, "txreq")) {
1450 27300
                ONLY_H2_CLIENT(s->hp, av);
1451 27300
                f.type = TYPE_HEADERS;
1452 27300
                f.flags |= END_STREAM;
1453
                method_done = 0;
1454
                path_done = 0;
1455
                scheme_done = 0;
1456 27300
        } else if (!strcmp(cmd_str, "txresp")) {
1457
                ONLY_H2_SERVER(s->hp, av);
1458
                f.type = TYPE_HEADERS;
1459 27300
                f.flags |= END_STREAM;
1460
                status_done = 0;
1461
        } else if (!strcmp(cmd_str, "txpush")) {
1462 27300
                ONLY_H2_SERVER(s->hp, av);
1463 27300
                f.type = TYPE_PUSH_PROMISE;
1464 27300
                method_done = 0;
1465 27300
                path_done = 0;
1466
                scheme_done = 0;
1467 27300
        }
1468 4914
1469 4914
        if (f.type == TYPE_PUSH_PROMISE) {
1470 4914
                *buf = 0;
1471 4914
                iter = HPK_NewIter(s->hp->encctx, buf + 4, BUF_SIZE - 4);
1472 4914
        } else
1473 4914
                iter = HPK_NewIter(s->hp->encctx, buf, BUF_SIZE);
1474 27300
1475 1008
#define AV_IS(str) !strcmp(*av, str)
1476 1008
#define CMD_IS(str) !strcmp(cmd_str, str)
1477 1008
        while (*++av) {
1478 1008
                memset(&hdr, 0, sizeof(hdr));
1479 22386
                hdr.t = hpk_not;
1480 21
                if (AV_IS("-noadd")) {
1481 21
                        path_done = 1;
1482 21
                        status_done = 1;
1483 21
                        method_done = 1;
1484 21
                        scheme_done = 1;
1485 21
                }
1486
                else if (AV_IS("-status") && CMD_IS("txresp")) {
1487 27300
                        ENC(hdr, ":status", av[1]);
1488 21
                        av++;
1489 21
                        status_done = 1;
1490 21
                }
1491 27279
                else if (AV_IS("-url") &&
1492
                                (CMD_IS("txreq") || CMD_IS("txpush"))) {
1493
                        ENC(hdr, ":path", av[1]);
1494
                        av++;
1495 79401
                        path_done = 1;
1496 52101
                }
1497 52101
                else if ((AV_IS("-method") || AV_IS("-req")) &&
1498 52101
                                (CMD_IS("txreq") || CMD_IS("txpush"))) {
1499 294
                        ENC(hdr, ":method", av[1]);
1500 294
                        av++;
1501 294
                        method_done = 1;
1502 294
                }
1503 294
                else if (AV_IS("-scheme") &&
1504 51807
                                (CMD_IS("txreq") || CMD_IS("txpush"))) {
1505 21
                        ENC(hdr, ":scheme", av[1]);
1506 21
                        av++;
1507 21
                        scheme_done = 1;
1508 21
                }
1509 51807
                else if (AV_IS("-hdr")) {
1510 1323
                        if (av[2] == NULL)
1511 1323
                                vtc_fatal(vl, "-hdr takes two arguments in http2");
1512 1323
                        ENC(hdr, av[1], av[2]);
1513 1323
                        av += 2;
1514 1323
                }
1515 50463
                else if (AV_IS("-idxHdr")) {
1516 777
                        hdr.t = hpk_idx;
1517 777
                        STRTOU32_CHECK(hdr.i, av, p, vl, "-idxHdr", 0);
1518 777
                        assert(HPK_EncHdr(iter, &hdr) != hpk_err);
1519 777
                }
1520 777
                else if (AV_IS("-litIdxHdr")) {
1521 49686
                        av++;
1522 147
                        if      (AV_IS("inc"))   { hdr.t = hpk_inc;   }
1523 147
                        else if (AV_IS("not"))   { hdr.t = hpk_not;   }
1524 147
                        else if (AV_IS("never")) { hdr.t = hpk_never; }
1525 147
                        else
1526 147
                                vtc_fatal(vl, "first -litidxHdr arg can be "
1527 49539
                                    "inc, not, never (got: %s)", *av);
1528 25410
1529 0
                        STRTOU32_CHECK(hdr.i, av, p, vl,
1530 25410
                            "second -litidxHdr arg", 0);
1531 25410
1532 25410
                        hdr.key.ptr = NULL;
1533 24129
                        hdr.key.len = 0;
1534 882
                        STR_ENC(av, value,   "third -litHdr");
1535 882
                        assert(HPK_EncHdr(iter, &hdr) != hpk_err);
1536 882
                }
1537 882
                else if (AV_IS("-litHdr")) {
1538 23247
                        av++;
1539 336
                        if      (AV_IS("inc"))   { hdr.t = hpk_inc;   }
1540 336
                        else if (AV_IS("not"))   { hdr.t = hpk_not;   }
1541 21
                        else if (AV_IS("never")) { hdr.t = hpk_never; }
1542 21
                        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 336
                        STR_ENC(av, value, "fourth -litHdr");
1548
                        assert(HPK_EncHdr(iter, &hdr) != hpk_err);
1549
                }
1550 336
                else if (AV_IS("-nostrend")) {
1551 336
                        f.flags &= ~END_STREAM;
1552 504
                }
1553 336
                else if (AV_IS("-nohdrend")) {
1554 336
                        f.flags &= ~END_HEADERS;
1555 22911
                }
1556 147
                else if (AV_IS("-promised") && CMD_IS("txpush")) {
1557 147
                        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 210
                        pad = strdup(*av);
1565 210
                }
1566 147
                else if (AV_IS("-padlen") && !CMD_IS("txcont")) {
1567 147
                        AZ(pad);
1568 22764
                        av++;
1569 819
                        pad = synth_body(*av, 0);
1570 819
                }
1571 21945
                else if (CMD_IS("txreq") || CMD_IS("txresp")) {
1572 21336
                        if (AV_IS("-body")) {
1573 21336
                                AZ(body);
1574 609
                                REPLACE(body, av[1]);
1575 21
                                AN(body);
1576 21
                                bodylen = strlen(body);
1577 21
                                f.flags &= ~END_STREAM;
1578 588
                                av++;
1579 126
                        }
1580 126
                        else if (AV_IS("-bodyfrom")) {
1581 126
                                AZ(body);
1582 126
                                body = VFIL_readfile(NULL, av[1], &len);
1583 126
                                AN(body);
1584 462
                                assert(len < INT_MAX);
1585 0
                                bodylen = len;
1586 0
                                f.flags &= ~END_STREAM;
1587 0
                                av++;
1588 0
                        }
1589 462
                        else if (AV_IS("-bodylen")) {
1590 462
                                AZ(body);
1591 168
                                body = synth_body(av[1], 0);
1592 168
                                bodylen = strlen(body);
1593 168
                                f.flags &= ~END_STREAM;
1594 168
                                av++;
1595 168
                        }
1596 168
                        else if (!strncmp(*av, "-gzip", 5)) {
1597 168
                                i = vtc_gzip_cmd(s->hp, av, &body, &bodylen);
1598 294
                                if (i == 0)
1599 42
                                        break;
1600 42
                                av++;
1601 42
                                if (i > 1) {
1602 42
                                        ENC(hdr, ":content-encoding", "gzip");
1603 42
                                        f.flags &= ~END_STREAM;
1604 42
                                }
1605 42
                        }
1606 42
                        else if (AV_IS("-dep")) {
1607 252
                                STRTOU32_CHECK(stid, av, p, vl, "-dep", 0);
1608 84
                                f.flags |= PRIORITY;
1609 84
                        }
1610 84
                        else if (AV_IS("-ex")) {
1611 84
                                exclusive = 1U << 31;
1612 84
                                f.flags |= PRIORITY;
1613 84
                        }
1614 168
                        else if (AV_IS("-weight")) {
1615 42
                                STRTOU32_CHECK(weight, av, p, vl, "-weight", 8);
1616 42
                                f.flags |= PRIORITY;
1617 0
                        } else
1618 42
                                break;
1619 42
                } else
1620 42
                        break;
1621 42
        }
1622 42
#undef CMD_IS
1623 42
#undef AV_IS
1624 126
        if (*av != NULL)
1625 63
                vtc_fatal(vl, "Unknown %s spec: %s\n", cmd_str, *av);
1626 63
1627 63
        memset(&hdr, 0, sizeof(hdr));
1628 63
        hdr.t = hpk_not;
1629 42
1630 42
        if (!status_done) { ENC(hdr, ":status", "200"); }
1631 42
        if (!path_done)   { ENC(hdr, ":path",   "/"); }
1632 21
        if (!method_done) { ENC(hdr, ":method", "GET"); }
1633 21
        if (!scheme_done) { ENC(hdr, ":scheme", "http"); }
1634 21
1635 21
        f.size = gethpk_iterLen(iter);
1636 0
        if (f.flags & PRIORITY) {
1637 462
                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 27300
                vbe32enc(buf, (stid | exclusive));
1643 0
                buf[4] = s->weight;
1644
                f.size += 5;
1645 27300
1646 27300
                vtc_log(vl, 4, "stream->dependency: %u", s->dependency);
1647
                vtc_log(vl, 4, "stream->weight: %u", s->weight);
1648 27300
                if (exclusive)
1649 27300
                        exclusive_stream_dependency(s);
1650 27300
        }
1651 27300
        if (pad) {
1652
                if (strlen(pad) > 255)
1653 27300
                        vtc_fatal(vl, "Padding is limited to 255 bytes");
1654 27300
                f.flags |= PADDED;
1655 84
                assert(f.size + strlen(pad) < BUF_SIZE);
1656 84
                memmove(buf + 1, buf, f.size);
1657
                buf[0] = strlen(pad);
1658 84
                f.size += 1;
1659 84
                memcpy(buf + f.size, pad, strlen(pad));
1660 84
                f.size += strlen(pad);
1661 84
                free(pad);
1662 84
        }
1663
        if (f.type == TYPE_PUSH_PROMISE)
1664 84
                f.size += 4;
1665 84
        f.data = buf;
1666 84
        HPK_FreeIter(iter);
1667 42
        write_frame(s, &f, 1);
1668 84
        free(buf);
1669 27300
1670 126
        if (!body)
1671 0
                return;
1672 126
1673 126
        INIT_FRAME(f, DATA, bodylen, s->id, END_STREAM);
1674 126
        f.data = body;
1675 126
1676 126
        write_frame(s, &f, 1);
1677 126
        free(body);
1678 126
}
1679 126
1680 126
/* SECTION: stream.spec.data_1 txdata
1681 27300
 *
1682 21
 * By default, data frames are empty. The receiving end will know the whole body
1683 27300
 * has been delivered thanks to the END_STREAM flag set in the last DATA frame,
1684 27300
 * and txdata automatically set it.
1685 27300
 *
1686 27300
 * \-data STRING
1687
 *      Data to be embedded into the frame.
1688 27300
 *
1689 26964
 * \-datalen INT
1690
 *      Generate and INT-bytes long string to be sent in the frame.
1691 336
 *
1692 336
 * \-pad STRING / -padlen INT
1693
 *      Add string as padding to the frame, either the one you provided with
1694 336
 *      \-pad, or one that is generated for you, of length INT is -padlen
1695 336
 *      case.
1696 27300
 *
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 6741
                        AZ(body);
1721
                        av++;
1722
                        body = synth_body(*av, 0);
1723 6741
                } else if (!strcmp(*av, "-pad")) {
1724
                        AZ(pad);
1725 6741
                        av++;
1726 6741
                        AN(*av);
1727
                        pad = strdup(*av);
1728 6741
                } else if (!strcmp(*av, "-padlen")) {
1729
                        AZ(pad);
1730 6741
                        av++;
1731
                        pad = synth_body(*av, 0);
1732 19740
                } else if (!strcmp(*av, "-nostrend"))
1733 12999
                        f.flags &= ~END_STREAM;
1734 546
                else
1735 546
                        break;
1736 546
        }
1737 12999
        if (*av != NULL)
1738 735
                vtc_fatal(vl, "Unknown txdata spec: %s\n", *av);
1739 735
1740 735
        if (!body)
1741 12453
                body = strdup("");
1742 21
1743 21
        if (pad) {
1744 21
                f.flags |= PADDED;
1745 21
                if (strlen(pad) > 255)
1746 11718
                        vtc_fatal(vl, "Padding is limited to 255 bytes");
1747 5628
                data = malloc( 1 + strlen(body) + strlen(pad));
1748 5628
                AN(data);
1749 5628
                *((uint8_t *)data) = strlen(pad);
1750 11697
                f.size = 1;
1751 6069
                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 6741
                f.data = data;
1756 0
        } else {
1757
                f.size = strlen(body);
1758 6741
                f.data = body;
1759 5460
        }
1760
        write_frame(s, &f, 1);
1761 6741
        free(body);
1762 5649
        free(pad);
1763 5649
        free(data);
1764 0
}
1765 5649
1766 5649
/* SECTION: stream.spec.reset_txrst txrst
1767 5649
 *
1768 5649
 * Send a RST_STREAM frame. By default, txrst will send a 0 error code
1769 5649
 * (NO_ERROR).
1770 5649
 *
1771 5649
 * \-err STRING|INT
1772 5649
 *      Sets the error code to be sent. The argument can be an integer or a
1773 5649
 *      string describing the error, such as NO_ERROR, or CANCEL (see
1774 5649
 *      rfc7540#11.4 for more strings).
1775 1092
 */
1776 1092
static void
1777
cmd_txrst(CMD_ARGS)
1778 6741
{
1779 6741
        struct stream *s;
1780 6741
        char *p;
1781 6741
        uint32_t err = 0;
1782 6741
        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 399
1796
                        if (h2_errs[err])
1797
                                continue;
1798
1799 399
                        STRTOU32(err, *av, p, vl, "-err");
1800
                } else
1801
                        break;
1802 399
        }
1803
        if (*av != NULL)
1804 399
                vtc_fatal(vl, "Unknown txrst spec: %s\n", *av);
1805
1806 567
        err = htonl(err);
1807 168
        f.data = (void *)&err;
1808 168
        write_frame(s, &f, 1);
1809 2520
}
1810 2352
1811 0
/* SECTION: stream.spec.prio_txprio txprio
1812 2352
 *
1813
 * Send a PRIORITY frame
1814 168
 *
1815 0
 * \-stream INT
1816
 *      indicate the id of the stream the sender stream depends on.
1817 168
 *
1818 168
 * \-ex
1819 0
 *      the dependency should be made exclusive (only this streams depends on
1820
 *      the parent stream).
1821 399
 *
1822 0
 * \-weight INT
1823
 *      an 8-bits integer is used to balance priority between streams
1824 399
 *      depending on the same streams.
1825 399
 */
1826 399
static void
1827 399
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 273
                } 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 273
                } else
1850
                        break;
1851 273
        }
1852 273
        if (*av != NULL)
1853
                vtc_fatal(vl, "Unknown txprio spec: %s\n", *av);
1854
        s->weight = weight & 0xff;
1855 273
        s->dependency = stid;
1856
1857 273
        if (exclusive)
1858 273
                exclusive_stream_dependency(s);
1859
1860 651
        vbe32enc(buf, (stid | exclusive));
1861 378
        buf[4] = s->weight;
1862 189
        write_frame(s, &f, 1);
1863 378
}
1864 21
1865 189
#define PUT_KV(av, vl, name, val, code) \
1866 168
        do {\
1867 168
                STRTOU32_CHECK(val, av, p, vl, #name, 0);       \
1868 0
                vbe16enc(cursor, code);                         \
1869
                cursor += sizeof(uint16_t);                     \
1870 273
                vbe32enc(cursor, val);                          \
1871 0
                cursor += sizeof(uint32_t);                     \
1872 273
                f.size += 6;                                    \
1873 273
        } while(0)
1874
1875 273
/* SECTION: stream.spec.settings_txsettings txsettings
1876 21
 *
1877
 * SETTINGS frames must be acknowledge, arguments are as follow (most of them
1878 273
 * are from  rfc7540#6.5.2):
1879 273
 *
1880 273
 * \-hdrtbl INT
1881 273
 *      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 8736
                }
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 8736
                        p = *av + 3;
1958
                        errno = 0;
1959
                        u = strtoul(p, &e, 16);
1960
                        if (*p == '\0' || *e != '\0' || u > 0xffff || errno != 0)
1961 8736
                                vtc_fatal(vl, "Invalid settings tag %s", p);
1962
                        assert(u <= 0xffff);
1963 8736
                        PUT_KV(av, vl, hdrtbl, val, (uint16_t)u);
1964 8736
                }
1965
                else if (!strcmp(*av, "-ack"))
1966 8736
                        f.flags |= 1;
1967 8736
                else
1968 8736
                        break;
1969
        }
1970 8736
        if (*av != NULL)
1971 13524
                vtc_fatal(vl, "Unknown txsettings spec: %s\n", *av);
1972 4788
1973 42
        AN(s->hp);
1974 4767
        write_frame(s, &f, 0);
1975 105
        PTOK(pthread_mutex_unlock(&hp->mtx));
1976 105
}
1977 105
1978 4662
/* SECTION: stream.spec.ping_txping txping
1979 21
 *
1980 4641
 * Send PING frame.
1981 168
 *
1982 357
 * \-data STRING
1983 189
 *      specify the payload of the frame, with STRING being an 8-char string.
1984 168
 *
1985 168
 * \-ack
1986 4473
 *      set the ACK flag.
1987 63
 */
1988 4410
static void
1989 21
cmd_txping(CMD_ARGS)
1990 4389
{
1991 42
        struct stream *s;
1992 4368
        struct frame f;
1993 42
        char buf[8];
1994 4347
1995 21
        memset(buf, 0, 8);
1996 4326
        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 4326
                                vtc_fatal(vl, "data must be a 8-char string, found  (%s)", *av);
2006 4326
                        f.data = *av;
2007
                } else if (!strcmp(*av, "-ack"))
2008 0
                        f.flags |= 1;
2009
                else
2010 8736
                        break;
2011 0
        }
2012
        if (*av != NULL)
2013 8736
                vtc_fatal(vl, "Unknown txping spec: %s\n", *av);
2014 8736
        if (!f.data)
2015 8736
                f.data = buf;
2016 8736
        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 147
 * \-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 147
 *      specify the debug data, if any to append to the frame.
2036 147
 */
2037 147
static void
2038
cmd_txgoaway(CMD_ARGS)
2039 294
{
2040 147
        struct stream *s;
2041 105
        char *p;
2042 105
        uint32_t err = 0;
2043 0
        uint32_t ls = 0;
2044 105
        struct frame f;
2045 0
2046 105
        CAST_OBJ_NOTNULL(s, priv, STREAM_MAGIC);
2047 147
2048 42
        INIT_FRAME(f, GOAWAY, 8, s->id, 0);
2049
2050 0
        while (*++av) {
2051
                if (!strcmp(*av, "-err")) {
2052 147
                        ++av;
2053 0
                        for (err = 0; h2_errs[err]; err++)
2054 147
                                if (!strcmp(h2_errs[err], *av))
2055 42
                                        break;
2056 147
2057 147
                        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 105
                f.data = malloc(8);
2079
                AN(f.data);
2080
        }
2081
        vbe32enc(f.data, ls);
2082 105
        vbe32enc(f.data + 4, err);
2083 105
        write_frame(s, &f, 1);
2084
        free(f.data);
2085
}
2086 105
2087
/* SECTION: stream.spec.winup_txwinup txwinup
2088 105
 *
2089
 * Transmit a WINDOW_UPDATE frame, increasing the amount of credit of the
2090 294
 * connection (from stream 0) or of the stream (any other stream).
2091 189
 *
2092 105
 * \-size INT
2093 1575
 *      give INT credits to the peer.
2094 1470
 */
2095 0
static void
2096
cmd_txwinup(CMD_ARGS)
2097 105
{
2098 0
        struct http *hp;
2099
        struct stream *s;
2100 105
        char *p;
2101 189
        struct frame f;
2102 42
        char buf[8];
2103 84
        uint32_t size = 0;
2104 42
2105 42
        CAST_OBJ_NOTNULL(s, priv, STREAM_MAGIC);
2106 0
        CAST_OBJ_NOTNULL(hp, s->hp, HTTP_MAGIC);
2107 42
        memset(buf, 0, 8);
2108 42
2109 42
        AN(av[1]);
2110 42
        AN(av[2]);
2111 42
2112 0
        INIT_FRAME(f, WINDOW_UPDATE, 4, s->id, 0);
2113
        f.data = buf;
2114 105
2115 0
        while (*++av)
2116
                if (!strcmp(*av, "-size")) {
2117 105
                        STRTOU32_CHECK(size, av, p, vl, "-size", 0);
2118 63
                } else
2119 63
                        break;
2120 63
        if (*av != NULL)
2121 105
                vtc_fatal(vl, "Unknown txwinup spec: %s\n", *av);
2122 105
2123 105
        PTOK(pthread_mutex_lock(&hp->mtx));
2124 105
        if (s->id == 0)
2125 105
                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 588
{
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 588
                assert(s->hp->wf >= 0);
2144
                s->hp->wf++;
2145 588
                s->wf = 1;
2146 588
                PTOK(pthread_cond_signal(&s->hp->cond));
2147 588
                PTOK(pthread_cond_wait(&s->cond, &s->hp->mtx));
2148
        }
2149 588
        if (VTAILQ_EMPTY(&s->fq)) {
2150 588
                PTOK(pthread_mutex_unlock(&s->hp->mtx));
2151
                return (NULL);
2152 588
        }
2153 588
        clean_frame(&s->frame);
2154
        f = VTAILQ_LAST(&s->fq, fq_head);
2155 1176
        CHECK_OBJ_NOTNULL(f, FRAME_MAGIC);
2156 588
        VTAILQ_REMOVE(&s->fq, f, list);
2157 588
        PTOK(pthread_mutex_unlock(&s->hp->mtx));
2158 588
        return (f);
2159 0
}
2160 588
2161 0
#define CHKFRAME(rt, wt, rcv, func) \
2162
        do { \
2163 588
        if (rt != wt) \
2164 588
                vtc_fatal(vl, "Frame #%d for %s was of type %s (%d) " \
2165 210
                    "instead of %s (%d)", \
2166 588
                    rcv, func, \
2167 588
                    rt < TYPE_MAX ? h2_types[rt] : "?", rt, \
2168
                    wt < TYPE_MAX ? h2_types[wt] : "?", wt); \
2169 588
        } while (0);
2170 588
2171 588
/* SECTION: stream.spec.data_11 rxhdrs
2172 588
 *
2173
 * ``rxhdrs`` will expect one HEADER frame, then, depending on the arguments,
2174
 * zero or more CONTINUATION frame.
2175 20307
 *
2176
 * \-all
2177
 *      Keep waiting for CONTINUATION frames until END_HEADERS flag is seen.
2178
 *
2179 20307
 * \-some INT
2180
 *      Retrieve INT - 1 CONTINUATION frames after the HEADER frame.
2181 20307
 *
2182 20307
 */
2183 20200
static void
2184 20200
cmd_rxhdrs(CMD_ARGS)
2185 20200
{
2186 20200
        struct stream *s;
2187 20200
        struct frame *f = NULL;
2188 20200
        char *p;
2189 20307
        int loop = 0;
2190 0
        unsigned long int times = 1;
2191 0
        unsigned rcv = 0;
2192
        enum h2_type_e expect = TYPE_HEADERS;
2193 20307
2194 20307
        CAST_OBJ_NOTNULL(s, priv, STREAM_MAGIC);
2195 20307
2196 20307
        while (*++av) {
2197 20307
                if (!strcmp(*av, "-some")) {
2198 20307
                        STRTOU32_CHECK(times, av, p, vl, "-some", 0);
2199 20307
                        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 294
        struct stream *s;
2225
        struct frame *f = NULL;
2226
        char *p;
2227 294
        int loop = 0;
2228
        unsigned long int times = 1;
2229 294
        unsigned rcv = 0;
2230 294
2231 294
        (void)av;
2232 294
        CAST_OBJ_NOTNULL(s, priv, STREAM_MAGIC);
2233
2234 294
        while (*++av)
2235
                if (!strcmp(*av, "-some")) {
2236 336
                        STRTOU32(times, *av, p, vl, "-some");
2237 42
                        if (!times)
2238 21
                                vtc_fatal(vl, "-some argument must be more"
2239 21
                                               "than 0 (found \"%s\")\n", *av);
2240 0
                } else if (!strcmp(*av, "-all"))
2241 0
                        loop = 1;
2242 42
                else
2243 21
                        break;
2244
        if (*av != NULL)
2245 0
                vtc_fatal(vl, "Unknown rxcont spec: %s\n", *av);
2246
2247 294
        do {
2248 0
                replace_frame(&f, rxstuff(s));
2249
                if (f == NULL)
2250 294
                        break;
2251 357
                rcv++;
2252 357
                CHKFRAME(f->type, TYPE_CONTINUATION, rcv, "rxcont");
2253 0
        } while (rcv < times || (loop && !(f->flags & END_HEADERS)));
2254 357
        replace_frame(&s->frame, f);
2255 357
}
2256 357
2257 357
2258 294
/* SECTION: stream.spec.data_13 rxdata
2259 294
 *
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 105
 * arguments:
2263
 *
2264
 * \-all
2265 105
 *      keep waiting for DATA frame until one sets the END_STREAM flag
2266
 *
2267 105
 * \-some INT
2268 105
 *      retrieve INT DATA frames.
2269 105
 *
2270
 */
2271 105
static void
2272 105
cmd_rxdata(CMD_ARGS)
2273
{
2274 105
        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 105
        while (*++av)
2285 0
                if (!strcmp(*av, "-some")) {
2286
                        av++;
2287 105
                        STRTOU32(times, *av, p, vl, "-some");
2288 105
                        if (!times)
2289 105
                                vtc_fatal(vl, "-some argument must be more"
2290 0
                                               "than 0 (found \"%s\")\n", *av);
2291 105
                } else if (!strcmp(*av, "-all"))
2292 105
                        loop = 1;
2293 105
                else
2294 105
                        break;
2295 105
        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 315
 * by a server, and rxresp by a client.
2313
 *
2314
 */
2315 315
2316
#define cmd_rxreq       cmd_rxmsg
2317 315
#define cmd_rxresp      cmd_rxmsg
2318 315
2319 315
static void
2320
cmd_rxmsg(CMD_ARGS)
2321 315
{
2322 315
        struct stream *s;
2323
        struct frame *f = NULL;
2324 336
        int end_stream;
2325 21
        int rcv = 0;
2326 21
2327 21
        CAST_OBJ_NOTNULL(s, priv, STREAM_MAGIC);
2328 21
2329 0
        if (!strcmp(av[0], "rxreq"))
2330 0
                ONLY_H2_SERVER(s->hp, av);
2331 21
        else
2332 0
                ONLY_H2_CLIENT(s->hp, av);
2333
2334 0
        do {
2335 315
                replace_frame(&f, rxstuff(s));
2336 0
                CHECK_OBJ_ORNULL(f, FRAME_MAGIC);
2337
                if (f == NULL)
2338 315
                        return;
2339 357
        } while (f->type == TYPE_WINDOW_UPDATE);
2340 357
2341 0
        rcv++;
2342 357
        CHKFRAME(f->type, TYPE_HEADERS, rcv, *av);
2343 357
2344 357
        end_stream = f->flags & END_STREAM;
2345 315
2346 315
        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 3885
                rcv++;
2361
                CHKFRAME(f->type, TYPE_DATA, rcv, *av);
2362
                end_stream = f->flags & END_STREAM;
2363 3885
        }
2364
        replace_frame(&s->frame, f);
2365 3885
}
2366
2367 3885
/* SECTION: stream.spec.data_12 rxpush
2368
 *
2369 3885
 * This works like ``rxhdrs``, expecting a PUSH frame and then zero or more
2370 987
 * CONTINUATION frames.
2371
 *
2372 2898
 * \-all
2373
 *      Keep waiting for CONTINUATION frames until END_HEADERS flag is seen.
2374 3885
 *
2375 3927
 * \-some INT
2376 3927
 *      Retrieve INT - 1 CONTINUATION frames after the PUSH frame.
2377 3927
 *
2378 0
 */
2379 3927
static void
2380
cmd_rxpush(CMD_ARGS)
2381 3885
{
2382 3885
        struct stream *s;
2383
        struct frame *f = NULL;
2384 3885
        char *p;
2385
        int loop = 0;
2386 4011
        unsigned long int times = 1;
2387 126
        unsigned rcv = 0;
2388 126
        enum h2_type_e expect = TYPE_PUSH_PROMISE;
2389 126
2390 0
        CAST_OBJ_NOTNULL(s, priv, STREAM_MAGIC);
2391 126
2392 126
        while (*++av) {
2393
                if (!strcmp(*av, "-some")) {
2394
                        STRTOU32_CHECK(times, av, p, vl, "-some", 0);
2395 7686
                        if (!times)
2396 3801
                                vtc_fatal(vl, "-some argument must be more"
2397 3801
                                               "than 0 (found \"%s\")\n", *av);
2398 3801
                } else if (!strcmp(*av, "-all")) {
2399 0
                        loop = 1;
2400 3801
                } else
2401 3801
                        break;
2402 3801
        }
2403
        if (*av != NULL)
2404 3885
                vtc_fatal(vl, "Unknown rxpush spec: %s\n", *av);
2405 3885
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 21
 */
2421
static void
2422
cmd_rxwinup(CMD_ARGS)
2423 21
{
2424
        struct stream *s;
2425 21
        struct frame *f;
2426 21
2427 21
        CAST_OBJ_NOTNULL(s, priv, STREAM_MAGIC);
2428 21
        s->frame = rxstuff(s);
2429
        CAST_OBJ_NOTNULL(f, s->frame, FRAME_MAGIC);
2430 21
        CHKFRAME(f->type, TYPE_WINDOW_UPDATE, 0, *av);
2431
        if (s->id == 0)
2432 21
                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 21
        struct stream *s, *s2;
2444 0
        uint32_t val = 0;
2445
        struct http *hp;
2446 21
        struct frame *f;
2447 21
2448 21
        CAST_OBJ_NOTNULL(s, priv, STREAM_MAGIC);
2449 0
        CAST_OBJ_NOTNULL(hp, s->hp, HTTP_MAGIC);
2450 21
        s->frame = rxstuff(s);
2451 21
        CAST_OBJ_NOTNULL(f, s->frame, FRAME_MAGIC);
2452 21
        CHKFRAME(f->type, TYPE_SETTINGS, 0, *av);
2453 21
        if (! isnan(f->md.settings[SETTINGS_INITIAL_WINDOW_SIZE])) {
2454 21
                val = (uint32_t)f->md.settings[SETTINGS_INITIAL_WINDOW_SIZE];
2455 21
                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 525
 * Receive a PRIORITY frame.
2463
 */
2464
static void
2465
cmd_rxprio (CMD_ARGS)
2466
{
2467 525
        struct stream *s;
2468 525
        (void)av;
2469 525
        CAST_OBJ_NOTNULL(s, priv, STREAM_MAGIC);
2470 525
        if (s->hp->no_rfc7540_priorities) {
2471 525
                vtc_log(vl, 4, "skipping rxprio: no_rfc7540_priorities is set");
2472 126
                return;
2473 525
        }
2474 525
        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 8799
        }
2482
}
2483
2484 8799
#define RXFUNC(lctype, upctype) \
2485
        static void \
2486
        cmd_rx ## lctype(CMD_ARGS) \
2487
        { \
2488 8799
                struct stream *s; \
2489 8799
                (void)av; \
2490 8799
                CAST_OBJ_NOTNULL(s, priv, STREAM_MAGIC); \
2491 8799
                s->frame = rxstuff(s); \
2492 8799
                if (s->frame != NULL && s->frame->type != TYPE_ ## upctype) { \
2493 8799
                        vtc_fatal(vl, \
2494 2877
                            "Wrong frame type %s (%d) wanted %s", \
2495 5901
                            s->frame->type < TYPE_MAX ? \
2496 3024
                            h2_types[s->frame->type] : "?", \
2497 2877
                            s->frame->type, #upctype); \
2498 2877
                } \
2499 8799
        }
2500
2501
/* SECTION: stream.spec.reset_rxrst rxrst
2502
 *
2503
 * Receive a RST_STREAM frame.
2504
 */
2505 189
RXFUNC(rst,     RST_STREAM)
2506
2507
/* SECTION: stream.spec.ping_rxping rxping
2508 189
 *
2509 189
 * Receive a PING frame.
2510 189
 */
2511 126
RXFUNC(ping,    PING)
2512 126
2513
/* SECTION: stream.spec.goaway_rxgoaway rxgoaway
2514 63
 *
2515 63
 * 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 189
 */
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 1239
        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 126
2552
        AN(av[0]);
2553
        AN(av[1]);
2554
        AN(av[2]);
2555
        AZ(av[3]);
2556
        PTOK(pthread_mutex_lock(&hp->mtx));
2557 861
        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 16275
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 16275
 *
2586 16275
 * write_body STRING
2587 16275
 *      Same as the ``write_body`` command for HTTP/1.
2588
 */
2589 16275
static void
2590 16275
cmd_write_body(CMD_ARGS)
2591 16275
{
2592 16275
        struct stream *s;
2593 16275
2594
        (void)vl;
2595 16275
        CAST_OBJ_NOTNULL(s, priv, STREAM_MAGIC);
2596 16275
        AN(av[0]);
2597 16275
        AN(av[1]);
2598 16275
        AZ(av[2]);
2599 16275
        AZ(strcmp(av[0], "write_body"));
2600 16275
        if (VFIL_writefile(NULL, av[1], s->body, s->bodylen) != 0)
2601 16275
                vtc_fatal(s->vl, "failed to write body: %s (%d)",
2602 16275
                    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 42
 */
2610
static const struct cmds stream_cmds[] = {
2611
#define CMD_STREAM(n) { CMDS_MAGIC, #n, cmd_##n },
2612
        /* spec */
2613
        CMD_STREAM(expect)
2614 42
        CMD_STREAM(gunzip)
2615 42
        CMD_STREAM(rxcont)
2616
        CMD_STREAM(rxdata)
2617 42
        CMD_STREAM(rxframe)
2618 42
        CMD_STREAM(rxgoaway)
2619 42
        CMD_STREAM(rxhdrs)
2620
        CMD_STREAM(rxping)
2621 42
        CMD_STREAM(rxprio)
2622 42
        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 42
        CMD_STREAM(txdata)
2631
        CMD_STREAM(txgoaway)
2632
        CMD_STREAM(txping)
2633
        CMD_STREAM(txprio)
2634 42
        CMD_STREAM(txpush)
2635 42
        CMD_STREAM(txreq)
2636 42
        CMD_STREAM(txresp)
2637 42
        CMD_STREAM(txrst)
2638 42
        CMD_STREAM(txsettings)
2639 42
        CMD_STREAM(txwinup)
2640 42
        CMD_STREAM(write_body)
2641 0
        { CMDS_MAGIC, NULL, NULL }
2642 0
#undef CMD_STREAM
2643 42
};
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 12389
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 12389
                    "(found %s)", name);
2691 12389
2692 12389
        CHECK_OBJ_NOTNULL(h, HTTP_MAGIC);
2693 12389
        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 10627
        return (s);
2701
}
2702
2703
/**********************************************************************
2704
 * Clean up stream
2705 10627
 */
2706 440
2707 294
static void
2708
stream_delete(struct stream *s)
2709 146
{
2710 440
        struct frame *f, *f2;
2711 440
2712
        CHECK_OBJ_NOTNULL(s, STREAM_MAGIC);
2713 10627
2714 10625
        VTAILQ_FOREACH_SAFE(f, &s->fq, list, f2) {
2715 10625
                VTAILQ_REMOVE(&s->fq, f, list);
2716 10625
                clean_frame(&f);
2717 10625
        }
2718 10625
        vtc_logclose(s->vl);
2719 10625
        clean_headers(s->req);
2720 10625
        clean_headers(s->resp);
2721 10625
        AZ(s->frame);
2722 10625
        free(s->body);
2723
        free(s->spec);
2724 10625
        free(s->name);
2725 10625
        FREE_OBJ(s);
2726
}
2727 10625
2728 10625
/**********************************************************************
2729 0
 * Start the stream thread
2730 0
 */
2731
2732 10625
static void
2733 10625
stream_start(struct stream *s)
2734 10625
{
2735
        CHECK_OBJ_NOTNULL(s, STREAM_MAGIC);
2736
        vtc_log(s->hp->vl, 2, "Starting stream %s (%p)", s->name, s);
2737 10625
        PTOK(pthread_create(&s->tp, NULL, stream_thread, s));
2738 10625
        s->running = 1;
2739 10625
}
2740 10625
2741
/**********************************************************************
2742
 * Wait for stream thread to stop
2743
 */
2744
static void
2745
stream_wait(struct stream *s)
2746
{
2747
        void *res;
2748 10625
        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 10625
        PTOK(pthread_join(s->tp, &res));
2753
        if (res != NULL)
2754 11360
                vtc_fatal(s->hp->vl, "Stream %u returned \"%s\"", s->id,
2755 735
                    (char *)res);
2756 735
2757 735
        VTAILQ_FOREACH_SAFE(f, &s->fq, list, f2) {
2758 10625
                VTAILQ_REMOVE(&s->fq, f, list);
2759 10625
                clean_frame(&f);
2760 10625
        }
2761 10625
        clean_frame(&s->frame);
2762 10625
        s->tp = 0;
2763 10625
        s->running = 0;
2764 10625
}
2765 10625
2766 10625
/**********************************************************************
2767
 * Run the stream thread
2768
 */
2769
2770
static void
2771
stream_run(struct stream *s)
2772
{
2773 12390
        stream_start(s);
2774
        stream_wait(s);
2775 12390
}
2776 12390
2777 12390
2778 12390
2779 12390
/* 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 12390
 *
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 12390
 *
2791 12390
 * Streams map roughly to a request in HTTP/2, a request is sent on
2792 12390
 * stream N, the response too, then the stream is discarded. The main
2793 12390
 * exception is the first stream, 0, that serves as coordinator.
2794 0
 *
2795 0
 * Stream syntax follow the client/server one::
2796
 *
2797 12558
 *      stream ID [SPEC] [ACTION]
2798 168
 *
2799 168
 * ID is the HTTP/2 stream number, while SPEC describes what will be
2800 168
 * done in that stream. If ID has the value ``next``, the actual stream
2801 12390
 * number is computed based on the last one.
2802 12390
 *
2803 12390
 * Note that, when parsing a stream action, if the entity isn't operating
2804 12390
 * 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 11296
 *          rxsettings
2812
 *          expect settings.ack == true
2813 11296
 *      } -run
2814 11296
 *
2815 11296
 * 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 12893
                        vtc_fatal(vl, "Unknown stream argument: %s", *av);
2871
                REPLACE(s->spec, *av);
2872
        }
2873
}
2874
2875 12893
void
2876 12893
b64_settings(struct http *hp, const char *s)
2877
{
2878 12893
        uint16_t i;
2879 12893
        uint64_t v, vv;
2880
        const char *buf;
2881 25514
        int shift;
2882 14889
2883 2268
        while (*s) {
2884 12893
                v = 0;
2885 10625
                for (shift = 42; shift >= 0; shift -= 6) {
2886 12893
                        if (*s >= 'A' && *s <= 'Z')
2887
                                vv = (*s - 'A');
2888 38193
                        else if (*s >= 'a' && *s <= 'z')
2889 25300
                                vv = (*s - 'a') + 26;
2890 0
                        else if (*s >= '0' && *s <= '9')
2891
                                vv = (*s - '0') + 52;
2892 25300
                        else if (*s == '-')
2893 525
                                vv = 62;
2894 525
                        else if (*s == '_')
2895
                                vv = 63;
2896
                        else
2897
                                vtc_fatal(hp->vl,
2898 24775
                                    "Bad \"HTTP2-Settings\" header");
2899 0
                        v |= vv << shift;
2900
                        s++;
2901 24775
                }
2902 1092
                i = v >> 32;
2903 1092
                v &= 0xffff;
2904
2905 23683
                if (i <= SETTINGS_MAX)
2906 11296
                        buf = h2_settings[i];
2907 11296
                else
2908
                        buf = "unknown";
2909 12387
2910 0
                if (i == SETTINGS_NO_RFC7540_PRIORITIES) {
2911 12387
                        hp->no_rfc7540_priorities = v;
2912 12387
                }
2913 12893
                if (i == SETTINGS_HEADER_TABLE_SIZE) {
2914
                        enum hpk_result hrs;
2915
                        if (hp->sfd) {
2916 63
                                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 252
                                vtc_fatal(hp->vl, "HPK resize failed %d\n", hrs);
2924 189
                }
2925 1701
2926 1512
                vtc_log(hp->vl, 4, "Upgrade: %s (%d): %ju",
2927 1260
                    buf, i, (intmax_t)v);
2928 252
        }
2929 126
}
2930 126
2931 0
void
2932 126
start_h2(struct http *hp)
2933 0
{
2934 126
        CHECK_OBJ_NOTNULL(hp, HTTP_MAGIC);
2935 126
        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 1512
        hp->h2_win_self->size = 0xffff;
2940 1512
        hp->h2_win_peer->init = 0xffff;
2941 1512
        hp->h2_win_peer->size = 0xffff;
2942 189
        hp->h2 = 1;
2943 189
2944
        hp->decctx = HPK_NewCtx(4096);
2945 189
        hp->encctx = HPK_NewCtx(4096);
2946 189
        PTOK(pthread_create(&hp->tp, NULL, receive_frame, hp));
2947
}
2948 0
2949
void
2950 189
stop_h2(struct http *hp)
2951 63
{
2952 63
        struct stream *s, *s2;
2953 189
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 378
        PTOK(pthread_cond_signal(&hp->cond));
2967 189
        PTOK(pthread_mutex_unlock(&hp->mtx));
2968
        PTOK(pthread_join(hp->tp, NULL));
2969 63
2970
        HPK_FreeCtx(hp->decctx);
2971
        HPK_FreeCtx(hp->encctx);
2972 4471
2973
        PTOK(pthread_mutex_destroy(&hp->mtx));
2974 4471
        PTOK(pthread_cond_destroy(&hp->cond));
2975 4471
}