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 }