lsquic_hcso_writer.c revision 5392f7a3
1/* Copyright (c) 2017 - 2019 LiteSpeed Technologies Inc. See LICENSE. */ 2/* 3 * lsquic_hcso_writer.c - write to outgoing HTTP Control Stream 4 */ 5 6#include <assert.h> 7#include <errno.h> 8#include <stdlib.h> 9#include <string.h> 10#include <sys/queue.h> 11 12#include "lsquic.h" 13#include "lsquic_types.h" 14#include "lsquic_int_types.h" 15#include "lsquic_sfcw.h" 16#include "lsquic_varint.h" 17#include "lsquic_hq.h" 18#include "lsquic_hash.h" 19#include "lsquic_stream.h" 20#include "lsquic_frab_list.h" 21#include "lsquic_varint.h" 22#include "lsquic_byteswap.h" 23#include "lsquic_hcso_writer.h" 24 25#define LSQUIC_LOGGER_MODULE LSQLM_HCSO_WRITER 26#define LSQUIC_LOG_CONN_ID \ 27 lsquic_conn_log_cid(lsquic_stream_conn(writer->how_stream)) 28#include "lsquic_logger.h" 29 30 31static int 32hcso_write_type (struct hcso_writer *writer) 33{ 34 int s; 35 36#ifndef NDEBUG 37 if (writer->how_flags & HOW_RAND_VARINT) 38 { 39 s = rand() & 3; 40 LSQ_DEBUG("writing %d-byte stream type", 1 << s); 41 } 42 else 43#endif 44 s = 0; 45 46 switch (s) 47 { 48 case 0: 49 return lsquic_frab_list_write(&writer->how_fral, 50 (unsigned char []) { HQUST_CONTROL }, 1); 51 case 1: 52 return lsquic_frab_list_write(&writer->how_fral, 53 (unsigned char []) { 0x40, HQUST_CONTROL }, 2); 54 case 2: 55 return lsquic_frab_list_write(&writer->how_fral, 56 (unsigned char []) { 0x80, 0x00, 0x00, HQUST_CONTROL }, 4); 57 default: 58 return lsquic_frab_list_write(&writer->how_fral, 59 (unsigned char []) { 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 60 HQUST_CONTROL }, 8); 61 } 62} 63 64 65 66static lsquic_stream_ctx_t * 67hcso_on_new (void *stream_if_ctx, struct lsquic_stream *stream) 68{ 69 struct hcso_writer *writer = stream_if_ctx; 70 writer->how_stream = stream; 71 lsquic_frab_list_init(&writer->how_fral, 0x100, NULL, NULL, NULL); 72#ifndef NDEBUG 73 const char *env = getenv("LSQUIC_RND_VARINT_LEN"); 74 if (env && atoi(env)) 75 { 76 writer->how_flags |= HOW_RAND_VARINT; 77 LSQ_INFO("will randomize varints"); 78 if (0 == (rand() & 3)) 79 { 80 writer->how_flags |= HOW_CHOP_STREAM; 81 LSQ_INFO("will chop beginning of stream into tiny STREAM frames"); 82 } 83 } 84#endif 85 if (0 != hcso_write_type(writer)) 86 { 87 LSQ_INFO("cannot write to frab list"); 88 /* TODO: abort connection */ 89 } 90 LSQ_DEBUG("create HTTP Control Stream Writer"); 91 lsquic_stream_wantwrite(stream, 1); 92 return stream_if_ctx; 93} 94 95 96static unsigned 97hcso_setting_type2bits (struct hcso_writer *writer, unsigned setting) 98{ 99 unsigned bits = vint_val2bits(setting); 100 101#ifndef NDEBUG 102 unsigned max_bits; 103 if (writer->how_flags & HOW_RAND_VARINT) 104 { 105 max_bits = rand() & 3; 106 if (max_bits > bits) 107 bits = max_bits; 108 LSQ_DEBUG("writing out HTTP/3 setting %u as %d-byte varint", 109 setting, 1 << bits); 110 } 111#endif 112 113 return bits; 114} 115 116 117int 118lsquic_hcso_write_settings (struct hcso_writer *writer, 119 const struct lsquic_engine_settings *settings, 120 int is_server) 121{ 122 unsigned char *p; 123 unsigned bits; 124 int was_empty; 125#ifdef NDEBUG 126 const unsigned frame_size_len = 1; 127#else 128 /* Need to use two bytes for frame length, as randomization may require 129 * more than 63 bytes. 130 */ 131 const unsigned frame_size_len = 2; 132#endif 133 unsigned char buf[1 /* Frame type */ + /* Frame size */ frame_size_len 134 /* There are maximum four settings that need to be written out and 135 * each value can be encoded in maximum 8 bytes: 136 */ 137 + 4 * ( 138#ifdef NDEBUG 139 1 /* Each setting needs 1-byte varint number, */ 140#else 141 8 /* but it can be up to 8 bytes when randomized */ 142#endif 143 + 8) ]; 144 145 p = buf; 146 *p++ = HQFT_SETTINGS; 147 p += frame_size_len; 148 149 if (is_server) 150 if (settings->es_h3_placeholders != HQ_DF_NUM_PLACEHOLDERS) 151 { 152 /* Write out SETTINGS_NUM_PLACEHOLDERS */ 153 bits = hcso_setting_type2bits(writer, HQSID_NUM_PLACEHOLDERS); 154 vint_write(p, HQSID_NUM_PLACEHOLDERS, bits, 1 << bits); 155 p += 1 << bits; 156 bits = vint_val2bits(settings->es_h3_placeholders); 157 vint_write(p, settings->es_h3_placeholders, bits, 1 << bits); 158 p += 1 << bits; 159 } 160 161 if (settings->es_max_header_list_size != HQ_DF_MAX_HEADER_LIST_SIZE) 162 { 163 /* Write out SETTINGS_MAX_HEADER_LIST_SIZE */ 164 bits = hcso_setting_type2bits(writer, HQSID_MAX_HEADER_LIST_SIZE); 165 vint_write(p, HQSID_MAX_HEADER_LIST_SIZE, bits, 1 << bits); 166 p += 1 << bits; 167 bits = vint_val2bits(settings->es_max_header_list_size); 168 vint_write(p, settings->es_max_header_list_size, bits, 1 << bits); 169 p += 1 << bits; 170 } 171 172 if (settings->es_qpack_dec_max_size != HQ_DF_QPACK_MAX_TABLE_CAPACITY) 173 { 174 /* Write out SETTINGS_QPACK_MAX_TABLE_CAPACITY */ 175 bits = hcso_setting_type2bits(writer, HQSID_QPACK_MAX_TABLE_CAPACITY); 176 vint_write(p, HQSID_QPACK_MAX_TABLE_CAPACITY, bits, 1 << bits); 177 p += 1 << bits; 178 bits = vint_val2bits(settings->es_qpack_dec_max_size); 179 vint_write(p, settings->es_qpack_dec_max_size, bits, 1 << bits); 180 p += 1 << bits; 181 } 182 183 if (settings->es_qpack_dec_max_blocked != HQ_DF_QPACK_BLOCKED_STREAMS) 184 { 185 /* Write out SETTINGS_QPACK_BLOCKED_STREAMS */ 186 bits = hcso_setting_type2bits(writer, HQSID_QPACK_BLOCKED_STREAMS); 187 vint_write(p, HQSID_QPACK_BLOCKED_STREAMS, bits, 1 << bits); 188 p += 1 << bits; 189 bits = vint_val2bits(settings->es_qpack_dec_max_size); 190 vint_write(p, settings->es_qpack_dec_max_blocked, bits, 1 << bits); 191 p += 1 << bits; 192 } 193 194#ifdef NDEBUG 195 buf[1] = p - buf - 2; 196#else 197 vint_write(buf + 1, p - buf - 3, 1, 2); 198#endif 199 200 was_empty = lsquic_frab_list_empty(&writer->how_fral); 201 202 if (0 != lsquic_frab_list_write(&writer->how_fral, buf, p - buf)) 203 { 204 LSQ_INFO("cannot write SETTINGS frame to frab list"); 205 return -1; 206 } 207 208 if (was_empty) 209 lsquic_stream_wantwrite(writer->how_stream, 1); 210 211 LSQ_DEBUG("generated %u-byte SETTINGS frame", (unsigned) (p - buf)); 212 return 0; 213} 214 215 216static const char * 217hqft2str (enum hq_frame_type type) 218{ 219 switch (type) 220 { 221 case HQFT_PUSH_PROMISE: return "PUSH_PROMISE"; 222 case HQFT_MAX_PUSH_ID: return "MAX_PUSH_ID"; 223 case HQFT_CANCEL_PUSH: return "CANCEL_PUSH"; 224 case HQFT_GOAWAY: return "GOAWAY"; 225 default: return "<unknown>"; 226 } 227} 228 229 230int 231hcso_write_number_frame (struct hcso_writer *writer, 232 enum hq_frame_type type, uint64_t value) 233{ 234 unsigned char *p; 235 unsigned bits; 236 int was_empty; 237 unsigned char buf[1 /* Frame type */ + /* Frame size */ 1 + 8 /* Value */ ]; 238 239 p = buf; 240 *p++ = type; 241 242 bits = vint_val2bits(value); 243 *p++ = 1 << bits; 244 245 vint_write(p, value, bits, 1 << bits); 246 p += 1 << bits; 247 248 was_empty = lsquic_frab_list_empty(&writer->how_fral); 249 250 if (0 != lsquic_frab_list_write(&writer->how_fral, buf, p - buf)) 251 { 252 LSQ_INFO("cannot write %s frame to frab list", hqft2str(type)); 253 return -1; 254 } 255 256 if (was_empty) 257 lsquic_stream_wantwrite(writer->how_stream, 1); 258 259 LSQ_DEBUG("generated %u-byte %s frame", (unsigned) (p - buf), 260 hqft2str(type)); 261 return 0; 262} 263 264 265int 266lsquic_hcso_write_goaway (struct hcso_writer *writer, 267 lsquic_stream_id_t stream_id) 268{ 269 return hcso_write_number_frame(writer, HQFT_GOAWAY, stream_id); 270} 271 272 273int 274lsquic_hcso_write_max_push_id (struct hcso_writer *writer, uint64_t max_push_id) 275{ 276 return hcso_write_number_frame(writer, HQFT_MAX_PUSH_ID, max_push_id); 277} 278 279 280int 281lsquic_hcso_write_cancel_push (struct hcso_writer *writer, uint64_t push_id) 282{ 283 return hcso_write_number_frame(writer, HQFT_CANCEL_PUSH, push_id); 284} 285 286 287#ifndef NDEBUG 288#define MIN(a, b) ((a) < (b) ? (a) : (b)) 289static size_t 290one_byte_limit_read (void *ctx, void *buf, size_t bufsz) 291{ 292 return lsquic_frab_list_read(ctx, buf, MIN(bufsz, 1)); 293} 294 295 296static size_t 297one_byte_limit_size (void *ctx) 298{ 299 size_t size; 300 301 size = lsquic_frab_list_size(ctx); 302 return MIN(size, 1); 303} 304#endif 305 306static void 307hcso_on_write (struct lsquic_stream *stream, lsquic_stream_ctx_t *ctx) 308{ 309 struct hcso_writer *const writer = (void *) ctx; 310 struct lsquic_reader reader = { 311 .lsqr_read = lsquic_frab_list_read, 312 .lsqr_size = lsquic_frab_list_size, 313 .lsqr_ctx = &writer->how_fral 314 }; 315 ssize_t nw; 316 317#ifndef NDEBUG 318 if (stream->tosend_off < 8 && (writer->how_flags & HOW_CHOP_STREAM)) 319 { 320 reader.lsqr_read = one_byte_limit_read; 321 reader.lsqr_size = one_byte_limit_size; 322 } 323#endif 324 325 nw = lsquic_stream_writef(stream, &reader); 326 if (nw >= 0) 327 { 328 LSQ_DEBUG("wrote %zd bytes to stream", nw); 329 (void) lsquic_stream_flush(stream); 330 if (lsquic_frab_list_empty(&writer->how_fral)) 331 lsquic_stream_wantwrite(stream, 0); 332 } 333 else 334 { 335 /* TODO: abort connection */ 336 LSQ_WARN("cannot write to stream: %s", strerror(errno)); 337 lsquic_stream_wantwrite(stream, 0); 338 } 339} 340 341 342static void 343hcso_on_close (struct lsquic_stream *stream, lsquic_stream_ctx_t *ctx) 344{ 345 struct hcso_writer *writer = (void *) ctx; 346 LSQ_DEBUG("close HTTP Control Stream Writer"); 347 lsquic_frab_list_cleanup(&writer->how_fral); 348 writer->how_stream = NULL; 349} 350 351 352static void 353hcso_on_read (struct lsquic_stream *stream, lsquic_stream_ctx_t *ctx) 354{ 355 assert(0); 356} 357 358 359static const struct lsquic_stream_if hcso_if = 360{ 361 .on_new_stream = hcso_on_new, 362 .on_read = hcso_on_read, 363 .on_write = hcso_on_write, 364 .on_close = hcso_on_close, 365}; 366 367const struct lsquic_stream_if *const lsquic_hcso_writer_if = &hcso_if; 368