h2_conn_streams.c (21819B)
1 /* SPDX-License-Identifier: LGPL-2.1-or-later OR (GPL-2.0-or-later WITH eCos-exception-2.0) */ 2 /* 3 This file is part of GNU libmicrohttpd. 4 Copyright (C) 2025 Evgeny Grin (Karlson2k) 5 6 GNU libmicrohttpd is free software; you can redistribute it and/or 7 modify it under the terms of the GNU Lesser General Public 8 License as published by the Free Software Foundation; either 9 version 2.1 of the License, or (at your option) any later version. 10 11 GNU libmicrohttpd is distributed in the hope that it will be useful, 12 but WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 Lesser General Public License for more details. 15 16 Alternatively, you can redistribute GNU libmicrohttpd and/or 17 modify it under the terms of the GNU General Public License as 18 published by the Free Software Foundation; either version 2 of 19 the License, or (at your option) any later version, together 20 with the eCos exception, as follows: 21 22 As a special exception, if other files instantiate templates or 23 use macros or inline functions from this file, or you compile this 24 file and link it with other works to produce a work based on this 25 file, this file does not by itself cause the resulting work to be 26 covered by the GNU General Public License. However the source code 27 for this file must still be made available in accordance with 28 section (3) of the GNU General Public License v2. 29 30 This exception does not invalidate any other reasons why a work 31 based on this file might be covered by the GNU General Public 32 License. 33 34 You should have received copies of the GNU Lesser General Public 35 License and the GNU General Public License along with this library; 36 if not, see <https://www.gnu.org/licenses/>. 37 */ 38 39 /** 40 * @file src/mhd2/h2/h2_conn_streams.c 41 * @brief Implementation of HTTP/2 connection streams processing functions 42 * @author Karlson2k (Evgeny Grin) 43 */ 44 45 #include "mhd_sys_options.h" 46 47 #include "sys_bool_type.h" 48 #include "sys_base_types.h" 49 50 #include "mhd_assert.h" 51 #include "mhd_unreachable.h" 52 53 #include "mhd_constexpr.h" 54 55 #include "compat_calloc.h" 56 #include "sys_malloc.h" 57 58 #include "mhd_dlinked_list.h" 59 60 #include "mhd_response.h" 61 #include "mhd_connection.h" 62 63 #include "mempool_funcs.h" 64 65 #include "response_destroy.h" 66 67 #include "h2_bit_masks.h" 68 #include "h2_err_codes.h" 69 70 #include "h2_stream_data.h" 71 #include "h2_proc_out.h" 72 #include "h2_proc_conn.h" 73 #include "h2_app_cb.h" 74 75 #include "h2_req_items_funcs.h" 76 #include "h2_req_fields.h" 77 #include "h2_reply_funcs.h" 78 79 #include "h2_conn_streams.h" 80 81 #include <string.h> 82 83 84 static MHD_FN_PAR_NONNULL_ALL_ struct mhd_H2Stream * 85 conn_add_new_stream (struct MHD_Connection *restrict c, 86 uint_least32_t stream_id) 87 { 88 struct mhd_H2Stream *s; 89 90 mhd_assert ((stream_id & mhd_H2_STREAM_ID_MASK) == stream_id); 91 mhd_assert (c->h2.rcv_cfg.max_concur_streams > c->h2.streams.num_streams); 92 93 s = (struct mhd_H2Stream *) mhd_calloc (1u, 94 sizeof(struct mhd_H2Stream)); 95 if (NULL == s) 96 return NULL; 97 98 s->is_h2 = true; 99 s->stream_id = stream_id; 100 101 #ifndef HAVE_NULL_PTR_ALL_ZEROS 102 mhd_DLINKEDL_INIT_LINKS (s, streams); 103 mhd_DLINKEDL_INIT_LINKS (s, send_q); 104 s->req.app_context = NULL; 105 #endif /* ! HAVE_NULL_PTR_ALL_ZEROS */ 106 107 s->c = c; 108 109 s->req.is_http2 = true; 110 s->req.stage = mhd_H2_REQ_STAGE_HEADERS_INCOMPLETE; 111 s->req.pos_method = mhd_H2_REQ_ITEM_POS_INVALID; 112 s->req.pos_path = mhd_H2_REQ_ITEM_POS_INVALID; 113 s->req.pos_authority = mhd_H2_REQ_ITEM_POS_INVALID; 114 115 s->state.recv_window = (int_least32_t) c->h2.rcv_cfg.stream_init_win_sz; 116 mhd_assert (0 < s->state.recv_window); 117 s->state.send_window = (int_least32_t) c->h2.peer.stream_init_win_sz; 118 mhd_assert (0 < s->state.send_window); 119 120 mhd_DLINKEDL_INS_LAST_D (&(c->h2.streams.active), s, streams); 121 mhd_assert (0u != ~(c->h2.streams.num_streams)); 122 ++(c->h2.streams.num_streams); 123 124 return s; 125 } 126 127 128 static MHD_FN_PAR_NONNULL_ALL_ void 129 conn_remove_stream (struct MHD_Connection *c, 130 struct mhd_H2Stream *restrict s) 131 { 132 mhd_assert (s->c == c); 133 134 if (NULL != s->rpl.response) 135 mhd_response_dec_use_count (s->rpl.response); 136 mhd_DLINKEDL_DEL_D (&(c->h2.streams.active), s, streams); 137 138 free (s); 139 mhd_assert (0u != c->h2.streams.num_streams); 140 --(c->h2.streams.num_streams); 141 } 142 143 144 static MHD_FN_PAR_NONNULL_ALL_ struct mhd_H2Stream * 145 conn_find_stream (struct MHD_Connection *restrict c, 146 uint_least32_t stream_id) 147 { 148 struct mhd_H2Stream *s; 149 150 // TODO: improve search. Binary tree or linear array? 151 for (s = mhd_DLINKEDL_GET_FIRST_D (&(c->h2.streams.active)); 152 NULL != s; 153 s = mhd_DLINKEDL_GET_NEXT (s, streams)) 154 { 155 if (stream_id == s->stream_id) 156 return s; 157 } 158 159 return NULL; 160 } 161 162 163 static void 164 stream_start_replying (struct mhd_H2Stream *restrict s) 165 MHD_FN_PAR_NONNULL_ALL_; 166 167 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool 168 mhd_h2_stream_req_problem (struct mhd_H2Stream *restrict s, 169 enum mhd_H2RequestProblemType problem_type) 170 { 171 // TODO: send error reply 172 (void) problem_type; 173 return mhd_h2_stream_abort (s, 174 mhd_H2_ERR_PROTOCOL_ERROR); // TODO: use correct error code 175 } 176 177 178 static MHD_FN_PAR_NONNULL_ALL_ bool 179 stream_send_rst_stream (struct mhd_H2Stream *restrict s, 180 enum mhd_H2ErrorCode err) 181 { 182 mhd_assert ((! s->state.sent_rst_stream) && 183 "RST_STREAM must not be sent more than once for active stream"); 184 185 if (! mhd_h2_q_rst_stream (s->c, 186 s->stream_id, 187 err)) 188 return false; 189 190 s->state.sent_rst_stream = true; 191 s->state.mhd_err = err; 192 193 return true; 194 } 195 196 197 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool 198 mhd_h2_stream_abort (struct mhd_H2Stream *restrict s, 199 enum mhd_H2ErrorCode err) 200 { 201 s->req.stage = mhd_H2_REQ_STAGE_BROKEN; 202 // TODO: Handle correctly by RST_STREAM 203 mhd_h2_conn_finish (s->c, 204 err, 205 true); 206 return false; 207 } 208 209 210 static MHD_FN_PAR_NONNULL_ALL_ bool 211 stream_proc_complete_headers (struct mhd_H2Stream *restrict s) 212 { 213 mhd_assert (mhd_H2_REQ_STAGE_HEADERS_INCOMPLETE == s->req.stage); 214 mhd_assert (0u == s->c->h2.state.continuation_stream_id); 215 mhd_assert (mhd_h2_items_debug_get_streamid (s->c->h2.mem.req_ib) 216 == s->stream_id); 217 218 s->req.stage = mhd_H2_REQ_STAGE_HEADERS_DECODING; 219 s->req.cntn_size = (s->req.got_end_stream ? 0u : MHD_SIZE_UNKNOWN); 220 221 if (! mhd_h2_req_headers_preprocess (s)) 222 return false; 223 224 if (! mhd_h2_stream_cb_early_uri (s)) 225 return false; 226 227 if (! mhd_h2_req_uri_parse (s)) 228 return false; 229 230 if (! mhd_h2_req_cookie_parse (s)) 231 return false; 232 233 s->req.stage = mhd_H2_REQ_STAGE_HEADERS_PROCESSING; 234 235 if (! mhd_h2_stream_cb_request (s)) 236 return false; 237 238 mhd_assert (mhd_H2_REQ_STAGE_BROKEN != s->req.stage); 239 240 if (s->req.got_end_stream) 241 { 242 s->req.stage = mhd_H2_REQ_STAGE_END_STREAM; 243 mhd_assert (NULL != s->rpl.response); 244 } 245 else 246 s->req.stage = mhd_H2_REQ_STAGE_HEADERS_COMPLETE; 247 248 if (NULL != s->rpl.response) 249 stream_start_replying (s); 250 251 return true; 252 } 253 254 255 static MHD_FN_PAR_NONNULL_ALL_ bool 256 stream_proc_in_headers (struct mhd_H2Stream *restrict s, 257 bool end_headers, 258 struct mhd_Buffer *restrict payload) 259 { 260 struct MHD_Connection *const c = s->c; 261 size_t unprocessed; 262 263 switch (mhd_h2_req_fields_decode (&(c->h2.hk_dec), 264 payload, 265 false, 266 c->h2.mem.req_ib, 267 &unprocessed)) 268 { 269 case mhd_H2_DEC_FIELDS_OK: 270 break; 271 case mhd_H2_DEC_FIELDS_NO_SPACE: 272 // TODO: Send error response before closing, use RST_STREAM 273 case mhd_H2_DEC_FIELDS_INT_ERR: 274 return mhd_h2_stream_req_problem (s, 275 mhd_H2_REQ_PRBLM_INT_ERROR); 276 case mhd_H2_DEC_FIELDS_BROKEN_DATA: 277 s->req.stage = mhd_H2_REQ_STAGE_BROKEN; 278 mhd_h2_conn_finish (c, 279 mhd_H2_ERR_COMPRESSION_ERROR, 280 true); 281 return false; 282 case mhd_H2_DEC_FIELDS_PROT_ABUSE: 283 s->req.stage = mhd_H2_REQ_STAGE_BROKEN; 284 mhd_h2_conn_finish (c, 285 mhd_H2_ERR_ENHANCE_YOUR_CALM, 286 true); 287 return false; 288 default: 289 mhd_UNREACHABLE (); 290 s->req.stage = mhd_H2_REQ_STAGE_BROKEN; 291 mhd_h2_conn_finish (c, 292 mhd_H2_ERR_INTERNAL_ERROR, 293 true); 294 return false; 295 } 296 297 if (end_headers && (0u != unprocessed)) 298 return ! mhd_h2_conn_finish (c, 299 mhd_H2_ERR_COMPRESSION_ERROR, 300 true); 301 302 if (! end_headers) 303 { 304 if (0u != unprocessed) 305 { 306 const size_t payload_offset = (size_t) (payload->data - c->read_buffer); 307 308 /* Unprocessed part may contain only a single field line. 309 Stop stream if it is larger than 3/4 of the max headers size. */ 310 if ((c->h2.rcv_cfg.max_header_list - c->h2.rcv_cfg.max_header_list / 4) <= 311 unprocessed) 312 return mhd_h2_stream_req_problem (s, 313 mhd_H2_REQ_PRBLM_HEADERS_TOO_LARGE); 314 315 mhd_assert (c->h2.rcv_cfg.max_frame_size < mhd_pool_get_size (c->pool)); 316 if (((mhd_pool_get_size (c->pool) - c->h2.rcv_cfg.max_frame_size) / 2) <= 317 unprocessed) 318 return mhd_h2_stream_req_problem (s, 319 mhd_H2_REQ_PRBLM_HEADERS_TOO_LARGE); 320 321 c->h2.buff.unproc_hdrs_pos = payload_offset + payload->size - unprocessed; 322 c->h2.buff.unproc_hdrs_size = unprocessed; 323 } 324 325 c->h2.state.continuation_stream_id = s->stream_id; 326 } 327 else 328 { 329 stream_proc_complete_headers (s); 330 } 331 332 return true; 333 } 334 335 336 static MHD_FN_PAR_NONNULL_ALL_ bool 337 conn_proc_new_in_stream (struct MHD_Connection *restrict c, 338 uint_least32_t stream_id, 339 bool end_stream, 340 bool end_headers, 341 struct mhd_Buffer *restrict payload) 342 { 343 struct mhd_H2Stream *s; 344 345 mhd_assert ((stream_id & mhd_H2_STREAM_ID_MASK) == stream_id); 346 mhd_assert (c->h2.state.top_seen_stream_id >= c->h2.state.top_proc_stream_id); 347 mhd_assert (c->h2.state.top_seen_stream_id < stream_id); 348 mhd_assert (0u == c->h2.state.continuation_stream_id); 349 mhd_assert (NULL == conn_find_stream (c, stream_id)); 350 mhd_assert (0u == c->h2.buff.unproc_hdrs_size); 351 mhd_assert (c->read_buffer <= payload->data); 352 mhd_assert ((c->read_buffer_size + c->read_buffer) >= 353 (payload->data + payload->size)); 354 355 if (c->h2.streams.num_streams >= c->h2.rcv_cfg.max_concur_streams) 356 return mhd_h2_q_rst_stream (c, 357 stream_id, 358 mhd_H2_ERR_REFUSED_STREAM); 359 360 s = conn_add_new_stream (c, 361 stream_id); 362 363 if (NULL == s) 364 return mhd_h2_q_rst_stream (c, /* REFUSED_STREAM indicates that stream has not been processed at all */ 365 stream_id, 366 mhd_H2_ERR_REFUSED_STREAM); 367 mhd_h2_items_block_reset (c->h2.mem.req_ib); 368 369 370 mhd_h2_items_debug_set_streamid (c->h2.mem.req_ib, 371 stream_id); 372 373 s->req.got_end_stream = end_stream; 374 375 /* The next call process frame data. Current function must not return 376 'false' (unless the connection is broken) beyond this point as the 377 connection data (HPACK) has been modified . */ 378 return stream_proc_in_headers (s, 379 end_headers, 380 payload); 381 } 382 383 384 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool 385 mhd_h2_conn_streamid_in_headers (struct MHD_Connection *restrict c, 386 uint_least32_t stream_id, 387 bool end_stream, 388 bool end_headers, 389 struct mhd_Buffer *restrict payload) 390 { 391 mhd_assert (0u != stream_id); 392 mhd_assert ((stream_id & mhd_H2_STREAM_ID_MASK) == stream_id); 393 mhd_assert (c->h2.state.top_seen_stream_id >= c->h2.state.top_proc_stream_id); 394 395 if (0u == (stream_id & 1u)) 396 return mhd_h2_conn_finish (c, 397 mhd_H2_ERR_PROTOCOL_ERROR, 398 false); 399 400 if (c->h2.state.top_seen_stream_id < stream_id) 401 return conn_proc_new_in_stream (c, 402 stream_id, 403 end_stream, 404 end_headers, 405 payload); 406 } 407 408 409 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool 410 mhd_h2_conn_streamid_in_continuation (struct MHD_Connection *restrict c, 411 uint_least32_t stream_id, 412 bool end_headers, 413 struct mhd_Buffer *payload) 414 { 415 struct mhd_Buffer combined_payload; 416 struct mhd_H2Stream *s; 417 418 mhd_assert (0u != stream_id); 419 mhd_assert ((stream_id & mhd_H2_STREAM_ID_MASK) == stream_id); 420 mhd_assert (c->h2.state.top_seen_stream_id >= c->h2.state.top_proc_stream_id); 421 422 if (c->h2.state.continuation_stream_id != stream_id) 423 return mhd_h2_conn_finish (c, 424 mhd_H2_ERR_PROTOCOL_ERROR, 425 false); 426 s = conn_find_stream (c, 427 stream_id); 428 mhd_assert (NULL != s); 429 430 if (0u == c->h2.buff.unproc_hdrs_size) 431 combined_payload = *payload; 432 else 433 { 434 /* Concatenate previously unprocessed part and the new part. 435 This will break CONTINUATION frame header, but the frame header is not 436 needed as all data from the header has been decoded. 437 However, unless connection is broken, 'false' must not be 438 returned by this function beyond this point as the same frame 439 cannot be decoded again. */ 440 memmove (payload->data - c->h2.buff.unproc_hdrs_size, 441 c->read_buffer + c->h2.buff.unproc_hdrs_pos, 442 c->h2.buff.unproc_hdrs_size); 443 combined_payload.data = payload->data - c->h2.buff.unproc_hdrs_size; 444 combined_payload.size = c->h2.buff.unproc_hdrs_size + payload->size; 445 } 446 447 return stream_proc_in_headers (s, 448 end_headers, 449 &combined_payload); 450 } 451 452 453 static MHD_FN_PAR_NONNULL_ALL_ void 454 stream_set_reply_props (struct mhd_H2Stream *restrict s) 455 { 456 mhd_assert (mhd_H2_RPL_STAGE_HEADERS_INCOMPLETE == s->rpl.stage); 457 mhd_assert ((mhd_H2_REQ_STAGE_END_STREAM == s->req.stage) || 458 (mhd_H2_REQ_STAGE_HEADERS_COMPLETE == s->req.stage)); 459 mhd_assert (NULL != s->rpl.response); 460 #if 0 // TODO: implement chained replies 461 if (199 >= s->rpl.response->sc) 462 { 463 s->rpl.fields.auto_cntn_len = false; 464 s->rpl.send_content = false; 465 s->rpl.chained = true; 466 } 467 else 468 #endif 469 if (MHD_HTTP_STATUS_NO_CONTENT == s->rpl.response->sc) 470 { 471 s->rpl.fields.auto_cntn_len = false; 472 s->rpl.send_content = false; 473 } 474 else if ((mhd_HTTP_METHOD_HEAD == s->req.method) || 475 (MHD_HTTP_STATUS_NOT_MODIFIED == s->rpl.response->sc)) 476 { 477 s->rpl.fields.auto_cntn_len = 478 (MHD_NO == s->rpl.response->cfg.head_only) 479 && (MHD_SIZE_UNKNOWN != s->rpl.response->cntn_size); 480 s->rpl.send_content = false; 481 } 482 else 483 { 484 s->rpl.fields.auto_cntn_len = (MHD_SIZE_UNKNOWN != s->rpl.response-> 485 cntn_size); 486 s->rpl.send_content = (0u != s->rpl.response->cntn_size); 487 } 488 s->rpl.cntn_read_pos = 0u; 489 } 490 491 492 static MHD_FN_PAR_NONNULL_ALL_ void 493 stream_start_replying (struct mhd_H2Stream *restrict s) 494 { 495 struct MHD_Connection *c = s->c; 496 497 /* The stream must not be in the sending queue */ 498 mhd_assert (NULL == mhd_DLINKEDL_GET_NEXT (s, send_q)); 499 mhd_assert (s != mhd_DLINKEDL_GET_LAST_D (&(c->h2.streams.send_q))); 500 mhd_assert (mhd_H2_RPL_STAGE_HEADERS_INCOMPLETE == s->rpl.stage); 501 mhd_assert (NULL != s->rpl.response); 502 503 stream_set_reply_props (s); 504 505 mhd_DLINKEDL_INS_LAST (&(c->h2.streams), s, send_q); 506 } 507 508 509 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool 510 mhd_h2_conn_streamid_in_rst_stream (struct MHD_Connection *restrict c, 511 uint_least32_t stream_id, 512 enum mhd_H2ErrorCode err) 513 { 514 515 } 516 517 518 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool 519 mhd_h2_conn_streamid_window_incr (struct MHD_Connection *restrict c, 520 uint_least32_t stream_id, 521 uint_least32_t incr) 522 { 523 struct mhd_H2Stream *s = conn_find_stream (c, 524 stream_id); 525 if (NULL == s) 526 { 527 if ((0u == (stream_id & 1u)) || 528 (c->h2.state.top_rst_stream_id < stream_id)) 529 return mhd_h2_conn_finish (c, 530 mhd_H2_ERR_PROTOCOL_ERROR, 531 false); 532 533 return true; /* Just ignore the frame */ 534 } 535 if ((0 < s->state.send_window) 536 && (0 > s->state.send_window + (int_least32_t) stream_id)) 537 return mhd_h2_stream_req_problem (s, 538 mhd_H2_REQ_PRBLM_FLOW_CONTROL); 539 s->state.send_window += (int_least32_t) stream_id; 540 return true; 541 } 542 543 544 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool 545 mhd_h2_conn_streamid_abort (struct MHD_Connection *restrict c, 546 uint_least32_t stream_id, 547 enum mhd_H2ErrorCode err) 548 { 549 550 } 551 552 553 static MHD_FN_PAR_NONNULL_ALL_ bool 554 stream_maintain_rcv_window (struct mhd_H2Stream *restrict s) 555 { 556 struct MHD_Connection *restrict c = s->c; 557 558 mhd_assert (0 <= s->state.recv_window); 559 /* Dumb algorithm: if receive windows is less than three quarters of the full 560 * window size, then bump to the full size. */ 561 562 if ((c->h2.rcv_cfg.stream_init_win_sz - c->h2.rcv_cfg.stream_init_win_sz / 4) 563 <= (uint_least32_t) s->state.recv_window) 564 { 565 uint_least32_t incr = 566 (uint_least32_t) 567 (c->h2.rcv_cfg.stream_init_win_sz 568 - (uint_least32_t) s->state.recv_window); 569 mhd_assert (0x7FFFFFFFu >= incr); 570 if (! mhd_h2_q_window_update (c, 571 s->stream_id, 572 incr)) 573 return false; 574 575 s->state.recv_window = (int_least32_t) c->h2.rcv_cfg.stream_init_win_sz; 576 } 577 578 return true; 579 } 580 581 582 mhd_static_inline MHD_FN_PAR_NONNULL_ALL_ bool 583 stream_is_closed (const struct mhd_H2Stream *restrict s) 584 { 585 /* If END_STREAM flag has been both send and received then stream is closed */ 586 if ((mhd_H2_REQ_STAGE_END_STREAM == s->req.stage) 587 && (mhd_H2_RPL_STAGE_END_STREAM == s->rpl.stage)) 588 return true; 589 590 if ((s->state.rcvd_rst_stream) || (s->state.sent_rst_stream)) 591 return true; 592 593 return false; 594 } 595 596 597 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool 598 mhd_h2_conn_maintain_streams_all (struct MHD_Connection *c) 599 { 600 struct mhd_H2Stream *next; 601 struct mhd_H2Stream *s; 602 mhd_assert ((! c->h2.state.sent_goaway.occurred) || 603 (mhd_H2_ERR_NO_ERROR == c->h2.state.sent_goaway.code)); 604 605 next = mhd_DLINKEDL_GET_FIRST_D (&(c->h2.streams.active)); 606 while (NULL != (s = next)) 607 { 608 next = mhd_DLINKEDL_GET_NEXT (s, streams); 609 610 /* Send RST_STREAM is needed */ 611 if ((! s->state.rcvd_rst_stream) 612 && (! s->state.sent_rst_stream)) 613 { 614 if ((mhd_H2_REQ_STAGE_BROKEN == s->req.stage) || 615 (mhd_H2_RPL_STAGE_BROKEN == s->rpl.stage)) 616 { 617 enum mhd_H2ErrorCode err; 618 err = s->state.mhd_err; 619 if (mhd_H2_ERR_NO_ERROR == err) 620 err = mhd_H2_ERR_INTERNAL_ERROR; 621 if (! stream_send_rst_stream (s, 622 err)) 623 return false; 624 } 625 } 626 627 /* Close and remove stream if it is finished */ 628 if (stream_is_closed (s)) 629 { 630 conn_remove_stream (c, 631 s); 632 continue; 633 } 634 635 if (mhd_H2_REQ_STAGE_END_STREAM > s->req.stage) 636 { 637 if (! stream_maintain_rcv_window (s)) 638 return false; 639 } 640 } 641 642 return true; 643 } 644 645 646 mhd_constexpr size_t min_usable_buff = 32u; 647 648 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool 649 mhd_h2_conn_process_streams_sending_queue (struct MHD_Connection *c) 650 { 651 struct mhd_H2Stream *already_processed; 652 mhd_assert ((! c->h2.state.sent_goaway.occurred) || 653 (mhd_H2_ERR_NO_ERROR == c->h2.state.sent_goaway.code)); 654 655 already_processed = NULL; 656 while (! 0) 657 { 658 struct mhd_H2Stream *const s = 659 mhd_DLINKEDL_GET_FIRST_D (&(c->h2.streams.send_q)); 660 661 if (NULL == s) 662 break; 663 664 if (already_processed == s) 665 break; 666 667 mhd_assert (! stream_is_closed (s)); 668 669 mhd_h2_stream_reply_send (s); 670 if (mhd_H2_ERR_NO_ERROR != c->h2.state.sent_goaway.code) 671 return false; 672 673 mhd_DLINKEDL_DEL_D (&(c->h2.streams.send_q), s, send_q); 674 if (stream_is_closed (s)) 675 conn_remove_stream (c, 676 s); 677 else if (mhd_H2_RPL_STAGE_END_STREAM > s->rpl.stage) 678 { 679 /* Still sending, move the stream to the end of the queue */ 680 mhd_DLINKEDL_INS_LAST_D (&(c->h2.streams.send_q), s, send_q); 681 if (NULL == already_processed) 682 already_processed = s; 683 } 684 if ((c->write_buffer_size - c->write_buffer_append_offset) < 685 min_usable_buff) 686 return false; 687 } 688 return true; 689 } 690 691 692 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ void 693 mhd_h2_conn_close_streams_all (struct MHD_Connection *restrict c) 694 { 695 while (! 0) 696 { 697 struct mhd_H2Stream *const s = 698 mhd_DLINKEDL_GET_FIRST_D (&(c->h2.streams.send_q)); 699 700 if (NULL == s) 701 break; 702 703 mhd_assert (! stream_is_closed (s)); 704 705 mhd_DLINKEDL_DEL_D (&(c->h2.streams.send_q), s, send_q); 706 conn_remove_stream (c, 707 s); 708 } 709 mhd_assert (0u == c->h2.streams.num_streams); 710 }