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