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