libmicrohttpd2

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

h2_proc_settings.c (10177B)


      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_proc_settings.c
     41  * @brief  Implementation of HTTP/2 connection settings 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 
     52 #include "mhd_connection.h"
     53 
     54 #include "h2_frame_types.h"
     55 #include "h2_frame_init.h"
     56 #include "h2_settings.h"
     57 
     58 #include "h2_frame_codec.h"
     59 #include "hpack/mhd_hpack_codec.h"
     60 #include "h2_proc_conn.h"
     61 #include "h2_proc_out.h"
     62 
     63 #include "h2_proc_settings.h"
     64 
     65 
     66 static bool
     67 h2_has_space_for_ack (struct MHD_Connection *restrict c)
     68 {
     69 #ifndef NDEBUG
     70   if (1)
     71   {
     72     union mhd_H2FrameUnion h2frame;
     73 
     74     mhd_h2_frame_init_settings (&h2frame,
     75                                 true);
     76     mhd_assert (0u == mhd_h2_frame_get_extra_hdr_size (&h2frame));
     77   }
     78 #endif /* NDEBUG */
     79   return mhd_h2_out_buff_has_space_sz (c,
     80                                        mhd_H2_FR_HDR_BASE_SIZE);
     81 }
     82 
     83 
     84 static bool
     85 h2_q_settings_ack (struct MHD_Connection *restrict c)
     86 {
     87   union mhd_H2FrameUnion h2frame;
     88 
     89   mhd_h2_frame_init_settings (&h2frame,
     90                               true);
     91   /* The actual size should match free space check */
     92   mhd_assert (0u == mhd_h2_frame_get_extra_hdr_size (&h2frame));
     93 
     94   return mhd_h2_q_frame_no_payload (c,
     95                                     &h2frame);
     96 }
     97 
     98 
     99 MHD_INTERNAL bool
    100 mhd_h2_proc_first_settings (struct MHD_Connection *restrict c,
    101                             const struct mhd_Buffer *restrict stngs_payload)
    102 {
    103   bool ack_succeed;
    104 
    105   /* The payload size must be checked by frame decoder */
    106   mhd_assert (0u == (stngs_payload->size % 6u));
    107 
    108   mhd_assert (h2_has_space_for_ack (c));
    109 
    110   if (stngs_payload->size > 6u)
    111   {
    112     size_t pos;
    113 
    114     for (pos = 0u; pos < (stngs_payload->size - 5u); pos += 6u)
    115     {
    116       struct mhd_H2Setting stng;
    117       mhd_h2_setting_decode ((uint8_t *)stngs_payload->data + pos,
    118                              &stng);
    119 
    120       switch (stng.identifier)
    121       {
    122       case mhd_H2_STNGS_HEADER_TABLE_SIZE:
    123         if (mhd_DTBL_MAX_SIZE >= stng.value)
    124         {
    125           if (4096u >= stng.value) // TODO: take the limit from the daemon
    126             mhd_hpack_enc_set_dyn_size (&(c->h2.hk_enc),
    127                                         (size_t)stng.value);
    128         }
    129         break;
    130       case mhd_H2_STNGS_ENABLE_PUSH:
    131         /* Ignored */
    132         break;
    133       case mhd_H2_STNGS_CONCURRENT_STREAMS:
    134         c->h2.peer.max_concur_streams = stng.value;
    135         break;
    136       case mhd_H2_STNGS_INITIAL_WINDOW_SIZE:
    137         if (0x7FFFFFFFu < stng.value)
    138         {
    139           mhd_h2_conn_finish (c,
    140                               mhd_H2_ERR_FLOW_CONTROL_ERROR,
    141                               true);
    142           return false; /* Failure exit point */
    143         }
    144 
    145         /* Set the initial size. No streams should be modified as no
    146            streams have been started yet. */
    147         c->h2.peer.stream_init_win_sz = stng.value;
    148         break;
    149       case mhd_H2_STNGS_MAX_FRAME_SIZE:
    150         if ((mhd_H2_STNG_MIN_MAX_FRAME_SIZE > stng.value)
    151             || (mhd_H2_STNG_MAX_MAX_FRAME_SIZE < stng.value))
    152         {
    153           mhd_h2_conn_finish (c,
    154                               mhd_H2_ERR_PROTOCOL_ERROR,
    155                               true);
    156           return false; /* Failure exit point */
    157         }
    158 #if 0 // TODO: use limit from the daemon settings
    159         if (mhd_H2_STNG_DEF_MAX_FRAME_SIZE >= stng.value)
    160           c->h2.peer.max_frame_size = stng.value;
    161 #endif
    162         break;
    163       case mhd_H2_STNGS_MAX_HEADER_LIST_SIZE:
    164         c->h2.peer.max_header_list = stng.value;
    165         break;
    166       case mhd_H2_STNGS_ENABLE_CONNECT_PROTOCOL:
    167       case mhd_H2_STNGS_NO_RFC7540_PRIORITIES:
    168         /* Ignored */
    169         break;
    170 #ifndef mhd_USE_ENUM_BASE_T
    171       case mhd_H2_STNGS_SENTINEL:
    172 #endif /*mhd_USE_ENUM_BASE_T */
    173       default:
    174         /* Unknown setting ignored */
    175         break;
    176       }
    177     }
    178   }
    179   c->h2.state.init.got_setns = true;
    180 
    181   ack_succeed = h2_q_settings_ack (c);
    182   mhd_assert (ack_succeed);
    183   (void)ack_succeed;
    184 
    185   return true; /* Success exit point */
    186 }
    187 
    188 
    189 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ enum mhd_H2SettingsProcessResult
    190 mhd_h2_proc_new_settings (struct MHD_Connection *restrict c,
    191                           const struct mhd_Buffer *restrict stngs_payload)
    192 {
    193   (void)c;
    194   (void)stngs_payload;            // TODO: implement
    195   return mhd_H2_STNGS_PROC_STNGS_ERR;
    196 }
    197 
    198 
    199 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool
    200 mhd_h2_q_settings_first_fr (struct MHD_Connection *restrict c)
    201 {
    202   size_t fr_hdr_size;
    203   size_t final_fr_hdr_size;
    204   size_t payload_space;
    205   uint8_t *payload;
    206   size_t payload_pos;
    207   bool set_all_settings;
    208   struct mhd_Buffer buff;
    209   union mhd_H2FrameUnion h2frame;
    210 
    211   mhd_h2_frame_init_settings (&h2frame,
    212                               false);
    213 
    214   /* This is the first data sent by the server */
    215   mhd_assert (0u == c->write_buffer_append_offset);
    216 
    217   if (!mhd_h2_out_buff_acquire_fr_w_payload (c,
    218                                              &h2frame,
    219                                              &buff,
    220                                              &fr_hdr_size))
    221   {
    222     mhd_H2_W_BUFF_UPDATING_CLEAR (&(c->h2));
    223     mhd_h2_conn_finish (c,
    224                         mhd_H2_ERR_INTERNAL_ERROR,
    225                         true);
    226     return false;
    227   }
    228 
    229   payload = (uint8_t *)buff.data + fr_hdr_size;
    230   payload_space = buff.size - fr_hdr_size;
    231 
    232   if (payload_space != (payload_space & 0xFFFFFFFFu))
    233     payload_space = 0xFFFFFFFFu;
    234 
    235   payload_pos = 0u;
    236 
    237   // TODO: use configurable values for settings
    238   if (payload_space >= payload_pos + mhd_H2_SETTING_SIZE)
    239   {
    240     mhd_h2_setting_encode3 (mhd_H2_STNGS_HEADER_TABLE_SIZE,
    241                             (uint_least32_t)c->h2.hk_dec.max_allowed_dyn_size,
    242                             payload + payload_pos);
    243     payload_pos += mhd_H2_SETTING_SIZE;
    244   }
    245 
    246   if (payload_space >= payload_pos + mhd_H2_SETTING_SIZE)
    247   {
    248     mhd_h2_setting_encode3 (mhd_H2_STNGS_ENABLE_PUSH,
    249                             0u,
    250                             payload + payload_pos);
    251     payload_pos += mhd_H2_SETTING_SIZE;
    252   }
    253 
    254   if (payload_space >= payload_pos + mhd_H2_SETTING_SIZE)
    255   {
    256     mhd_assert (0x7FFFFFFFu >= c->h2.rcv_cfg.stream_init_win_sz);
    257     mhd_h2_setting_encode3 (mhd_H2_STNGS_INITIAL_WINDOW_SIZE,
    258                             c->h2.rcv_cfg.stream_init_win_sz,
    259                             payload + payload_pos);
    260     payload_pos += mhd_H2_SETTING_SIZE;
    261   }
    262 
    263   if (payload_space >= payload_pos + mhd_H2_SETTING_SIZE)
    264   {
    265     mhd_assert (0x7FFFFFFFu >= c->h2.rcv_cfg.max_frame_size);
    266     mhd_h2_setting_encode3 (mhd_H2_STNGS_MAX_FRAME_SIZE,
    267                             c->h2.rcv_cfg.max_frame_size,
    268                             payload + payload_pos);
    269     payload_pos += mhd_H2_SETTING_SIZE;
    270   }
    271 
    272   if (payload_space >= payload_pos + mhd_H2_SETTING_SIZE)
    273   {
    274     mhd_h2_setting_encode3 (mhd_H2_STNGS_MAX_HEADER_LIST_SIZE,
    275                             c->h2.rcv_cfg.max_header_list,
    276                             payload + payload_pos);
    277     payload_pos += mhd_H2_SETTING_SIZE;
    278   }
    279 
    280   if (payload_space >= payload_pos + mhd_H2_SETTING_SIZE)
    281   {
    282     mhd_h2_setting_encode3 (mhd_H2_STNGS_ENABLE_CONNECT_PROTOCOL,
    283                             0u,
    284                             payload + payload_pos);
    285     payload_pos += mhd_H2_SETTING_SIZE;
    286   }
    287 
    288   if (payload_space >= payload_pos + mhd_H2_SETTING_SIZE)
    289   {
    290     mhd_h2_setting_encode3 (mhd_H2_STNGS_NO_RFC7540_PRIORITIES,
    291                             1u,
    292                             payload + payload_pos);
    293     payload_pos += mhd_H2_SETTING_SIZE;
    294     set_all_settings = true;
    295   }
    296   else
    297     set_all_settings = false;
    298 
    299   if (!set_all_settings)
    300   {
    301     mhd_H2_W_BUFF_UPDATING_CLEAR (&(c->h2));
    302     mhd_h2_conn_finish (c,
    303                         mhd_H2_ERR_INTERNAL_ERROR,
    304                         true);
    305     return false;
    306   }
    307 
    308   mhd_assert (0u == payload_pos % mhd_H2_SETTING_SIZE);
    309 
    310   mhd_h2_frame_set_payload_size (&h2frame,
    311                                  payload_pos);
    312 
    313   final_fr_hdr_size = mhd_h2_frame_hdr_encode (&h2frame,
    314                                                buff.size,
    315                                                (uint8_t *)buff.data);
    316   mhd_assert (fr_hdr_size == final_fr_hdr_size);
    317   mhd_h2_out_buff_unlock (c,
    318                           final_fr_hdr_size + payload_pos);
    319 
    320   c->h2.state.init.sent_setns = true;
    321   ++c->h2.state.sent_setns_noakc;
    322 
    323   return true;
    324 }