test_h3_framing.c revision 927d9cb2
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_ID34; 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