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