libmicrohttpd2

HTTP server C library (MHD 2.x, alpha)
Log | Files | Refs | README | LICENSE

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 }