test_frame_chop.c revision 9a690580
1/* Copyright (c) 2017 - 2020 LiteSpeed Technologies Inc.  See LICENSE. */
2/*
3 * Write several things to HEADERS stream and check the results.  What
4 * varies is the amount of bytes that are written to stream every time.
5 * This will exercise buffering in frame writer and verify that contents
6 * are written out correctly no matter where frab writing leaves off
7 * and picks up.
8 */
9
10#include <assert.h>
11#include <errno.h>
12#include <limits.h>
13#include <stdio.h>
14#include <stdlib.h>
15#include <string.h>
16#ifndef WIN32
17#include <unistd.h>
18#else
19#include <getopt.h>
20#endif
21#include <sys/queue.h>
22
23#include "lsquic.h"
24#include "lshpack.h"
25#include "lsquic_logger.h"
26#include "lsquic_mm.h"
27#include "lsquic_frame_common.h"
28#include "lsquic_frame_writer.h"
29#include "lsquic_frame_reader.h"
30#if LSQUIC_CONN_STATS
31#include "lsquic_int_types.h"
32#include "lsquic_conn.h"
33#endif
34
35
36struct lsquic_stream
37{
38    size_t          sm_write_off,
39                    sm_buf_sz;      /* Number of bytes allocated */
40    size_t          sm_max_write;
41    size_t          sm_read_off;
42    unsigned char  *sm_buf;
43};
44
45
46static struct lsquic_stream *
47stream_new (size_t max_write)
48{
49    struct lsquic_stream *stream = calloc(1, sizeof(*stream));
50    stream->sm_max_write = max_write;
51    return stream;
52}
53
54
55static void
56stream_destroy (struct lsquic_stream *stream)
57{
58    free(stream->sm_buf);
59    free(stream);
60}
61
62
63#define reset_output(max_) do {         \
64    output.sz = 0;                      \
65    if (max_)                           \
66        output.max = max_;              \
67    else                                \
68        output.max = sizeof(output.buf);\
69} while (0)
70
71
72static ssize_t
73stream_write (struct lsquic_stream *stream, struct lsquic_reader *reader)
74{
75    size_t sz;
76
77    sz = reader->lsqr_size(reader->lsqr_ctx);
78    if (sz > stream->sm_max_write)
79        sz = stream->sm_max_write;
80    if (stream->sm_write_off + sz > stream->sm_buf_sz)
81    {
82        if (stream->sm_write_off + sz < stream->sm_buf_sz * 2)
83            stream->sm_buf_sz *= 2;
84        else
85            stream->sm_buf_sz = stream->sm_write_off + sz;
86        stream->sm_buf = realloc(stream->sm_buf, stream->sm_buf_sz);
87    }
88
89    sz = reader->lsqr_read(reader->lsqr_ctx,
90                                    stream->sm_buf + stream->sm_write_off, sz);
91    stream->sm_write_off += sz;
92
93    return sz;
94}
95
96
97#define XHDR(name_, value_) .buf = value_, .name_ptr = name_, .val_len = sizeof(value_) - 1, .name_len = sizeof(name_) - 1
98
99
100static void
101test_chop (unsigned max_write_sz)
102{
103    struct lsquic_frame_writer *fw;
104    struct lsquic_stream *stream;
105    struct lsquic_mm mm;
106    struct lshpack_enc henc;
107    int s;
108
109#if LSQUIC_CONN_STATS
110    struct conn_stats conn_stats;
111    memset(&conn_stats, 0, sizeof(conn_stats));
112#endif
113
114    lsquic_mm_init(&mm);
115    lshpack_enc_init(&henc);
116    stream = stream_new(max_write_sz);
117
118    fw = lsquic_frame_writer_new(&mm, stream, 0, &henc, stream_write,
119#if LSQUIC_CONN_STATS
120                                 &conn_stats,
121#endif
122                                0);
123
124    struct lsxpack_header header_arr[] =
125    {
126        { XHDR(":status", "302") },
127    };
128
129    struct lsquic_http_headers headers = {
130        .count = 1,
131        .headers = header_arr,
132    };
133
134    s = lsquic_frame_writer_write_headers(fw, 12345, &headers, 0, 100);
135    assert(0 == s);
136
137    struct lsquic_http2_setting settings[] = { { 1, 2, }, { 3, 4, } };
138    s = lsquic_frame_writer_write_settings(fw, settings, 2);
139    assert(0 == s);
140
141    /* TODO: server must not send priority frames, add a check for that
142     * error condition.
143     */
144    s = lsquic_frame_writer_write_priority(fw, 3, 0, 1, 256);
145    assert(0 == s);
146
147    while (lsquic_frame_writer_have_leftovers(fw))
148    {
149        s = lsquic_frame_writer_flush(fw);
150        assert(0 == s);
151    }
152
153    const unsigned char expected_buf[] = {
154        /* Length: */       0x00, 0x00, 0x09,
155        /* Type: */         HTTP_FRAME_HEADERS,
156        /* Flags: */        HFHF_END_HEADERS|HFHF_PRIORITY,
157        /* Stream Id: */    0x00, 0x00, 0x30, 0x39,
158        /* Dep stream id: */0x00, 0x00, 0x00, 0x00,
159        /* Weight: */       100 - 1,
160        /* Block fragment: */
161                            0x48, 0x82, 0x64, 0x02,
162        /* Length: */       0x00, 0x00, 0x0C,
163        /* Type: */         HTTP_FRAME_SETTINGS,
164        /* Flags: */        0x00,
165        /* Stream Id: */    0x00, 0x00, 0x00, 0x00,
166        /* Payload: */      0x00, 0x01, 0x00, 0x00, 0x00, 0x02,
167                            0x00, 0x03, 0x00, 0x00, 0x00, 0x04,
168        /* Length: */       0x00, 0x00, 5,
169        /* Type: */         HTTP_FRAME_PRIORITY,
170        /* Flags: */        0x00,
171        /* Stream Id: */    0x00, 0x00, 0x00, 0x03,
172        /* Dep stream Id: */0x00, 0x00, 0x00, 0x01,
173        /* Weight: */       0xFF,
174    };
175
176    assert(stream->sm_write_off == sizeof(expected_buf));
177    assert(0 == memcmp(stream->sm_buf, expected_buf, sizeof(expected_buf)));
178
179    lsquic_frame_writer_destroy(fw);
180    stream_destroy(stream);
181    lshpack_enc_cleanup(&henc);
182    lsquic_mm_cleanup(&mm);
183}
184
185
186int
187main (int argc, char **argv)
188{
189    const unsigned write_sizes[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 20,
190                                     30, 100, 200, 255, 0xFFF, 0x1000, 0x100D,
191                                     UINT_MAX, };
192    unsigned i;
193    int opt, max_write_sz = -1;
194
195    while (-1 != (opt = getopt(argc, argv, "l:s:")))
196    {
197        switch (opt)
198        {
199        case 'l':
200            lsquic_log_to_fstream(stderr, LLTS_NONE);
201            lsquic_logger_lopt(optarg);
202            break;
203        case 's':
204            max_write_sz = atoi(optarg);
205            break;
206        default:
207            exit(1);
208        }
209    }
210
211    if (-1 == max_write_sz)
212        for (i = 0; i < sizeof(write_sizes) / sizeof(write_sizes[0]); ++i)
213            test_chop(write_sizes[i]);
214    else
215        test_chop(max_write_sz);
216
217    return 0;
218}
219