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 }