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