test_frame_writer.c revision 9a690580
1/* Copyright (c) 2017 - 2020 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 = value_, .name_ptr = name_, .val_len = sizeof(value_) - 1, .name_len = sizeof(name_) - 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 165 166static void 167test_oversize_header (void) 168{ 169 struct lshpack_enc henc; 170 struct lsquic_frame_writer *fw; 171 int s; 172 struct lsquic_mm mm; 173 const size_t big_len = LSXPACK_MAX_STRLEN - 20; 174 char *value; 175 176 lshpack_enc_init(&henc); 177 lsquic_mm_init(&mm); 178 fw = lsquic_frame_writer_new(&mm, NULL, 0x200, &henc, output_write, 179#if LSQUIC_CONN_STATS 180 &s_conn_stats, 181#endif 182 0); 183 lsquic_frame_writer_max_header_list_size(fw, 1 << 16); 184 reset_output(0); 185 186 value = malloc(big_len); 187 memset(value, 'A', big_len); 188 189 struct lsxpack_header header_arr[3] = 190 { 191 { XHDR(":status", "302") }, 192 }; 193 lsxpack_header_set_ptr(&header_arr[1], "some-header", 10, value, big_len); 194 lsxpack_header_set_ptr(&header_arr[2], "another-header", 10, value, big_len); 195 196 struct lsquic_http_headers headers = { 197 .count = sizeof(header_arr) / sizeof(header_arr[0]), 198 .headers = header_arr, 199 }; 200 201 s = lsquic_frame_writer_write_headers(fw, 12345, &headers, 0, 100); 202 assert(-1 == s); 203 204 lsquic_frame_writer_destroy(fw); 205 lshpack_enc_cleanup(&henc); 206 lsquic_mm_cleanup(&mm); 207 free(value); 208} 209 210 211static void 212test_continuations (void) 213{ 214 struct lsquic_frame_writer *fw; 215 struct lshpack_enc henc; 216 int s; 217 struct lsquic_mm mm; 218 219 lshpack_enc_init(&henc); 220 lsquic_mm_init(&mm); 221 fw = lsquic_frame_writer_new(&mm, NULL, 6, &henc, output_write, 222#if LSQUIC_CONN_STATS 223 &s_conn_stats, 224#endif 225 0); 226 reset_output(0); 227 228/* 229perl tools/henc.pl :status 302 x-some-header some-value | hexdump -C 23000000000 48 82 64 02 40 8a f2 b2 0f 49 56 9c a3 90 b6 7f |H.d.@....IV.....| 23100000010 87 41 e9 2a dd c7 45 a5 |.A.*..E.| 232*/ 233 234 struct lsxpack_header header_arr[] = 235 { 236 { XHDR(":status", "302") }, 237 { XHDR("x-some-header", "some-value") }, 238 }; 239 240 struct lsquic_http_headers headers = { 241 .count = 2, 242 .headers = header_arr, 243 }; 244 245 s = lsquic_frame_writer_write_headers(fw, 12345, &headers, 0, 100); 246 assert(0 == s); 247 248 /* Expected payload is 5 bytes of http_prio_frame and 24 bytes of 249 * compressed headers, split into 4 15-byte chunks (9-byte header 250 * 6-byte payload) and 1 14-byte chunk (9-byte header and 5-byte 251 * payload). 252 */ 253 unsigned char expected_buf[] = { 254 /* Length: */ 0x00, 0x00, 0x06, /* 1 */ 255 /* Type: */ HTTP_FRAME_HEADERS, 256 /* Flags: */ HFHF_PRIORITY, 257 /* Stream Id: */ 0x00, 0x00, 0x30, 0x39, 258 /* Payload (priority info): */ 259 0x00, 0x00, 0x00, 0x00, 100 - 1, 260 /* Payload (headers): */ 261 0x48, 262 /* Length: */ 0x00, 0x00, 0x06, /* 2 */ 263 /* Type: */ HTTP_FRAME_CONTINUATION, 264 /* Flags: */ 0x00, 265 /* Stream Id: */ 0x00, 0x00, 0x30, 0x39, 266 /* Payload (headers): */ 267 0x82, 0x64, 0x02, 0x40, 0x8A, 0xF2, 268 /* Length: */ 0x00, 0x00, 0x06, /* 3 */ 269 /* Type: */ HTTP_FRAME_CONTINUATION, 270 /* Flags: */ 0x00, 271 /* Stream Id: */ 0x00, 0x00, 0x30, 0x39, 272 /* Payload (headers): */ 273 0xb2, 0x0f, 0x49, 0x56, 0x9c, 0xa3, 274 /* Length: */ 0x00, 0x00, 0x06, /* 4 */ 275 /* Type: */ HTTP_FRAME_CONTINUATION, 276 /* Flags: */ 0x00, 277 /* Stream Id: */ 0x00, 0x00, 0x30, 0x39, 278 /* Payload (headers): */ 279 0x90, 0xb6, 0x7f, 0x87, 0x41, 0xe9, 280 /* Length: */ 0x00, 0x00, 0x05, /* 5 */ 281 /* Type: */ HTTP_FRAME_CONTINUATION, 282 /* Flags: */ HFHF_END_HEADERS, 283 /* Stream Id: */ 0x00, 0x00, 0x30, 0x39, 284 /* Payload (headers): */ 285 0x2a, 0xdd, 0xc7, 0x45, 0xa5, 286 }; 287 288 assert(sizeof(expected_buf) == output.sz); 289 290 assert(0 == memcmp(output.buf + 0, expected_buf + 0, 15)); 291 assert(0 == memcmp(output.buf + 15, expected_buf + 15, 15)); 292 assert(0 == memcmp(output.buf + 30, expected_buf + 30, 15)); 293 assert(0 == memcmp(output.buf + 45, expected_buf + 45, 15)); 294 assert(0 == memcmp(output.buf + 60, expected_buf + 60, 14)); 295 296 lsquic_frame_writer_destroy(fw); 297 lshpack_enc_cleanup(&henc); 298 lsquic_mm_cleanup(&mm); 299} 300 301 302static void 303test_settings_short (void) 304{ 305 struct lsquic_frame_writer *fw; 306 int s; 307 struct lsquic_mm mm; 308 309 lsquic_mm_init(&mm); 310 fw = lsquic_frame_writer_new(&mm, NULL, 7, NULL, output_write, 311#if LSQUIC_CONN_STATS 312 &s_conn_stats, 313#endif 314 0); 315 316 { 317 reset_output(0); 318 struct lsquic_http2_setting settings[] = { { 1, 2, }, { 3, 4, } }; 319 s = lsquic_frame_writer_write_settings(fw, settings, 2); 320 assert(0 == s); 321 const unsigned char exp_buf[] = { 322 /* Length: */ 0x00, 0x00, 0x06, 323 /* Type: */ HTTP_FRAME_SETTINGS, 324 /* Flags: */ 0x00, 325 /* Stream Id: */ 0x00, 0x00, 0x00, 0x00, 326 /* Payload: */ 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 327 /* Length: */ 0x00, 0x00, 0x06, 328 /* Type: */ HTTP_FRAME_SETTINGS, 329 /* Flags: */ 0x00, 330 /* Stream Id: */ 0x00, 0x00, 0x00, 0x00, 331 /* Payload: */ 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 332 }; 333 assert(output.sz == sizeof(exp_buf)); 334 assert(0 == memcmp(output.buf, exp_buf, sizeof(exp_buf))); 335 } 336 337 { 338 reset_output(0); 339 struct lsquic_http2_setting settings[] = { { 1, 2, }, { 3, 4, } }; 340 s = lsquic_frame_writer_write_settings(fw, settings, 0); 341 assert(-1 == s); 342 assert(EINVAL == errno); 343 } 344 345 lsquic_frame_writer_destroy(fw); 346 lsquic_mm_cleanup(&mm); 347} 348 349 350static void 351test_settings_normal (void) 352{ 353 struct lsquic_frame_writer *fw; 354 int s; 355 struct lsquic_mm mm; 356 357 lsquic_mm_init(&mm); 358 fw = lsquic_frame_writer_new(&mm, NULL, 0, NULL, output_write, 359#if LSQUIC_CONN_STATS 360 &s_conn_stats, 361#endif 362 0); 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, 2); 368 assert(0 == s); 369 const unsigned char exp_buf[] = { 370 /* Length: */ 0x00, 0x00, 0x0C, 371 /* Type: */ HTTP_FRAME_SETTINGS, 372 /* Flags: */ 0x00, 373 /* Stream Id: */ 0x00, 0x00, 0x00, 0x00, 374 /* Payload: */ 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 375 /* Payload: */ 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 376 }; 377 assert(output.sz == sizeof(exp_buf)); 378 assert(0 == memcmp(output.buf, exp_buf, sizeof(exp_buf))); 379 } 380 381 { 382 reset_output(0); 383 struct lsquic_http2_setting settings[] = { { 1, 2, }, { 3, 4, } }; 384 s = lsquic_frame_writer_write_settings(fw, settings, 0); 385 assert(-1 == s); 386 assert(EINVAL == errno); 387 } 388 389 lsquic_frame_writer_destroy(fw); 390 lsquic_mm_cleanup(&mm); 391} 392 393 394static struct lsquic_conn my_conn = LSCONN_INITIALIZER_CIDLEN(my_conn, 8); 395 396 397#if !defined(NDEBUG) && __GNUC__ 398__attribute__((weak)) 399#endif 400lsquic_conn_t * 401lsquic_stream_conn (const lsquic_stream_t *stream) 402{ 403 return &my_conn; 404} 405 406 407static void 408test_priority (void) 409{ 410 struct lsquic_frame_writer *fw; 411 int s; 412 struct lsquic_mm mm; 413 414 lsquic_mm_init(&mm); 415 fw = lsquic_frame_writer_new(&mm, NULL, 6, NULL, output_write, 416#if LSQUIC_CONN_STATS 417 &s_conn_stats, 418#endif 419 0); 420 421 s = lsquic_frame_writer_write_priority(fw, 3, 0, 1UL << 31, 256); 422 assert(s < 0); /* Invalid dependency stream ID */ 423 424 s = lsquic_frame_writer_write_priority(fw, 3, 0, 1, 0); 425 assert(s < 0); /* Invalid priority stream ID */ 426 427 s = lsquic_frame_writer_write_priority(fw, 3, 0, 1, 257); 428 assert(s < 0); /* Invalid priority stream ID */ 429 430 { 431 reset_output(0); 432 s = lsquic_frame_writer_write_priority(fw, 3, 0, 1, 256); 433 assert(0 == s); 434 const unsigned char exp_buf[] = { 435 /* Length: */ 0x00, 0x00, 5, 436 /* Type: */ HTTP_FRAME_PRIORITY, 437 /* Flags: */ 0x00, 438 /* Stream Id: */ 0x00, 0x00, 0x00, 0x03, 439 /* Dep stream Id: */0x00, 0x00, 0x00, 0x01, 440 /* Weight: */ 0xFF, 441 }; 442 assert(output.sz == sizeof(exp_buf)); 443 assert(0 == memcmp(output.buf, exp_buf, sizeof(exp_buf))); 444 } 445 446 { 447 reset_output(0); 448 s = lsquic_frame_writer_write_priority(fw, 20, 1, 100, 256); 449 assert(0 == s); 450 const unsigned char exp_buf[] = { 451 /* Length: */ 0x00, 0x00, 5, 452 /* Type: */ HTTP_FRAME_PRIORITY, 453 /* Flags: */ 0x00, 454 /* Stream Id: */ 0x00, 0x00, 0x00, 0x14, 455 /* Dep stream Id: */0x80, 0x00, 0x00, 0x64, 456 /* Weight: */ 0xFF, 457 }; 458 assert(output.sz == sizeof(exp_buf)); 459 assert(0 == memcmp(output.buf, exp_buf, sizeof(exp_buf))); 460 } 461 462 lsquic_frame_writer_destroy(fw); 463 lsquic_mm_cleanup(&mm); 464} 465 466 467 468static void 469test_errors (void) 470{ 471 struct lsquic_frame_writer *fw; 472 struct lsquic_mm mm; 473 struct lshpack_enc henc; 474 int s; 475 476 lshpack_enc_init(&henc); 477 lsquic_mm_init(&mm); 478 fw = lsquic_frame_writer_new(&mm, NULL, 0x200, &henc, output_write, 479#if LSQUIC_CONN_STATS 480 &s_conn_stats, 481#endif 482 1); 483 reset_output(0); 484 485 { 486 struct lsxpack_header header_arr[] = 487 { 488 { XHDR(":status", "200") }, 489 { XHDR("Content-type", "text/html") }, 490 }; 491 struct lsquic_http_headers headers = { 492 .count = 2, 493 .headers = header_arr, 494 }; 495 s = lsquic_frame_writer_write_headers(fw, 12345, &headers, 0, 80); 496 assert(-1 == s); 497 assert(EINVAL == errno); 498 } 499 500 { 501 struct lsxpack_header header_arr[] = 502 { 503 { XHDR(":status", "200") }, 504 { XHDR("content-type", "text/html") }, 505 }; 506 struct lsquic_http_headers headers = { 507 .count = 2, 508 .headers = header_arr, 509 }; 510 lsquic_frame_writer_max_header_list_size(fw, 40); 511 s = lsquic_frame_writer_write_headers(fw, 12345, &headers, 0, 80); 512 /* Server ignores SETTINGS_MAX_HEADER_LIST_SIZE setting */ 513 assert(s == 0); 514 } 515 516 lsquic_frame_writer_destroy(fw); 517 lshpack_enc_cleanup(&henc); 518 lsquic_mm_cleanup(&mm); 519} 520 521 522static void 523test_push_promise (void) 524{ 525 struct lshpack_enc henc; 526 struct lsquic_frame_writer *fw; 527 int s; 528 struct lsquic_mm mm; 529 530 lshpack_enc_init(&henc); 531 lsquic_mm_init(&mm); 532 fw = lsquic_frame_writer_new(&mm, NULL, 0x200, &henc, output_write, 533#if LSQUIC_CONN_STATS 534 &s_conn_stats, 535#endif 536 1); 537 reset_output(0); 538 539/* 540perl tools/hpack.pl :method GET :path /index.html :authority www.example.com :scheme https x-some-header some-value| hexdump -C 54100000000 82 85 41 8c f1 e3 c2 e5 f2 3a 6b a0 ab 90 f4 ff |..A......:k.....| 54200000010 87 40 8a f2 b2 0f 49 56 9c a3 90 b6 7f 87 41 e9 |.@....IV......A.| 54300000020 2a dd c7 45 a5 |*..E.| 544*/ 545 546 const unsigned char exp_headers[] = { 547 0x82, 0x85, 0x41, 0x8c, 0xf1, 0xe3, 0xc2, 0xe5, 0xf2, 0x3a, 548 0x6b, 0xa0, 0xab, 0x90, 0xf4, 0xff, 0x87, 0x40, 0x8a, 0xf2, 549 0xb2, 0x0f, 0x49, 0x56, 0x9c, 0xa3, 0x90, 0xb6, 0x7f, 0x87, 550 0x41, 0xe9, 0x2a, 0xdd, 0xc7, 0x45, 0xa5, 551 }; 552 553 struct lsxpack_header header_arr[] = 554 { 555 { XHDR(":method", "GET") }, 556 { XHDR(":path", "/index.html") }, 557 { XHDR(":authority", "www.example.com") }, 558 { XHDR(":scheme", "https") }, 559 { XHDR("x-some-header", "some-value") }, 560 }; 561 562 struct lsquic_http_headers headers = { 563 .count = 5, 564 .headers = header_arr, 565 }; 566 567 s = lsquic_frame_writer_write_promise(fw, 12345, 0xEEEE, &headers); 568 assert(0 == s); 569 570 struct http_frame_header fh; 571 struct http_push_promise_frame push_frame; 572 573 assert(sizeof(exp_headers) + sizeof(struct http_frame_header) + 574 sizeof(struct http_push_promise_frame) == output.sz); 575 576 memcpy(&fh, output.buf, sizeof(fh)); 577 assert(sizeof(exp_headers) + sizeof(struct http_push_promise_frame) == hfh_get_length(&fh)); 578 assert(HTTP_FRAME_PUSH_PROMISE == fh.hfh_type); 579 assert(HFHF_END_HEADERS == fh.hfh_flags); 580 assert(fh.hfh_stream_id[0] == 0); 581 assert(fh.hfh_stream_id[1] == 0); 582 assert(fh.hfh_stream_id[2] == 0x30); 583 assert(fh.hfh_stream_id[3] == 0x39); 584 585 memcpy(&push_frame, output.buf + sizeof(struct http_frame_header), 586 sizeof(struct http_push_promise_frame)); 587 588 assert(push_frame.hppf_promised_id[0] == 0); 589 assert(push_frame.hppf_promised_id[1] == 0); 590 assert(push_frame.hppf_promised_id[2] == 0xEE); 591 assert(push_frame.hppf_promised_id[3] == 0xEE); 592 593 assert(0 == memcmp(output.buf + sizeof(struct http_frame_header) + 594 sizeof(struct http_push_promise_frame), exp_headers, 595 sizeof(exp_headers))); 596 597 lsquic_frame_writer_destroy(fw); 598 lshpack_enc_cleanup(&henc); 599 lsquic_mm_cleanup(&mm); 600} 601 602 603 604int 605main (void) 606{ 607 test_one_header(); 608 test_oversize_header(); 609 test_continuations(); 610 test_settings_normal(); 611 test_settings_short(); 612 test_priority(); 613 test_push_promise(); 614 test_errors(); 615 test_max_frame_size(); 616 return 0; 617} 618