test_h3_framing.c revision b1a7c3f9
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 void
1155test_frame_header_split (unsigned n_packets, unsigned extra_sz,
1156                                                        int add_one_more)
1157{
1158    struct test_objs tobjs;
1159    struct lsquic_stream *stream;
1160    size_t nw;
1161    int fin, s;
1162    unsigned char *buf_in, *buf_out;
1163    const unsigned wsize = 70;
1164    const size_t buf_in_sz = wsize, buf_out_sz = 0x500000;
1165
1166    struct lsxpack_header header = { XHDR(":method", "GET") };
1167    struct lsquic_http_headers headers = { 1, &header, };
1168
1169    buf_in = malloc(buf_in_sz);
1170    buf_out = malloc(buf_out_sz);
1171    assert(buf_in && buf_out);
1172
1173    struct packetization_test_stream_ctx packet_stream_ctx =
1174    {
1175        .buf = buf_in,
1176        .off = 0,
1177        .len = buf_in_sz,
1178        .write_size = wsize,
1179        .flush_after_each_write = 0,
1180    };
1181
1182    init_buf(buf_in, buf_in_sz);
1183
1184    init_test_ctl_settings(&g_ctl_settings);
1185    g_ctl_settings.tcs_schedule_stream_packets_immediately = 1;
1186
1187    stream_ctor_flags |= SCF_IETF;
1188    init_test_objs(&tobjs, 0x1000, buf_out_sz, 1252);
1189    tobjs.stream_if_ctx = &packet_stream_ctx;
1190    tobjs.ctor_flags |= SCF_HTTP|SCF_IETF;
1191
1192    g_ctl_settings.tcs_can_send = n_packets;
1193    tobjs.stream_if = &packetization_inside_once_stream_if;
1194    tobjs.ctor_flags |= SCF_DISP_RW_ONCE;
1195
1196    struct lsquic_packet_out *const packet_out
1197        = lsquic_send_ctl_new_packet_out(&tobjs.send_ctl, 0, PNS_APP, &network_path);
1198    assert(packet_out);
1199    const size_t pad_size = packet_out->po_n_alloc
1200                          - 2   /* STREAM header */
1201                          - 5   /* 3-byte HEADERS frame */
1202                          - extra_sz;
1203    packet_out->po_data_sz = pad_size;
1204    lsquic_send_ctl_scheduled_one(&tobjs.send_ctl, packet_out);
1205
1206    stream = new_stream(&tobjs, 0, buf_out_sz);
1207
1208    s = lsquic_stream_send_headers(stream, &headers, 0);
1209    assert(0 == s);
1210
1211    const ssize_t w = lsquic_stream_write(stream, buf_in, buf_in_sz);
1212    assert(w >= 0 && (size_t) w == buf_in_sz);
1213    lsquic_stream_flush(stream);
1214
1215    if (add_one_more)
1216    {
1217        ++g_ctl_settings.tcs_can_send;
1218        lsquic_stream_flush(stream);
1219    }
1220
1221    /* Verify written data: */
1222    nw = read_from_scheduled_packets(&tobjs.send_ctl, 0, buf_out, buf_out_sz,
1223                                     0, &fin, 1);
1224    {   /* Remove framing and verify contents */
1225        const unsigned char *src;
1226        unsigned char *dst;
1227        uint64_t sz;
1228        unsigned frame_type;
1229        int s;
1230
1231        src = buf_out;
1232        dst = buf_out;
1233        while (src < buf_out + nw)
1234        {
1235            frame_type = *src++;
1236            s = vint_read(src, buf_out + buf_out_sz, &sz);
1237            assert(s > 0);
1238            assert(sz > 0);
1239            assert(sz < (1 << 14));
1240            src += s;
1241            if (src == buf_out + s + 1)
1242            {
1243                /* Ignore headers */
1244                assert(frame_type == HQFT_HEADERS);
1245                src += sz;
1246            }
1247            else
1248            {
1249                assert(frame_type == HQFT_DATA);
1250                if (src + sz > buf_out + nw)    /* Chopped DATA frame (last) */
1251                    sz = buf_out + nw - src;
1252                memmove(dst, src, sz);
1253                dst += sz;
1254                src += sz;
1255            }
1256        }
1257        assert(0 == memcmp(buf_in, buf_out, (uintptr_t) dst - (uintptr_t) buf_out));
1258    }
1259
1260    lsquic_stream_destroy(stream);
1261    deinit_test_objs(&tobjs);
1262    free(buf_in);
1263    free(buf_out);
1264
1265    stream_ctor_flags &= ~SCF_IETF;
1266}
1267
1268
1269static void
1270test_zero_size_frame (void)
1271{
1272    struct test_objs tobjs;
1273    struct lsquic_stream *stream;
1274    ssize_t w;
1275    size_t nw;
1276    int fin, s;
1277    unsigned char *buf_in, *buf_out;
1278    const unsigned wsize = 7000;
1279    const size_t buf_in_sz = wsize, buf_out_sz = 0x500000;
1280
1281    struct lsxpack_header header = { XHDR(":method", "GET") };
1282    struct lsquic_http_headers headers = { 1, &header, };
1283
1284    buf_in = malloc(buf_in_sz);
1285    buf_out = malloc(buf_out_sz);
1286    assert(buf_in && buf_out);
1287
1288    struct packetization_test_stream_ctx packet_stream_ctx =
1289    {
1290        .buf = buf_in,
1291        .off = 0,
1292        .len = buf_in_sz,
1293        .write_size = wsize,
1294        .flush_after_each_write = 0,
1295    };
1296
1297    init_buf(buf_in, buf_in_sz);
1298
1299    init_test_ctl_settings(&g_ctl_settings);
1300    g_ctl_settings.tcs_schedule_stream_packets_immediately = 1;
1301
1302    stream_ctor_flags |= SCF_IETF;
1303    init_test_objs(&tobjs, 0x1000, buf_out_sz, 1252);
1304    tobjs.stream_if_ctx = &packet_stream_ctx;
1305    tobjs.ctor_flags |= SCF_HTTP|SCF_IETF;
1306
1307    g_ctl_settings.tcs_can_send = 1;
1308    tobjs.stream_if = &packetization_inside_once_stream_if;
1309    tobjs.ctor_flags |= SCF_DISP_RW_ONCE;
1310
1311    struct lsquic_packet_out *const packet_out
1312        = lsquic_send_ctl_new_packet_out(&tobjs.send_ctl, 0, PNS_APP, &network_path);
1313    assert(packet_out);
1314    const size_t pad_size = packet_out->po_n_alloc
1315                          - 2   /* STREAM header */
1316                          - 5   /* 3-byte HEADERS frame */
1317                          - 3;
1318    packet_out->po_data_sz = pad_size;
1319    lsquic_send_ctl_scheduled_one(&tobjs.send_ctl, packet_out);
1320
1321    stream = new_stream(&tobjs, 0, buf_out_sz);
1322
1323    s = lsquic_stream_send_headers(stream, &headers, 0);
1324    assert(0 == s);
1325
1326    w = lsquic_stream_write(stream, buf_in, buf_in_sz);
1327    assert(w >= 0);
1328    lsquic_stream_flush(stream);
1329
1330    g_ctl_settings.tcs_can_send++;
1331    w = lsquic_stream_write(stream, buf_in, buf_in_sz);
1332    assert(w >= 0);
1333    lsquic_stream_flush(stream);
1334
1335    /* Verify written data: */
1336    nw = read_from_scheduled_packets(&tobjs.send_ctl, 0, buf_out, buf_out_sz,
1337                                     0, &fin, 1);
1338    {   /* Remove framing and verify contents */
1339        const unsigned char *src;
1340        unsigned char *dst;
1341        uint64_t sz;
1342        unsigned frame_type;
1343        int s;
1344
1345        src = buf_out;
1346        dst = buf_out;
1347        while (src < buf_out + nw)
1348        {
1349            frame_type = *src++;
1350            s = vint_read(src, buf_out + buf_out_sz, &sz);
1351            assert(s > 0);
1352            assert(sz < (1 << 14));
1353            src += s;
1354            if (src == buf_out + s + 1)
1355            {
1356                /* Ignore headers */
1357                assert(frame_type == HQFT_HEADERS);
1358                src += sz;
1359            }
1360            else
1361            {
1362                assert(frame_type == HQFT_DATA);
1363                if (src + sz > buf_out + nw)    /* Chopped DATA frame (last) */
1364                    sz = buf_out + nw - src;
1365                memmove(dst, src, sz);
1366                dst += sz;
1367                src += sz;
1368            }
1369        }
1370        assert(0 == memcmp(buf_in, buf_out, (uintptr_t) dst - (uintptr_t) buf_out));
1371    }
1372
1373    lsquic_stream_destroy(stream);
1374    deinit_test_objs(&tobjs);
1375    free(buf_in);
1376    free(buf_out);
1377
1378    stream_ctor_flags &= ~SCF_IETF;
1379}
1380
1381
1382/* Create a new stream frame.  Each stream frame has a real packet_in to
1383 * back it up, just like in real code.  The contents of the packet do
1384 * not matter.
1385 */
1386static stream_frame_t *
1387new_frame_in_ext (struct test_objs *tobjs, size_t off, size_t sz, int fin,
1388                                                            const void *data)
1389{
1390    lsquic_packet_in_t *packet_in;
1391    stream_frame_t *frame;
1392
1393    assert(sz <= 1370);
1394
1395    packet_in = lsquic_mm_get_packet_in(&tobjs->eng_pub.enp_mm);
1396    if (data)
1397        packet_in->pi_data = (void *) data;
1398    else
1399    {
1400        packet_in->pi_data = lsquic_mm_get_packet_in_buf(&tobjs->eng_pub.enp_mm, 1370);
1401        packet_in->pi_flags |= PI_OWN_DATA;
1402        memset(packet_in->pi_data, 'A', sz);
1403    }
1404    /* This is not how stream frame looks in the packet: we have no
1405     * header.  In our test case it does not matter, as we only care
1406     * about stream frame.
1407     */
1408    packet_in->pi_data_sz = sz;
1409    packet_in->pi_refcnt = 1;
1410
1411    frame = lsquic_malo_get(tobjs->eng_pub.enp_mm.malo.stream_frame);
1412    memset(frame, 0, sizeof(*frame));
1413    frame->packet_in = packet_in;
1414    frame->data_frame.df_offset = off;
1415    frame->data_frame.df_size = sz;
1416    frame->data_frame.df_data = &packet_in->pi_data[0];
1417    frame->data_frame.df_fin  = fin;
1418
1419    return frame;
1420}
1421
1422
1423/* Receiving DATA frame with zero payload should result in lsquic_stream_read()
1424 * returning -1.
1425 */
1426static void
1427test_reading_zero_size_data_frame (void)
1428{
1429    struct test_objs tobjs;
1430    struct lsquic_stream *stream;
1431    struct stream_frame *frame;
1432    ssize_t nr;
1433    int s;
1434    unsigned char buf[2];
1435
1436    init_test_ctl_settings(&g_ctl_settings);
1437
1438    stream_ctor_flags |= SCF_IETF;
1439    init_test_objs(&tobjs, 0x1000, 0x2000, 1252);
1440    tobjs.ctor_flags |= SCF_HTTP|SCF_IETF;
1441
1442    stream = new_stream(&tobjs, 0, 0x1000);
1443
1444    /* Fake out reading of HEADERS frame: */
1445    stream->stream_flags |= STREAM_HAVE_UH;
1446    stream->sm_hq_filter.hqfi_hist_buf = 1 /* CODE_HEADER */;
1447    stream->sm_hq_filter.hqfi_hist_idx++;
1448
1449    /* One-byte DATA frame */
1450    frame = new_frame_in_ext(&tobjs, 0, 3, 0, (uint8_t[3]){ 0, 1, 'a', });
1451    s = lsquic_stream_frame_in(stream, frame);
1452    assert(s == 0);     /* Self-check */
1453
1454    /* Zero-length DATA frame */
1455    frame = new_frame_in_ext(&tobjs, 3, 2, 0, (uint8_t[2]){ 0, 0, });
1456    s = lsquic_stream_frame_in(stream, frame);
1457    assert(s == 0);     /* Self-check */
1458
1459    assert(stream->read_offset == 2);   /* Self-check */
1460
1461    /* Read 'a' */
1462    nr = lsquic_stream_read(stream, buf, 1);
1463    assert(nr == 1);
1464    assert(buf[0] == 'a');
1465
1466    /* Check that read returns -1 */
1467    nr = lsquic_stream_read(stream, buf, sizeof(buf));
1468    assert(nr == -1);
1469
1470    /* DATA frame was consumed: */
1471    assert(stream->read_offset == 5);
1472
1473    lsquic_stream_destroy(stream);
1474    deinit_test_objs(&tobjs);
1475
1476    stream_ctor_flags &= ~SCF_IETF;
1477}
1478
1479
1480/* Receiving DATA frame with zero payload should result in lsquic_stream_read()
1481 * returning -1.
1482 */
1483static void
1484test_reading_zero_size_data_frame_scenario2 (void)
1485{
1486    struct test_objs tobjs;
1487    struct lsquic_stream *stream;
1488    struct stream_frame *frame;
1489    ssize_t nr;
1490    int s;
1491    unsigned char buf[2];
1492
1493    init_test_ctl_settings(&g_ctl_settings);
1494
1495    stream_ctor_flags |= SCF_IETF;
1496    init_test_objs(&tobjs, 0x1000, 0x2000, 1252);
1497    tobjs.ctor_flags |= SCF_HTTP|SCF_IETF;
1498
1499    stream = new_stream(&tobjs, 0, 0x1000);
1500
1501    /* Fake out reading of HEADERS frame: */
1502    stream->stream_flags |= STREAM_HAVE_UH;
1503    stream->sm_hq_filter.hqfi_hist_buf = 1 /* CODE_HEADER */;
1504    stream->sm_hq_filter.hqfi_hist_idx++;
1505
1506    /* Zero-length DATA frame */
1507    frame = new_frame_in_ext(&tobjs, 0, 5, 0, (uint8_t[5]){ 0, 1, 'a', 0, 0, });
1508    s = lsquic_stream_frame_in(stream, frame);
1509    assert(s == 0);     /* Self-check */
1510
1511    assert(stream->read_offset == 2);   /* Self-check */
1512
1513    /* Read 'a' */
1514    nr = lsquic_stream_read(stream, buf, 1);
1515    assert(nr == 1);
1516    assert(buf[0] == 'a');
1517
1518    /* Check that read returns -1 */
1519    nr = lsquic_stream_read(stream, buf, sizeof(buf));
1520    assert(nr == -1);
1521
1522    /* DATA frame was consumed: */
1523    assert(stream->read_offset == 5);
1524
1525    lsquic_stream_destroy(stream);
1526    deinit_test_objs(&tobjs);
1527
1528    stream_ctor_flags &= ~SCF_IETF;
1529}
1530
1531
1532/* Receiving DATA frame with zero payload should result in lsquic_stream_read()
1533 * returning -1.
1534 */
1535static void
1536test_reading_zero_size_data_frame_scenario3 (void)
1537{
1538    struct test_objs tobjs;
1539    struct lsquic_stream *stream;
1540    struct stream_frame *frame;
1541    ssize_t nr;
1542    int s;
1543    unsigned char buf[2];
1544
1545    init_test_ctl_settings(&g_ctl_settings);
1546
1547    stream_ctor_flags |= SCF_IETF;
1548    init_test_objs(&tobjs, 0x1000, 0x2000, 1252);
1549    tobjs.ctor_flags |= SCF_HTTP|SCF_IETF;
1550
1551    stream = new_stream(&tobjs, 0, 0x1000);
1552
1553    /* Fake out reading of HEADERS frame: */
1554    stream->stream_flags |= STREAM_HAVE_UH;
1555    stream->sm_hq_filter.hqfi_hist_buf = 1 /* CODE_HEADER */;
1556    stream->sm_hq_filter.hqfi_hist_idx++;
1557
1558    /* Zero-length DATA frame */
1559    frame = new_frame_in_ext(&tobjs, 0, 4, 0, (uint8_t[4]){ 0, 1, 'a', 0, });
1560    s = lsquic_stream_frame_in(stream, frame);
1561    assert(s == 0);     /* Self-check */
1562
1563    assert(stream->read_offset == 2);   /* Self-check */
1564
1565    /* Read 'a' */
1566    nr = lsquic_stream_read(stream, buf, 1);
1567    assert(nr == 1);
1568    assert(buf[0] == 'a');
1569
1570    /* Check that read returns -1 */
1571    nr = lsquic_stream_read(stream, buf, sizeof(buf));
1572    assert(nr == -1);
1573
1574    /* Zero-length DATA frame */
1575    frame = new_frame_in_ext(&tobjs, 4, 1, 0, (uint8_t[1]){ 0, });
1576    s = lsquic_stream_frame_in(stream, frame);
1577    assert(s == 0);     /* Self-check */
1578
1579    /* Check that read returns -1 */
1580    nr = lsquic_stream_read(stream, buf, sizeof(buf));
1581    assert(nr == -1);
1582
1583    /* DATA frame was consumed: */
1584    assert(stream->read_offset == 5);
1585
1586    lsquic_stream_destroy(stream);
1587    deinit_test_objs(&tobjs);
1588
1589    stream_ctor_flags &= ~SCF_IETF;
1590}
1591
1592
1593int
1594main (int argc, char **argv)
1595{
1596    const char *fuzz_hq_framing_input = NULL;
1597    const char *fuzz_pwritev_input = NULL;
1598    int opt, add_one_more;
1599    unsigned n_packets, extra_sz;
1600
1601    lsquic_global_init(LSQUIC_GLOBAL_SERVER);
1602
1603    while (-1 != (opt = getopt(argc, argv, "f:p:l:")))
1604    {
1605        switch (opt)
1606        {
1607        case 'f':
1608            fuzz_hq_framing_input = optarg;
1609            break;
1610        case 'p':
1611            fuzz_pwritev_input = optarg;
1612            break;
1613        case 'l':
1614            lsquic_log_to_fstream(stderr, 0);
1615            lsquic_logger_lopt(optarg);
1616            break;
1617        default:
1618            exit(1);
1619        }
1620    }
1621
1622    init_test_ctl_settings(&g_ctl_settings);
1623
1624    if (fuzz_hq_framing_input)
1625        fuzz_guided_hq_framing_testing(fuzz_hq_framing_input);
1626    else if (fuzz_pwritev_input)
1627        fuzz_guided_pwritev_testing(fuzz_pwritev_input);
1628    else
1629    {
1630        main_test_pwritev();
1631        main_test_hq_framing();
1632        for (n_packets = 1; n_packets <= 2; ++n_packets)
1633            for (extra_sz = 0; extra_sz <= 2; ++extra_sz)
1634                for (add_one_more = 0; add_one_more <= 1; ++add_one_more)
1635                    test_frame_header_split(n_packets, extra_sz, add_one_more);
1636        test_zero_size_frame();
1637        test_reading_zero_size_data_frame();
1638        test_reading_zero_size_data_frame_scenario2();
1639        test_reading_zero_size_data_frame_scenario3();
1640    }
1641
1642    return 0;
1643}
1644
1645static const char on_being_idle[] =
1646"ON BEING IDLE."
1647""
1648"Now, this is a subject on which I flatter myself I really am _au fait_."
1649"The gentleman who, when I was young, bathed me at wisdom's font for nine"
1650"guineas a term--no extras--used to say he never knew a boy who could"
1651"do less work in more time; and I remember my poor grandmother once"
1652"incidentally observing, in the course of an instruction upon the use"
1653"of the Prayer-book, that it was highly improbable that I should ever do"
1654"much that I ought not to do, but that she felt convinced beyond a doubt"
1655"that I should leave undone pretty well everything that I ought to do."
1656""
1657"I am afraid I have somewhat belied half the dear old lady's prophecy."
1658"Heaven help me! I have done a good many things that I ought not to have"
1659"done, in spite of my laziness. But I have fully confirmed the accuracy"
1660"of her judgment so far as neglecting much that I ought not to have"
1661"neglected is concerned. Idling always has been my strong point. I take"
1662"no credit to myself in the matter--it is a gift. Few possess it. There"
1663"are plenty of lazy people and plenty of slow-coaches, but a genuine"
1664"idler is a rarity. He is not a man who slouches about with his hands in"
1665"his pockets. On the contrary, his most startling characteristic is that"
1666"he is always intensely busy."
1667""
1668"It is impossible to enjoy idling thoroughly unless one has plenty of"
1669"work to do. There is no fun in doing nothing when you have nothing to"
1670"do. Wasting time is merely an occupation then, and a most exhausting"
1671"one. Idleness, like kisses, to be sweet must be stolen."
1672""
1673"Many years ago, when I was a young man, I was taken very ill--I never"
1674"could see myself that much was the matter with me, except that I had"
1675"a beastly cold. But I suppose it was something very serious, for the"
1676"doctor said that I ought to have come to him a month before, and that"
1677"if it (whatever it was) had gone on for another week he would not have"
1678"answered for the consequences. It is an extraordinary thing, but I"
1679"never knew a doctor called into any case yet but what it transpired"
1680"that another day's delay would have rendered cure hopeless. Our medical"
1681"guide, philosopher, and friend is like the hero in a melodrama--he"
1682"always comes upon the scene just, and only just, in the nick of time. It"
1683"is Providence, that is what it is."
1684""
1685"Well, as I was saying, I was very ill and was ordered to Buxton for a"
1686"month, with strict injunctions to do nothing whatever all the while"
1687"that I was there. \"Rest is what you require,\" said the doctor, \"perfect"
1688"rest.\""
1689""
1690"It seemed a delightful prospect. \"This man evidently understands my"
1691"complaint,\" said I, and I pictured to myself a glorious time--a four"
1692"weeks' _dolce far niente_ with a dash of illness in it. Not too much"
1693"illness, but just illness enough--just sufficient to give it the flavor"
1694"of suffering and make it poetical. I should get up late, sip chocolate,"
1695"and have my breakfast in slippers and a dressing-gown. I should lie out"
1696"in the garden in a hammock and read sentimental novels with a melancholy"
1697"ending, until the books should fall from my listless hand, and I should"
1698"recline there, dreamily gazing into the deep blue of the firmament,"
1699"watching the fleecy clouds floating like white-sailed ships across"
1700"its depths, and listening to the joyous song of the birds and the low"
1701"rustling of the trees. Or, on becoming too weak to go out of doors,"
1702"I should sit propped up with pillows at the open window of the"
1703"ground-floor front, and look wasted and interesting, so that all the"
1704"pretty girls would sigh as they passed by."
1705""
1706"And twice a day I should go down in a Bath chair to the Colonnade to"
1707"drink the waters. Oh, those waters! I knew nothing about them then,"
1708"and was rather taken with the idea. \"Drinking the waters\" sounded"
1709"fashionable and Queen Anne-fied, and I thought I should like them. But,"
1710"ugh! after the first three or four mornings! Sam Weller's description of"
1711"them as \"having a taste of warm flat-irons\" conveys only a faint idea of"
1712"their hideous nauseousness. If anything could make a sick man get well"
1713"quickly, it would be the knowledge that he must drink a glassful of them"
1714"every day until he was recovered. I drank them neat for six consecutive"
1715"days, and they nearly killed me; but after then I adopted the plan of"
1716"taking a stiff glass of brandy-and-water immediately on the top of them,"
1717"and found much relief thereby. I have been informed since, by various"
1718"eminent medical gentlemen, that the alcohol must have entirely"
1719"counteracted the effects of the chalybeate properties contained in the"
1720"water. I am glad I was lucky enough to hit upon the right thing."
1721;
1722
1723static void
1724init_buf (void *buf, size_t sz)
1725{
1726    unsigned char *p = buf;
1727    unsigned char *const end = (unsigned char*)buf + sz;
1728    size_t n;
1729
1730    while (p < end)
1731    {
1732        n = end - p;
1733        if (sizeof(on_being_idle) - 1 < n)
1734            n = sizeof(on_being_idle) - 1;
1735        memcpy(p, on_being_idle, n);
1736        p +=n;
1737    }
1738
1739    assert(p == end);
1740}
1741