response_add_header.c (10861B)
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 ((0u == name_len) 254 || (NULL != memchr (name, ' ', name_len)) 255 || (NULL != memchr (name, '\t', name_len)) 256 || (NULL != memchr (name, ':', name_len)) 257 || (NULL != memchr (name, '\n', name_len)) 258 || (NULL != memchr (name, '\r', name_len))) 259 return MHD_SC_RESP_HEADER_NAME_INVALID; 260 if ((NULL != memchr (value, '\n', value_len)) 261 || (NULL != memchr (value, '\r', value_len))) 262 return MHD_SC_RESP_HEADER_VALUE_INVALID; 263 264 if (mhd_str_equal_caseless_n_st (MHD_HTTP_HEADER_DATE, 265 name, 266 name_len)) 267 { 268 if (response->cfg.has_hdr_date) 269 return MHD_SC_RESP_HEADER_DATE_DUPLICATE; 270 271 if (!response_add_header_no_check (response, name_len, name, 272 value_len, value)) 273 return MHD_SC_RESP_HEADER_MEM_ALLOC_FAILED; 274 275 response->cfg.has_hdr_date = true; 276 277 return MHD_SC_OK; 278 } 279 280 if (mhd_str_equal_caseless_n_st (MHD_HTTP_HEADER_CONNECTION, 281 name, 282 name_len)) 283 { 284 if (response->cfg.has_hdr_conn) 285 return MHD_SC_RESP_HEADER_CONNECTION_DUPLICATE; 286 287 if (!response_add_header_no_check (response, name_len, name, 288 value_len, value)) 289 return MHD_SC_RESP_HEADER_MEM_ALLOC_FAILED; 290 291 response->cfg.has_hdr_conn = true; 292 293 return MHD_SC_OK; 294 } 295 296 // TODO: add special processing for "Content-Length", "Transfer-Encoding" 297 298 if (!response_add_header_no_check (response, name_len, name, 299 value_len, value)) 300 return MHD_SC_RESP_HEADER_MEM_ALLOC_FAILED; 301 302 return MHD_SC_OK; 303 } 304 305 306 MHD_EXTERN_ 307 MHD_FN_PAR_NONNULL_ (2) MHD_FN_PAR_CSTR_ (2) 308 MHD_FN_PAR_NONNULL_ (3) MHD_FN_PAR_CSTR_ (3) enum MHD_StatusCode 309 MHD_response_add_header (struct MHD_Response *MHD_RESTRICT response, 310 const char *MHD_RESTRICT name, 311 const char *MHD_RESTRICT value) 312 { 313 bool need_unlock; 314 enum MHD_StatusCode res; 315 316 if (NULL == response) 317 return MHD_SC_RESP_POINTER_NULL; 318 if (response->frozen) 319 return MHD_SC_TOO_LATE; 320 321 if (response->reuse.reusable) 322 { 323 need_unlock = true; 324 if (!mhd_mutex_lock (&(response->reuse.settings_lock))) 325 return MHD_SC_RESP_MUTEX_LOCK_FAILED; 326 mhd_assert (1 == mhd_atomic_counter_get (&(response->reuse.counter))); 327 } 328 else 329 need_unlock = false; 330 331 res = response_add_header_int (response, name, value); 332 333 if (need_unlock) 334 mhd_mutex_unlock_chk (&(response->reuse.settings_lock)); 335 336 return res; 337 } 338 339 340 MHD_EXTERN_ 341 MHD_FN_PAR_NONNULL_ (1) 342 MHD_FN_PAR_NONNULL_ (3) MHD_FN_PAR_CSTR_ (3) enum MHD_StatusCode 343 MHD_response_add_predef_header (struct MHD_Response *MHD_RESTRICT response, 344 enum MHD_PredefinedHeader stk, 345 const char *MHD_RESTRICT content) 346 { 347 (void)response; 348 (void)stk; 349 (void)content; 350 return MHD_SC_FEATURE_DISABLED; 351 }