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