response_add_header.c (10842B)
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) 2021-2024 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/response_add_header.c 41 * @brief The definitions of MHD_response_add_*header() functions 42 * @author Karlson2k (Evgeny Grin) 43 * @author Christian Grothoff 44 */ 45 46 #include "mhd_sys_options.h" 47 48 #include "mhd_str_macros.h" 49 #include "mhd_arr_num_elems.h" 50 51 #include "response_add_header.h" 52 #include "mhd_response.h" 53 #include "mhd_locks.h" 54 55 #include <string.h> 56 #include "sys_malloc.h" 57 58 #include "mhd_str.h" 59 60 #include "mhd_public_api.h" 61 62 #ifdef MHD_SUPPORT_HTTP2 63 static MHD_FN_PAR_NONNULL_ALL_ 64 MHD_FN_PAR_CSTR_ (2) MHD_FN_PAR_IN_SIZE_ (2,1) bool 65 is_name_h2_allowed (size_t name_len, 66 const char name[MHD_FN_PAR_DYN_ARR_SIZE_ (name_len)]) 67 { 68 static const struct MHD_String h2_forbidden[] = { 69 mhd_MSTR_INIT ("Connection"), 70 mhd_MSTR_INIT ("Transfer-Encoding"), 71 mhd_MSTR_INIT ("Upgrade"), 72 mhd_MSTR_INIT ("Keep-Alive"), 73 mhd_MSTR_INIT ("Proxy-Connection") 74 }; 75 size_t i; 76 77 for (i = 0u; i < mhd_ARR_NUM_ELEMS (h2_forbidden); ++i) 78 { 79 const struct MHD_String *const frbdn = h2_forbidden + i; 80 81 if (frbdn->len != name_len) 82 continue; 83 if (mhd_str_equal_caseless_bin_n (frbdn->cstr, 84 name, 85 name_len)) 86 return false; 87 } 88 return true; 89 } 90 91 92 #endif /* MHD_SUPPORT_HTTP2 */ 93 94 static 95 MHD_FN_PAR_NONNULL_ (1) 96 MHD_FN_PAR_NONNULL_ (3) MHD_FN_PAR_CSTR_ (3) 97 MHD_FN_PAR_NONNULL_ (5) MHD_FN_PAR_CSTR_ (5) bool 98 response_add_header_no_check ( 99 struct MHD_Response *restrict response, 100 size_t name_len, 101 const char *restrict name, 102 size_t value_len, 103 const char *restrict value) 104 { 105 char *buf; 106 size_t pos; 107 struct mhd_ResponseHeader *new_hdr; 108 size_t strings_size = 0u; 109 #ifdef MHD_SUPPORT_HTTP2 110 bool h2_allowed; 111 bool name_is_lower = false; 112 size_t val_empty_prf = 0u; 113 size_t val_empty_suf = 0u; 114 115 mhd_assert (0 == name[name_len]); 116 mhd_assert (0 == value[value_len]); 117 118 h2_allowed = is_name_h2_allowed (name_len, 119 name); 120 if (h2_allowed) 121 { 122 name_is_lower = mhd_str_is_lowercase_bin_n (name_len, 123 name); 124 strings_size += (name_is_lower ? 0u : (name_len + 1u)); 125 126 while ((' ' == value[val_empty_prf]) || ('\t' == value[val_empty_prf])) 127 ++val_empty_prf; 128 if (val_empty_prf != value_len) 129 { 130 while ((' ' == value[value_len - 1u - val_empty_suf]) 131 || ('\t' == value[value_len - 1u - val_empty_suf])) 132 ++val_empty_suf; 133 } 134 135 mhd_assert (val_empty_prf <= value_len); 136 mhd_assert ((val_empty_suf < value_len) || (0u == value_len)); 137 mhd_assert (val_empty_prf + val_empty_suf <= value_len); 138 139 if ((0u != val_empty_prf) || (0u != val_empty_suf)) 140 strings_size += value_len + 1u - val_empty_prf - val_empty_suf; 141 } 142 #endif /* MHD_SUPPORT_HTTP2 */ 143 144 mhd_assert (0 == name[name_len]); 145 mhd_assert (0 == value[value_len]); 146 147 strings_size += name_len + 1u; 148 strings_size += value_len + 1u; 149 150 new_hdr = (struct mhd_ResponseHeader *) 151 malloc (sizeof(struct mhd_ResponseHeader) + strings_size); 152 if (NULL == new_hdr) 153 return false; 154 155 buf = ((char *) new_hdr) + sizeof(struct mhd_ResponseHeader); 156 pos = 0u; 157 memcpy (buf + pos, name, name_len + 1u); 158 new_hdr->name.cstr = buf + pos; 159 new_hdr->name.len = name_len; 160 pos += name_len + 1u; 161 memcpy (buf + pos, value, value_len + 1u); 162 new_hdr->value.cstr = buf + pos; 163 new_hdr->value.len = value_len; 164 pos += value_len + 1u; 165 166 #ifdef MHD_SUPPORT_HTTP2 167 if (h2_allowed) 168 { 169 if (! name_is_lower) 170 { 171 mhd_str_to_lowercase_bin_n (name_len + 1u, 172 name, 173 buf + pos); 174 new_hdr->h2.name.data = buf + pos; 175 new_hdr->h2.name.size = name_len; 176 pos += name_len + 1u; 177 } 178 else 179 { 180 new_hdr->h2.name.data = new_hdr->name.cstr; 181 new_hdr->h2.name.size = new_hdr->name.len; 182 } 183 184 if ((0u != val_empty_prf) || (0u != val_empty_suf)) 185 { 186 memcpy (buf + pos, 187 value + val_empty_prf, 188 value_len - val_empty_prf - val_empty_suf); 189 buf[pos + value_len - val_empty_prf - val_empty_suf] = 0; 190 new_hdr->h2.value.data = buf + pos; 191 new_hdr->h2.value.size = value_len - val_empty_prf - val_empty_suf; 192 pos += value_len - val_empty_prf - val_empty_suf + 1u; 193 } 194 else 195 { 196 new_hdr->h2.value.data = new_hdr->value.cstr; 197 new_hdr->h2.value.size = new_hdr->value.len; 198 } 199 // TODO: implement checking name for "never-index" patterns and other indexing preferences patterns 200 } 201 else 202 { 203 new_hdr->h2.name.data = NULL; 204 new_hdr->h2.name.size = 0u; 205 new_hdr->h2.value.data = NULL; 206 new_hdr->h2.value.size = 0u; 207 } 208 mhd_assert ((NULL != new_hdr->h2.name.data) 209 || (0u == new_hdr->h2.name.size)); 210 mhd_assert ((NULL != new_hdr->h2.value.data) 211 || (0u == new_hdr->h2.value.size)); 212 mhd_assert ((NULL != new_hdr->h2.name.data) || \ 213 (NULL == new_hdr->h2.value.data)); 214 mhd_assert ((NULL != new_hdr->h2.value.data) || \ 215 (NULL == new_hdr->h2.name.data)); 216 #endif /* MHD_SUPPORT_HTTP2 */ 217 218 mhd_assert (strings_size == pos); 219 (void) pos; /* Mute compiler warning in non-debug builds */ 220 221 mhd_DLINKEDL_INIT_LINKS (new_hdr, headers); 222 mhd_DLINKEDL_INS_LAST (response, new_hdr, headers); 223 return true; 224 } 225 226 227 MHD_INTERNAL 228 MHD_FN_PAR_NONNULL_ (1) void 229 mhd_response_remove_all_headers (struct MHD_Response *restrict r) 230 { 231 struct mhd_ResponseHeader *hdr; 232 233 for (hdr = mhd_DLINKEDL_GET_LAST (r, headers); NULL != hdr; 234 hdr = mhd_DLINKEDL_GET_LAST (r, headers)) 235 { 236 mhd_DLINKEDL_DEL (r, hdr, headers); 237 free (hdr); 238 } 239 } 240 241 242 static enum MHD_StatusCode 243 response_add_header_int (struct MHD_Response *restrict response, 244 const char *restrict name, 245 const char *restrict value) 246 { 247 const size_t name_len = strlen (name); 248 const size_t value_len = strlen (value); 249 250 if (response->frozen) /* Re-check with the lock held */ 251 return MHD_SC_TOO_LATE; 252 253 if ((NULL != memchr (name, ' ', name_len)) || 254 (NULL != memchr (name, '\t', name_len)) || 255 (NULL != memchr (name, ':', name_len)) || 256 (NULL != memchr (name, '\n', name_len)) || 257 (NULL != memchr (name, '\r', name_len))) 258 return MHD_SC_RESP_HEADER_NAME_INVALID; 259 if ((NULL != memchr (value, '\n', value_len)) || 260 (NULL != memchr (value, '\r', value_len))) 261 return MHD_SC_RESP_HEADER_VALUE_INVALID; 262 263 if (mhd_str_equal_caseless_n_st (MHD_HTTP_HEADER_DATE, 264 name, 265 name_len)) 266 { 267 if (response->cfg.has_hdr_date) 268 return MHD_SC_RESP_HEADER_DATE_DUPLICATE; 269 270 if (! response_add_header_no_check (response, name_len, name, 271 value_len, value)) 272 return MHD_SC_RESP_HEADER_MEM_ALLOC_FAILED; 273 274 response->cfg.has_hdr_date = true; 275 276 return MHD_SC_OK; 277 } 278 279 if (mhd_str_equal_caseless_n_st (MHD_HTTP_HEADER_CONNECTION, 280 name, 281 name_len)) 282 { 283 if (response->cfg.has_hdr_conn) 284 return MHD_SC_RESP_HEADER_CONNECTION_DUPLICATE; 285 286 if (! response_add_header_no_check (response, name_len, name, 287 value_len, value)) 288 return MHD_SC_RESP_HEADER_MEM_ALLOC_FAILED; 289 290 response->cfg.has_hdr_conn = true; 291 292 return MHD_SC_OK; 293 } 294 295 // TODO: add special processing for "Content-Length", "Transfer-Encoding" 296 297 if (! response_add_header_no_check (response, name_len, name, 298 value_len, value)) 299 return MHD_SC_RESP_HEADER_MEM_ALLOC_FAILED; 300 301 return MHD_SC_OK; 302 } 303 304 305 MHD_EXTERN_ 306 MHD_FN_PAR_NONNULL_ (2) MHD_FN_PAR_CSTR_ (2) 307 MHD_FN_PAR_NONNULL_ (3) MHD_FN_PAR_CSTR_ (3) enum MHD_StatusCode 308 MHD_response_add_header (struct MHD_Response *MHD_RESTRICT response, 309 const char *MHD_RESTRICT name, 310 const char *MHD_RESTRICT value) 311 { 312 bool need_unlock; 313 enum MHD_StatusCode res; 314 315 if (NULL == response) 316 return MHD_SC_RESP_POINTER_NULL; 317 if (response->frozen) 318 return MHD_SC_TOO_LATE; 319 320 if (response->reuse.reusable) 321 { 322 need_unlock = true; 323 if (! mhd_mutex_lock (&(response->reuse.settings_lock))) 324 return MHD_SC_RESP_MUTEX_LOCK_FAILED; 325 mhd_assert (1 == mhd_atomic_counter_get (&(response->reuse.counter))); 326 } 327 else 328 need_unlock = false; 329 330 res = response_add_header_int (response, name, value); 331 332 if (need_unlock) 333 mhd_mutex_unlock_chk (&(response->reuse.settings_lock)); 334 335 return res; 336 } 337 338 339 MHD_EXTERN_ 340 MHD_FN_PAR_NONNULL_ (1) 341 MHD_FN_PAR_NONNULL_ (3) MHD_FN_PAR_CSTR_ (3) enum MHD_StatusCode 342 MHD_response_add_predef_header (struct MHD_Response *MHD_RESTRICT response, 343 enum MHD_PredefinedHeader stk, 344 const char *MHD_RESTRICT content) 345 { 346 (void) response; (void) stk; (void) content; 347 return MHD_SC_FEATURE_DISABLED; 348 }