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