libmicrohttpd2

HTTP server C library (MHD 2.x, alpha)
Log | Files | Refs | README | LICENSE

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 }