libmicrohttpd2

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

response_from.c (13035B)


      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_from.c
     41  * @brief  The definitions of MHD_response_from_X() functions and related
     42  *         internal functions
     43  * @author Karlson2k (Evgeny Grin)
     44  */
     45 
     46 #include "mhd_sys_options.h"
     47 
     48 #include "response_from.h"
     49 
     50 #include <string.h>
     51 
     52 #include "sys_bool_type.h"
     53 #include "sys_base_types.h"
     54 
     55 #include "compat_calloc.h"
     56 #include "sys_malloc.h"
     57 #include "sys_file_fd.h"
     58 
     59 #include "mhd_public_api.h"
     60 
     61 #include "mhd_locks.h"
     62 #include "mhd_response.h"
     63 #include "response_options.h"
     64 
     65 #include "mhd_assert.h"
     66 
     67 #include "mhd_limits.h"
     68 
     69 static struct MHD_Response *
     70 response_create_basic (enum MHD_HTTP_StatusCode sc,
     71                        uint_fast64_t cntn_size,
     72                        MHD_FreeCallback free_cb,
     73                        void *free_cb_cls)
     74 {
     75   struct MHD_Response *restrict r;
     76   struct ResponseOptions *restrict s;
     77 
     78   if ((100 > sc) || (999 < sc))
     79     return NULL;
     80 
     81   r = (struct MHD_Response *)
     82       mhd_calloc (1,
     83                   sizeof(struct MHD_Response));
     84   if (NULL != r)
     85   {
     86     s = (struct ResponseOptions *)
     87         mhd_calloc (1,
     88                     sizeof(struct ResponseOptions));
     89     if (NULL != s)
     90     {
     91 #ifndef HAVE_NULL_PTR_ALL_ZEROS
     92       mhd_DLINKEDL_INIT_LIST (r, headers);
     93 #ifdef MHD_SUPPORT_AUTH_DIGEST
     94       mhd_DLINKEDL_INIT_LIST (r, auth_d_hdrs);
     95 #endif
     96       r->free.cb = NULL;
     97       r->free.cls = NULL;
     98       r->special_resp.spec_hdr = NULL;
     99 
    100       s->termination_callback.v_ended_cb = NULL;
    101       s->termination_callback.v_ended_cb_cls = NULL;
    102 #endif /* ! HAVE_NULL_PTR_ALL_ZEROS */
    103 
    104       r->sc = sc;
    105       r->cntn_size = cntn_size;
    106       r->free.cb = free_cb;
    107       r->free.cls = free_cb_cls;
    108       r->settings = s;
    109 
    110       return r; /* Success exit point */
    111     }
    112     free (r);
    113   }
    114   return NULL; /* Failure exit point */
    115 }
    116 
    117 
    118 MHD_INTERNAL
    119 MHD_FN_PAR_NONNULL_ (1) void
    120 mhd_response_deinit_content_data (struct MHD_Response *restrict r)
    121 {
    122   mhd_assert (mhd_RESPONSE_CONTENT_DATA_INVALID != r->cntn_dtype);
    123   if (mhd_RESPONSE_CONTENT_DATA_IOVEC == r->cntn_dtype)
    124     free (r->cntn.iovec.iov);
    125   else if (mhd_RESPONSE_CONTENT_DATA_FILE == r->cntn_dtype)
    126     close (r->cntn.file.fd);
    127   /* For #mhd_RESPONSE_CONTENT_DATA_BUFFER clean-up performed by callback
    128      for both modes: internal copy and external cleanup */
    129   if (NULL != r->free.cb)
    130     r->free.cb (r->free.cls);
    131 }
    132 
    133 
    134 MHD_EXTERN_ struct MHD_Response *
    135 MHD_response_from_callback (enum MHD_HTTP_StatusCode sc,
    136                             uint_fast64_t size,
    137                             MHD_DynamicContentCreator dyn_cont,
    138                             void *dyn_cont_cls,
    139                             MHD_FreeCallback dyn_cont_fc)
    140 {
    141   struct MHD_Response *restrict res;
    142   res = response_create_basic (sc, size, dyn_cont_fc, dyn_cont_cls);
    143   if (NULL != res)
    144   {
    145     res->cntn_dtype = mhd_RESPONSE_CONTENT_DATA_CALLBACK;
    146     res->cntn.dyn.cb = dyn_cont;
    147     res->cntn.dyn.cls = dyn_cont_cls;
    148   }
    149   return res;
    150 }
    151 
    152 
    153 static const unsigned char empty_buf[1] = { 0 };
    154 
    155 MHD_EXTERN_
    156 MHD_FN_PAR_IN_SIZE_ (3,2) struct MHD_Response *
    157 MHD_response_from_buffer (
    158   enum MHD_HTTP_StatusCode sc,
    159   size_t buffer_size,
    160   const char *buffer,
    161   MHD_FreeCallback free_cb,
    162   void *free_cb_cls)
    163 {
    164   struct MHD_Response *restrict res;
    165 
    166 #if SIZEOF_SIZE_T >= 8
    167   if (MHD_SIZE_UNKNOWN == buffer_size)
    168     return NULL;
    169 #endif /* SIZEOF_SIZE_T >= 8 */
    170 
    171   res = response_create_basic (sc, buffer_size, free_cb, free_cb_cls);
    172   if (NULL != res)
    173   {
    174     res->cntn_dtype = mhd_RESPONSE_CONTENT_DATA_BUFFER;
    175     res->cntn.buf = (0 != buffer_size) ?
    176                     (const unsigned char *) buffer : empty_buf;
    177   }
    178   return res;
    179 }
    180 
    181 
    182 static void
    183 response_cntn_free_buf (void *ptr)
    184 {
    185   free (ptr);
    186 }
    187 
    188 
    189 MHD_EXTERN_
    190 MHD_FN_PAR_IN_SIZE_ (3,2) struct MHD_Response *
    191 MHD_response_from_buffer_copy (
    192   enum MHD_HTTP_StatusCode sc,
    193   size_t buffer_size,
    194   const char buffer[MHD_FN_PAR_DYN_ARR_SIZE_ (buffer_size)])
    195 {
    196   struct MHD_Response *restrict res;
    197   const unsigned char *buf_copy;
    198   unsigned char *new_buf;
    199 
    200 #if SIZEOF_SIZE_T >= 8
    201   if (MHD_SIZE_UNKNOWN == buffer_size)
    202     return NULL;
    203 #endif /* SIZEOF_SIZE_T >= 8 */
    204 
    205   if (0 != buffer_size)
    206   {
    207     new_buf = (unsigned char *) malloc (buffer_size);
    208     if (NULL == new_buf)
    209       return NULL;
    210     memcpy (new_buf, buffer, buffer_size);
    211     res = response_create_basic (sc, buffer_size,
    212                                  response_cntn_free_buf, new_buf);
    213     buf_copy = new_buf;
    214   }
    215   else
    216   {
    217     new_buf = NULL;
    218     buf_copy = empty_buf;
    219     res = response_create_basic (sc, 0, (MHD_FreeCallback) NULL, NULL);
    220   }
    221 
    222   if (NULL != res)
    223   {
    224     res->cntn_dtype = mhd_RESPONSE_CONTENT_DATA_BUFFER;
    225     res->cntn.buf = buf_copy;
    226     return res; /* Success exit point */
    227   }
    228 
    229   /* Cleanup path */
    230   if (NULL != new_buf)
    231     free (new_buf);
    232   return NULL;
    233 }
    234 
    235 
    236 MHD_EXTERN_ struct MHD_Response *
    237 MHD_response_from_iovec (
    238   enum MHD_HTTP_StatusCode sc,
    239   unsigned int iov_count,
    240   const struct MHD_IoVec iov[MHD_FN_PAR_DYN_ARR_SIZE_ (iov_count)],
    241   MHD_FreeCallback free_cb,
    242   void *free_cb_cls)
    243 {
    244   unsigned int i;
    245   size_t i_cp = 0;   /**< Index in the copy of iov */
    246   uint_fast64_t total_size = 0;
    247 
    248   /* Calculate final size, number of valid elements, and check 'iov' */
    249   for (i = 0; i < iov_count; ++i)
    250   {
    251     if (0 == iov[i].iov_len)
    252       continue;     /* skip zero-sized elements */
    253     if (NULL == iov[i].iov_base)
    254       return NULL;  /* NULL pointer with non-zero size */
    255 
    256     total_size += iov[i].iov_len;
    257     if ((total_size < iov[i].iov_len) || (0 > (ssize_t) total_size)
    258         || (((size_t) total_size) != total_size))
    259       return NULL; /* Larger than send function may report as success */
    260 #if defined(MHD_SOCKETS_KIND_POSIX) || ! defined(_WIN64)
    261     i_cp++;
    262 #else  /* ! MHD_SOCKETS_KIND_POSIX && _WIN64 */
    263     if (1)
    264     {
    265       size_t i_add;
    266 
    267       i_add = (size_t) (iov[i].iov_len / mhd_IOV_ELMN_MAX_SIZE);
    268       if (0 != iov[i].iov_len % mhd_IOV_ELMN_MAX_SIZE)
    269         i_add++;
    270       i_cp += i_add;
    271       if (i_cp < i_add)
    272         return NULL; /* Counter overflow */
    273     }
    274 #endif /* ! MHD_SOCKETS_KIND_POSIX && _WIN64 */
    275   }
    276   if (0 == total_size)
    277   {
    278     struct MHD_Response *restrict res;
    279 
    280     res = response_create_basic (sc, 0, free_cb, free_cb_cls);
    281     if (NULL != res)
    282     {
    283       res->cntn_dtype = mhd_RESPONSE_CONTENT_DATA_BUFFER;
    284       res->cntn.buf = empty_buf;
    285     }
    286     return res;
    287   }
    288   if (MHD_SIZE_UNKNOWN == total_size)
    289     return NULL;
    290 
    291   mhd_assert (0 < i_cp);
    292   if (1)
    293   { /* for local variables local scope only */
    294     struct MHD_Response *restrict res;
    295     mhd_iovec *iov_copy;
    296     size_t num_copy_elements = i_cp;
    297 
    298     iov_copy = (mhd_iovec *)
    299                mhd_calloc (num_copy_elements,
    300                            sizeof(mhd_iovec));
    301     if (NULL == iov_copy)
    302       return NULL;
    303 
    304     i_cp = 0;
    305     for (i = 0; i < iov_count; ++i)
    306     {
    307       size_t element_size = iov[i].iov_len;
    308       const unsigned char *buf = (const unsigned char *) iov[i].iov_base;
    309 
    310       if (0 == element_size)
    311         continue;         /* skip zero-sized elements */
    312 #if defined(MHD_SOCKETS_KIND_WINSOCK) && defined(_WIN64)
    313       while (mhd_IOV_ELMN_MAX_SIZE < element_size)
    314       {
    315         iov_copy[i_cp].iov_base =
    316           (mhd_IOV_ELMN_PTR_TYPE) mhd_DROP_CONST (buf);
    317         iov_copy[i_cp].iov_len = mhd_IOV_ELMN_MAX_SIZE;
    318         buf += mhd_IOV_ELMN_MAX_SIZE;
    319         element_size -= mhd_IOV_ELMN_MAX_SIZE;
    320         i_cp++;
    321       }
    322 #endif /* MHD_SOCKETS_KIND_WINSOCK && _WIN64 */
    323       iov_copy[i_cp].iov_base = (mhd_IOV_ELMN_PTR_TYPE) mhd_DROP_CONST (buf);
    324       iov_copy[i_cp].iov_len = (mhd_iov_elmn_size) element_size;
    325       i_cp++;
    326     }
    327     mhd_assert (num_copy_elements == i_cp);
    328     mhd_assert (0 < i_cp);
    329 
    330     res = response_create_basic (sc, total_size, free_cb, free_cb_cls);
    331     if (NULL != res)
    332     {
    333       res->cntn_dtype = mhd_RESPONSE_CONTENT_DATA_IOVEC;
    334       res->cntn.iovec.iov = iov_copy;
    335       res->cntn.iovec.cnt = i_cp;
    336       return res; /* Success exit point */
    337     }
    338 
    339     /* Below is a cleanup path */
    340     free (iov_copy);
    341   }
    342   return NULL;
    343 }
    344 
    345 
    346 MHD_EXTERN_
    347 MHD_FN_PAR_FD_READ_ (2) struct MHD_Response *
    348 MHD_response_from_fd (enum MHD_HTTP_StatusCode sc,
    349                       int fd,
    350                       uint_fast64_t offset,
    351                       uint_fast64_t size)
    352 {
    353   struct MHD_Response *restrict res;
    354   if (offset == MHD_SIZE_UNKNOWN)
    355     return NULL;
    356   if (size != MHD_SIZE_UNKNOWN)
    357   {
    358     if (size > ((size + offset) & 0xFFFFFFFFFFFFFFFFU))
    359       return NULL;
    360   }
    361   res = response_create_basic (sc, size, (MHD_FreeCallback) NULL, NULL);
    362   if (NULL != res)
    363   {
    364     res->cntn_dtype = mhd_RESPONSE_CONTENT_DATA_FILE;
    365     res->cntn.file.fd = fd;
    366     res->cntn.file.offset = offset;
    367 #ifdef mhd_USE_SENDFILE
    368     res->cntn.file.use_sf = (size < MHD_SIZE_UNKNOWN);
    369 #endif
    370     res->cntn.file.is_pipe = false; /* Not necessary */
    371   }
    372   return res;
    373 }
    374 
    375 
    376 MHD_EXTERN_
    377 MHD_FN_PAR_FD_READ_ (2) struct MHD_Response *
    378 MHD_response_from_pipe (enum MHD_HTTP_StatusCode sc,
    379                         int fd)
    380 {
    381   struct MHD_Response *restrict res;
    382   res = response_create_basic (sc,
    383                                MHD_SIZE_UNKNOWN,
    384                                (MHD_FreeCallback) NULL,
    385                                NULL);
    386   if (NULL != res)
    387   {
    388     res->cntn_dtype = mhd_RESPONSE_CONTENT_DATA_FILE;
    389     res->cntn.file.fd = fd;
    390     res->cntn.file.offset = 0; /* Not necessary */
    391 #ifdef mhd_USE_SENDFILE
    392     res->cntn.file.use_sf = false; /* Not necessary */
    393 #endif
    394     res->cntn.file.is_pipe = true;
    395   }
    396   return res;
    397 }
    398 
    399 
    400 /**
    401  * Create special internal response for sending error reply
    402  * @param sc the HTTP status code
    403  * @param cntn_len the length of the @a cntn
    404  * @param cntn the content of the response, could be NULL
    405  * @param spec_hdr_len the length of the @a spec_hdr
    406  * @param spec_hdr the special header line, without last CRLF,
    407  *                 if not NULL it will be deallocated by free().
    408  * @return the pointer to the created object if succeed,
    409  *         NULL otherwise
    410  *
    411  */
    412 MHD_INTERNAL
    413 MHD_FN_PAR_CSTR_ (3) MHD_FN_PAR_CSTR_ (5) struct MHD_Response *
    414 mhd_response_special_for_error (unsigned int sc,
    415                                 size_t cntn_len,
    416                                 const char *cntn,
    417                                 size_t spec_hdr_len,
    418                                 char *spec_hdr)
    419 {
    420   struct MHD_Response *restrict res;
    421 
    422   mhd_assert (100 <= sc);
    423   mhd_assert (600 > sc);
    424   mhd_assert ((NULL != cntn) || (0 == cntn_len));
    425   mhd_assert ((NULL != spec_hdr) || (0 == spec_hdr_len));
    426 
    427   res = (struct MHD_Response *)
    428         mhd_calloc (1,
    429                     sizeof(struct MHD_Response));
    430   if (NULL == res)
    431     return NULL;
    432 
    433 #ifndef HAVE_NULL_PTR_ALL_ZEROS
    434   mhd_DLINKEDL_INIT_LIST (res, headers);
    435   res->free.cb = NULL;
    436   res->free.cls = NULL;
    437   res->special_resp.spec_hdr = NULL;
    438 #endif /* ! HAVE_NULL_PTR_ALL_ZEROS */
    439   res->sc = (enum MHD_HTTP_StatusCode) sc;
    440   res->cntn_size = cntn_len;
    441   res->cntn_dtype = mhd_RESPONSE_CONTENT_DATA_BUFFER;
    442   res->cntn.buf = (const unsigned char *) ((0 != cntn_len) ? cntn : "");
    443   res->cfg.close_forced = true;
    444   res->cfg.int_err_resp = true;
    445   res->special_resp.spec_hdr_len = spec_hdr_len;
    446   res->special_resp.spec_hdr = spec_hdr;
    447   res->frozen = true;
    448 
    449   return res;
    450 }