lsquic_qdec_hdl.c revision a5fa05f9
1/* Copyright (c) 2017 - 2020 LiteSpeed Technologies Inc. See LICENSE. */ 2/* 3 * lsquic_qdec_hdl.c -- QPACK decoder streams handler 4 */ 5 6#include <assert.h> 7#include <errno.h> 8#include <inttypes.h> 9#include <stdlib.h> 10#include <string.h> 11#include <sys/queue.h> 12 13#include "lsquic.h" 14#include "lsquic_types.h" 15#include "lsxpack_header.h" 16#include "lsquic_int_types.h" 17#include "lsquic_sfcw.h" 18#include "lsquic_varint.h" 19#include "lsquic_hq.h" 20#include "lsquic_hash.h" 21#include "lsquic_stream.h" 22#include "lsquic_frab_list.h" 23#include "lsqpack.h" 24#include "lsquic_http1x_if.h" 25#include "lsquic_qdec_hdl.h" 26#include "lsquic_mm.h" 27#include "lsquic_engine_public.h" 28#include "lsquic_headers.h" 29#include "lsquic_conn.h" 30 31#define LSQUIC_LOGGER_MODULE LSQLM_QDEC_HDL 32#define LSQUIC_LOG_CONN_ID lsquic_conn_log_cid(qdh->qdh_conn) 33#include "lsquic_logger.h" 34 35static void 36qdh_hblock_unblocked (void *); 37 38 39static int 40qdh_write_decoder (struct qpack_dec_hdl *qdh, const unsigned char *buf, 41 size_t sz) 42{ 43 ssize_t nw; 44 45 if (!(qdh->qdh_dec_sm_out && lsquic_frab_list_empty(&qdh->qdh_fral))) 46 { 47 write_to_frab: 48 if (0 == lsquic_frab_list_write(&qdh->qdh_fral, 49 (unsigned char *) buf, sz)) 50 { 51 LSQ_DEBUG("wrote %zu bytes to frab list", sz); 52 lsquic_stream_wantwrite(qdh->qdh_dec_sm_out, 1); 53 return 0; 54 } 55 else 56 { 57 LSQ_INFO("error writing to frab list"); 58 return -1; 59 } 60 } 61 62 nw = lsquic_stream_write(qdh->qdh_dec_sm_out, buf, sz); 63 if (nw < 0) 64 { 65 LSQ_INFO("error writing to outgoing QPACK decoder stream: %s", 66 strerror(errno)); 67 return -1; 68 } 69 LSQ_DEBUG("wrote %zd bytes to outgoing QPACK decoder stream", nw); 70 71 if ((size_t) nw == sz) 72 return 0; 73 74 buf = buf + nw; 75 sz -= (size_t) nw; 76 goto write_to_frab; 77} 78 79 80static int 81qdh_write_type (struct qpack_dec_hdl *qdh) 82{ 83 int s; 84 85#ifndef NDEBUG 86 const char *env = getenv("LSQUIC_RND_VARINT_LEN"); 87 if (env && atoi(env)) 88 { 89 s = rand() & 3; 90 LSQ_DEBUG("writing %d-byte stream type", 1 << s); 91 } 92 else 93#endif 94 s = 0; 95 96 switch (s) 97 { 98 case 0: 99 return qdh_write_decoder(qdh, 100 (unsigned char []) { HQUST_QPACK_DEC }, 1); 101 case 1: 102 return qdh_write_decoder(qdh, 103 (unsigned char []) { 0x40, HQUST_QPACK_DEC }, 2); 104 case 2: 105 return qdh_write_decoder(qdh, 106 (unsigned char []) { 0x80, 0x00, 0x00, HQUST_QPACK_DEC }, 4); 107 default: 108 return qdh_write_decoder(qdh, 109 (unsigned char []) { 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 110 HQUST_QPACK_DEC }, 8); 111 } 112} 113 114 115static void 116qdh_begin_out (struct qpack_dec_hdl *qdh) 117{ 118 if (0 != qdh_write_type(qdh)) 119 { 120 LSQ_WARN("%s: could not write to decoder", __func__); 121 qdh->qdh_conn->cn_if->ci_internal_error(qdh->qdh_conn, 122 "cannot write to decoder stream"); 123 } 124} 125 126 127int 128lsquic_qdh_init (struct qpack_dec_hdl *qdh, struct lsquic_conn *conn, 129 int is_server, const struct lsquic_engine_public *enpub, 130 unsigned dyn_table_size, unsigned max_risked_streams) 131{ 132 qdh->qdh_conn = conn; 133 lsquic_frab_list_init(&qdh->qdh_fral, 0x400, NULL, NULL, NULL); 134 lsqpack_dec_init(&qdh->qdh_decoder, (void *) conn, dyn_table_size, 135 max_risked_streams, qdh_hblock_unblocked); 136 qdh->qdh_flags |= QDH_INITIALIZED; 137 qdh->qdh_enpub = enpub; 138 if (qdh->qdh_enpub->enp_hsi_if == lsquic_http1x_if) 139 { 140 qdh->qdh_h1x_ctor_ctx = (struct http1x_ctor_ctx) { 141 .conn = conn, 142 .max_headers_sz = MAX_HTTP1X_HEADERS_SIZE, 143 .is_server = is_server, 144 }; 145 qdh->qdh_hsi_ctx = &qdh->qdh_h1x_ctor_ctx; 146 } 147 else 148 qdh->qdh_hsi_ctx = qdh->qdh_enpub->enp_hsi_ctx; 149 if (qdh->qdh_dec_sm_out) 150 qdh_begin_out(qdh); 151 if (qdh->qdh_enc_sm_in) 152 lsquic_stream_wantread(qdh->qdh_enc_sm_in, 1); 153 LSQ_DEBUG("initialized"); 154 return 0; 155} 156 157 158void 159lsquic_qdh_cleanup (struct qpack_dec_hdl *qdh) 160{ 161 if (qdh->qdh_flags & QDH_INITIALIZED) 162 { 163 LSQ_DEBUG("cleanup"); 164 lsqpack_dec_cleanup(&qdh->qdh_decoder); 165 lsquic_frab_list_cleanup(&qdh->qdh_fral); 166 qdh->qdh_flags &= ~QDH_INITIALIZED; 167 } 168} 169 170static lsquic_stream_ctx_t * 171qdh_out_on_new (void *stream_if_ctx, struct lsquic_stream *stream) 172{ 173 struct qpack_dec_hdl *const qdh = stream_if_ctx; 174 qdh->qdh_dec_sm_out = stream; 175 if (qdh->qdh_flags & QDH_INITIALIZED) 176 qdh_begin_out(qdh); 177 LSQ_DEBUG("initialized outgoing decoder stream"); 178 return (void *) qdh; 179} 180 181 182static void 183qdh_out_on_write (struct lsquic_stream *stream, lsquic_stream_ctx_t *ctx) 184{ 185 struct qpack_dec_hdl *const qdh = (void *) ctx; 186 struct lsquic_reader reader; 187 ssize_t nw; 188 unsigned char buf[LSQPACK_LONGEST_ICI]; 189 190 if (lsqpack_dec_ici_pending(&qdh->qdh_decoder)) 191 { 192 nw = lsqpack_dec_write_ici(&qdh->qdh_decoder, buf, sizeof(buf)); 193 if (nw > 0) 194 { 195 if (0 == qdh_write_decoder(qdh, buf, nw)) 196 LSQ_DEBUG("wrote %zd-byte TSS instruction", nw); 197 else 198 goto err; 199 } 200 else if (nw < 0) 201 { 202 LSQ_WARN("could not generate TSS instruction"); 203 goto err; 204 } 205 } 206 207 if (lsquic_frab_list_empty(&qdh->qdh_fral)) 208 { 209 LSQ_DEBUG("%s: nothing to write", __func__); 210 lsquic_stream_wantwrite(stream, 0); 211 return; 212 } 213 214 reader = (struct lsquic_reader) { 215 .lsqr_read = lsquic_frab_list_read, 216 .lsqr_size = lsquic_frab_list_size, 217 .lsqr_ctx = &qdh->qdh_fral, 218 }; 219 220 nw = lsquic_stream_writef(stream, &reader); 221 if (nw >= 0) 222 { 223 LSQ_DEBUG("wrote %zd bytes to stream", nw); 224 (void) lsquic_stream_flush(stream); 225 if (lsquic_frab_list_empty(&qdh->qdh_fral)) 226 { 227 lsquic_stream_wantwrite(stream, 0); 228 if (qdh->qdh_on_dec_sent_func) 229 { 230 LSQ_DEBUG("buffered data written: call callback"); 231 qdh->qdh_on_dec_sent_func(qdh->qdh_on_dec_sent_ctx); 232 qdh->qdh_on_dec_sent_func = NULL; 233 qdh->qdh_on_dec_sent_ctx = NULL; 234 } 235 } 236 } 237 else 238 { 239 LSQ_WARN("cannot write to stream: %s", strerror(errno)); 240 err: 241 lsquic_stream_wantwrite(stream, 0); 242 qdh->qdh_conn->cn_if->ci_internal_error(qdh->qdh_conn, 243 "cannot write to stream"); 244 } 245} 246 247 248static void 249qdh_out_on_close (struct lsquic_stream *stream, lsquic_stream_ctx_t *ctx) 250{ 251 struct qpack_dec_hdl *const qdh = (void *) ctx; 252 qdh->qdh_dec_sm_out = NULL; 253 LSQ_DEBUG("closed outgoing decoder stream"); 254} 255 256 257static void 258qdh_out_on_read (struct lsquic_stream *stream, lsquic_stream_ctx_t *ctx) 259{ 260 assert(0); 261} 262 263 264static const struct lsquic_stream_if qdh_dec_sm_out_if = 265{ 266 .on_new_stream = qdh_out_on_new, 267 .on_read = qdh_out_on_read, 268 .on_write = qdh_out_on_write, 269 .on_close = qdh_out_on_close, 270}; 271const struct lsquic_stream_if *const lsquic_qdh_dec_sm_out_if = 272 &qdh_dec_sm_out_if; 273 274 275static lsquic_stream_ctx_t * 276qdh_in_on_new (void *stream_if_ctx, struct lsquic_stream *stream) 277{ 278 struct qpack_dec_hdl *const qdh = stream_if_ctx; 279 qdh->qdh_enc_sm_in = stream; 280 if (qdh->qdh_flags & QDH_INITIALIZED) 281 lsquic_stream_wantread(qdh->qdh_enc_sm_in, 1); 282 LSQ_DEBUG("initialized incoming encoder stream"); 283 return (void *) qdh; 284} 285 286 287static size_t 288qdh_read_encoder_stream (void *ctx, const unsigned char *buf, size_t sz, 289 int fin) 290{ 291 struct qpack_dec_hdl *const qdh = (void *) ctx; 292 const struct lsqpack_dec_err *qerr; 293 int s; 294 295 if (fin) 296 { 297 LSQ_INFO("encoder stream is closed"); 298 qdh->qdh_conn->cn_if->ci_abort_error(qdh->qdh_conn, 1, 299 HEC_CLOSED_CRITICAL_STREAM, "Peer closed QPACK encoder stream"); 300 goto end; 301 } 302 303 s = lsqpack_dec_enc_in(&qdh->qdh_decoder, buf, sz); 304 if (s != 0) 305 { 306 LSQ_INFO("error reading encoder stream"); 307 qerr = lsqpack_dec_get_err_info(&qdh->qdh_decoder); 308 qdh->qdh_conn->cn_if->ci_abort_error(qdh->qdh_conn, 1, 309 HEC_QPACK_DECODER_STREAM_ERROR, "Error interpreting QPACK encoder " 310 "stream; offset %"PRIu64", line %d", qerr->off, qerr->line); 311 goto end; 312 } 313 if (qdh->qdh_dec_sm_out 314 && lsqpack_dec_ici_pending(&qdh->qdh_decoder)) 315 lsquic_stream_wantwrite(qdh->qdh_dec_sm_out, 1); 316 317 LSQ_DEBUG("successfully fed %zu bytes to QPACK decoder", sz); 318 319 end: 320 return sz; 321} 322 323 324static void 325qdh_in_on_read (struct lsquic_stream *stream, lsquic_stream_ctx_t *ctx) 326{ 327 struct qpack_dec_hdl *const qdh = (void *) ctx; 328 ssize_t nread; 329 330 nread = lsquic_stream_readf(stream, qdh_read_encoder_stream, qdh); 331 if (nread <= 0) 332 { 333 if (nread < 0) 334 { 335 LSQ_WARN("cannot read from encoder stream: %s", strerror(errno)); 336 qdh->qdh_conn->cn_if->ci_internal_error(qdh->qdh_conn, 337 "cannot read from encoder stream"); 338 } 339 else 340 { 341 LSQ_INFO("encoder stream closed by peer: abort connection"); 342 qdh->qdh_conn->cn_if->ci_abort_error(qdh->qdh_conn, 1, 343 HEC_CLOSED_CRITICAL_STREAM, "encoder stream closed"); 344 } 345 lsquic_stream_wantread(stream, 0); 346 } 347} 348 349 350static void 351qdh_in_on_close (struct lsquic_stream *stream, lsquic_stream_ctx_t *ctx) 352{ 353 struct qpack_dec_hdl *const qdh = (void *) ctx; 354 LSQ_DEBUG("closed incoming encoder stream"); 355 qdh->qdh_enc_sm_in = NULL; 356} 357 358 359static void 360qdh_in_on_write (struct lsquic_stream *stream, lsquic_stream_ctx_t *ctx) 361{ 362 assert(0); 363} 364 365 366static const struct lsquic_stream_if qdh_enc_sm_in_if = 367{ 368 .on_new_stream = qdh_in_on_new, 369 .on_read = qdh_in_on_read, 370 .on_write = qdh_in_on_write, 371 .on_close = qdh_in_on_close, 372}; 373const struct lsquic_stream_if *const lsquic_qdh_enc_sm_in_if = 374 &qdh_enc_sm_in_if; 375 376 377static void 378qdh_hblock_unblocked (void *stream_p) 379{ 380 struct lsquic_stream *const stream = stream_p; 381 struct qpack_dec_hdl *const qdh = lsquic_stream_get_qdh(stream); 382 383 LSQ_DEBUG("header block for stream %"PRIu64" unblocked", stream->id); 384 lsquic_stream_qdec_unblocked(stream); 385} 386 387 388struct cont_len 389{ 390 unsigned long long value; 391 int has; /* 1: set, 0: not set, -1: invalid */ 392}; 393 394 395static void 396process_content_length (const struct qpack_dec_hdl *qdh /* for logging */, 397 struct cont_len *cl, const char *val /* not NUL-terminated */, 398 unsigned len) 399{ 400 char *endcl, cont_len_buf[30]; 401 402 if (0 == cl->has) 403 { 404 if (len >= sizeof(cont_len_buf)) 405 { 406 LSQ_DEBUG("content-length has invalid value `%.*s'", 407 (int) len, val); 408 cl->has = -1; 409 return; 410 } 411 memcpy(cont_len_buf, val, len); 412 cont_len_buf[len] = '\0'; 413 cl->value = strtoull(cont_len_buf, &endcl, 10); 414 if (*endcl == '\0' && !(ULLONG_MAX == cl->value && ERANGE == errno)) 415 { 416 cl->has = 1; 417 LSQ_DEBUG("content length is %llu", cl->value); 418 } 419 else 420 { 421 cl->has = -1; 422 LSQ_DEBUG("content-length has invalid value `%.*s'", 423 (int) len, val); 424 } 425 } 426 else if (cl->has > 0) 427 { 428 LSQ_DEBUG("header set has two content-length: ambiguous, " 429 "turn off checking"); 430 cl->has = -1; 431 } 432} 433 434 435static int 436is_content_length (const struct lsqpack_header *header) 437{ 438 return ((header->qh_flags & QH_ID_SET) && header->qh_static_id == 4) 439 || (header->qh_name_len == 14 && header->qh_name[0] == 'c' 440 && 0 == memcmp(header->qh_name + 1, "ontent-length", 13)) 441 ; 442} 443 444 445static int 446qdh_supply_hset_to_stream (struct qpack_dec_hdl *qdh, 447 struct lsquic_stream *stream, struct lsqpack_header_list *qlist) 448{ 449 const struct lsquic_hset_if *const hset_if = qdh->qdh_enpub->enp_hsi_if; 450 struct uncompressed_headers *uh = NULL; 451 const struct lsqpack_header *header; 452 int st; 453 int push_promise; 454 unsigned i; 455 void *hset; 456 struct cont_len cl; 457 struct lsxpack_header *xhdr; 458 size_t extra; 459 460 push_promise = lsquic_stream_header_is_pp(stream); 461 hset = hset_if->hsi_create_header_set(qdh->qdh_hsi_ctx, push_promise); 462 if (!hset) 463 { 464 LSQ_INFO("call to hsi_create_header_set failed"); 465 return -1; 466 } 467 468 LSQ_DEBUG("got header set for stream %"PRIu64, stream->id); 469 470 cl.has = 0; 471 for (i = 0; i < qlist->qhl_count; ++i) 472 { 473 header = qlist->qhl_headers[i]; 474 LSQ_DEBUG("%.*s: %.*s", header->qh_name_len, header->qh_name, 475 header->qh_value_len, header->qh_value); 476 extra = header->qh_name_len + header->qh_value_len + 4; 477 xhdr = hset_if->hsi_prepare_decode(hset, NULL, extra); 478 if (!xhdr) 479 { 480 LSQ_DEBUG("prepare_decode(%zd) failed", extra); 481 goto err; 482 } 483 memcpy(xhdr->buf + xhdr->name_offset, header->qh_name, 484 header->qh_name_len); 485 xhdr->name_len = header->qh_name_len; 486 memcpy(xhdr->buf + xhdr->name_offset + xhdr->name_len, ": ", 2); 487 xhdr->val_offset = xhdr->name_offset + xhdr->name_len + 2; 488 memcpy(xhdr->buf + xhdr->val_offset, 489 header->qh_value, header->qh_value_len); 490 xhdr->val_len = header->qh_value_len; 491 memcpy(xhdr->buf + xhdr->name_offset + xhdr->name_len + 2 492 + xhdr->val_len, "\r\n", 2); 493 xhdr->dec_overhead = 4; 494 if (header->qh_flags & QH_ID_SET) 495 { 496 xhdr->flags |= LSXPACK_QPACK_IDX; 497 xhdr->qpack_index = header->qh_static_id; 498 } 499 st = hset_if->hsi_process_header(hset, xhdr); 500 if (st != 0) 501 { 502 LSQ_INFO("header process returned non-OK code %d", st); 503 goto err; 504 } 505 if (is_content_length(header)) 506 process_content_length(qdh, &cl, header->qh_value, 507 header->qh_value_len); 508 } 509 510 lsqpack_dec_destroy_header_list(qlist); 511 qlist = NULL; 512 st = hset_if->hsi_process_header(hset, NULL); 513 if (st != 0) 514 goto err; 515 516 uh = calloc(1, sizeof(*uh)); 517 if (!uh) 518 goto err; 519 uh->uh_stream_id = stream->id; 520 uh->uh_oth_stream_id = 0; 521 uh->uh_weight = 0; 522 uh->uh_exclusive = -1; 523 if (hset_if == lsquic_http1x_if) 524 uh->uh_flags |= UH_H1H; 525 uh->uh_hset = hset; 526 if (0 != lsquic_stream_uh_in(stream, uh)) 527 goto err; 528 LSQ_DEBUG("converted qlist to hset and gave it to stream %"PRIu64, 529 stream->id); 530 if (cl.has > 0) 531 (void) lsquic_stream_verify_len(stream, cl.value); 532 return 0; 533 534 err: 535 if (qlist) 536 lsqpack_dec_destroy_header_list(qlist); 537 hset_if->hsi_discard_header_set(hset); 538 free(uh); 539 return -1; 540} 541 542 543/* Releases qlist */ 544static int 545qdh_process_qlist (struct qpack_dec_hdl *qdh, 546 struct lsquic_stream *stream, struct lsqpack_header_list *qlist) 547{ 548 if (!lsquic_stream_header_is_trailer(stream)) 549 return qdh_supply_hset_to_stream(qdh, stream, qlist); 550 else 551 { 552 LSQ_DEBUG("discard trailer header set"); 553 lsqpack_dec_destroy_header_list(qlist); 554 return 0; 555 } 556} 557 558 559static enum lsqpack_read_header_status 560qdh_header_read_results (struct qpack_dec_hdl *qdh, 561 struct lsquic_stream *stream, enum lsqpack_read_header_status rhs, 562 struct lsqpack_header_list *qlist, const unsigned char *dec_buf, 563 size_t dec_buf_sz) 564{ 565 const struct lsqpack_dec_err *qerr; 566 567 if (rhs == LQRHS_DONE) 568 { 569 if (qlist) 570 { 571 if (0 != qdh_process_qlist(qdh, stream, qlist)) 572 return LQRHS_ERROR; 573 if (qdh->qdh_dec_sm_out) 574 { 575 if (dec_buf_sz 576 && 0 != qdh_write_decoder(qdh, dec_buf, dec_buf_sz)) 577 { 578 return LQRHS_ERROR; 579 } 580 if (dec_buf_sz || lsqpack_dec_ici_pending(&qdh->qdh_decoder)) 581 lsquic_stream_wantwrite(qdh->qdh_dec_sm_out, 1); 582 } 583 } 584 else 585 { 586 LSQ_WARN("read header status is DONE but header list is not set"); 587 assert(0); 588 return LQRHS_ERROR; 589 } 590 } 591 else if (rhs == LQRHS_ERROR) 592 { 593 qerr = lsqpack_dec_get_err_info(&qdh->qdh_decoder); 594 qdh->qdh_conn->cn_if->ci_abort_error(qdh->qdh_conn, 1, 595 HEC_QPACK_DECOMPRESSION_FAILED, "QPACK decompression error; " 596 "stream %"PRIu64", offset %"PRIu64", line %d", qerr->stream_id, 597 qerr->off, qerr->line); 598 } 599 600 return rhs; 601} 602 603 604enum lsqpack_read_header_status 605lsquic_qdh_header_in_begin (struct qpack_dec_hdl *qdh, 606 struct lsquic_stream *stream, uint64_t header_size, 607 const unsigned char **buf, size_t bufsz) 608{ 609 enum lsqpack_read_header_status rhs; 610 struct lsqpack_header_list *qlist; 611 size_t dec_buf_sz; 612 unsigned char dec_buf[LSQPACK_LONGEST_HEADER_ACK]; 613 614 if (qdh->qdh_flags & QDH_INITIALIZED) 615 { 616 dec_buf_sz = sizeof(dec_buf); 617 rhs = lsqpack_dec_header_in(&qdh->qdh_decoder, stream, stream->id, 618 header_size, buf, bufsz, &qlist, dec_buf, &dec_buf_sz); 619 return qdh_header_read_results(qdh, stream, rhs, qlist, dec_buf, 620 dec_buf_sz); 621 } 622 else 623 { 624 LSQ_WARN("not initialized: cannot process header block"); 625 return LQRHS_ERROR; 626 } 627 628} 629 630 631enum lsqpack_read_header_status 632lsquic_qdh_header_in_continue (struct qpack_dec_hdl *qdh, 633 struct lsquic_stream *stream, const unsigned char **buf, size_t bufsz) 634{ 635 enum lsqpack_read_header_status rhs; 636 struct lsqpack_header_list *qlist; 637 size_t dec_buf_sz; 638 unsigned char dec_buf[LSQPACK_LONGEST_HEADER_ACK]; 639 640 if (qdh->qdh_flags & QDH_INITIALIZED) 641 { 642 dec_buf_sz = sizeof(dec_buf); 643 rhs = lsqpack_dec_header_read(&qdh->qdh_decoder, stream, 644 buf, bufsz, &qlist, dec_buf, &dec_buf_sz); 645 return qdh_header_read_results(qdh, stream, rhs, qlist, dec_buf, 646 dec_buf_sz); 647 } 648 else 649 { 650 LSQ_WARN("not initialized: cannot process header block"); 651 return LQRHS_ERROR; 652 } 653} 654 655 656void 657lsquic_qdh_unref_stream (struct qpack_dec_hdl *qdh, 658 struct lsquic_stream *stream) 659{ 660 if (0 == lsqpack_dec_unref_stream(&qdh->qdh_decoder, stream)) 661 LSQ_DEBUG("unreffed stream %"PRIu64, stream->id); 662 else 663 LSQ_WARN("cannot unref stream %"PRIu64, stream->id); 664} 665 666 667void 668lsquic_qdh_cancel_stream (struct qpack_dec_hdl *qdh, 669 struct lsquic_stream *stream) 670{ 671 ssize_t nw; 672 unsigned char buf[LSQPACK_LONGEST_CANCEL]; 673 674 nw = lsqpack_dec_cancel_stream(&qdh->qdh_decoder, stream, buf, sizeof(buf)); 675 if (nw > 0) 676 { 677 if (0 == qdh_write_decoder(qdh, buf, nw)) 678 LSQ_DEBUG("cancelled stream %"PRIu64" and wrote %zd-byte Cancel " 679 "Stream instruction to the decoder stream", stream->id, nw); 680 } 681 else if (nw == 0) 682 LSQ_WARN("cannot cancel stream %"PRIu64" -- not found", stream->id); 683 else 684 { 685 LSQ_WARN("cannot cancel stream %"PRIu64" -- not enough buffer space " 686 "to encode Cancel Stream instructin", stream->id); 687 lsquic_qdh_unref_stream(qdh, stream); 688 } 689} 690 691 692int 693lsquic_qdh_arm_if_unsent (struct qpack_dec_hdl *qdh, void (*func)(void *), 694 void *ctx) 695{ 696 size_t bytes; 697 698 /* Use size of a single frab list buffer as an arbitrary threshold */ 699 bytes = lsquic_frab_list_size(&qdh->qdh_fral); 700 if (bytes <= qdh->qdh_fral.fl_buf_size) 701 return 0; 702 else 703 { 704 LSQ_DEBUG("have %zu bytes of unsent QPACK decoder stream data: set " 705 "up callback", bytes); 706 qdh->qdh_on_dec_sent_func = func; 707 qdh->qdh_on_dec_sent_ctx = ctx; 708 return 1; 709 } 710} 711