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; (void) stngs_payload; // TODO: implement
    194   return mhd_H2_STNGS_PROC_STNGS_ERR;
    195 }
    196 
    197 
    198 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool
    199 mhd_h2_q_settings_first_fr (struct MHD_Connection *restrict c)
    200 {
    201   size_t fr_hdr_size;
    202   size_t final_fr_hdr_size;
    203   size_t payload_space;
    204   uint8_t *payload;
    205   size_t payload_pos;
    206   bool set_all_settings;
    207   struct mhd_Buffer buff;
    208   union mhd_H2FrameUnion h2frame;
    209 
    210   mhd_h2_frame_init_settings (&h2frame,
    211                               false);
    212 
    213   /* This is the first data sent by the server */
    214   mhd_assert (0u == c->write_buffer_append_offset);
    215 
    216   if (! mhd_h2_out_buff_acquire_fr_w_payload (c,
    217                                               &h2frame,
    218                                               &buff,
    219                                               &fr_hdr_size))
    220   {
    221     mhd_H2_W_BUFF_UPDATING_CLEAR (&(c->h2));
    222     mhd_h2_conn_finish (c,
    223                         mhd_H2_ERR_INTERNAL_ERROR,
    224                         true);
    225     return false;
    226   }
    227 
    228   payload = (uint8_t *) buff.data + fr_hdr_size;
    229   payload_space = buff.size - fr_hdr_size;
    230 
    231   if (payload_space != (payload_space & 0xFFFFFFFFu))
    232     payload_space = 0xFFFFFFFFu;
    233 
    234   payload_pos = 0u;
    235 
    236   // TODO: use configurable values for settings
    237   if (payload_space >= payload_pos + mhd_H2_SETTING_SIZE)
    238   {
    239     mhd_h2_setting_encode3 (mhd_H2_STNGS_HEADER_TABLE_SIZE,
    240                             (uint_least32_t) c->h2.hk_dec.max_allowed_dyn_size,
    241                             payload + payload_pos);
    242     payload_pos += mhd_H2_SETTING_SIZE;
    243   }
    244 
    245   if (payload_space >= payload_pos + mhd_H2_SETTING_SIZE)
    246   {
    247     mhd_h2_setting_encode3 (mhd_H2_STNGS_ENABLE_PUSH,
    248                             0u,
    249                             payload + payload_pos);
    250     payload_pos += mhd_H2_SETTING_SIZE;
    251   }
    252 
    253   if (payload_space >= payload_pos + mhd_H2_SETTING_SIZE)
    254   {
    255     mhd_assert (0x7FFFFFFFu >= c->h2.rcv_cfg.stream_init_win_sz);
    256     mhd_h2_setting_encode3 (mhd_H2_STNGS_INITIAL_WINDOW_SIZE,
    257                             c->h2.rcv_cfg.stream_init_win_sz,
    258                             payload + payload_pos);
    259     payload_pos += mhd_H2_SETTING_SIZE;
    260   }
    261 
    262   if (payload_space >= payload_pos + mhd_H2_SETTING_SIZE)
    263   {
    264     mhd_assert (0x7FFFFFFFu >= c->h2.rcv_cfg.max_frame_size);
    265     mhd_h2_setting_encode3 (mhd_H2_STNGS_MAX_FRAME_SIZE,
    266                             c->h2.rcv_cfg.max_frame_size,
    267                             payload + payload_pos);
    268     payload_pos += mhd_H2_SETTING_SIZE;
    269   }
    270 
    271   if (payload_space >= payload_pos + mhd_H2_SETTING_SIZE)
    272   {
    273     mhd_h2_setting_encode3 (mhd_H2_STNGS_MAX_HEADER_LIST_SIZE,
    274                             c->h2.rcv_cfg.max_header_list,
    275                             payload + payload_pos);
    276     payload_pos += mhd_H2_SETTING_SIZE;
    277   }
    278 
    279   if (payload_space >= payload_pos + mhd_H2_SETTING_SIZE)
    280   {
    281     mhd_h2_setting_encode3 (mhd_H2_STNGS_ENABLE_CONNECT_PROTOCOL,
    282                             0u,
    283                             payload + payload_pos);
    284     payload_pos += mhd_H2_SETTING_SIZE;
    285   }
    286 
    287   if (payload_space >= payload_pos + mhd_H2_SETTING_SIZE)
    288   {
    289     mhd_h2_setting_encode3 (mhd_H2_STNGS_NO_RFC7540_PRIORITIES,
    290                             1u,
    291                             payload + payload_pos);
    292     payload_pos += mhd_H2_SETTING_SIZE;
    293     set_all_settings = true;
    294   }
    295   else
    296     set_all_settings = false;
    297 
    298   if (! set_all_settings)
    299   {
    300     mhd_H2_W_BUFF_UPDATING_CLEAR (&(c->h2));
    301     mhd_h2_conn_finish (c,
    302                         mhd_H2_ERR_INTERNAL_ERROR,
    303                         true);
    304     return false;
    305   }
    306 
    307   mhd_assert (0u == payload_pos % mhd_H2_SETTING_SIZE);
    308 
    309   mhd_h2_frame_set_payload_size (&h2frame,
    310                                  payload_pos);
    311 
    312   final_fr_hdr_size = mhd_h2_frame_hdr_encode (&h2frame,
    313                                                buff.size,
    314                                                (uint8_t *) buff.data);
    315   mhd_assert (fr_hdr_size == final_fr_hdr_size);
    316   mhd_h2_out_buff_unlock (c,
    317                           final_fr_hdr_size + payload_pos);
    318 
    319   c->h2.state.init.sent_setns = true;
    320   ++c->h2.state.sent_setns_noakc;
    321 
    322   return true;
    323 }