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