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