libmicrohttpd2

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

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 }