test_frame_writer.c revision 06b2a236
1/* Copyright (c) 2017 - 2021 LiteSpeed Technologies Inc. See LICENSE. */ 2#include <assert.h> 3#include <errno.h> 4#include <stdio.h> 5#include <stdlib.h> 6#include <string.h> 7#ifndef WIN32 8#include <sys/time.h> 9#endif 10#include <sys/queue.h> 11 12#include "lsquic.h" 13#include "lshpack.h" 14#include "lsquic_mm.h" 15#include "lsquic_int_types.h" 16#include "lsquic_hash.h" 17#include "lsquic_conn.h" 18#include "lsquic_frame_common.h" 19#include "lsquic_frame_writer.h" 20#if LSQUIC_CONN_STATS 21#include "lsquic_int_types.h" 22#include "lsquic_conn.h" 23#endif 24 25#if LSQUIC_CONN_STATS 26static struct conn_stats s_conn_stats; 27#endif 28 29 30static struct { 31 size_t sz; 32 size_t max; 33 unsigned char buf[0x1000]; 34} output; 35 36 37#define reset_output(max_) do { \ 38 output.sz = 0; \ 39 if (max_) \ 40 output.max = max_; \ 41 else \ 42 output.max = sizeof(output.buf);\ 43} while (0) 44 45 46static ssize_t 47output_write (struct lsquic_stream *stream, struct lsquic_reader *reader) 48{ 49 size_t sz; 50 51 sz = reader->lsqr_size(reader->lsqr_ctx); 52 if (output.max - output.sz < sz) 53 { 54 errno = ENOBUFS; 55 return -1; 56 } 57 58 sz = reader->lsqr_read(reader->lsqr_ctx, output.buf + output.sz, sz); 59 output.sz += sz; 60 61 return sz; 62} 63 64 65#define IOV(v) { .iov_base = (v), .iov_len = sizeof(v) - 1, } 66#define XHDR(name_, value_) .buf = name_ value_, .name_offset = 0, .name_len = sizeof(name_) - 1, .val_offset = sizeof(name_) - 1, .val_len = sizeof(value_) - 1, 67 68 69static void 70test_max_frame_size (void) 71{ 72 struct lshpack_enc henc; 73 struct lsquic_mm mm; 74 struct lsquic_frame_writer *fw; 75 unsigned max_size; 76 77 lshpack_enc_init(&henc); 78 lsquic_mm_init(&mm); 79 80 for (max_size = 1; max_size < 6 /* one settings frame */; ++max_size) 81 { 82 fw = lsquic_frame_writer_new(&mm, NULL, max_size, &henc, 83 output_write, 84#if LSQUIC_CONN_STATS 85 &s_conn_stats, 86#endif 87 0); 88 assert(!fw); 89 } 90 91 fw = lsquic_frame_writer_new(&mm, NULL, max_size, &henc, output_write, 92#if LSQUIC_CONN_STATS 93 &s_conn_stats, 94#endif 95 0); 96 assert(fw); 97 98 lsquic_frame_writer_destroy(fw); 99 lshpack_enc_cleanup(&henc); 100 lsquic_mm_cleanup(&mm); 101} 102 103 104static void 105test_one_header (void) 106{ 107 struct lshpack_enc henc; 108 struct lsquic_frame_writer *fw; 109 int s; 110 struct lsquic_mm mm; 111 112 lshpack_enc_init(&henc); 113 lsquic_mm_init(&mm); 114 fw = lsquic_frame_writer_new(&mm, NULL, 0x200, &henc, output_write, 115#if LSQUIC_CONN_STATS 116 &s_conn_stats, 117#endif 118 0); 119 reset_output(0); 120 121 struct lsxpack_header header_arr[] = 122 { 123 { XHDR(":status", "302") }, 124 }; 125 126 struct lsquic_http_headers headers = { 127 .count = 1, 128 .headers = header_arr, 129 }; 130 131 s = lsquic_frame_writer_write_headers(fw, 12345, &headers, 0, 100); 132 assert(0 == s); 133 134 struct http_frame_header fh; 135 struct http_prio_frame prio_frame; 136 137 assert(4 + sizeof(struct http_frame_header) + sizeof(struct http_prio_frame) == output.sz); 138 139 memcpy(&fh, output.buf, sizeof(fh)); 140 assert(4 + sizeof(struct http_prio_frame) == hfh_get_length(&fh)); 141 assert(HTTP_FRAME_HEADERS == fh.hfh_type); 142 assert((HFHF_END_HEADERS|HFHF_PRIORITY) == fh.hfh_flags); 143 assert(fh.hfh_stream_id[0] == 0); 144 assert(fh.hfh_stream_id[1] == 0); 145 assert(fh.hfh_stream_id[2] == 0x30); 146 assert(fh.hfh_stream_id[3] == 0x39); 147 148 memcpy(&prio_frame, output.buf + sizeof(struct http_frame_header), 149 sizeof(struct http_prio_frame)); 150 151 assert(prio_frame.hpf_stream_id[0] == 0); 152 assert(prio_frame.hpf_stream_id[1] == 0); 153 assert(prio_frame.hpf_stream_id[2] == 0); 154 assert(prio_frame.hpf_stream_id[3] == 0); 155 assert(prio_frame.hpf_weight == 100 - 1); 156 157 assert(0 == memcmp(output.buf + sizeof(struct http_frame_header) + 158 sizeof(struct http_prio_frame), "\x48\x82\x64\x02", 4)); 159 160 lsquic_frame_writer_destroy(fw); 161 lshpack_enc_cleanup(&henc); 162 lsquic_mm_cleanup(&mm); 163} 164 165struct header_buf 166{ 167 unsigned off; 168 char buf[UINT16_MAX]; 169}; 170 171 172int 173header_set_ptr (struct lsxpack_header *hdr, struct header_buf *header_buf, 174 const char *name, size_t name_len, 175 const char *val, size_t val_len) 176{ 177 if (header_buf->off + name_len + val_len <= sizeof(header_buf->buf)) 178 { 179 memcpy(header_buf->buf + header_buf->off, name, name_len); 180 memcpy(header_buf->buf + header_buf->off + name_len, val, val_len); 181 lsxpack_header_set_offset2(hdr, header_buf->buf + header_buf->off, 182 0, name_len, name_len, val_len); 183 header_buf->off += name_len + val_len; 184 return 0; 185 } 186 else 187 return -1; 188} 189 190 191static void 192test_oversize_header (void) 193{ 194 struct lshpack_enc henc; 195 struct lsquic_frame_writer *fw; 196 int s; 197 struct lsquic_mm mm; 198 const size_t big_len = LSXPACK_MAX_STRLEN - 20; 199 char *value; 200 struct header_buf hbuf; 201 202 lshpack_enc_init(&henc); 203 lsquic_mm_init(&mm); 204 fw = lsquic_frame_writer_new(&mm, NULL, 0x200, &henc, output_write, 205#if LSQUIC_CONN_STATS 206 &s_conn_stats, 207#endif 208 0); 209 lsquic_frame_writer_max_header_list_size(fw, 1 << 16); 210 reset_output(0); 211 212 value = malloc(big_len); 213 memset(value, 'A', big_len); 214 215 struct lsxpack_header header_arr[3] = 216 { 217 { XHDR(":status", "302") }, 218 }; 219 hbuf.off = 0; 220 header_set_ptr(&header_arr[1], &hbuf, "some-header", 10, value, big_len); 221 header_set_ptr(&header_arr[2], &hbuf, "another-header", 10, value, big_len); 222 223 struct lsquic_http_headers headers = { 224 .count = sizeof(header_arr) / sizeof(header_arr[0]), 225 .headers = header_arr, 226 }; 227 228 s = lsquic_frame_writer_write_headers(fw, 12345, &headers, 0, 100); 229 assert(-1 == s); 230 231 lsquic_frame_writer_destroy(fw); 232 lshpack_enc_cleanup(&henc); 233 lsquic_mm_cleanup(&mm); 234 free(value); 235} 236 237 238static void 239test_continuations (void) 240{ 241 struct lsquic_frame_writer *fw; 242 struct lshpack_enc henc; 243 int s; 244 struct lsquic_mm mm; 245 246 lshpack_enc_init(&henc); 247 lsquic_mm_init(&mm); 248 fw = lsquic_frame_writer_new(&mm, NULL, 6, &henc, output_write, 249#if LSQUIC_CONN_STATS 250 &s_conn_stats, 251#endif 252 0); 253 reset_output(0); 254 255/* 256perl tools/henc.pl :status 302 x-some-header some-value | hexdump -C 25700000000 48 82 64 02 40 8a f2 b2 0f 49 56 9c a3 90 b6 7f |H.d.@....IV.....| 25800000010 87 41 e9 2a dd c7 45 a5 |.A.*..E.| 259*/ 260 261 struct lsxpack_header header_arr[] = 262 { 263 { XHDR(":status", "302") }, 264 { XHDR("x-some-header", "some-value") }, 265 }; 266 267 struct lsquic_http_headers headers = { 268 .count = 2, 269 .headers = header_arr, 270 }; 271 272 s = lsquic_frame_writer_write_headers(fw, 12345, &headers, 0, 100); 273 assert(0 == s); 274 275 /* Expected payload is 5 bytes of http_prio_frame and 24 bytes of 276 * compressed headers, split into 4 15-byte chunks (9-byte header 277 * 6-byte payload) and 1 14-byte chunk (9-byte header and 5-byte 278 * payload). 279 */ 280 unsigned char expected_buf[] = { 281 /* Length: */ 0x00, 0x00, 0x06, /* 1 */ 282 /* Type: */ HTTP_FRAME_HEADERS, 283 /* Flags: */ HFHF_PRIORITY, 284 /* Stream Id: */ 0x00, 0x00, 0x30, 0x39, 285 /* Payload (priority info): */ 286 0x00, 0x00, 0x00, 0x00, 100 - 1, 287 /* Payload (headers): */ 288 0x48, 289 /* Length: */ 0x00, 0x00, 0x06, /* 2 */ 290 /* Type: */ HTTP_FRAME_CONTINUATION, 291 /* Flags: */ 0x00, 292 /* Stream Id: */ 0x00, 0x00, 0x30, 0x39, 293 /* Payload (headers): */ 294 0x82, 0x64, 0x02, 0x40, 0x8A, 0xF2, 295 /* Length: */ 0x00, 0x00, 0x06, /* 3 */ 296 /* Type: */ HTTP_FRAME_CONTINUATION, 297 /* Flags: */ 0x00, 298 /* Stream Id: */ 0x00, 0x00, 0x30, 0x39, 299 /* Payload (headers): */ 300 0xb2, 0x0f, 0x49, 0x56, 0x9c, 0xa3, 301 /* Length: */ 0x00, 0x00, 0x06, /* 4 */ 302 /* Type: */ HTTP_FRAME_CONTINUATION, 303 /* Flags: */ 0x00, 304 /* Stream Id: */ 0x00, 0x00, 0x30, 0x39, 305 /* Payload (headers): */ 306 0x90, 0xb6, 0x7f, 0x87, 0x41, 0xe9, 307 /* Length: */ 0x00, 0x00, 0x05, /* 5 */ 308 /* Type: */ HTTP_FRAME_CONTINUATION, 309 /* Flags: */ HFHF_END_HEADERS, 310 /* Stream Id: */ 0x00, 0x00, 0x30, 0x39, 311 /* Payload (headers): */ 312 0x2a, 0xdd, 0xc7, 0x45, 0xa5, 313 }; 314 315 assert(sizeof(expected_buf) == output.sz); 316 317 assert(0 == memcmp(output.buf + 0, expected_buf + 0, 15)); 318 assert(0 == memcmp(output.buf + 15, expected_buf + 15, 15)); 319 assert(0 == memcmp(output.buf + 30, expected_buf + 30, 15)); 320 assert(0 == memcmp(output.buf + 45, expected_buf + 45, 15)); 321 assert(0 == memcmp(output.buf + 60, expected_buf + 60, 14)); 322 323 lsquic_frame_writer_destroy(fw); 324 lshpack_enc_cleanup(&henc); 325 lsquic_mm_cleanup(&mm); 326} 327 328 329static void 330test_settings_short (void) 331{ 332 struct lsquic_frame_writer *fw; 333 int s; 334 struct lsquic_mm mm; 335 336 lsquic_mm_init(&mm); 337 fw = lsquic_frame_writer_new(&mm, NULL, 7, NULL, output_write, 338#if LSQUIC_CONN_STATS 339 &s_conn_stats, 340#endif 341 0); 342 343 { 344 reset_output(0); 345 struct lsquic_http2_setting settings[] = { { 1, 2, }, { 3, 4, } }; 346 s = lsquic_frame_writer_write_settings(fw, settings, 2); 347 assert(0 == s); 348 const unsigned char exp_buf[] = { 349 /* Length: */ 0x00, 0x00, 0x06, 350 /* Type: */ HTTP_FRAME_SETTINGS, 351 /* Flags: */ 0x00, 352 /* Stream Id: */ 0x00, 0x00, 0x00, 0x00, 353 /* Payload: */ 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 354 /* Length: */ 0x00, 0x00, 0x06, 355 /* Type: */ HTTP_FRAME_SETTINGS, 356 /* Flags: */ 0x00, 357 /* Stream Id: */ 0x00, 0x00, 0x00, 0x00, 358 /* Payload: */ 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 359 }; 360 assert(output.sz == sizeof(exp_buf)); 361 assert(0 == memcmp(output.buf, exp_buf, sizeof(exp_buf))); 362 } 363 364 { 365 reset_output(0); 366 struct lsquic_http2_setting settings[] = { { 1, 2, }, { 3, 4, } }; 367 s = lsquic_frame_writer_write_settings(fw, settings, 0); 368 assert(-1 == s); 369 assert(EINVAL == errno); 370 } 371 372 lsquic_frame_writer_destroy(fw); 373 lsquic_mm_cleanup(&mm); 374} 375 376 377static void 378test_settings_normal (void) 379{ 380 struct lsquic_frame_writer *fw; 381 int s; 382 struct lsquic_mm mm; 383 384 lsquic_mm_init(&mm); 385 fw = lsquic_frame_writer_new(&mm, NULL, 0, NULL, output_write, 386#if LSQUIC_CONN_STATS 387 &s_conn_stats, 388#endif 389 0); 390 391 { 392 reset_output(0); 393 struct lsquic_http2_setting settings[] = { { 1, 2, }, { 3, 4, } }; 394 s = lsquic_frame_writer_write_settings(fw, settings, 2); 395 assert(0 == s); 396 const unsigned char exp_buf[] = { 397 /* Length: */ 0x00, 0x00, 0x0C, 398 /* Type: */ HTTP_FRAME_SETTINGS, 399 /* Flags: */ 0x00, 400 /* Stream Id: */ 0x00, 0x00, 0x00, 0x00, 401 /* Payload: */ 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 402 /* Payload: */ 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 403 }; 404 assert(output.sz == sizeof(exp_buf)); 405 assert(0 == memcmp(output.buf, exp_buf, sizeof(exp_buf))); 406 } 407 408 { 409 reset_output(0); 410 struct lsquic_http2_setting settings[] = { { 1, 2, }, { 3, 4, } }; 411 s = lsquic_frame_writer_write_settings(fw, settings, 0); 412 assert(-1 == s); 413 assert(EINVAL == errno); 414 } 415 416 lsquic_frame_writer_destroy(fw); 417 lsquic_mm_cleanup(&mm); 418} 419 420 421static struct lsquic_conn my_conn = LSCONN_INITIALIZER_CIDLEN(my_conn, 8); 422 423 424#if !defined(NDEBUG) && __GNUC__ 425__attribute__((weak)) 426#endif 427lsquic_conn_t * 428lsquic_stream_conn (const lsquic_stream_t *stream) 429{ 430 return &my_conn; 431} 432 433 434static void 435test_priority (void) 436{ 437 struct lsquic_frame_writer *fw; 438 int s; 439 struct lsquic_mm mm; 440 441 lsquic_mm_init(&mm); 442 fw = lsquic_frame_writer_new(&mm, NULL, 6, NULL, output_write, 443#if LSQUIC_CONN_STATS 444 &s_conn_stats, 445#endif 446 0); 447 448 s = lsquic_frame_writer_write_priority(fw, 3, 0, 1UL << 31, 256); 449 assert(s < 0); /* Invalid dependency stream ID */ 450 451 s = lsquic_frame_writer_write_priority(fw, 3, 0, 1, 0); 452 assert(s < 0); /* Invalid priority stream ID */ 453 454 s = lsquic_frame_writer_write_priority(fw, 3, 0, 1, 257); 455 assert(s < 0); /* Invalid priority stream ID */ 456 457 { 458 reset_output(0); 459 s = lsquic_frame_writer_write_priority(fw, 3, 0, 1, 256); 460 assert(0 == s); 461 const unsigned char exp_buf[] = { 462 /* Length: */ 0x00, 0x00, 5, 463 /* Type: */ HTTP_FRAME_PRIORITY, 464 /* Flags: */ 0x00, 465 /* Stream Id: */ 0x00, 0x00, 0x00, 0x03, 466 /* Dep stream Id: */0x00, 0x00, 0x00, 0x01, 467 /* Weight: */ 0xFF, 468 }; 469 assert(output.sz == sizeof(exp_buf)); 470 assert(0 == memcmp(output.buf, exp_buf, sizeof(exp_buf))); 471 } 472 473 { 474 reset_output(0); 475 s = lsquic_frame_writer_write_priority(fw, 20, 1, 100, 256); 476 assert(0 == s); 477 const unsigned char exp_buf[] = { 478 /* Length: */ 0x00, 0x00, 5, 479 /* Type: */ HTTP_FRAME_PRIORITY, 480 /* Flags: */ 0x00, 481 /* Stream Id: */ 0x00, 0x00, 0x00, 0x14, 482 /* Dep stream Id: */0x80, 0x00, 0x00, 0x64, 483 /* Weight: */ 0xFF, 484 }; 485 assert(output.sz == sizeof(exp_buf)); 486 assert(0 == memcmp(output.buf, exp_buf, sizeof(exp_buf))); 487 } 488 489 lsquic_frame_writer_destroy(fw); 490 lsquic_mm_cleanup(&mm); 491} 492 493 494 495static void 496test_errors (void) 497{ 498 struct lsquic_frame_writer *fw; 499 struct lsquic_mm mm; 500 struct lshpack_enc henc; 501 int s; 502 503 lshpack_enc_init(&henc); 504 lsquic_mm_init(&mm); 505 fw = lsquic_frame_writer_new(&mm, NULL, 0x200, &henc, output_write, 506#if LSQUIC_CONN_STATS 507 &s_conn_stats, 508#endif 509 1); 510 reset_output(0); 511 512 { 513 struct lsxpack_header header_arr[] = 514 { 515 { XHDR(":status", "200") }, 516 { XHDR("Content-type", "text/html") }, 517 }; 518 struct lsquic_http_headers headers = { 519 .count = 2, 520 .headers = header_arr, 521 }; 522 s = lsquic_frame_writer_write_headers(fw, 12345, &headers, 0, 80); 523 assert(-1 == s); 524 assert(EINVAL == errno); 525 } 526 527 { 528 struct lsxpack_header header_arr[] = 529 { 530 { XHDR(":status", "200") }, 531 { XHDR("content-type", "text/html") }, 532 }; 533 struct lsquic_http_headers headers = { 534 .count = 2, 535 .headers = header_arr, 536 }; 537 lsquic_frame_writer_max_header_list_size(fw, 40); 538 s = lsquic_frame_writer_write_headers(fw, 12345, &headers, 0, 80); 539 /* Server ignores SETTINGS_MAX_HEADER_LIST_SIZE setting */ 540 assert(s == 0); 541 } 542 543 lsquic_frame_writer_destroy(fw); 544 lshpack_enc_cleanup(&henc); 545 lsquic_mm_cleanup(&mm); 546} 547 548 549static void 550test_push_promise (void) 551{ 552 struct lshpack_enc henc; 553 struct lsquic_frame_writer *fw; 554 int s; 555 struct lsquic_mm mm; 556 557 lshpack_enc_init(&henc); 558 lsquic_mm_init(&mm); 559 fw = lsquic_frame_writer_new(&mm, NULL, 0x200, &henc, output_write, 560#if LSQUIC_CONN_STATS 561 &s_conn_stats, 562#endif 563 1); 564 reset_output(0); 565 566/* 567perl tools/hpack.pl :method GET :path /index.html :authority www.example.com :scheme https x-some-header some-value| hexdump -C 56800000000 82 85 41 8c f1 e3 c2 e5 f2 3a 6b a0 ab 90 f4 ff |..A......:k.....| 56900000010 87 40 8a f2 b2 0f 49 56 9c a3 90 b6 7f 87 41 e9 |.@....IV......A.| 57000000020 2a dd c7 45 a5 |*..E.| 571*/ 572 573 const unsigned char exp_headers[] = { 574 0x82, 0x85, 0x41, 0x8c, 0xf1, 0xe3, 0xc2, 0xe5, 0xf2, 0x3a, 575 0x6b, 0xa0, 0xab, 0x90, 0xf4, 0xff, 0x87, 0x40, 0x8a, 0xf2, 576 0xb2, 0x0f, 0x49, 0x56, 0x9c, 0xa3, 0x90, 0xb6, 0x7f, 0x87, 577 0x41, 0xe9, 0x2a, 0xdd, 0xc7, 0x45, 0xa5, 578 }; 579 580 struct lsxpack_header header_arr[] = 581 { 582 { XHDR(":method", "GET") }, 583 { XHDR(":path", "/index.html") }, 584 { XHDR(":authority", "www.example.com") }, 585 { XHDR(":scheme", "https") }, 586 { XHDR("x-some-header", "some-value") }, 587 }; 588 589 struct lsquic_http_headers headers = { 590 .count = 5, 591 .headers = header_arr, 592 }; 593 594 s = lsquic_frame_writer_write_promise(fw, 12345, 0xEEEE, &headers); 595 assert(0 == s); 596 597 struct http_frame_header fh; 598 struct http_push_promise_frame push_frame; 599 600 assert(sizeof(exp_headers) + sizeof(struct http_frame_header) + 601 sizeof(struct http_push_promise_frame) == output.sz); 602 603 memcpy(&fh, output.buf, sizeof(fh)); 604 assert(sizeof(exp_headers) + sizeof(struct http_push_promise_frame) == hfh_get_length(&fh)); 605 assert(HTTP_FRAME_PUSH_PROMISE == fh.hfh_type); 606 assert(HFHF_END_HEADERS == fh.hfh_flags); 607 assert(fh.hfh_stream_id[0] == 0); 608 assert(fh.hfh_stream_id[1] == 0); 609 assert(fh.hfh_stream_id[2] == 0x30); 610 assert(fh.hfh_stream_id[3] == 0x39); 611 612 memcpy(&push_frame, output.buf + sizeof(struct http_frame_header), 613 sizeof(struct http_push_promise_frame)); 614 615 assert(push_frame.hppf_promised_id[0] == 0); 616 assert(push_frame.hppf_promised_id[1] == 0); 617 assert(push_frame.hppf_promised_id[2] == 0xEE); 618 assert(push_frame.hppf_promised_id[3] == 0xEE); 619 620 assert(0 == memcmp(output.buf + sizeof(struct http_frame_header) + 621 sizeof(struct http_push_promise_frame), exp_headers, 622 sizeof(exp_headers))); 623 624 lsquic_frame_writer_destroy(fw); 625 lshpack_enc_cleanup(&henc); 626 lsquic_mm_cleanup(&mm); 627} 628 629 630 631int 632main (void) 633{ 634 test_one_header(); 635 test_oversize_header(); 636 test_continuations(); 637 test_settings_normal(); 638 test_settings_short(); 639 test_priority(); 640 test_push_promise(); 641 test_errors(); 642 test_max_frame_size(); 643 return 0; 644} 645