libmicrohttpd2

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

h2_comm.c (19386B)


      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_comm.c
     41  * @brief  Implementation of HTTP/2 connection communication 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 <string.h>
     51 
     52 #include "mhd_unreachable.h"
     53 #include "mhd_constexpr.h"
     54 #include "mhd_assert.h"
     55 
     56 #include "mhd_connection.h"
     57 #include "mhd_daemon.h"
     58 
     59 #include "mempool_funcs.h"
     60 
     61 #ifdef MHD_SUPPORT_HTTPS
     62 #  include "mhd_tls_funcs.h"
     63 #endif
     64 
     65 #include "respond_with_error.h"
     66 #include "stream_funcs.h"
     67 #include "daemon_logger.h"
     68 
     69 #include "h2_req_items_funcs.h"
     70 #include "h2_frame_codec.h"
     71 #include "h2_proc_settings.h"
     72 #include "h2_proc_conn.h"
     73 #include "h2_proc_in.h"
     74 #include "h2_conn_streams.h"
     75 #include "hpack/mhd_hpack_codec.h"
     76 
     77 #include "h2_comm.h"
     78 
     79 #ifndef HAVE_NULL_PTR_ALL_ZEROS
     80 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ void
     81 mhd_h2_blank_init (struct MHD_Connection *restrict c)
     82 {
     83   c->write_buffer = NULL;
     84 }
     85 
     86 
     87 #endif /* HAVE_NULL_PTR_ALL_ZEROS */
     88 
     89 /**
     90  * HTTP/2 connection preface
     91  *
     92  * Extracted from RFC 9113, Section 3.4
     93  */
     94 mhd_constexpr uint8_t mhd_h2_preface[mhd_H2_PREFACE_LEN] = {
     95   0x50u, 0x52u, 0x49u, 0x20u, 0x2Au, 0x20u, 0x48u, 0x54u, 0x54u, 0x50u,
     96   0x2Fu, 0x32u, 0x2Eu, 0x30u, 0x0Du, 0x0Au, 0x0Du, 0x0Au, 0x53u, 0x4Du,
     97   0x0Du, 0x0Au, 0x0Du, 0x0Au
     98 };
     99 
    100 
    101 /**
    102  * Result of HTTP/2 preface check
    103  */
    104 enum MHD_FIXED_ENUM_ mhd_H2PrefaceCheckResult
    105 {
    106   /**
    107    * Received data matches HTTP/2 preface
    108    */
    109   mhd_H2_PREFACE_CHECK_IS_HTTP2
    110   ,
    111   /**
    112    * Received data does not match HTTP/2 preface
    113    */
    114   mhd_H2_PREFACE_CHECK_IS_NOT_HTTP2
    115   ,
    116   /**
    117    * Not enough data has been received
    118    */
    119   mhd_H2_PREFACE_CHECK_NEED_MORE_DATA
    120 };
    121 
    122 /**
    123  * Check HTTP/2 connection preface
    124  * @param c the connection to process
    125  * @return enum mhd_H2PrefaceCheckResult status code
    126  */
    127 mhd_static_inline MHD_FN_PAR_NONNULL_ALL_ enum mhd_H2PrefaceCheckResult
    128 mhd_h2_check_preface (struct MHD_Connection *restrict c)
    129 {
    130   bool have_enough_data;
    131 
    132   mhd_assert (mhd_HTTP_LAYER_PREFACE == c->h_layer.state);
    133   mhd_assert (NULL != c->read_buffer);
    134   mhd_assert (mhd_H2_PREFACE_LEN <= c->read_buffer_size);
    135 
    136   if (0u == c->read_buffer_offset)
    137     return mhd_H2_PREFACE_CHECK_NEED_MORE_DATA;
    138 
    139   have_enough_data = (mhd_H2_PREFACE_LEN <= c->read_buffer_offset);
    140 
    141   if (0 !=
    142       memcmp (mhd_h2_preface,
    143               c->read_buffer,
    144               have_enough_data ? mhd_H2_PREFACE_LEN : c->read_buffer_offset))
    145     return mhd_H2_PREFACE_CHECK_IS_NOT_HTTP2;
    146 
    147   return (have_enough_data ?
    148           mhd_H2_PREFACE_CHECK_IS_HTTP2 : mhd_H2_PREFACE_CHECK_NEED_MORE_DATA);
    149 }
    150 
    151 
    152 #define ERR_RSP_H2_NOT_SUPPORTED \
    153         "<html><head><title>HTTP/2 is not supported</title></head>" \
    154         "<body>HTTP/2 protocol is not supported.</body></html>"
    155 
    156 #define ERR_RSP_H2_WITH_ALPN_HTTP1 \
    157         "<html><head><title>HTTP/2 without matching ALPN</title></head>" \
    158         "<body>ALPN selected HTTP/1.x protocol, HTTP/2 cannot be used " \
    159         "over TLS if ALPN selected another application protocol.</body></html>"
    160 
    161 #define ERR_RSP_H2_WITHOUT_ALPN \
    162         "<html><head><title>HTTP/2 without ALPN on HTTPS</title></head>" \
    163         "<body>HTTP/2 cannot be used over TLS without ALPN.</body></html>"
    164 
    165 /**
    166  * Perform switching connection to HTTP/2 mode
    167  * @param c the connection to switch
    168  * @return 'true' if switched successfully,
    169  *         'false' if connection is broken and should be closed
    170  */
    171 static MHD_FN_PAR_NONNULL_ALL_ bool
    172 h2_switch_to_h2 (struct MHD_Connection *restrict c)
    173 {
    174   size_t buff_size;
    175   mhd_assert (mhd_HTTP_LAYER_PREFACE == c->h_layer.state);
    176   mhd_assert (mhd_H2_PREFACE_LEN <= c->read_buffer_offset);
    177   mhd_assert (mhd_HTTP_VER_FAM_NOT_SET == c->h_layer.fam);
    178 
    179   mhd_DLINKEDL_INIT_LIST_D (&(c->h2.streams.active));
    180   mhd_DLINKEDL_INIT_LIST_D (&(c->h2.streams.send_q));
    181   c->h2.streams.num_streams = 0u;
    182 
    183   c->h2.state.init.got_setns = false;
    184   c->h2.state.init.sent_setns = false;
    185 
    186   c->h2.state.sent_setns_noakc = 0u;
    187 
    188   c->h2.state.send_window = mhd_H2_STNG_DEF_INIT_WIN_SIZE;
    189   c->h2.state.recv_window = mhd_H2_STNG_DEF_INIT_WIN_SIZE;
    190 
    191   c->h2.state.top_seen_stream_id = 0u;
    192 
    193   // TODO: make some parameters configurable
    194   c->h2.rcv_cfg.stream_init_win_sz = 0x7FFFFFFFu;
    195   c->h2.rcv_cfg.conn_full_win_sz = 0x7FFFFFFFu;
    196   c->h2.rcv_cfg.max_frame_size = mhd_H2_STNG_DEF_MAX_FRAME_SIZE;
    197   c->h2.rcv_cfg.max_header_list = 16u * 1024u;
    198   c->h2.rcv_cfg.max_concur_streams = 100u;
    199 
    200   c->h2.peer.stream_init_win_sz = mhd_H2_STNG_DEF_INIT_WIN_SIZE;
    201   c->h2.peer.max_frame_size = mhd_H2_STNG_DEF_MAX_FRAME_SIZE;
    202   c->h2.peer.max_header_list = (uint_least32_t) (~((uint_least32_t) 0u));
    203   c->h2.peer.max_concur_streams = (uint_least32_t) (~((uint_least32_t) 0u));
    204 
    205   buff_size = mhd_pool_get_size (c->pool);
    206   c->read_buffer =
    207     (char *)
    208     mhd_pool_reset (c->pool,
    209                     c->read_buffer,
    210                     c->read_buffer_offset,
    211                     buff_size);
    212   mhd_assert (NULL != c->read_buffer);
    213   c->read_buffer_size = buff_size;
    214   c->h2.buff.r_cur_frame = mhd_H2_PREFACE_LEN;
    215 
    216   c->h2.mem.send_pool =
    217     mhd_pool_create (c->daemon->conns.cfg.mem_pool_size,
    218                      c->daemon->conns.cfg.mem_pool_zeroing);
    219   if (NULL != c->h2.mem.send_pool)
    220   {
    221     buff_size = mhd_pool_get_size (c->h2.mem.send_pool);
    222     c->write_buffer =
    223       (char *)
    224       mhd_pool_allocate (c->h2.mem.send_pool,
    225                          buff_size,
    226                          false);
    227     mhd_assert (NULL != c->write_buffer);
    228     c->write_buffer_size = buff_size;
    229     c->write_buffer_append_offset = 0u;
    230     c->write_buffer_send_offset = 0u;
    231 
    232     if (mhd_hpack_dec_init (&(c->h2.hk_dec)))
    233     {
    234       if (mhd_hpack_enc_init (&(c->h2.hk_enc)))
    235       {
    236         // TODO: make the size configurable
    237         c->h2.mem.req_ib = mhd_h2_items_block_create (16u * 1024);
    238 
    239         if (NULL != c->h2.mem.req_ib)
    240         {
    241           c->h_layer.fam = mhd_HTTP_VER_FAM_2;
    242           c->h_layer.state = mhd_HTTP_LAYER_CONNECTED;
    243 
    244           return true; /* Success exit point */
    245         }
    246         /* Clean-up */
    247         mhd_hpack_enc_deinit (&(c->h2.hk_enc));
    248       }
    249       mhd_hpack_dec_deinit (&(c->h2.hk_dec));
    250     }
    251     c->write_buffer = NULL;
    252     mhd_pool_destroy (c->h2.mem.send_pool);
    253     c->h2.mem.send_pool = NULL;
    254     mhd_LOG_MSG (c->daemon, MHD_SC_H2_CONN_MEM_ALLOC_FAILURE, \
    255                  "Failed to allocate memory for the HTTP/2 "
    256                  "connection resources.");
    257   }
    258   else
    259     mhd_LOG_MSG (c->daemon, MHD_SC_POOL_MEM_ALLOC_FAILURE, \
    260                  "Failed to allocate memory for the HTTP/2 send buffer.");
    261 
    262   mhd_conn_start_closing_no_sys_res (c);
    263   c->h_layer.fam = mhd_HTTP_VER_FAM_NOT_SET;
    264   c->h_layer.state = mhd_HTTP_LAYER_BROKEN;
    265   return false;
    266 }
    267 
    268 
    269 /**
    270  * Perform de-initialisation of HTTP/2-specific data and start connection
    271  * closing
    272  * @param c the connection to de-initialise
    273  */
    274 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ void
    275 mhd_h2_conn_h2_deinit_start_closing (struct MHD_Connection *restrict c)
    276 {
    277   mhd_assert (mhd_C_IS_HTTP2 (c));
    278   mhd_assert ((mhd_HTTP_LAYER_CONNECTED == c->h_layer.state) ||
    279               (mhd_HTTP_LAYER_CLOSING == c->h_layer.state) ||
    280               (mhd_HTTP_LAYER_BROKEN == c->h_layer.state));
    281   mhd_assert (! c->h2.dbg.h2_deinited);
    282 
    283   mhd_h2_conn_close_streams_all (c);
    284 
    285   mhd_h2_items_block_destroy (c->h2.mem.req_ib);
    286   c->h2.mem.req_ib = NULL;
    287   mhd_hpack_enc_deinit (&(c->h2.hk_enc));
    288   mhd_hpack_dec_deinit (&(c->h2.hk_dec));
    289 
    290   mhd_assert (NULL != c->write_buffer);
    291   mhd_assert (NULL != c->h2.mem.send_pool);
    292   c->write_buffer_send_offset = 0u;
    293   c->write_buffer_append_offset = 0u;
    294   c->write_buffer_size = 0u;
    295   c->write_buffer = NULL;
    296   mhd_pool_destroy (c->h2.mem.send_pool);
    297   c->h2.mem.send_pool = NULL;
    298 
    299 #ifndef NDEBUG
    300   c->h2.dbg.h2_deinited = true;
    301 #endif /* ! NDEBUG */
    302 
    303   if ((mhd_HTTP_LAYER_BROKEN == c->h_layer.state) ||
    304       (mhd_SOCKET_ERR_NO_ERROR != c->sk.state.discnt_err) ||
    305       (0 != (c->sk.ready & mhd_SOCKET_NET_STATE_ERROR_READY)))
    306     mhd_conn_start_closing_h2_hard (c);
    307   else
    308     mhd_conn_start_closing_h2_soft (c);
    309 
    310   if (mhd_HTTP_LAYER_CLOSED > c->h_layer.state)
    311     c->h_layer.state = mhd_HTTP_LAYER_CLOSED;
    312 }
    313 
    314 
    315 /**
    316  * Handle detection of HTTP/2 preface
    317  * @param c the connection to process
    318  * @return 'true' if connection should be continued to be processed as
    319  *                HTTP connection (possibly to send already scheduled error
    320  *                response),
    321  *         'false' if connection is broken and should be closed
    322  */
    323 static MHD_FN_PAR_NONNULL_ALL_ bool
    324 h2_handle_preface_found (struct MHD_Connection *restrict c)
    325 {
    326   const bool allow_on_alpn_mismatch =
    327     (c->daemon->req_cfg.strictness <= MHD_PSL_EXTRA_PERMISSIVE);
    328   const bool allow_without_alpn =
    329     (c->daemon->req_cfg.strictness <= MHD_PSL_PERMISSIVE);
    330 
    331   mhd_assert (mhd_HTTP_LAYER_PREFACE == c->h_layer.state);
    332   mhd_assert (mhd_H2_PREFACE_LEN <= c->read_buffer_offset);
    333 
    334   if (! mhd_D_IS_HTTP2_ENABLED (c->daemon))
    335   {
    336     c->h_layer.state = mhd_HTTP_LAYER_CONNECTED;
    337     c->h_layer.fam = mhd_HTTP_VER_FAM_NOT_2;
    338     mhd_RESPOND_WITH_ERROR_STATIC (c,
    339                                    MHD_HTTP_STATUS_HTTP_VERSION_NOT_SUPPORTED,
    340                                    ERR_RSP_H2_NOT_SUPPORTED);
    341     return true; /* Send error response */
    342   }
    343 
    344 #ifdef MHD_SUPPORT_HTTPS
    345   if (mhd_C_HAS_TLS (c))
    346   {
    347     switch (mhd_tls_conn_get_alpn_prot (c->tls))
    348     {
    349     case mhd_TLS_ALPN_PROT_HTTP1_0:
    350     case mhd_TLS_ALPN_PROT_HTTP1_1:
    351       if (! allow_on_alpn_mismatch)
    352       {
    353         c->h_layer.state = mhd_HTTP_LAYER_CONNECTED;
    354         c->h_layer.fam = mhd_HTTP_VER_FAM_NOT_2;
    355         mhd_RESPOND_WITH_ERROR_STATIC (c,
    356                                        MHD_HTTP_STATUS_BAD_REQUEST,
    357                                        ERR_RSP_H2_WITH_ALPN_HTTP1);
    358         return true; /* Send error response */
    359       }
    360       break;
    361     case mhd_TLS_ALPN_PROT_HTTP2:
    362       break;
    363     case mhd_TLS_ALPN_PROT_NOT_SELECTED:
    364     case mhd_TLS_ALPN_PROT_ERROR:
    365       if (! allow_without_alpn)
    366       {
    367         c->h_layer.state = mhd_HTTP_LAYER_CONNECTED;
    368         c->h_layer.fam = mhd_HTTP_VER_FAM_NOT_2;
    369         mhd_RESPOND_WITH_ERROR_STATIC (c,
    370                                        MHD_HTTP_STATUS_BAD_REQUEST,
    371                                        ERR_RSP_H2_WITHOUT_ALPN);
    372         return true; /* Send error response */
    373       }
    374       break;
    375     default:
    376       mhd_UNREACHABLE ();
    377       return false;
    378     }
    379   }
    380 #endif /* MHD_SUPPORT_HTTPS */
    381   return h2_switch_to_h2 (c);
    382 }
    383 
    384 
    385 #define ERR_RSP_H2_REQUIRED \
    386         "<html><head><title>HTTP/2 is required</title></head>" \
    387         "<body>HTTP/2 protocol is required</body></html>"
    388 
    389 #define ERR_RSP_NOT_H2_WITH_ALPN_H2 \
    390         "<html><head><title>ALPN selected HTTP/2</title></head>" \
    391         "<body>ALPN selected HTTP/2 protocol, " \
    392         "only HTTP/2 communication could be used.</body></html>"
    393 
    394 /**
    395  * Handle absence of HTTP/2 preface
    396  * @param c the connection to process
    397  * @return 'true' if connection should be continued to be processed as
    398  *                HTTP connection (possibly to send already scheduled error
    399  *                response),
    400  *         'false' if connection is broken and should be closed
    401  */
    402 static MHD_FN_PAR_NONNULL_ALL_ bool
    403 h2_handle_preface_not_found (struct MHD_Connection *restrict c)
    404 {
    405   const bool allow_on_alpn_mismatch =
    406     (c->daemon->req_cfg.strictness <= MHD_PSL_EXTRA_PERMISSIVE);
    407 
    408   mhd_assert (mhd_HTTP_LAYER_PREFACE == c->h_layer.state);
    409   mhd_assert (mhd_H2_PREFACE_LEN <= c->read_buffer_offset);
    410 
    411 #ifdef MHD_SUPPORT_HTTPS
    412   if (mhd_C_HAS_TLS (c))
    413   {
    414     switch (mhd_tls_conn_get_alpn_prot (c->tls))
    415     {
    416     case mhd_TLS_ALPN_PROT_HTTP1_0:
    417     case mhd_TLS_ALPN_PROT_HTTP1_1:
    418     case mhd_TLS_ALPN_PROT_NOT_SELECTED:
    419     case mhd_TLS_ALPN_PROT_ERROR:
    420       break;
    421     case mhd_TLS_ALPN_PROT_HTTP2:
    422       if (! allow_on_alpn_mismatch)
    423       {
    424         c->h_layer.state = mhd_HTTP_LAYER_BROKEN;
    425         c->h_layer.fam = mhd_HTTP_VER_FAM_INVALID;
    426         mhd_conn_start_closing (c,
    427                                 mhd_CONN_CLOSE_H2_PREFACE_MISSING,
    428                                 mhd_MSG4LOG ("No valid HTTP/2 preface on " \
    429                                              "TLS connection with 'h2' "
    430                                              "selected by ALPN"));
    431         return false;
    432       }
    433       break;
    434     default:
    435       mhd_UNREACHABLE ();
    436       c->h_layer.state = mhd_HTTP_LAYER_BROKEN;
    437       c->h_layer.fam = mhd_HTTP_VER_FAM_INVALID;
    438       return false;
    439       break;
    440     }
    441   }
    442 #endif /* MHD_SUPPORT_HTTPS */
    443 
    444   if (! mhd_D_IS_HTTP1_ENABLED (c->daemon))
    445   {
    446     c->h_layer.state = mhd_HTTP_LAYER_CONNECTED;
    447     c->h_layer.fam = mhd_HTTP_VER_FAM_NOT_2;
    448     mhd_RESPOND_WITH_ERROR_STATIC (c,
    449                                    MHD_HTTP_STATUS_HTTP_VERSION_NOT_SUPPORTED,
    450                                    ERR_RSP_H2_REQUIRED);
    451     return true; /* Send error response */
    452   }
    453 
    454   c->h_layer.state = mhd_HTTP_LAYER_CONNECTED;
    455   c->h_layer.fam = mhd_HTTP_VER_FAM_NOT_2;
    456   /* The data in the receive buffer will be re-interpreted as HTTP/1.x request */
    457   return true;
    458 }
    459 
    460 
    461 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ MHD_INTERNAL enum mhd_CommLayerState
    462 mhd_h2_process_preface (struct MHD_Connection *restrict c)
    463 {
    464   mhd_assert (mhd_HTTP_LAYER_PREFACE == c->h_layer.state);
    465 
    466   switch (mhd_h2_check_preface (c))
    467   {
    468   case mhd_H2_PREFACE_CHECK_IS_HTTP2:
    469     return
    470       h2_handle_preface_found (c) ? mhd_COMM_LAYER_OK : mhd_COMM_LAYER_BROKEN;
    471   case mhd_H2_PREFACE_CHECK_IS_NOT_HTTP2:
    472     return
    473       h2_handle_preface_not_found (c) ?
    474       mhd_COMM_LAYER_OK : mhd_COMM_LAYER_BROKEN;
    475   case mhd_H2_PREFACE_CHECK_NEED_MORE_DATA:
    476     return mhd_COMM_LAYER_PROCESSING;
    477   default:
    478     break;
    479   }
    480   mhd_UNREACHABLE ();
    481   return mhd_COMM_LAYER_BROKEN;
    482 }
    483 
    484 
    485 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ void
    486 mhd_h2_conn_state_update (struct MHD_Connection *restrict c)
    487 {
    488   unsigned int new_state;
    489 
    490   mhd_assert (mhd_C_IS_HTTP2 (c));
    491 
    492   mhd_assert (0u == (c->event_loop_info & MHD_EVENT_LOOP_INFO_CLEANUP));
    493 #ifdef MHD_SUPPORT_UPGRADE
    494   mhd_assert (0u == (c->event_loop_info & MHD_EVENT_LOOP_INFO_UPGRADED));
    495 #endif /* MHD_SUPPORT_UPGRADE */
    496 
    497   new_state = 0u;
    498 
    499   if (c->read_buffer_offset < c->read_buffer_size)
    500     new_state |= (unsigned int) MHD_EVENT_LOOP_INFO_RECV;
    501 
    502   if (c->write_buffer_send_offset < c->write_buffer_append_offset)
    503     new_state |= (unsigned int) MHD_EVENT_LOOP_INFO_SEND;
    504 
    505   c->event_loop_info = (enum MHD_ConnectionEventLoopInfo) new_state;
    506 }
    507 
    508 
    509 static MHD_FN_PAR_NONNULL_ALL_ void
    510 h2_conn_manage_buff_out (struct MHD_Connection *restrict c)
    511 {
    512   mhd_assert (c->write_buffer_send_offset <= c->write_buffer_append_offset);
    513   if (c->write_buffer_send_offset == c->write_buffer_append_offset)
    514   {
    515     c->write_buffer_send_offset = 0u;
    516     c->write_buffer_append_offset = 0u;
    517   }
    518   else if ((0u != c->write_buffer_append_offset) &&
    519            (c->write_buffer_send_offset > c->write_buffer_size / 128))
    520   {
    521     const size_t left_unsent =
    522       c->write_buffer_append_offset - c->write_buffer_send_offset;
    523     memmove (c->write_buffer,
    524              c->write_buffer + c->write_buffer_send_offset,
    525              left_unsent);
    526     c->write_buffer_send_offset = 0u;
    527     c->write_buffer_append_offset = left_unsent;
    528   }
    529 }
    530 
    531 
    532 static MHD_FN_PAR_NONNULL_ALL_ void
    533 h2_conn_process_data_inner (struct MHD_Connection *restrict c)
    534 {
    535   /* Check whether first SETTINGS frame was send (queued).
    536      It is a part of connection initialisation. */
    537   if (! c->h2.state.init.sent_setns)
    538   {
    539     if (! mhd_h2_q_settings_first_fr (c))
    540       return;
    541   }
    542   /* Check whether first peer SETTINGS frame was received.
    543      It is a part of connection initialisation. */
    544   if (! c->h2.state.init.got_setns)
    545   {
    546     if (! mhd_h2_conn_process_first_fr (c))
    547       return; /* HTTP/2 cannot be processed */
    548   }
    549 
    550   h2_conn_manage_buff_out (c);
    551 
    552   /* Process incoming data.
    553      When incoming frames are processed, short control frames (such as
    554      PING ACK, SETTINGS ACK, RST_STREAM) could be added to the sending buffer.
    555      If connection is broken or output buffer has no space to add required
    556      frame, this connection processing is stopped here until the output buffer
    557      got more space. */
    558   if (! mhd_h2_conn_process_in_data (c))
    559     return;
    560 
    561   /* Close broken streams, update receive windows.
    562      Short control frames, like WINDOW_UPDATE, RST_STREAM could be added to the
    563      sending buffer.
    564      If connection is broken or output buffer has no space to add required
    565      frame, this connection processing is stopped here until the output buffer
    566      got more space. */
    567   if (! mhd_h2_conn_process_changes (c))
    568     return;
    569 
    570   /* Finally send the replies (if output buffer still have any space).  */
    571   mhd_h2_conn_process_streams_sending_queue (c);
    572 
    573   return;
    574 }
    575 
    576 
    577 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool
    578 mhd_h2_conn_process_data (struct MHD_Connection *restrict c)
    579 {
    580   mhd_assert (mhd_C_IS_HTTP2 (c));
    581 
    582   mhd_assert (mhd_HTTP_LAYER_CONNECTED <= c->h_layer.state);
    583   mhd_assert (mhd_HTTP_LAYER_CLOSING >= c->h_layer.state);
    584 
    585   if (mhd_HTTP_LAYER_CLOSING == c->h_layer.state)
    586   {
    587     if ((c->write_buffer_append_offset == c->write_buffer_send_offset)
    588         || (mhd_SOCKET_ERR_NO_ERROR != c->sk.state.discnt_err))
    589     {
    590       mhd_h2_conn_h2_deinit_start_closing (c);
    591       return false;
    592     }
    593     return true;
    594   }
    595 
    596   h2_conn_process_data_inner (c);
    597 
    598   mhd_assert (mhd_HTTP_LAYER_CLOSED != c->h_layer.state);
    599   if ((mhd_SOCKET_ERR_NO_ERROR != c->sk.state.discnt_err) ||
    600       (mhd_HTTP_LAYER_CLOSING == c->h_layer.state))
    601   {
    602     mhd_h2_conn_h2_deinit_start_closing (c);
    603     return false;
    604   }
    605 
    606   return true;
    607 }