1a74702c6SGeorge Wang/* Copyright (c) 2017 - 2022 LiteSpeed Technologies Inc.  See LICENSE. */
250aadb33SDmitri Tikhonov/*
350aadb33SDmitri Tikhonov * Write several things to HEADERS stream and check the results.  What
450aadb33SDmitri Tikhonov * varies is the amount of bytes that are written to stream every time.
550aadb33SDmitri Tikhonov * This will exercise buffering in frame writer and verify that contents
650aadb33SDmitri Tikhonov * are written out correctly no matter where frab writing leaves off
750aadb33SDmitri Tikhonov * and picks up.
850aadb33SDmitri Tikhonov */
950aadb33SDmitri Tikhonov
1050aadb33SDmitri Tikhonov#include <assert.h>
1150aadb33SDmitri Tikhonov#include <errno.h>
1250aadb33SDmitri Tikhonov#include <limits.h>
1350aadb33SDmitri Tikhonov#include <stdio.h>
1450aadb33SDmitri Tikhonov#include <stdlib.h>
1550aadb33SDmitri Tikhonov#include <string.h>
16461e84d8SAmol Deshpande#ifndef WIN32
1750aadb33SDmitri Tikhonov#include <unistd.h>
18461e84d8SAmol Deshpande#else
19461e84d8SAmol Deshpande#include <getopt.h>
20461e84d8SAmol Deshpande#endif
2150aadb33SDmitri Tikhonov#include <sys/queue.h>
2250aadb33SDmitri Tikhonov
2350aadb33SDmitri Tikhonov#include "lsquic.h"
24bea64822SDmitri Tikhonov#include "lshpack.h"
2550aadb33SDmitri Tikhonov#include "lsquic_logger.h"
2650aadb33SDmitri Tikhonov#include "lsquic_mm.h"
2750aadb33SDmitri Tikhonov#include "lsquic_frame_common.h"
2850aadb33SDmitri Tikhonov#include "lsquic_frame_writer.h"
2950aadb33SDmitri Tikhonov#include "lsquic_frame_reader.h"
3019f667fbSDmitri Tikhonov#if LSQUIC_CONN_STATS
3119f667fbSDmitri Tikhonov#include "lsquic_int_types.h"
32758aff32SDmitri Tikhonov#include "lsquic_hash.h"
3319f667fbSDmitri Tikhonov#include "lsquic_conn.h"
3419f667fbSDmitri Tikhonov#endif
3550aadb33SDmitri Tikhonov
3650aadb33SDmitri Tikhonov
3750aadb33SDmitri Tikhonovstruct lsquic_stream
3850aadb33SDmitri Tikhonov{
3950aadb33SDmitri Tikhonov    size_t          sm_write_off,
4050aadb33SDmitri Tikhonov                    sm_buf_sz;      /* Number of bytes allocated */
4150aadb33SDmitri Tikhonov    size_t          sm_max_write;
4250aadb33SDmitri Tikhonov    size_t          sm_read_off;
4350aadb33SDmitri Tikhonov    unsigned char  *sm_buf;
4450aadb33SDmitri Tikhonov};
4550aadb33SDmitri Tikhonov
4650aadb33SDmitri Tikhonov
4750aadb33SDmitri Tikhonovstatic struct lsquic_stream *
4850aadb33SDmitri Tikhonovstream_new (size_t max_write)
4950aadb33SDmitri Tikhonov{
5050aadb33SDmitri Tikhonov    struct lsquic_stream *stream = calloc(1, sizeof(*stream));
5150aadb33SDmitri Tikhonov    stream->sm_max_write = max_write;
5250aadb33SDmitri Tikhonov    return stream;
5350aadb33SDmitri Tikhonov}
5450aadb33SDmitri Tikhonov
5550aadb33SDmitri Tikhonov
5650aadb33SDmitri Tikhonovstatic void
5750aadb33SDmitri Tikhonovstream_destroy (struct lsquic_stream *stream)
5850aadb33SDmitri Tikhonov{
5950aadb33SDmitri Tikhonov    free(stream->sm_buf);
6050aadb33SDmitri Tikhonov    free(stream);
6150aadb33SDmitri Tikhonov}
6250aadb33SDmitri Tikhonov
6350aadb33SDmitri Tikhonov
6450aadb33SDmitri Tikhonov#define reset_output(max_) do {         \
6550aadb33SDmitri Tikhonov    output.sz = 0;                      \
6650aadb33SDmitri Tikhonov    if (max_)                           \
6750aadb33SDmitri Tikhonov        output.max = max_;              \
6850aadb33SDmitri Tikhonov    else                                \
6950aadb33SDmitri Tikhonov        output.max = sizeof(output.buf);\
7050aadb33SDmitri Tikhonov} while (0)
7150aadb33SDmitri Tikhonov
7250aadb33SDmitri Tikhonov
7350aadb33SDmitri Tikhonovstatic ssize_t
745392f7a3SLiteSpeed Techstream_write (struct lsquic_stream *stream, struct lsquic_reader *reader)
7550aadb33SDmitri Tikhonov{
765392f7a3SLiteSpeed Tech    size_t sz;
775392f7a3SLiteSpeed Tech
785392f7a3SLiteSpeed Tech    sz = reader->lsqr_size(reader->lsqr_ctx);
7950aadb33SDmitri Tikhonov    if (sz > stream->sm_max_write)
8050aadb33SDmitri Tikhonov        sz = stream->sm_max_write;
8150aadb33SDmitri Tikhonov    if (stream->sm_write_off + sz > stream->sm_buf_sz)
8250aadb33SDmitri Tikhonov    {
8350aadb33SDmitri Tikhonov        if (stream->sm_write_off + sz < stream->sm_buf_sz * 2)
8450aadb33SDmitri Tikhonov            stream->sm_buf_sz *= 2;
8550aadb33SDmitri Tikhonov        else
8650aadb33SDmitri Tikhonov            stream->sm_buf_sz = stream->sm_write_off + sz;
8750aadb33SDmitri Tikhonov        stream->sm_buf = realloc(stream->sm_buf, stream->sm_buf_sz);
8850aadb33SDmitri Tikhonov    }
8950aadb33SDmitri Tikhonov
905392f7a3SLiteSpeed Tech    sz = reader->lsqr_read(reader->lsqr_ctx,
915392f7a3SLiteSpeed Tech                                    stream->sm_buf + stream->sm_write_off, sz);
9250aadb33SDmitri Tikhonov    stream->sm_write_off += sz;
9350aadb33SDmitri Tikhonov
9450aadb33SDmitri Tikhonov    return sz;
9550aadb33SDmitri Tikhonov}
9650aadb33SDmitri Tikhonov
9750aadb33SDmitri Tikhonov
98fb3e20e0SDmitri Tikhonov#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,
9950aadb33SDmitri Tikhonov
10050aadb33SDmitri Tikhonov
10150aadb33SDmitri Tikhonovstatic void
10250aadb33SDmitri Tikhonovtest_chop (unsigned max_write_sz)
10350aadb33SDmitri Tikhonov{
10450aadb33SDmitri Tikhonov    struct lsquic_frame_writer *fw;
10550aadb33SDmitri Tikhonov    struct lsquic_stream *stream;
10650aadb33SDmitri Tikhonov    struct lsquic_mm mm;
107bea64822SDmitri Tikhonov    struct lshpack_enc henc;
10850aadb33SDmitri Tikhonov    int s;
10950aadb33SDmitri Tikhonov
11019f667fbSDmitri Tikhonov#if LSQUIC_CONN_STATS
11119f667fbSDmitri Tikhonov    struct conn_stats conn_stats;
11219f667fbSDmitri Tikhonov    memset(&conn_stats, 0, sizeof(conn_stats));
11319f667fbSDmitri Tikhonov#endif
11419f667fbSDmitri Tikhonov
11550aadb33SDmitri Tikhonov    lsquic_mm_init(&mm);
116bea64822SDmitri Tikhonov    lshpack_enc_init(&henc);
11750aadb33SDmitri Tikhonov    stream = stream_new(max_write_sz);
11850aadb33SDmitri Tikhonov
11919f667fbSDmitri Tikhonov    fw = lsquic_frame_writer_new(&mm, stream, 0, &henc, stream_write,
12019f667fbSDmitri Tikhonov#if LSQUIC_CONN_STATS
12119f667fbSDmitri Tikhonov                                 &conn_stats,
12219f667fbSDmitri Tikhonov#endif
12319f667fbSDmitri Tikhonov                                0);
12450aadb33SDmitri Tikhonov
12555613f44SDmitri Tikhonov    struct lsxpack_header header_arr[] =
12650aadb33SDmitri Tikhonov    {
12755613f44SDmitri Tikhonov        { XHDR(":status", "302") },
12850aadb33SDmitri Tikhonov    };
12950aadb33SDmitri Tikhonov
13050aadb33SDmitri Tikhonov    struct lsquic_http_headers headers = {
13150aadb33SDmitri Tikhonov        .count = 1,
13250aadb33SDmitri Tikhonov        .headers = header_arr,
13350aadb33SDmitri Tikhonov    };
13450aadb33SDmitri Tikhonov
13550aadb33SDmitri Tikhonov    s = lsquic_frame_writer_write_headers(fw, 12345, &headers, 0, 100);
13650aadb33SDmitri Tikhonov    assert(0 == s);
13750aadb33SDmitri Tikhonov
13850aadb33SDmitri Tikhonov    struct lsquic_http2_setting settings[] = { { 1, 2, }, { 3, 4, } };
13950aadb33SDmitri Tikhonov    s = lsquic_frame_writer_write_settings(fw, settings, 2);
14050aadb33SDmitri Tikhonov    assert(0 == s);
14150aadb33SDmitri Tikhonov
14250aadb33SDmitri Tikhonov    /* TODO: server must not send priority frames, add a check for that
14350aadb33SDmitri Tikhonov     * error condition.
14450aadb33SDmitri Tikhonov     */
14550aadb33SDmitri Tikhonov    s = lsquic_frame_writer_write_priority(fw, 3, 0, 1, 256);
14650aadb33SDmitri Tikhonov    assert(0 == s);
14750aadb33SDmitri Tikhonov
14850aadb33SDmitri Tikhonov    while (lsquic_frame_writer_have_leftovers(fw))
14950aadb33SDmitri Tikhonov    {
15050aadb33SDmitri Tikhonov        s = lsquic_frame_writer_flush(fw);
15150aadb33SDmitri Tikhonov        assert(0 == s);
15250aadb33SDmitri Tikhonov    }
15350aadb33SDmitri Tikhonov
15450aadb33SDmitri Tikhonov    const unsigned char expected_buf[] = {
15550aadb33SDmitri Tikhonov        /* Length: */       0x00, 0x00, 0x09,
15650aadb33SDmitri Tikhonov        /* Type: */         HTTP_FRAME_HEADERS,
15750aadb33SDmitri Tikhonov        /* Flags: */        HFHF_END_HEADERS|HFHF_PRIORITY,
15850aadb33SDmitri Tikhonov        /* Stream Id: */    0x00, 0x00, 0x30, 0x39,
15950aadb33SDmitri Tikhonov        /* Dep stream id: */0x00, 0x00, 0x00, 0x00,
16050aadb33SDmitri Tikhonov        /* Weight: */       100 - 1,
16150aadb33SDmitri Tikhonov        /* Block fragment: */
16250aadb33SDmitri Tikhonov                            0x48, 0x82, 0x64, 0x02,
16350aadb33SDmitri Tikhonov        /* Length: */       0x00, 0x00, 0x0C,
16450aadb33SDmitri Tikhonov        /* Type: */         HTTP_FRAME_SETTINGS,
16550aadb33SDmitri Tikhonov        /* Flags: */        0x00,
16650aadb33SDmitri Tikhonov        /* Stream Id: */    0x00, 0x00, 0x00, 0x00,
16750aadb33SDmitri Tikhonov        /* Payload: */      0x00, 0x01, 0x00, 0x00, 0x00, 0x02,
16850aadb33SDmitri Tikhonov                            0x00, 0x03, 0x00, 0x00, 0x00, 0x04,
16950aadb33SDmitri Tikhonov        /* Length: */       0x00, 0x00, 5,
17050aadb33SDmitri Tikhonov        /* Type: */         HTTP_FRAME_PRIORITY,
17150aadb33SDmitri Tikhonov        /* Flags: */        0x00,
17250aadb33SDmitri Tikhonov        /* Stream Id: */    0x00, 0x00, 0x00, 0x03,
17350aadb33SDmitri Tikhonov        /* Dep stream Id: */0x00, 0x00, 0x00, 0x01,
17450aadb33SDmitri Tikhonov        /* Weight: */       0xFF,
17550aadb33SDmitri Tikhonov    };
17650aadb33SDmitri Tikhonov
17750aadb33SDmitri Tikhonov    assert(stream->sm_write_off == sizeof(expected_buf));
17850aadb33SDmitri Tikhonov    assert(0 == memcmp(stream->sm_buf, expected_buf, sizeof(expected_buf)));
17950aadb33SDmitri Tikhonov
18050aadb33SDmitri Tikhonov    lsquic_frame_writer_destroy(fw);
18150aadb33SDmitri Tikhonov    stream_destroy(stream);
182bea64822SDmitri Tikhonov    lshpack_enc_cleanup(&henc);
18350aadb33SDmitri Tikhonov    lsquic_mm_cleanup(&mm);
18450aadb33SDmitri Tikhonov}
18550aadb33SDmitri Tikhonov
18650aadb33SDmitri Tikhonov
18750aadb33SDmitri Tikhonovint
18850aadb33SDmitri Tikhonovmain (int argc, char **argv)
18950aadb33SDmitri Tikhonov{
19050aadb33SDmitri Tikhonov    const unsigned write_sizes[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 20,
19150aadb33SDmitri Tikhonov                                     30, 100, 200, 255, 0xFFF, 0x1000, 0x100D,
19250aadb33SDmitri Tikhonov                                     UINT_MAX, };
19350aadb33SDmitri Tikhonov    unsigned i;
19450aadb33SDmitri Tikhonov    int opt, max_write_sz = -1;
19550aadb33SDmitri Tikhonov
19650aadb33SDmitri Tikhonov    while (-1 != (opt = getopt(argc, argv, "l:s:")))
19750aadb33SDmitri Tikhonov    {
19850aadb33SDmitri Tikhonov        switch (opt)
19950aadb33SDmitri Tikhonov        {
20050aadb33SDmitri Tikhonov        case 'l':
20150aadb33SDmitri Tikhonov            lsquic_log_to_fstream(stderr, LLTS_NONE);
20250aadb33SDmitri Tikhonov            lsquic_logger_lopt(optarg);
20350aadb33SDmitri Tikhonov            break;
20450aadb33SDmitri Tikhonov        case 's':
20550aadb33SDmitri Tikhonov            max_write_sz = atoi(optarg);
20650aadb33SDmitri Tikhonov            break;
20750aadb33SDmitri Tikhonov        default:
20850aadb33SDmitri Tikhonov            exit(1);
20950aadb33SDmitri Tikhonov        }
21050aadb33SDmitri Tikhonov    }
21150aadb33SDmitri Tikhonov
21250aadb33SDmitri Tikhonov    if (-1 == max_write_sz)
21350aadb33SDmitri Tikhonov        for (i = 0; i < sizeof(write_sizes) / sizeof(write_sizes[0]); ++i)
21450aadb33SDmitri Tikhonov            test_chop(write_sizes[i]);
21550aadb33SDmitri Tikhonov    else
21650aadb33SDmitri Tikhonov        test_chop(max_write_sz);
21750aadb33SDmitri Tikhonov
21850aadb33SDmitri Tikhonov    return 0;
21950aadb33SDmitri Tikhonov}
220