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