1/* Copyright (c) 2017 - 2022 LiteSpeed Technologies Inc. See LICENSE. */ 2#include <assert.h> 3#include <errno.h> 4#include <stdio.h> 5#include <stdlib.h> 6#include <string.h> 7#include <sys/queue.h> 8#ifndef WIN32 9#include <unistd.h> 10#endif 11 12#include "lsquic.h" 13 14#include "lsquic_int_types.h" 15#include "lsquic_packet_common.h" 16#include "lsquic_packet_in.h" 17#include "lsquic_conn_flow.h" 18#include "lsquic_sfcw.h" 19#include "lsquic_varint.h" 20#include "lsquic_hq.h" 21#include "lsquic_hash.h" 22#include "lsquic_conn.h" 23#include "lsquic_stream.h" 24#include "lsquic_types.h" 25#include "lsquic_rtt.h" 26#include "lsquic_conn_flow.h" 27#include "lsquic_conn_public.h" 28#include "lsquic_mm.h" 29#include "lsquic_min_heap.h" 30#include "lsquic_hpi.h" 31#include "lsquic_logger.h" 32 33 34/* 35 * DSL: 36 * 37 * S\d+:\d+:\d+ Create stream and insert it into list. The three numbers 38 * are stream ID, priority, and incremental boolean flag. 39 * Priority can be negative, which indicates a critical 40 * stream. Otherwise, the priorirty should be in the range 41 * [0, MAX_HTTP_PRIORITY]. The incremental flag is ignored 42 * for critical streams. 43 * 44 * F\d+ Use filter identified by the number. For "F" to take 45 * effect, it should be called before "I". 46 * 47 * I Initialize the iterator. 48 * 49 * D[hH] Call drop high (h) or drop non-high (H). 50 * 51 * N\d+ Call "next" and verify that the stream ID is this number. 52 * If "next" returns NULL, the test fails. 53 * 54 */ 55 56static const struct test_spec { 57 int lineno; 58 const char *prog; 59} test_specs[] = { 60 61 { __LINE__, 62 "S0:3:0;" 63 "I;" 64 "N0;" 65 }, 66 67 { __LINE__, 68 /* Insert non-incremental streams with same priority, check that 69 * they come back out in the order of stream IDs. 70 */ 71 "S1:3:0;" "S0:3:0;" "S2:3:0;" 72 "I;" 73 "N0;" "N1;" "N2;" 74 }, 75 76 { __LINE__, 77 /* Insert incremental streams with same priority, check that they 78 * come back out in the same order. 79 */ 80 "S1:3:1;" "S0:3:1;" "S2:3:1;" 81 "I;" 82 "N1;" "N0;" "N2;" 83 }, 84 85 { __LINE__, 86 /* Insert incremental streams with same priority, filter out out odd 87 * IDs, check that they come back out in the same order and without 88 * the odd stream ID" 89 */ 90 "S1:3:1;" "S0:3:1;" "S2:3:1;" 91 "F;" 92 "I;" 93 "N0;" "N2;" 94 }, 95 96 { __LINE__, 97 /* Insert incremental and non-incremental streams with same priority. 98 * Check that non-incrementals are returned first. 99 */ 100 "S1:3:1;" "S0:3:1;" "S2:3:1;" 101 "S6:3:0;" "S10:3:0;" "S3:3:0;" 102 "I;" 103 "N3;N6;N10;" 104 "N1;N0;N2;" 105 }, 106 107 { __LINE__, 108 /* Drop high with same priority: nothing should be dropped */ 109 "S1:3:1;" "S0:3:1;" "S2:3:1;" 110 "I;" 111 "Dh;" 112 "N1;" "N0;" "N2;" 113 }, 114 115 { __LINE__, 116 /* Drop non-high with same priority: nothing should be dropped */ 117 "S1:3:1;" "S0:3:1;" "S2:3:1;" 118 "I;" 119 "DH;" 120 "N1;" "N0;" "N2;" 121 }, 122 123 { __LINE__, 124 /* Drop high with same priority: drop non-incrementals */ 125 "S1:3:1;" "S0:3:1;" "S2:3:1;" 126 "S6:3:0;" "S10:3:0;" "S3:3:0;" 127 "I;" 128 "Dh;" 129 "N1;" "N0;" "N2;" 130 }, 131 132 { __LINE__, 133 /* Drop non-high with same priority: drop incrementals */ 134 "S1:3:1;" "S0:3:1;" "S2:3:1;" 135 "S6:3:0;" "S10:3:0;" "S3:3:0;" 136 "I;" 137 "DH;" 138 "N3;N6;N10;" 139 }, 140 141 { __LINE__, 142 /* Insert streams with different priorities */ 143 "S1:1:1;" "S2:2:1;" "S3:3:1;" 144 "S6:6:0;" "S5:5:0;" "S4:4:0;" 145 "I;" 146 "N1;N2;N3;N4;N5;N6;" 147 }, 148 149 { __LINE__, 150 /* Insert regular and critical streams */ 151 "S1:1:1;" "S2:2:1;" "S3333:-1:1;" 152 "S6:6:0;" "S2222:-1:0;" "S4:4:0;" 153 "I;" 154 "N3333;N2222;N1;N2;N4;N6;" 155 }, 156 157 { __LINE__, 158 /* Insert regular and critical streams; drop high */ 159 "S1:1:1;" "S2:2:1;" "S3333:-1:1;" 160 "S6:6:0;" "S2222:-1:0;" "S4:4:0;" 161 "I;" 162 "Dh;" 163 "N1;N2;N4;N6;" 164 }, 165 166 { __LINE__, 167 /* Insert regular and critical streams; drop non-high */ 168 "S1:1:1;" "S2:2:1;" "S3333:-1:1;" 169 "S6:6:0;" "S2222:-1:0;" "S4:4:0;" 170 "I;" 171 "DH;" 172 "N3333;N2222;" 173 }, 174 175 { __LINE__, 176 /* Insert streams with different priorities, non-incremental, 177 * several per bucket. 178 */ 179 "S1:1:0;" "S4:2:0;" "S3:2:0;" 180 "S6:1:0;" "S5:1:0;" "S2:2:0;" 181 "I;" 182 "N1;N5;N6;N2;N3;N4;" 183 }, 184 185}; 186 187/* Sharing the same HPI tests safety of reusing the same iterator object 188 * (no need to deinitialize it). 189 */ 190static struct http_prio_iter hpi; 191 192static struct lsquic_conn lconn = LSCONN_INITIALIZER_CIDLEN(lconn, 0); 193 194static struct lsquic_conn_public conn_pub = { .lconn = &lconn, }; 195 196 197static struct lsquic_stream * 198new_stream (lsquic_stream_id_t stream_id, int priority, int incr) 199{ 200 struct lsquic_stream *stream = calloc(1, sizeof(*stream)); 201 stream->id = stream_id; 202 if (priority >= 0) 203 { 204 stream->sm_priority = priority; 205 if (incr) 206 stream->sm_bflags |= SMBF_INCREMENTAL; 207 } 208 else 209 stream->sm_bflags |= SMBF_CRITICAL; 210 /* Critical streams are never incremental */ 211 return stream; 212} 213 214 215#define MAGIC 0x12312312U 216 217struct my_filter_ctx 218{ 219 unsigned magic; 220}; 221 222static int 223filter_out_odd_stream_ids (void *ctx, struct lsquic_stream *stream) 224{ 225 struct my_filter_ctx *fctx = ctx; 226 assert(fctx->magic == MAGIC); 227 return 0 == (stream->id & 1); 228} 229 230 231static int (*const filters[])(void *, struct lsquic_stream *) = { 232 filter_out_odd_stream_ids, 233}; 234 235/* Just pick one (as long as it's not next_prio_stream) */ 236#define next_field next_write_stream 237 238 239static void 240run_test (const struct test_spec *spec) 241{ 242 struct lsquic_streams_tailq streams; 243 struct lsquic_stream *stream; 244 lsquic_stream_id_t stream_id; 245 long incr, priority, tmp; 246 const char *pc; 247 char cmd; 248 char name[20]; 249 struct my_filter_ctx filter_ctx = { MAGIC }; 250 int (*filter_cb)(void *, struct lsquic_stream *) = NULL; 251 int first_called = 0; 252 struct lsquic_mm mm; 253 254 lsquic_mm_init(&mm); 255 conn_pub.mm = &mm; 256 TAILQ_INIT(&streams); 257 snprintf(name, sizeof(name), "line-%d", spec->lineno); 258 259 for (pc = spec->prog; *pc; ++pc) 260 { 261 cmd = *pc++; 262 switch (cmd) 263 { 264 case 'S': 265 stream_id = strtol(pc, (char **) &pc, 10); 266 assert(':' == *pc); 267 priority = strtol(pc + 1, (char **) &pc, 10); 268 assert(':' == *pc); 269 incr = strtol(pc + 1, (char **) &pc, 10); 270 stream = new_stream(stream_id, priority, incr); 271 TAILQ_INSERT_TAIL(&streams, stream, next_field); 272 break; 273 case 'I': 274 lsquic_hpi_init(&hpi, TAILQ_FIRST(&streams), 275 TAILQ_LAST(&streams, lsquic_streams_tailq), 276 (uintptr_t) &TAILQ_NEXT((lsquic_stream_t *) NULL, next_field), 277 &conn_pub, name, filter_cb, &filter_ctx); 278 break; 279 case 'N': 280 stream_id = strtol(pc, (char **) &pc, 10); 281 if (first_called) 282 stream = lsquic_hpi_next(&hpi); 283 else 284 { 285 stream = lsquic_hpi_first(&hpi); 286 first_called = 1; 287 } 288 assert(stream); 289 assert(stream->id == stream_id); 290 break; 291 case 'F': 292 tmp = strtol(pc, (char **) &pc, 10); 293 assert(tmp >= 0 294 && (size_t) tmp < sizeof(filters) / sizeof(filters[0])); 295 filter_cb = filters[tmp]; 296 break; 297 case 'D': 298 switch (*pc++) 299 { 300 case 'h': 301 lsquic_hpi_drop_high(&hpi); 302 break; 303 case 'H': 304 lsquic_hpi_drop_non_high(&hpi); 305 break; 306 default: 307 assert(0); 308 break; 309 } 310 break; 311 default: 312 assert(0); 313 } 314 assert(*pc == ';'); 315 } 316 lsquic_hpi_cleanup(&hpi); 317 318 while (stream = TAILQ_FIRST(&streams), stream != NULL) 319 { 320 TAILQ_REMOVE(&streams, stream, next_field); 321 free(stream); 322 } 323 lsquic_mm_cleanup(&mm); 324} 325 326 327int 328main (int argc, char **argv) 329{ 330 unsigned n; 331 332 lsquic_log_to_fstream(stderr, LLTS_NONE); 333 lsq_log_levels[LSQLM_HPI] = LSQ_LOG_DEBUG; 334 335 for (n = 0; n < sizeof(test_specs) / sizeof(test_specs[0]); ++n) 336 run_test(&test_specs[n]); 337 338#ifndef NDEBUG 339 lsquic_hpi_set_heap_test(LSQUIC_HPI_HEAP_TEST_STACK_OK); 340 for (n = 0; n < sizeof(test_specs) / sizeof(test_specs[0]); ++n) 341 run_test(&test_specs[n]); 342 343 lsquic_hpi_set_heap_test(LSQUIC_HPI_HEAP_TEST_4K_OK); 344 for (n = 0; n < sizeof(test_specs) / sizeof(test_specs[0]); ++n) 345 run_test(&test_specs[n]); 346 347 lsquic_hpi_set_heap_test(0); 348 for (n = 0; n < sizeof(test_specs) / sizeof(test_specs[0]); ++n) 349 run_test(&test_specs[n]); 350#endif 351 352 return 0; 353} 354