test_h3_framing.c revision 084338b1
1/* Copyright (c) 2017 - 2021 LiteSpeed Technologies Inc. See LICENSE. */ 2/* 3 * test_h3_framing.c -- test generation of H3 frames 4 */ 5 6#include <assert.h> 7#include <errno.h> 8#include <stdio.h> 9#include <stdlib.h> 10#include <string.h> 11#include <sys/queue.h> 12#include <sys/types.h> 13#include <fcntl.h> 14#include <limits.h> 15#ifndef WIN32 16#include <unistd.h> 17#else 18#include <getopt.h> 19#endif 20 21#include "lsquic.h" 22 23#include "lsquic_packet_common.h" 24#include "lsquic_packet_ietf.h" 25#include "lsquic_alarmset.h" 26#include "lsquic_packet_in.h" 27#include "lsquic_conn_flow.h" 28#include "lsquic_rtt.h" 29#include "lsquic_sfcw.h" 30#include "lsquic_varint.h" 31#include "lsquic_hq.h" 32#include "lsquic_hash.h" 33#include "lsquic_stream.h" 34#include "lsquic_types.h" 35#include "lsquic_malo.h" 36#include "lsquic_mm.h" 37#include "lsquic_conn_public.h" 38#include "lsquic_logger.h" 39#include "lsquic_parse.h" 40#include "lsquic_conn.h" 41#include "lsquic_engine_public.h" 42#include "lsquic_cubic.h" 43#include "lsquic_pacer.h" 44#include "lsquic_senhist.h" 45#include "lsquic_bw_sampler.h" 46#include "lsquic_minmax.h" 47#include "lsquic_bbr.h" 48#include "lsquic_adaptive_cc.h" 49#include "lsquic_send_ctl.h" 50#include "lsquic_ver_neg.h" 51#include "lsquic_packet_out.h" 52#include "lsquic_enc_sess.h" 53#include "lsqpack.h" 54#include "lsxpack_header.h" 55#include "lsquic_frab_list.h" 56#include "lsquic_qenc_hdl.h" 57#include "lsquic_http1x_if.h" 58#include "lsquic_qdec_hdl.h" 59#include "lsquic_varint.h" 60#include "lsquic_hq.h" 61#include "lsquic_data_in_if.h" 62 63#define MIN(a, b) ((a) < (b) ? (a) : (b)) 64#define MAX(a, b) ((a) > (b) ? (a) : (b)) 65 66static const struct parse_funcs *g_pf = select_pf_by_ver(LSQVER_ID27); 67 68struct test_ctl_settings 69{ 70 int tcs_schedule_stream_packets_immediately; 71 int tcs_have_delayed_packets; 72 unsigned tcs_can_send; 73 enum buf_packet_type 74 tcs_bp_type; 75 enum packno_bits 76 tcs_guess_packno_bits, 77 tcs_calc_packno_bits; 78}; 79 80 81static struct test_ctl_settings g_ctl_settings; 82 83 84static void 85init_buf (void *buf, size_t sz); 86 87 88/* Set values to default */ 89static void 90init_test_ctl_settings (struct test_ctl_settings *settings) 91{ 92 settings->tcs_schedule_stream_packets_immediately = 1; 93 settings->tcs_have_delayed_packets = 0; 94 settings->tcs_can_send = UINT_MAX; 95 settings->tcs_bp_type = BPT_HIGHEST_PRIO; 96 settings->tcs_guess_packno_bits = PACKNO_BITS_1; 97 settings->tcs_calc_packno_bits = PACKNO_BITS_1; 98} 99 100 101enum packno_bits 102lsquic_send_ctl_calc_packno_bits (struct lsquic_send_ctl *ctl) 103{ 104 return g_ctl_settings.tcs_calc_packno_bits; 105} 106 107 108int 109lsquic_send_ctl_schedule_stream_packets_immediately (struct lsquic_send_ctl *ctl) 110{ 111 return g_ctl_settings.tcs_schedule_stream_packets_immediately; 112} 113 114 115int 116lsquic_send_ctl_have_delayed_packets (const struct lsquic_send_ctl *ctl) 117{ 118 return g_ctl_settings.tcs_have_delayed_packets; 119} 120 121 122int 123lsquic_send_ctl_can_send (struct lsquic_send_ctl *ctl) 124{ 125 return ctl->sc_n_scheduled < g_ctl_settings.tcs_can_send; 126} 127 128 129enum packno_bits 130lsquic_send_ctl_guess_packno_bits (struct lsquic_send_ctl *ctl) 131{ 132 return g_ctl_settings.tcs_guess_packno_bits; 133} 134 135 136enum buf_packet_type 137lsquic_send_ctl_determine_bpt (struct lsquic_send_ctl *ctl, 138 const struct lsquic_stream *stream) 139{ 140 return g_ctl_settings.tcs_bp_type; 141} 142 143 144/* This function is only here to avoid crash in the test: */ 145void 146lsquic_engine_add_conn_to_tickable (struct lsquic_engine_public *enpub, 147 lsquic_conn_t *conn) 148{ 149} 150 151 152static unsigned n_closed; 153static enum stream_ctor_flags stream_ctor_flags = 154 SCF_CALL_ON_NEW|SCF_DI_AUTOSWITCH; 155 156struct test_ctx { 157 lsquic_stream_t *stream; 158}; 159 160 161static lsquic_stream_ctx_t * 162on_new_stream (void *stream_if_ctx, lsquic_stream_t *stream) 163{ 164 struct test_ctx *test_ctx = stream_if_ctx; 165 test_ctx->stream = stream; 166 return NULL; 167} 168 169 170static void 171on_close (lsquic_stream_t *stream, lsquic_stream_ctx_t *st_h) 172{ 173 ++n_closed; 174} 175 176 177const struct lsquic_stream_if stream_if = { 178 .on_new_stream = on_new_stream, 179 .on_close = on_close, 180}; 181 182 183static size_t 184read_from_scheduled_packets (lsquic_send_ctl_t *send_ctl, lsquic_stream_id_t stream_id, 185 unsigned char *const begin, size_t bufsz, uint64_t first_offset, int *p_fin, 186 int fullcheck) 187{ 188 const struct parse_funcs *const pf_local = send_ctl->sc_conn_pub->lconn->cn_pf; 189 unsigned char *p = begin; 190 unsigned char *const end = p + bufsz; 191 const struct frame_rec *frec; 192 struct packet_out_frec_iter pofi; 193 struct lsquic_packet_out *packet_out; 194 struct stream_frame frame; 195 enum quic_frame_type expected_type; 196 int len, fin = 0; 197 198 expected_type = QUIC_FRAME_STREAM; 199 200 TAILQ_FOREACH(packet_out, &send_ctl->sc_scheduled_packets, po_next) 201 for (frec = lsquic_pofi_first(&pofi, packet_out); frec; 202 frec = lsquic_pofi_next(&pofi)) 203 { 204 if (fullcheck) 205 { 206 assert(frec->fe_frame_type == expected_type); 207 if (0 && packet_out->po_packno != 1) 208 { 209 /* First packet may contain two stream frames, do not 210 * check it. 211 */ 212 assert(!lsquic_pofi_next(&pofi)); 213 if (TAILQ_NEXT(packet_out, po_next)) 214 { 215 assert(packet_out->po_data_sz == packet_out->po_n_alloc); 216 assert(frec->fe_len == packet_out->po_data_sz); 217 } 218 } 219 } 220 if (frec->fe_frame_type == expected_type && 221 frec->fe_stream->id == stream_id) 222 { 223 assert(!fin); 224 if (QUIC_FRAME_STREAM == expected_type) 225 len = pf_local->pf_parse_stream_frame(packet_out->po_data + frec->fe_off, 226 packet_out->po_data_sz - frec->fe_off, &frame); 227 else 228 len = pf_local->pf_parse_crypto_frame(packet_out->po_data + frec->fe_off, 229 packet_out->po_data_sz - frec->fe_off, &frame); 230 assert(len > 0); 231 if (QUIC_FRAME_STREAM == expected_type) 232 assert(frame.stream_id == frec->fe_stream->id); 233 else 234 assert(frame.stream_id == ~0ULL); 235 /* Otherwise not enough to copy to: */ 236 assert(end - p >= frame.data_frame.df_size); 237 /* Checks offset ordering: */ 238 assert(frame.data_frame.df_offset == 239 first_offset + (uintptr_t) (p - begin)); 240 if (frame.data_frame.df_fin) 241 { 242 assert(!fin); 243 fin = 1; 244 } 245 memcpy(p, packet_out->po_data + frec->fe_off + len - 246 frame.data_frame.df_size, frame.data_frame.df_size); 247 p += frame.data_frame.df_size; 248 } 249 } 250 251 if (p_fin) 252 *p_fin = fin; 253 return p + bufsz - end; 254} 255 256 257static struct test_ctx test_ctx; 258 259 260struct test_objs { 261 struct lsquic_engine_public eng_pub; 262 struct lsquic_conn lconn; 263 struct lsquic_conn_public conn_pub; 264 struct lsquic_send_ctl send_ctl; 265 struct lsquic_alarmset alset; 266 void *stream_if_ctx; 267 struct ver_neg ver_neg; 268 const struct lsquic_stream_if * 269 stream_if; 270 unsigned initial_stream_window; 271 enum stream_ctor_flags ctor_flags; 272 struct qpack_enc_hdl qeh; 273 struct qpack_dec_hdl qdh; 274 struct lsquic_hset_if hsi_if; 275}; 276 277static int s_ack_written; 278 279static void 280write_ack (struct lsquic_conn *conn, struct lsquic_packet_out *packet_out) 281{ 282 /* We don't need to generate full-blown ACK, as logic in 283 * lsquic_send_ctl_rollback() only looks at po_frame_types. 284 */ 285 packet_out->po_frame_types |= QUIC_FRAME_ACK; 286 s_ack_written = 1; 287} 288 289static int s_can_write_ack; 290 291static int 292can_write_ack (struct lsquic_conn *lconn) 293{ 294 return s_can_write_ack; 295} 296 297 298static struct network_path network_path; 299 300static struct network_path * 301get_network_path (struct lsquic_conn *lconn, const struct sockaddr *sa) 302{ 303 return &network_path; 304} 305 306static enum { 307 SNAPSHOT_STATE_NONE = 0, 308 SNAPSHOT_STATE_TAKEN = 1 << 0, 309 SNAPSHOT_STATE_ROLLED_BACK = 1 << 1, 310} s_snapshot_state; 311 312static void 313ack_snapshot (struct lsquic_conn *lconn, struct ack_state *ack_state) 314{ 315 s_snapshot_state |= SNAPSHOT_STATE_TAKEN; 316} 317 318static void 319ack_rollback (struct lsquic_conn *lconn, struct ack_state *ack_state) 320{ 321 s_snapshot_state |= SNAPSHOT_STATE_ROLLED_BACK; 322} 323 324static const struct conn_iface our_conn_if = 325{ 326 .ci_can_write_ack = can_write_ack, 327 .ci_write_ack = write_ack, 328 .ci_get_path = get_network_path, 329 .ci_ack_snapshot = ack_snapshot, 330 .ci_ack_rollback = ack_rollback, 331}; 332 333 334#if LSQUIC_CONN_STATS 335static struct conn_stats s_conn_stats; 336#endif 337 338static void 339init_test_objs (struct test_objs *tobjs, unsigned initial_conn_window, 340 unsigned initial_stream_window, unsigned short packet_sz) 341{ 342 int s; 343 memset(tobjs, 0, sizeof(*tobjs)); 344 LSCONN_INITIALIZE(&tobjs->lconn); 345 tobjs->lconn.cn_pf = g_pf; 346 tobjs->lconn.cn_version = LSQVER_ID27; 347 tobjs->lconn.cn_esf_c = &lsquic_enc_session_common_ietf_v1; 348 network_path.np_pack_size = packet_sz; 349 tobjs->lconn.cn_if = &our_conn_if; 350 lsquic_mm_init(&tobjs->eng_pub.enp_mm); 351 TAILQ_INIT(&tobjs->conn_pub.sending_streams); 352 TAILQ_INIT(&tobjs->conn_pub.read_streams); 353 TAILQ_INIT(&tobjs->conn_pub.write_streams); 354 TAILQ_INIT(&tobjs->conn_pub.service_streams); 355 lsquic_cfcw_init(&tobjs->conn_pub.cfcw, &tobjs->conn_pub, 356 initial_conn_window); 357 lsquic_conn_cap_init(&tobjs->conn_pub.conn_cap, initial_conn_window); 358 lsquic_alarmset_init(&tobjs->alset, 0); 359 tobjs->conn_pub.mm = &tobjs->eng_pub.enp_mm; 360 tobjs->conn_pub.lconn = &tobjs->lconn; 361 tobjs->conn_pub.enpub = &tobjs->eng_pub; 362 tobjs->conn_pub.send_ctl = &tobjs->send_ctl; 363 tobjs->conn_pub.packet_out_malo = 364 lsquic_malo_create(sizeof(struct lsquic_packet_out)); 365 tobjs->conn_pub.path = &network_path; 366#if LSQUIC_CONN_STATS 367 tobjs->conn_pub.conn_stats = &s_conn_stats; 368#endif 369 tobjs->initial_stream_window = initial_stream_window; 370 tobjs->eng_pub.enp_settings.es_cc_algo = 1; /* Cubic */ 371 tobjs->eng_pub.enp_hsi_if = &tobjs->hsi_if; 372 lsquic_send_ctl_init(&tobjs->send_ctl, &tobjs->alset, &tobjs->eng_pub, 373 &tobjs->ver_neg, &tobjs->conn_pub, 0); 374 tobjs->send_ctl.sc_adaptive_cc.acc_cubic.cu_cwnd = ~0ull; 375 tobjs->send_ctl.sc_cong_ctl = &tobjs->send_ctl.sc_adaptive_cc.acc_cubic; 376 tobjs->stream_if = &stream_if; 377 tobjs->stream_if_ctx = &test_ctx; 378 tobjs->ctor_flags = stream_ctor_flags; 379 if ((1 << tobjs->lconn.cn_version) & LSQUIC_IETF_VERSIONS) 380 { 381 lsquic_qeh_init(&tobjs->qeh, &tobjs->lconn); 382 s = lsquic_qeh_settings(&tobjs->qeh, 0, 0, 0, 0); 383 assert(0 == s); 384 tobjs->conn_pub.u.ietf.qeh = &tobjs->qeh; 385 lsquic_qdh_init(&tobjs->qdh, &tobjs->lconn, 0, &tobjs->eng_pub, 0, 0); 386 tobjs->conn_pub.u.ietf.qdh = &tobjs->qdh; 387 } 388} 389 390 391static void 392deinit_test_objs (struct test_objs *tobjs) 393{ 394 assert(!lsquic_malo_first(tobjs->eng_pub.enp_mm.malo.stream_frame)); 395 lsquic_send_ctl_cleanup(&tobjs->send_ctl); 396 lsquic_malo_destroy(tobjs->conn_pub.packet_out_malo); 397 lsquic_mm_cleanup(&tobjs->eng_pub.enp_mm); 398 if ((1 << tobjs->lconn.cn_version) & LSQUIC_IETF_VERSIONS) 399 lsquic_qeh_cleanup(&tobjs->qeh); 400} 401 402 403static struct lsquic_stream * 404new_stream (struct test_objs *tobjs, unsigned stream_id, uint64_t send_off) 405{ 406 return lsquic_stream_new(stream_id, &tobjs->conn_pub, tobjs->stream_if, 407 tobjs->stream_if_ctx, tobjs->initial_stream_window, send_off, 408 tobjs->ctor_flags); 409} 410 411 412struct packetization_test_stream_ctx 413{ 414 const unsigned char *buf; 415 unsigned len, off, write_size; 416 int flush_after_each_write; 417}; 418 419 420static lsquic_stream_ctx_t * 421packetization_on_new_stream (void *stream_if_ctx, lsquic_stream_t *stream) 422{ 423 lsquic_stream_wantwrite(stream, 1); 424 return stream_if_ctx; 425} 426 427 428static void 429packetization_on_close (lsquic_stream_t *stream, lsquic_stream_ctx_t *st_h) 430{ 431} 432 433 434#define RANDOM_WRITE_SIZE ~0U 435 436static unsigned 437calc_n_to_write (unsigned write_size) 438{ 439 if (write_size == RANDOM_WRITE_SIZE) 440 return rand() % 1000 + 1; 441 else 442 return write_size; 443} 444 445 446static void 447packetization_write_as_much_as_you_can (lsquic_stream_t *stream, 448 lsquic_stream_ctx_t *ctx) 449{ 450 struct packetization_test_stream_ctx *const pack_ctx = (void *) ctx; 451 unsigned n_to_write, n_sched; 452 ssize_t n_written; 453 size_t avail; 454 int s; 455 456 while (pack_ctx->off < pack_ctx->len) 457 { 458 n_to_write = calc_n_to_write(pack_ctx->write_size); 459 n_sched = lsquic_send_ctl_n_scheduled(stream->conn_pub->send_ctl); 460 if (n_to_write > pack_ctx->len - pack_ctx->off) 461 n_to_write = pack_ctx->len - pack_ctx->off; 462 n_written = lsquic_stream_write(stream, pack_ctx->buf + pack_ctx->off, 463 n_to_write); 464 if (n_written == 0) 465 { 466 if (n_to_write && SSHS_BEGIN == stream->sm_send_headers_state 467 && lsquic_send_ctl_can_send(stream->conn_pub->send_ctl)) 468 { 469 avail = lsquic_stream_write_avail(stream); 470 assert(avail == 0 471 || lsquic_send_ctl_n_scheduled( 472 stream->conn_pub->send_ctl) > n_sched); 473 } 474 break; 475 } 476 pack_ctx->off += n_written; 477 if (pack_ctx->flush_after_each_write) 478 { 479 s = lsquic_stream_flush(stream); 480 assert(s == 0); 481 } 482 } 483 484 s = lsquic_stream_flush(stream); 485 assert(s == 0); 486 lsquic_stream_wantwrite(stream, 0); 487} 488 489 490static void 491packetization_perform_one_write (lsquic_stream_t *stream, 492 lsquic_stream_ctx_t *ctx) 493{ 494 struct packetization_test_stream_ctx *const pack_ctx = (void *) ctx; 495 unsigned n_to_write, n_sched; 496 ssize_t n_written; 497 size_t avail; 498 int s; 499 500 n_to_write = calc_n_to_write(pack_ctx->write_size); 501 if (n_to_write > pack_ctx->len - pack_ctx->off) 502 n_to_write = pack_ctx->len - pack_ctx->off; 503 n_sched = lsquic_send_ctl_n_scheduled(stream->conn_pub->send_ctl); 504 n_written = lsquic_stream_write(stream, pack_ctx->buf + pack_ctx->off, 505 n_to_write); 506 assert(n_written >= 0); 507 if (n_written == 0 && SSHS_BEGIN == stream->sm_send_headers_state 508 && n_to_write 509 && lsquic_send_ctl_can_send(stream->conn_pub->send_ctl)) 510 { 511 avail = lsquic_stream_write_avail(stream); 512 assert(avail == 0 513 || lsquic_send_ctl_n_scheduled( 514 stream->conn_pub->send_ctl) > n_sched); 515 } 516 pack_ctx->off += n_written; 517 if (pack_ctx->flush_after_each_write) 518 { 519 s = lsquic_stream_flush(stream); 520 assert(s == 0); 521 } 522 if (n_written == 0) 523 lsquic_stream_wantwrite(stream, 0); 524} 525 526 527static const struct lsquic_stream_if packetization_inside_once_stream_if = { 528 .on_new_stream = packetization_on_new_stream, 529 .on_close = packetization_on_close, 530 .on_write = packetization_write_as_much_as_you_can, 531}; 532 533 534static const struct lsquic_stream_if packetization_inside_many_stream_if = { 535 .on_new_stream = packetization_on_new_stream, 536 .on_close = packetization_on_close, 537 .on_write = packetization_perform_one_write, 538}; 539 540 541#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, 542 543static void 544test_hq_framing (int sched_immed, int dispatch_once, unsigned wsize, 545 int flush_after_each_write, size_t conn_limit, 546 unsigned n_packets, unsigned short packet_sz) 547{ 548 struct test_objs tobjs; 549 struct lsquic_stream *stream; 550 size_t nw; 551 int fin, s; 552 unsigned char *buf_in, *buf_out; 553 const size_t buf_in_sz = 0x40000, buf_out_sz = 0x500000; 554 555 /* We'll write headers first after which stream will switch to using 556 * data-framing writer. This is simply so that we don't have to 557 * expose more stream things only for testing. 558 */ 559 struct lsxpack_header header = { XHDR(":method", "GET") }; 560 struct lsquic_http_headers headers = { 1, &header, }; 561 562 buf_in = malloc(buf_in_sz); 563 buf_out = malloc(buf_out_sz); 564 assert(buf_in && buf_out); 565 566 struct packetization_test_stream_ctx packet_stream_ctx = 567 { 568 .buf = buf_in, 569 .off = 0, 570 .len = buf_in_sz, 571 .write_size = wsize, 572 .flush_after_each_write = flush_after_each_write, 573 }; 574 575 init_buf(buf_in, buf_in_sz); 576 577 init_test_ctl_settings(&g_ctl_settings); 578 g_ctl_settings.tcs_schedule_stream_packets_immediately = sched_immed; 579 580 stream_ctor_flags |= SCF_IETF; 581 init_test_objs(&tobjs, conn_limit ? conn_limit : buf_out_sz, buf_out_sz, packet_sz); 582 tobjs.stream_if_ctx = &packet_stream_ctx; 583 tobjs.ctor_flags |= SCF_HTTP|SCF_IETF; 584 if (sched_immed) 585 { 586 g_ctl_settings.tcs_can_send = n_packets; 587 if (dispatch_once) 588 { 589 tobjs.stream_if = &packetization_inside_once_stream_if; 590 tobjs.ctor_flags |= SCF_DISP_RW_ONCE; 591 } 592 else 593 tobjs.stream_if = &packetization_inside_many_stream_if; 594 } 595 else 596 { 597 lsquic_send_ctl_set_max_bpq_count(n_packets); 598 g_ctl_settings.tcs_can_send = INT_MAX; 599 /* Need this for on_new_stream() callback not to mess with 600 * the context, otherwise this is not used. 601 */ 602 tobjs.stream_if = &packetization_inside_many_stream_if; 603 } 604 605 stream = new_stream(&tobjs, 0, buf_out_sz); 606 607 s = lsquic_stream_send_headers(stream, &headers, 0); 608 assert(0 == s); 609 610 if (sched_immed) 611 { 612 lsquic_stream_dispatch_write_events(stream); 613 lsquic_stream_flush(stream); 614 } 615 else 616 { 617 packetization_write_as_much_as_you_can(stream, 618 (void *) &packet_stream_ctx); 619 g_ctl_settings.tcs_schedule_stream_packets_immediately = 1; 620 lsquic_send_ctl_schedule_buffered(&tobjs.send_ctl, BPT_HIGHEST_PRIO); 621 g_ctl_settings.tcs_schedule_stream_packets_immediately = 0; 622 } 623 lsquic_send_ctl_set_max_bpq_count(10); 624 625 /* Verify written data: */ 626 nw = read_from_scheduled_packets(&tobjs.send_ctl, 0, buf_out, buf_out_sz, 627 0, &fin, 1); 628 if (!conn_limit) 629 assert(nw > buf_in_sz); 630 { /* Remove framing and verify contents */ 631 const unsigned char *src; 632 unsigned char *dst; 633 uint64_t sz; 634 unsigned frame_type; 635 int s; 636 637 src = buf_out; 638 dst = buf_out; 639 while (src < buf_out + nw) 640 { 641 frame_type = *src++; 642 s = vint_read(src, buf_out + buf_out_sz, &sz); 643 assert(s > 0); 644 /* In some rare circumstances it is possible to produce zero-length 645 * DATA frames: 646 * 647 * assert(sz > 0); 648 */ 649 assert(sz < (1 << 14)); 650 src += s; 651 if (src == buf_out + s + 1) 652 { 653 /* Ignore headers */ 654 assert(frame_type == HQFT_HEADERS); 655 src += sz; 656 } 657 else 658 { 659 assert(frame_type == HQFT_DATA); 660 if (src + sz > buf_out + nw) /* Chopped DATA frame (last) */ 661 sz = buf_out + nw - src; 662 memmove(dst, src, sz); 663 dst += sz; 664 src += sz; 665 } 666 } 667 if (!conn_limit) 668 assert(buf_in_sz == (uintptr_t) dst - (uintptr_t) buf_out); 669 assert(0 == memcmp(buf_in, buf_out, (uintptr_t) dst - (uintptr_t) buf_out)); 670 } 671 672 lsquic_stream_destroy(stream); 673 deinit_test_objs(&tobjs); 674 free(buf_in); 675 free(buf_out); 676 677 stream_ctor_flags &= ~SCF_IETF; 678} 679 680 681static void 682main_test_hq_framing (void) 683{ 684 const unsigned wsizes[] = { 1, 2, 3, 7, 10, 50, 100, 201, 211, 1000, 2003, 20000, }; 685 const size_t conn_limits[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 686 14, 15, 16, 17, 18, 19, 20, 21, 30, 31, 32, 33, 63, 64, 128, 200, 255, 687 256, 512, 1024, 2045, 2048, 2049, 3000, 4091, 4096, 4097, 5000, 8192, 688 16 * 1024 - 1, 16 * 1024, 32 * 1024, 33 * 1024, }; 689 const unsigned n_packets[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 100, UINT_MAX, }; 690 const unsigned short packet_sz[] = { 1252, 1370, 0x1000, 0xFF00, }; 691 unsigned i, j, k, l; 692 int sched_immed, dispatch_once, flush_after_each_write; 693 694 for (sched_immed = 0; sched_immed <= 1; ++sched_immed) 695 for (dispatch_once = 0; dispatch_once <= 1; ++dispatch_once) 696 for (i = 0; i < sizeof(wsizes) / sizeof(wsizes[i]); ++i) 697 for (j = 0; j < sizeof(conn_limits) / sizeof(conn_limits[j]); ++j) 698 for (flush_after_each_write = 0; flush_after_each_write < 2; ++flush_after_each_write) 699 for (k = 0; k < sizeof(n_packets) / sizeof(n_packets[0]); ++k) 700 for (l = 0; l < sizeof(packet_sz) / sizeof(packet_sz[0]); ++l) 701 test_hq_framing(sched_immed, dispatch_once, wsizes[i], flush_after_each_write, conn_limits[j], n_packets[k], packet_sz[l]); 702} 703 704 705/* Instead of the not-very-random testing done in main_test_hq_framing(), 706 * the fuzz-guided testing initializes parameters based on the fuzz input 707 * file. This allows afl-fuzz explore the code paths. 708 */ 709void 710fuzz_guided_hq_framing_testing (const char *input) 711{ 712 /* Range */ /* Bytes from file */ 713 unsigned short packet_sz; /* [200, 0x3FFF] */ /* 2 */ 714 unsigned wsize; /* [1, 20000] */ /* 2 */ 715 unsigned n_packets; /* [1, 255] and UINT_MAX */ /* 1 */ 716 size_t conn_limit; /* [1, 33K] */ /* 2 */ 717 int sched_immed; /* 0 or 1 */ /* 1 */ 718 int dispatch_once; /* 0 or 1 */ /* 0 (same as above) */ 719 int flush_after_each_write; /* 0 or 1 */ /* 0 (same as above) */ 720 /* TOTAL: 8 bytes */ 721 722 FILE *f; 723 size_t nread; 724 uint16_t tmp; 725 unsigned char buf[9]; 726 727 f = fopen(input, "rb"); 728 if (!f) 729 { 730 assert(0); 731 return; 732 } 733 734 nread = fread(buf, 1, sizeof(buf), f); 735 if (nread != 8) 736 goto cleanup; 737 738 memcpy(&tmp, &buf[0], 2); 739 if (tmp < 200) 740 tmp = 200; 741 else if (tmp > IQUIC_MAX_OUT_PACKET_SZ) 742 tmp = IQUIC_MAX_OUT_PACKET_SZ; 743 packet_sz = tmp; 744 745 memcpy(&tmp, &buf[2], 2); 746 if (tmp < 1) 747 tmp = 1; 748 else if (tmp > 20000) 749 tmp = 20000; 750 wsize = tmp; 751 752 if (buf[4]) 753 n_packets = buf[4]; 754 else 755 n_packets = UINT_MAX; 756 757 memcpy(&tmp, &buf[5], 2); 758 if (tmp < 1) 759 tmp = 1; 760 else if (tmp > 33 * 1024) 761 tmp = 33 * 1024; 762 conn_limit = tmp; 763 764 sched_immed = !!(buf[7] & 1); 765 dispatch_once = !!(buf[7] & 2); 766 flush_after_each_write = !!(buf[7] & 4); 767 768 test_hq_framing(sched_immed, dispatch_once, wsize, 769 flush_after_each_write, conn_limit, n_packets, packet_sz); 770 771 cleanup: 772 (void) fclose(f); 773} 774 775 776struct pwritev_stream_ctx 777{ 778 int limit; /* Test limit */ 779 size_t avail; 780 const unsigned char *input; 781 size_t input_sz; 782 ssize_t nw; /* Number of bytes written */ 783}; 784 785 786static ssize_t 787my_preadv (void *user_data, const struct iovec *iov, int iovcnt) 788{ 789 struct pwritev_stream_ctx *const pw_ctx = user_data; 790 const unsigned char *p; 791 size_t ntoread, tocopy; 792 int i; 793 794 ntoread = 0; 795 for (i = 0; i < iovcnt; ++i) 796 ntoread += iov[i].iov_len; 797 798 if (pw_ctx->limit < 0) 799 { 800 if ((size_t) -pw_ctx->limit < ntoread) 801 ntoread -= (size_t) -pw_ctx->limit; 802 } 803 else if ((size_t) pw_ctx->limit < ntoread) 804 ntoread = (size_t) pw_ctx->limit; 805 806 assert(ntoread <= pw_ctx->input_sz); /* Self-check */ 807 808 p = pw_ctx->input; 809 for (i = 0; i < iovcnt; ++i) 810 { 811 tocopy = MIN(iov[i].iov_len, ntoread - (p - pw_ctx->input)); 812 memcpy(iov[i].iov_base, p, tocopy); 813 p += tocopy; 814 if (ntoread == (size_t) (p - pw_ctx->input)) 815 break; 816 } 817 818 assert(ntoread == (size_t) (p - pw_ctx->input)); 819 return (ssize_t) (p - pw_ctx->input); 820} 821 822 823static void 824pwritev_on_write (lsquic_stream_t *stream, lsquic_stream_ctx_t *ctx) 825{ 826 struct pwritev_stream_ctx *const pw_ctx = (void *) ctx; 827 ssize_t nw; 828 829 nw = lsquic_stream_pwritev(stream, my_preadv, pw_ctx, pw_ctx->input_sz); 830 pw_ctx->nw = nw; 831 lsquic_stream_wantwrite(stream, 0); 832} 833 834 835 836static const struct lsquic_stream_if pwritev_stream_if = { 837 .on_new_stream = packetization_on_new_stream, 838 .on_close = packetization_on_close, 839 .on_write = pwritev_on_write, 840}; 841 842 843static void 844test_pwritev (enum lsquic_version version, int http, int sched_immed, 845 int limit, unsigned short packet_sz, size_t prologue_sz, 846 unsigned n_packets) 847{ 848 struct test_objs tobjs; 849 struct lsquic_stream *stream; 850 size_t nw; 851 int fin, s; 852 unsigned char *buf_in, *buf_out; 853 /* Some values that are large enough: */ 854 const size_t buf_in_sz = MAX(n_packets * packet_sz, 0x1000), 855 buf_out_sz = (float) buf_in_sz * 1.1; 856 const int ietf = (1 << version) & LSQUIC_IETF_VERSIONS; 857 const enum stream_ctor_flags ietf_flags = ietf ? SCF_IETF : 0; 858 859 s_snapshot_state = 0; 860 s_ack_written = 0; 861 862 /* We'll write headers first after which stream will switch to using 863 * data-framing writer. This is simply so that we don't have to 864 * expose more stream things only for testing. 865 */ 866 struct lsxpack_header header = { XHDR(":method", "GET") }; 867 struct lsquic_http_headers headers = { 1, &header, }; 868 869 buf_in = malloc(buf_in_sz); 870 buf_out = malloc(buf_out_sz); 871 assert(buf_in && buf_out); 872 873 struct pwritev_stream_ctx pwritev_stream_ctx = 874 { 875 .input = buf_in + prologue_sz, 876 .input_sz = buf_in_sz - prologue_sz, 877 .limit = limit, 878 }; 879 880 init_buf(buf_in, buf_in_sz); 881 882 init_test_ctl_settings(&g_ctl_settings); 883 g_ctl_settings.tcs_schedule_stream_packets_immediately = sched_immed; 884 885 stream_ctor_flags |= ietf_flags; 886 init_test_objs(&tobjs, buf_out_sz, buf_out_sz, packet_sz); 887 tobjs.lconn.cn_version = version; 888 tobjs.lconn.cn_esf_c = select_esf_common_by_ver(version); 889 tobjs.stream_if_ctx = &pwritev_stream_ctx; 890 tobjs.ctor_flags |= (http ? SCF_HTTP : 0)|ietf_flags; 891 if (sched_immed) 892 { 893 g_ctl_settings.tcs_can_send = n_packets; 894 tobjs.stream_if = &pwritev_stream_if; 895 } 896 else 897 { 898 lsquic_send_ctl_set_max_bpq_count(n_packets); 899 g_ctl_settings.tcs_can_send = INT_MAX; 900 g_ctl_settings.tcs_bp_type = BPT_OTHER_PRIO; 901 /* Need this for on_new_stream() callback not to mess with 902 * the context, otherwise this is not used. 903 */ 904 tobjs.stream_if = &pwritev_stream_if; 905 } 906 907 stream = new_stream(&tobjs, 0, buf_out_sz); 908 909 if (http) 910 { 911 if (ietf) 912 { 913 s = lsquic_stream_send_headers(stream, &headers, 0); 914 assert(0 == s); 915 } 916 else 917 /* Here we fake it in order not to have to set up frame writer. */ 918 stream->stream_flags |= STREAM_HEADERS_SENT; 919 } 920 921 if (prologue_sz) 922 { 923 ssize_t written = lsquic_stream_write(stream, buf_in, prologue_sz); 924 assert(written > 0 && (size_t) written == prologue_sz); 925 } 926 927 if (sched_immed) 928 { 929 lsquic_stream_dispatch_write_events(stream); 930 assert(!(s_snapshot_state & SNAPSHOT_STATE_TAKEN)); 931 // lsquic_stream_flush(stream); 932 } 933 else 934 { 935 pwritev_on_write(stream, (void *) &pwritev_stream_ctx); 936 assert(s_snapshot_state & SNAPSHOT_STATE_TAKEN); 937 if (n_packets > 0 938 && s_ack_written 939 && tobjs.send_ctl.sc_buffered_packets[BPT_OTHER_PRIO].bpq_count == 0) 940 assert(s_snapshot_state & SNAPSHOT_STATE_ROLLED_BACK); 941 g_ctl_settings.tcs_schedule_stream_packets_immediately = 1; 942 lsquic_send_ctl_schedule_buffered(&tobjs.send_ctl, BPT_OTHER_PRIO); 943 g_ctl_settings.tcs_schedule_stream_packets_immediately = 0; 944 lsquic_send_ctl_set_max_bpq_count(10); 945 } 946 947 assert(pwritev_stream_ctx.nw >= 0); 948 949 /* Verify written data: */ 950 nw = read_from_scheduled_packets(&tobjs.send_ctl, 0, buf_out, buf_out_sz, 951 0, &fin, 1); 952 assert(nw <= buf_in_sz); 953 954 if (ietf && http) 955 { /* Remove framing and verify contents */ 956 const unsigned char *src; 957 unsigned char *dst; 958 uint64_t sz; 959 unsigned frame_type; 960 int s; 961 962 src = buf_out; 963 dst = buf_out; 964 while (src < buf_out + nw) 965 { 966 frame_type = *src++; 967 s = vint_read(src, buf_out + buf_out_sz, &sz); 968 assert(s > 0); 969 /* In some rare circumstances it is possible to produce zero-length 970 * DATA frames: 971 * 972 * assert(sz > 0); 973 */ 974 assert(sz < (1 << 14)); 975 src += s; 976 if (src == buf_out + s + 1) 977 { 978 /* Ignore headers */ 979 assert(frame_type == HQFT_HEADERS); 980 src += sz; 981 } 982 else 983 { 984 assert(frame_type == HQFT_DATA); 985 if (src + sz > buf_out + nw) /* Chopped DATA frame (last) */ 986 sz = buf_out + nw - src; 987 memmove(dst, src, sz); 988 dst += sz; 989 src += sz; 990 } 991 } 992 assert(nw <= buf_in_sz); 993 if (n_packets && pwritev_stream_ctx.nw) 994 { 995 assert((size_t) pwritev_stream_ctx.nw + prologue_sz == (uintptr_t) dst - (uintptr_t) buf_out); 996 assert(0 == memcmp(buf_in, buf_out, (uintptr_t) dst - (uintptr_t) buf_out)); 997 } 998 else 999 assert((uintptr_t) dst - (uintptr_t) buf_out == 0 1000 || (uintptr_t) dst - (uintptr_t) buf_out == prologue_sz); 1001 } 1002 else 1003 { 1004 assert(nw <= buf_in_sz); 1005 assert(nw <= buf_out_sz); 1006 if (n_packets && pwritev_stream_ctx.nw) 1007 { 1008 assert((size_t) pwritev_stream_ctx.nw + prologue_sz == nw); 1009 assert(0 == memcmp(buf_in, buf_out, (size_t) nw)); 1010 } 1011 else 1012 assert(nw == 0 || nw == prologue_sz); 1013 } 1014 1015 lsquic_stream_destroy(stream); 1016 deinit_test_objs(&tobjs); 1017 free(buf_in); 1018 free(buf_out); 1019 1020 stream_ctor_flags &= ~ietf_flags; 1021} 1022 1023 1024static void 1025main_test_pwritev (void) 1026{ 1027 const int limits[] = { INT_MAX, -1, -2, -3, -7, -10, -50, -100, -201, -211, 1028 -1000, -2003, -3000, -4000, -17803, -20000, 16 * 1024, 16 * 1024 - 1, 1029 16 * 1024 - 2, 8000, 273, 65, 63, 10, 5, 1, 0, }; 1030 unsigned n_packets; 1031 const unsigned short packet_sz[] = { 1252, 1370, 0x1000, 0xFF00, }; 1032 const size_t prologues[] = { 0, 17, 238, }; 1033 unsigned i, j, k; 1034 enum lsquic_version version; 1035 int http, sched_immed; 1036 const struct { unsigned iovecs, frames; } combos[] = 1037 { 1038 { 32, 16, }, 1039 { 16, 16, }, 1040 { 16, 8, }, 1041 { 3, 7, }, 1042 { 7, 3, }, 1043 { 100, 100, }, 1044 }, *combo = combos; 1045 1046 s_can_write_ack = 1; 1047 1048 run_test: 1049 for (version = 0; version < N_LSQVER; ++version) 1050 if ((1 << version) & LSQUIC_SUPPORTED_VERSIONS) 1051 for (http = 0; http < 2; ++http) 1052 for (sched_immed = 0; sched_immed <= 1; ++sched_immed) 1053 for (i = 0; i < sizeof(limits) / sizeof(limits[i]); ++i) 1054 for (j = 0; j < sizeof(packet_sz) / sizeof(packet_sz[0]); 1055 ++j) 1056 for (k = 0; k < sizeof(prologues) / sizeof(prologues[0]); ++k) 1057 for (n_packets = 1; n_packets < 21; ++n_packets) 1058 test_pwritev(version, http, sched_immed, 1059 limits[i], packet_sz[j], prologues[k], n_packets); 1060 1061 if (combo < combos + sizeof(combos) / sizeof(combos[0])) 1062 { 1063 lsquic_stream_set_pwritev_params(combo->iovecs, combo->frames); 1064 ++combo; 1065 goto run_test; 1066 } 1067 1068 s_can_write_ack = 0; 1069} 1070 1071 1072/* Instead of the not-very-random testing done in main_test_pwritev(), 1073 * the fuzz-guided testing initializes parameters based on the fuzz input 1074 * file. This allows afl-fuzz explore the code paths. 1075 */ 1076void 1077fuzz_guided_pwritev_testing (const char *input) 1078{ 1079 /* Range */ /* Bytes from file */ 1080 unsigned short packet_sz; /* [1200, 0xFF00] */ /* 2 */ 1081 int limit; /* [INT_MIN, INT_MAX] */ /* 2 */ 1082 unsigned n_packets; /* [0, 255] */ /* 1 */ 1083 unsigned n_iovecs; /* [0, 255] */ /* 1 */ 1084 unsigned n_frames; /* [0, 255] */ /* 1 */ 1085 size_t prologue_sz; /* [0, 170] */ /* 1 */ 1086 enum lsquic_version version;/* [0,7] */ /* 1 */ 1087 int sched_immed; /* 0 or 1 */ /* 1 (same byte) */ 1088 int http; /* 0 or 1 */ /* 1 (same byte) */ 1089 1090 /* TOTAL: 9 bytes */ 1091 1092 FILE *f; 1093 size_t nread; 1094 union { 1095 uint16_t tmp; 1096 int16_t itmp; 1097 } u; 1098 unsigned char buf[10]; 1099 1100 f = fopen(input, "rb"); 1101 if (!f) 1102 { 1103 assert(0); 1104 return; 1105 } 1106 1107 nread = fread(buf, 1, sizeof(buf), f); 1108 if (nread != 9) 1109 goto cleanup; 1110 1111 memcpy(&u.tmp, &buf[0], 2); 1112 if (u.tmp < 1200) 1113 u.tmp = 1200; 1114 else if (u.tmp > 0xFF00) 1115 u.tmp = 0xFF00; 1116 packet_sz = u.tmp; 1117 1118 memcpy(&u.itmp, &buf[2], 2); 1119 if (u.itmp < SHRT_MIN / 2) 1120 limit = INT_MIN; 1121 else if (u.itmp < SHRT_MIN / 4) 1122 limit = 0; 1123 else if (u.itmp > SHRT_MAX / 2) 1124 limit = INT_MAX; 1125 else if (u.itmp > SHRT_MAX / 2) 1126 limit = 0; 1127 else 1128 limit = u.itmp; 1129 1130 n_packets = buf[4]; 1131 n_iovecs = buf[5]; 1132 n_frames = buf[6]; 1133 1134 prologue_sz = buf[7]; 1135 if (prologue_sz > 170) 1136 prologue_sz = 170; 1137 1138 switch (buf[8] & 7) 1139 { 1140 case 0: version = LSQVER_043; break; 1141 case 1: version = LSQVER_046; break; 1142 case 2: version = LSQVER_050; break; 1143 case 3: version = LSQVER_ID27; break; 1144 case 4: version = LSQVER_ID29; break; 1145 default: 1146 case 5: version = LSQVER_I001; break; 1147 } 1148 1149 sched_immed = !!(buf[8] & 0x08); 1150 http = !!(buf[8] & 0x10); 1151 1152 lsquic_stream_set_pwritev_params(n_iovecs, n_frames); 1153 test_pwritev(version, http, sched_immed, limit, packet_sz, prologue_sz, 1154 n_packets); 1155 1156 cleanup: 1157 (void) fclose(f); 1158} 1159 1160 1161static unsigned 1162count_hq_frames (const struct lsquic_stream *stream) 1163{ 1164 const struct stream_hq_frame *shf; 1165 unsigned n_frames = 0; 1166 STAILQ_FOREACH(shf, &stream->sm_hq_frames, shf_next) 1167 ++n_frames; 1168 return n_frames; 1169} 1170 1171 1172static void 1173test_frame_header_split (unsigned n_packets, unsigned extra_sz, 1174 int add_one_more) 1175{ 1176 struct test_objs tobjs; 1177 struct lsquic_stream *stream; 1178 size_t nw; 1179 int fin, s; 1180 unsigned char *buf_in, *buf_out; 1181 const unsigned wsize = 70; 1182 const size_t buf_in_sz = wsize, buf_out_sz = 0x500000; 1183 unsigned n_frames; 1184 1185 struct lsxpack_header header = { XHDR(":method", "GET") }; 1186 struct lsquic_http_headers headers = { 1, &header, }; 1187 1188 buf_in = malloc(buf_in_sz); 1189 buf_out = malloc(buf_out_sz); 1190 assert(buf_in && buf_out); 1191 1192 struct packetization_test_stream_ctx packet_stream_ctx = 1193 { 1194 .buf = buf_in, 1195 .off = 0, 1196 .len = buf_in_sz, 1197 .write_size = wsize, 1198 .flush_after_each_write = 0, 1199 }; 1200 1201 init_buf(buf_in, buf_in_sz); 1202 1203 init_test_ctl_settings(&g_ctl_settings); 1204 g_ctl_settings.tcs_schedule_stream_packets_immediately = 1; 1205 1206 stream_ctor_flags |= SCF_IETF; 1207 init_test_objs(&tobjs, 0x1000, buf_out_sz, 1252); 1208 tobjs.stream_if_ctx = &packet_stream_ctx; 1209 tobjs.ctor_flags |= SCF_HTTP|SCF_IETF; 1210 1211 g_ctl_settings.tcs_can_send = n_packets; 1212 tobjs.stream_if = &packetization_inside_once_stream_if; 1213 tobjs.ctor_flags |= SCF_DISP_RW_ONCE; 1214 1215 struct lsquic_packet_out *const packet_out 1216 = lsquic_send_ctl_new_packet_out(&tobjs.send_ctl, 0, PNS_APP, &network_path); 1217 assert(packet_out); 1218 const size_t pad_size = packet_out->po_n_alloc 1219 - 2 /* STREAM header */ 1220 - 5 /* 3-byte HEADERS frame */ 1221 - extra_sz; 1222 packet_out->po_data_sz = pad_size; 1223 lsquic_send_ctl_scheduled_one(&tobjs.send_ctl, packet_out); 1224 1225 stream = new_stream(&tobjs, 0, buf_out_sz); 1226 1227 s = lsquic_stream_send_headers(stream, &headers, 0); 1228 assert(0 == s); 1229 1230 const ssize_t w = lsquic_stream_write(stream, buf_in, buf_in_sz); 1231 assert(w >= 0 && (size_t) w == buf_in_sz); 1232 n_frames = count_hq_frames(stream); 1233 assert((int) n_frames == 1 + (w > 0)); 1234 1235 lsquic_stream_flush(stream); 1236 n_frames = count_hq_frames(stream); 1237 assert((int) n_frames == !!stream->sm_n_buffered); 1238 1239 if (add_one_more) 1240 { 1241 ++g_ctl_settings.tcs_can_send; 1242 lsquic_stream_flush(stream); 1243 } 1244 1245 /* Verify written data: */ 1246 nw = read_from_scheduled_packets(&tobjs.send_ctl, 0, buf_out, buf_out_sz, 1247 0, &fin, 1); 1248 { /* Remove framing and verify contents */ 1249 const unsigned char *src; 1250 unsigned char *dst; 1251 uint64_t sz; 1252 unsigned frame_type; 1253 int s; 1254 1255 src = buf_out; 1256 dst = buf_out; 1257 while (src < buf_out + nw) 1258 { 1259 frame_type = *src++; 1260 s = vint_read(src, buf_out + buf_out_sz, &sz); 1261 assert(s > 0); 1262 assert(sz > 0); 1263 assert(sz < (1 << 14)); 1264 src += s; 1265 if (src == buf_out + s + 1) 1266 { 1267 /* Ignore headers */ 1268 assert(frame_type == HQFT_HEADERS); 1269 src += sz; 1270 } 1271 else 1272 { 1273 assert(frame_type == HQFT_DATA); 1274 if (src + sz > buf_out + nw) /* Chopped DATA frame (last) */ 1275 sz = buf_out + nw - src; 1276 memmove(dst, src, sz); 1277 dst += sz; 1278 src += sz; 1279 } 1280 } 1281 assert(0 == memcmp(buf_in, buf_out, (uintptr_t) dst - (uintptr_t) buf_out)); 1282 } 1283 1284 lsquic_stream_destroy(stream); 1285 deinit_test_objs(&tobjs); 1286 free(buf_in); 1287 free(buf_out); 1288 1289 stream_ctor_flags &= ~SCF_IETF; 1290} 1291 1292 1293static void 1294test_zero_size_frame (void) 1295{ 1296 struct test_objs tobjs; 1297 struct lsquic_stream *stream; 1298 ssize_t w; 1299 size_t nw; 1300 int fin, s; 1301 unsigned char *buf_in, *buf_out; 1302 const unsigned wsize = 7000; 1303 const size_t buf_in_sz = wsize, buf_out_sz = 0x500000; 1304 1305 struct lsxpack_header header = { XHDR(":method", "GET") }; 1306 struct lsquic_http_headers headers = { 1, &header, }; 1307 1308 buf_in = malloc(buf_in_sz); 1309 buf_out = malloc(buf_out_sz); 1310 assert(buf_in && buf_out); 1311 1312 struct packetization_test_stream_ctx packet_stream_ctx = 1313 { 1314 .buf = buf_in, 1315 .off = 0, 1316 .len = buf_in_sz, 1317 .write_size = wsize, 1318 .flush_after_each_write = 0, 1319 }; 1320 1321 init_buf(buf_in, buf_in_sz); 1322 1323 init_test_ctl_settings(&g_ctl_settings); 1324 g_ctl_settings.tcs_schedule_stream_packets_immediately = 1; 1325 1326 stream_ctor_flags |= SCF_IETF; 1327 init_test_objs(&tobjs, 0x1000, buf_out_sz, 1252); 1328 tobjs.stream_if_ctx = &packet_stream_ctx; 1329 tobjs.ctor_flags |= SCF_HTTP|SCF_IETF; 1330 1331 g_ctl_settings.tcs_can_send = 1; 1332 tobjs.stream_if = &packetization_inside_once_stream_if; 1333 tobjs.ctor_flags |= SCF_DISP_RW_ONCE; 1334 1335 struct lsquic_packet_out *const packet_out 1336 = lsquic_send_ctl_new_packet_out(&tobjs.send_ctl, 0, PNS_APP, &network_path); 1337 assert(packet_out); 1338 const size_t pad_size = packet_out->po_n_alloc 1339 - 2 /* STREAM header */ 1340 - 5 /* 3-byte HEADERS frame */ 1341 - 3; 1342 packet_out->po_data_sz = pad_size; 1343 lsquic_send_ctl_scheduled_one(&tobjs.send_ctl, packet_out); 1344 1345 stream = new_stream(&tobjs, 0, buf_out_sz); 1346 1347 s = lsquic_stream_send_headers(stream, &headers, 0); 1348 assert(0 == s); 1349 1350 w = lsquic_stream_write(stream, buf_in, buf_in_sz); 1351 assert(w >= 0); 1352 lsquic_stream_flush(stream); 1353 1354 g_ctl_settings.tcs_can_send++; 1355 w = lsquic_stream_write(stream, buf_in, buf_in_sz); 1356 assert(w >= 0); 1357 lsquic_stream_flush(stream); 1358 1359 /* Verify written data: */ 1360 nw = read_from_scheduled_packets(&tobjs.send_ctl, 0, buf_out, buf_out_sz, 1361 0, &fin, 1); 1362 { /* Remove framing and verify contents */ 1363 const unsigned char *src; 1364 unsigned char *dst; 1365 uint64_t sz; 1366 unsigned frame_type; 1367 int s; 1368 1369 src = buf_out; 1370 dst = buf_out; 1371 while (src < buf_out + nw) 1372 { 1373 frame_type = *src++; 1374 s = vint_read(src, buf_out + buf_out_sz, &sz); 1375 assert(s > 0); 1376 assert(sz < (1 << 14)); 1377 src += s; 1378 if (src == buf_out + s + 1) 1379 { 1380 /* Ignore headers */ 1381 assert(frame_type == HQFT_HEADERS); 1382 src += sz; 1383 } 1384 else 1385 { 1386 assert(frame_type == HQFT_DATA); 1387 if (src + sz > buf_out + nw) /* Chopped DATA frame (last) */ 1388 sz = buf_out + nw - src; 1389 memmove(dst, src, sz); 1390 dst += sz; 1391 src += sz; 1392 } 1393 } 1394 assert(0 == memcmp(buf_in, buf_out, (uintptr_t) dst - (uintptr_t) buf_out)); 1395 } 1396 1397 lsquic_stream_destroy(stream); 1398 deinit_test_objs(&tobjs); 1399 free(buf_in); 1400 free(buf_out); 1401 1402 stream_ctor_flags &= ~SCF_IETF; 1403} 1404 1405 1406/* Create a new stream frame. Each stream frame has a real packet_in to 1407 * back it up, just like in real code. The contents of the packet do 1408 * not matter. 1409 */ 1410static stream_frame_t * 1411new_frame_in_ext (struct test_objs *tobjs, size_t off, size_t sz, int fin, 1412 const void *data) 1413{ 1414 lsquic_packet_in_t *packet_in; 1415 stream_frame_t *frame; 1416 1417 assert(sz <= 1370); 1418 1419 packet_in = lsquic_mm_get_packet_in(&tobjs->eng_pub.enp_mm); 1420 if (data) 1421 packet_in->pi_data = (void *) data; 1422 else 1423 { 1424 packet_in->pi_data = lsquic_mm_get_packet_in_buf(&tobjs->eng_pub.enp_mm, 1370); 1425 packet_in->pi_flags |= PI_OWN_DATA; 1426 memset(packet_in->pi_data, 'A', sz); 1427 } 1428 /* This is not how stream frame looks in the packet: we have no 1429 * header. In our test case it does not matter, as we only care 1430 * about stream frame. 1431 */ 1432 packet_in->pi_data_sz = sz; 1433 packet_in->pi_refcnt = 1; 1434 1435 frame = lsquic_malo_get(tobjs->eng_pub.enp_mm.malo.stream_frame); 1436 memset(frame, 0, sizeof(*frame)); 1437 frame->packet_in = packet_in; 1438 frame->data_frame.df_offset = off; 1439 frame->data_frame.df_size = sz; 1440 frame->data_frame.df_data = &packet_in->pi_data[0]; 1441 frame->data_frame.df_fin = fin; 1442 1443 return frame; 1444} 1445 1446 1447/* Receiving DATA frame with zero payload should result in lsquic_stream_read() 1448 * returning -1. 1449 */ 1450static void 1451test_reading_zero_size_data_frame (void) 1452{ 1453 struct test_objs tobjs; 1454 struct lsquic_stream *stream; 1455 struct stream_frame *frame; 1456 ssize_t nr; 1457 int s; 1458 unsigned char buf[2]; 1459 1460 init_test_ctl_settings(&g_ctl_settings); 1461 1462 stream_ctor_flags |= SCF_IETF; 1463 init_test_objs(&tobjs, 0x1000, 0x2000, 1252); 1464 tobjs.ctor_flags |= SCF_HTTP|SCF_IETF; 1465 1466 stream = new_stream(&tobjs, 0, 0x1000); 1467 1468 /* Fake out reading of HEADERS frame: */ 1469 stream->stream_flags |= STREAM_HAVE_UH; 1470 stream->sm_hq_filter.hqfi_flags |= HQFI_FLAG_HEADER; 1471 1472 /* One-byte DATA frame */ 1473 frame = new_frame_in_ext(&tobjs, 0, 3, 0, (uint8_t[3]){ 0, 1, 'a', }); 1474 s = lsquic_stream_frame_in(stream, frame); 1475 assert(s == 0); /* Self-check */ 1476 1477 /* Zero-length DATA frame */ 1478 frame = new_frame_in_ext(&tobjs, 3, 2, 0, (uint8_t[2]){ 0, 0, }); 1479 s = lsquic_stream_frame_in(stream, frame); 1480 assert(s == 0); /* Self-check */ 1481 1482 assert(stream->read_offset == 2); /* Self-check */ 1483 1484 /* Read 'a' */ 1485 nr = lsquic_stream_read(stream, buf, 1); 1486 assert(nr == 1); 1487 assert(buf[0] == 'a'); 1488 1489 /* Check that read returns -1 */ 1490 nr = lsquic_stream_read(stream, buf, sizeof(buf)); 1491 assert(nr == -1); 1492 1493 /* DATA frame was consumed: */ 1494 assert(stream->read_offset == 5); 1495 1496 lsquic_stream_destroy(stream); 1497 deinit_test_objs(&tobjs); 1498 1499 stream_ctor_flags &= ~SCF_IETF; 1500} 1501 1502 1503/* Receiving DATA frame with zero payload should result in lsquic_stream_read() 1504 * returning -1. 1505 */ 1506static void 1507test_reading_zero_size_data_frame_scenario2 (void) 1508{ 1509 struct test_objs tobjs; 1510 struct lsquic_stream *stream; 1511 struct stream_frame *frame; 1512 ssize_t nr; 1513 int s; 1514 unsigned char buf[2]; 1515 1516 init_test_ctl_settings(&g_ctl_settings); 1517 1518 stream_ctor_flags |= SCF_IETF; 1519 init_test_objs(&tobjs, 0x1000, 0x2000, 1252); 1520 tobjs.ctor_flags |= SCF_HTTP|SCF_IETF; 1521 1522 stream = new_stream(&tobjs, 0, 0x1000); 1523 1524 /* Fake out reading of HEADERS frame: */ 1525 stream->stream_flags |= STREAM_HAVE_UH; 1526 stream->sm_hq_filter.hqfi_flags |= HQFI_FLAG_HEADER; 1527 1528 /* Zero-length DATA frame */ 1529 frame = new_frame_in_ext(&tobjs, 0, 5, 0, (uint8_t[5]){ 0, 1, 'a', 0, 0, }); 1530 s = lsquic_stream_frame_in(stream, frame); 1531 assert(s == 0); /* Self-check */ 1532 1533 assert(stream->read_offset == 2); /* Self-check */ 1534 1535 /* Read 'a' */ 1536 nr = lsquic_stream_read(stream, buf, 1); 1537 assert(nr == 1); 1538 assert(buf[0] == 'a'); 1539 1540 /* Check that read returns -1 */ 1541 nr = lsquic_stream_read(stream, buf, sizeof(buf)); 1542 assert(nr == -1); 1543 1544 /* DATA frame was consumed: */ 1545 assert(stream->read_offset == 5); 1546 1547 lsquic_stream_destroy(stream); 1548 deinit_test_objs(&tobjs); 1549 1550 stream_ctor_flags &= ~SCF_IETF; 1551} 1552 1553 1554/* Receiving DATA frame with zero payload should result in lsquic_stream_read() 1555 * returning -1. 1556 */ 1557static void 1558test_reading_zero_size_data_frame_scenario3 (void) 1559{ 1560 struct test_objs tobjs; 1561 struct lsquic_stream *stream; 1562 struct stream_frame *frame; 1563 ssize_t nr; 1564 int s; 1565 unsigned char buf[2]; 1566 1567 init_test_ctl_settings(&g_ctl_settings); 1568 1569 stream_ctor_flags |= SCF_IETF; 1570 init_test_objs(&tobjs, 0x1000, 0x2000, 1252); 1571 tobjs.ctor_flags |= SCF_HTTP|SCF_IETF; 1572 1573 stream = new_stream(&tobjs, 0, 0x1000); 1574 1575 /* Fake out reading of HEADERS frame: */ 1576 stream->stream_flags |= STREAM_HAVE_UH; 1577 stream->sm_hq_filter.hqfi_flags |= HQFI_FLAG_HEADER; 1578 1579 /* Zero-length DATA frame */ 1580 frame = new_frame_in_ext(&tobjs, 0, 4, 0, (uint8_t[4]){ 0, 1, 'a', 0, }); 1581 s = lsquic_stream_frame_in(stream, frame); 1582 assert(s == 0); /* Self-check */ 1583 1584 assert(stream->read_offset == 2); /* Self-check */ 1585 1586 /* Read 'a' */ 1587 nr = lsquic_stream_read(stream, buf, 1); 1588 assert(nr == 1); 1589 assert(buf[0] == 'a'); 1590 1591 /* Check that read returns -1 */ 1592 nr = lsquic_stream_read(stream, buf, sizeof(buf)); 1593 assert(nr == -1); 1594 1595 /* Zero-length DATA frame */ 1596 frame = new_frame_in_ext(&tobjs, 4, 1, 0, (uint8_t[1]){ 0, }); 1597 s = lsquic_stream_frame_in(stream, frame); 1598 assert(s == 0); /* Self-check */ 1599 1600 /* Check that read returns -1 */ 1601 nr = lsquic_stream_read(stream, buf, sizeof(buf)); 1602 assert(nr == -1); 1603 1604 /* DATA frame was consumed: */ 1605 assert(stream->read_offset == 5); 1606 1607 lsquic_stream_destroy(stream); 1608 deinit_test_objs(&tobjs); 1609 1610 stream_ctor_flags &= ~SCF_IETF; 1611} 1612 1613 1614int 1615main (int argc, char **argv) 1616{ 1617 const char *fuzz_hq_framing_input = NULL; 1618 const char *fuzz_pwritev_input = NULL; 1619 int opt, add_one_more; 1620 unsigned n_packets, extra_sz; 1621 1622 lsquic_global_init(LSQUIC_GLOBAL_SERVER); 1623 1624 while (-1 != (opt = getopt(argc, argv, "f:p:l:"))) 1625 { 1626 switch (opt) 1627 { 1628 case 'f': 1629 fuzz_hq_framing_input = optarg; 1630 break; 1631 case 'p': 1632 fuzz_pwritev_input = optarg; 1633 break; 1634 case 'l': 1635 lsquic_log_to_fstream(stderr, 0); 1636 lsquic_logger_lopt(optarg); 1637 break; 1638 default: 1639 exit(1); 1640 } 1641 } 1642 1643 init_test_ctl_settings(&g_ctl_settings); 1644 1645 if (fuzz_hq_framing_input) 1646 fuzz_guided_hq_framing_testing(fuzz_hq_framing_input); 1647 else if (fuzz_pwritev_input) 1648 fuzz_guided_pwritev_testing(fuzz_pwritev_input); 1649 else 1650 { 1651 main_test_pwritev(); 1652 main_test_hq_framing(); 1653 for (n_packets = 1; n_packets <= 2; ++n_packets) 1654 for (extra_sz = 0; extra_sz <= 2; ++extra_sz) 1655 for (add_one_more = 0; add_one_more <= 1; ++add_one_more) 1656 test_frame_header_split(n_packets, extra_sz, add_one_more); 1657 test_zero_size_frame(); 1658 test_reading_zero_size_data_frame(); 1659 test_reading_zero_size_data_frame_scenario2(); 1660 test_reading_zero_size_data_frame_scenario3(); 1661 } 1662 1663 return 0; 1664} 1665 1666static const char on_being_idle[] = 1667"ON BEING IDLE." 1668"" 1669"Now, this is a subject on which I flatter myself I really am _au fait_." 1670"The gentleman who, when I was young, bathed me at wisdom's font for nine" 1671"guineas a term--no extras--used to say he never knew a boy who could" 1672"do less work in more time; and I remember my poor grandmother once" 1673"incidentally observing, in the course of an instruction upon the use" 1674"of the Prayer-book, that it was highly improbable that I should ever do" 1675"much that I ought not to do, but that she felt convinced beyond a doubt" 1676"that I should leave undone pretty well everything that I ought to do." 1677"" 1678"I am afraid I have somewhat belied half the dear old lady's prophecy." 1679"Heaven help me! I have done a good many things that I ought not to have" 1680"done, in spite of my laziness. But I have fully confirmed the accuracy" 1681"of her judgment so far as neglecting much that I ought not to have" 1682"neglected is concerned. Idling always has been my strong point. I take" 1683"no credit to myself in the matter--it is a gift. Few possess it. There" 1684"are plenty of lazy people and plenty of slow-coaches, but a genuine" 1685"idler is a rarity. He is not a man who slouches about with his hands in" 1686"his pockets. On the contrary, his most startling characteristic is that" 1687"he is always intensely busy." 1688"" 1689"It is impossible to enjoy idling thoroughly unless one has plenty of" 1690"work to do. There is no fun in doing nothing when you have nothing to" 1691"do. Wasting time is merely an occupation then, and a most exhausting" 1692"one. Idleness, like kisses, to be sweet must be stolen." 1693"" 1694"Many years ago, when I was a young man, I was taken very ill--I never" 1695"could see myself that much was the matter with me, except that I had" 1696"a beastly cold. But I suppose it was something very serious, for the" 1697"doctor said that I ought to have come to him a month before, and that" 1698"if it (whatever it was) had gone on for another week he would not have" 1699"answered for the consequences. It is an extraordinary thing, but I" 1700"never knew a doctor called into any case yet but what it transpired" 1701"that another day's delay would have rendered cure hopeless. Our medical" 1702"guide, philosopher, and friend is like the hero in a melodrama--he" 1703"always comes upon the scene just, and only just, in the nick of time. It" 1704"is Providence, that is what it is." 1705"" 1706"Well, as I was saying, I was very ill and was ordered to Buxton for a" 1707"month, with strict injunctions to do nothing whatever all the while" 1708"that I was there. \"Rest is what you require,\" said the doctor, \"perfect" 1709"rest.\"" 1710"" 1711"It seemed a delightful prospect. \"This man evidently understands my" 1712"complaint,\" said I, and I pictured to myself a glorious time--a four" 1713"weeks' _dolce far niente_ with a dash of illness in it. Not too much" 1714"illness, but just illness enough--just sufficient to give it the flavor" 1715"of suffering and make it poetical. I should get up late, sip chocolate," 1716"and have my breakfast in slippers and a dressing-gown. I should lie out" 1717"in the garden in a hammock and read sentimental novels with a melancholy" 1718"ending, until the books should fall from my listless hand, and I should" 1719"recline there, dreamily gazing into the deep blue of the firmament," 1720"watching the fleecy clouds floating like white-sailed ships across" 1721"its depths, and listening to the joyous song of the birds and the low" 1722"rustling of the trees. Or, on becoming too weak to go out of doors," 1723"I should sit propped up with pillows at the open window of the" 1724"ground-floor front, and look wasted and interesting, so that all the" 1725"pretty girls would sigh as they passed by." 1726"" 1727"And twice a day I should go down in a Bath chair to the Colonnade to" 1728"drink the waters. Oh, those waters! I knew nothing about them then," 1729"and was rather taken with the idea. \"Drinking the waters\" sounded" 1730"fashionable and Queen Anne-fied, and I thought I should like them. But," 1731"ugh! after the first three or four mornings! Sam Weller's description of" 1732"them as \"having a taste of warm flat-irons\" conveys only a faint idea of" 1733"their hideous nauseousness. If anything could make a sick man get well" 1734"quickly, it would be the knowledge that he must drink a glassful of them" 1735"every day until he was recovered. I drank them neat for six consecutive" 1736"days, and they nearly killed me; but after then I adopted the plan of" 1737"taking a stiff glass of brandy-and-water immediately on the top of them," 1738"and found much relief thereby. I have been informed since, by various" 1739"eminent medical gentlemen, that the alcohol must have entirely" 1740"counteracted the effects of the chalybeate properties contained in the" 1741"water. I am glad I was lucky enough to hit upon the right thing." 1742; 1743 1744static void 1745init_buf (void *buf, size_t sz) 1746{ 1747 unsigned char *p = buf; 1748 unsigned char *const end = (unsigned char*)buf + sz; 1749 size_t n; 1750 1751 while (p < end) 1752 { 1753 n = end - p; 1754 if (sizeof(on_being_idle) - 1 < n) 1755 n = sizeof(on_being_idle) - 1; 1756 memcpy(p, on_being_idle, n); 1757 p +=n; 1758 } 1759 1760 assert(p == end); 1761} 1762