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