response_add_header.c (9908B)
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 (! response_add_header_no_check (response, name_len, name, 264 value_len, value)) 265 return MHD_SC_RESPONSE_HEADER_MEM_ALLOC_FAILED; 266 267 return MHD_SC_OK; 268 } 269 270 271 MHD_EXTERN_ 272 MHD_FN_PAR_NONNULL_ (2) MHD_FN_PAR_CSTR_ (2) 273 MHD_FN_PAR_NONNULL_ (3) MHD_FN_PAR_CSTR_ (3) enum MHD_StatusCode 274 MHD_response_add_header (struct MHD_Response *MHD_RESTRICT response, 275 const char *MHD_RESTRICT name, 276 const char *MHD_RESTRICT value) 277 { 278 bool need_unlock; 279 enum MHD_StatusCode res; 280 281 if (NULL == response) 282 return MHD_SC_RESP_POINTER_NULL; 283 if (response->frozen) 284 return MHD_SC_TOO_LATE; 285 286 if (response->reuse.reusable) 287 { 288 need_unlock = true; 289 if (! mhd_mutex_lock (&(response->reuse.settings_lock))) 290 return MHD_SC_RESPONSE_MUTEX_LOCK_FAILED; 291 mhd_assert (1 == mhd_atomic_counter_get (&(response->reuse.counter))); 292 } 293 else 294 need_unlock = false; 295 296 // TODO: add special processing for "Date", "Connection", "Content-Length", "Transfer-Encoding" 297 298 res = response_add_header_int (response, name, value); 299 300 if (need_unlock) 301 mhd_mutex_unlock_chk (&(response->reuse.settings_lock)); 302 303 return res; 304 } 305 306 307 MHD_EXTERN_ 308 MHD_FN_PAR_NONNULL_ (1) 309 MHD_FN_PAR_NONNULL_ (3) MHD_FN_PAR_CSTR_ (3) enum MHD_StatusCode 310 MHD_response_add_predef_header (struct MHD_Response *MHD_RESTRICT response, 311 enum MHD_PredefinedHeader stk, 312 const char *MHD_RESTRICT content) 313 { 314 (void) response; (void) stk; (void) content; 315 return MHD_SC_FEATURE_DISABLED; 316 }