libmicrohttpd2

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

response_from.c (14076B)


      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_2cls (enum MHD_HTTP_StatusCode sc,
    136                                  uint_fast64_t size,
    137                                  MHD_DynamicContentCreator dyn_cont,
    138                                  void *dyn_cont_cls,
    139                                  MHD_FreeCallback free_cb,
    140                                  void *free_cb_cls)
    141 {
    142   struct MHD_Response *restrict res;
    143   res = response_create_basic (sc, size, free_cb, free_cb_cls);
    144   if (NULL != res)
    145   {
    146     res->cntn_dtype = mhd_RESPONSE_CONTENT_DATA_CALLBACK;
    147     res->cntn.dyn.cb = dyn_cont;
    148     res->cntn.dyn.cls = dyn_cont_cls;
    149   }
    150   else if (NULL != free_cb)
    151     free_cb (free_cb_cls);
    152   return res;
    153 }
    154 
    155 
    156 static const unsigned char empty_buf[1] = { 0 };
    157 
    158 MHD_EXTERN_
    159 MHD_FN_PAR_IN_SIZE_ (3, 2) struct MHD_Response *
    160 MHD_response_from_buffer (
    161   enum MHD_HTTP_StatusCode sc,
    162   size_t buffer_size,
    163   const char *buffer,
    164   MHD_FreeCallback free_cb,
    165   void *free_cb_cls)
    166 {
    167   struct MHD_Response *restrict res;
    168 
    169 #if SIZEOF_SIZE_T >= 8
    170   if (MHD_SIZE_UNKNOWN == buffer_size)
    171   {
    172     if (NULL != free_cb)
    173       free_cb (free_cb_cls);
    174     return NULL;
    175   }
    176 #endif /* SIZEOF_SIZE_T >= 8 */
    177 
    178   res = response_create_basic (sc, buffer_size, free_cb, free_cb_cls);
    179   if (NULL != res)
    180   {
    181     res->cntn_dtype = mhd_RESPONSE_CONTENT_DATA_BUFFER;
    182     res->cntn.buf = (0 != buffer_size) ?
    183                     (const unsigned char *)buffer : empty_buf;
    184   }
    185   else if (NULL != free_cb)
    186     free_cb (free_cb_cls);
    187   return res;
    188 }
    189 
    190 
    191 static void
    192 response_cntn_free_buf (void *ptr)
    193 {
    194   free (ptr);
    195 }
    196 
    197 
    198 MHD_EXTERN_
    199 MHD_FN_PAR_IN_SIZE_ (3, 2) struct MHD_Response *
    200 MHD_response_from_buffer_copy (
    201   enum MHD_HTTP_StatusCode sc,
    202   size_t buffer_size,
    203   const char buffer[MHD_FN_PAR_DYN_ARR_SIZE_ (buffer_size)])
    204 {
    205   struct MHD_Response *restrict res;
    206   const unsigned char *buf_copy;
    207   unsigned char *new_buf;
    208 
    209 #if SIZEOF_SIZE_T >= 8
    210   if (MHD_SIZE_UNKNOWN == buffer_size)
    211     return NULL;
    212 #endif /* SIZEOF_SIZE_T >= 8 */
    213 
    214   if (0 != buffer_size)
    215   {
    216     new_buf = (unsigned char *)malloc (buffer_size);
    217     if (NULL == new_buf)
    218       return NULL;
    219     memcpy (new_buf, buffer, buffer_size);
    220     res = response_create_basic (sc, buffer_size,
    221                                  response_cntn_free_buf, new_buf);
    222     buf_copy = new_buf;
    223   }
    224   else
    225   {
    226     new_buf = NULL;
    227     buf_copy = empty_buf;
    228     res = response_create_basic (sc, 0, (MHD_FreeCallback)NULL, NULL);
    229   }
    230 
    231   if (NULL != res)
    232   {
    233     res->cntn_dtype = mhd_RESPONSE_CONTENT_DATA_BUFFER;
    234     res->cntn.buf = buf_copy;
    235     return res; /* Success exit point */
    236   }
    237 
    238   /* Cleanup path */
    239   if (NULL != new_buf)
    240     free (new_buf);
    241   return NULL;
    242 }
    243 
    244 
    245 static struct MHD_Response *
    246 response_from_iovec_inner (
    247   enum MHD_HTTP_StatusCode sc,
    248   unsigned int iov_count,
    249   const struct MHD_IoVec iov[MHD_FN_PAR_DYN_ARR_SIZE_ (iov_count)],
    250   MHD_FreeCallback free_cb,
    251   void *free_cb_cls)
    252 {
    253   unsigned int i;
    254   size_t i_cp = 0;   /**< Index in the copy of iov */
    255   uint_fast64_t total_size = 0;
    256 
    257   /* Calculate final size, number of valid elements, and check 'iov' */
    258   for (i = 0; i < iov_count; ++i)
    259   {
    260     if (0 == iov[i].iov_len)
    261       continue;     /* skip zero-sized elements */
    262     if (NULL == iov[i].iov_base)
    263       return NULL;  /* NULL pointer with non-zero size */
    264 
    265     total_size += iov[i].iov_len;
    266     if ((total_size < iov[i].iov_len) || (0 > (ssize_t)total_size)
    267         || (((size_t)total_size) != total_size))
    268       return NULL; /* Larger than send function may report as success */
    269 #if defined(MHD_SOCKETS_KIND_POSIX) || !defined(_WIN64)
    270     i_cp++;
    271 #else  /* ! MHD_SOCKETS_KIND_POSIX && _WIN64 */
    272     if (1)
    273     {
    274       size_t i_add;
    275 
    276       i_add = (size_t)(iov[i].iov_len / mhd_IOV_ELMN_MAX_SIZE);
    277       if (0 != iov[i].iov_len % mhd_IOV_ELMN_MAX_SIZE)
    278         i_add++;
    279       i_cp += i_add;
    280       if (i_cp < i_add)
    281         return NULL; /* Counter overflow */
    282     }
    283 #endif /* ! MHD_SOCKETS_KIND_POSIX && _WIN64 */
    284   }
    285   if (0 == total_size)
    286   {
    287     struct MHD_Response *restrict res;
    288 
    289     res = response_create_basic (sc, 0, free_cb, free_cb_cls);
    290     if (NULL != res)
    291     {
    292       res->cntn_dtype = mhd_RESPONSE_CONTENT_DATA_BUFFER;
    293       res->cntn.buf = empty_buf;
    294     }
    295     return res;
    296   }
    297   if (MHD_SIZE_UNKNOWN == total_size)
    298     return NULL;
    299 
    300   mhd_assert (0 < i_cp);
    301   if (1)
    302   { /* for local variables local scope only */
    303     struct MHD_Response *restrict res;
    304     mhd_iovec *iov_copy;
    305     size_t num_copy_elements = i_cp;
    306 
    307     iov_copy = (mhd_iovec *)
    308                mhd_calloc (num_copy_elements,
    309                            sizeof(mhd_iovec));
    310     if (NULL == iov_copy)
    311       return NULL;
    312 
    313     i_cp = 0;
    314     for (i = 0; i < iov_count; ++i)
    315     {
    316       size_t element_size = iov[i].iov_len;
    317       const unsigned char *buf = (const unsigned char *)iov[i].iov_base;
    318 
    319       if (0 == element_size)
    320         continue;         /* skip zero-sized elements */
    321 #if defined(MHD_SOCKETS_KIND_WINSOCK) && defined(_WIN64)
    322       while (mhd_IOV_ELMN_MAX_SIZE < element_size)
    323       {
    324         iov_copy[i_cp].iov_base =
    325           (mhd_IOV_ELMN_PTR_TYPE)mhd_DROP_CONST (buf);
    326         iov_copy[i_cp].iov_len = mhd_IOV_ELMN_MAX_SIZE;
    327         buf += mhd_IOV_ELMN_MAX_SIZE;
    328         element_size -= mhd_IOV_ELMN_MAX_SIZE;
    329         i_cp++;
    330       }
    331 #endif /* MHD_SOCKETS_KIND_WINSOCK && _WIN64 */
    332       iov_copy[i_cp].iov_base = (mhd_IOV_ELMN_PTR_TYPE)mhd_DROP_CONST (buf);
    333       iov_copy[i_cp].iov_len = (mhd_iov_elmn_size)element_size;
    334       i_cp++;
    335     }
    336     mhd_assert (num_copy_elements == i_cp);
    337     mhd_assert (0 < i_cp);
    338 
    339     res = response_create_basic (sc, total_size, free_cb, free_cb_cls);
    340     if (NULL != res)
    341     {
    342       res->cntn_dtype = mhd_RESPONSE_CONTENT_DATA_IOVEC;
    343       res->cntn.iovec.iov = iov_copy;
    344       res->cntn.iovec.cnt = i_cp;
    345       return res; /* Success exit point */
    346     }
    347 
    348     /* Below is a cleanup path */
    349     free (iov_copy);
    350   }
    351   return NULL;
    352 }
    353 
    354 
    355 MHD_EXTERN_ struct MHD_Response *
    356 MHD_response_from_iovec (
    357   enum MHD_HTTP_StatusCode sc,
    358   unsigned int iov_count,
    359   const struct MHD_IoVec iov[MHD_FN_PAR_DYN_ARR_SIZE_ (iov_count)],
    360   MHD_FreeCallback free_cb,
    361   void *free_cb_cls)
    362 {
    363   struct MHD_Response *res =
    364     response_from_iovec_inner (sc, iov_count, iov, free_cb, free_cb_cls);
    365   if ((NULL == res) && (NULL != free_cb))
    366     free_cb (free_cb_cls);
    367 
    368   return res;
    369 }
    370 
    371 
    372 static
    373 MHD_FN_PAR_FD_READ_ (2) struct MHD_Response *
    374 response_from_fd_inner (enum MHD_HTTP_StatusCode sc,
    375                         int fd,
    376                         uint_fast64_t offset,
    377                         uint_fast64_t size)
    378 {
    379   struct MHD_Response *restrict res;
    380   if (offset == MHD_SIZE_UNKNOWN)
    381     return NULL;
    382   if (size != MHD_SIZE_UNKNOWN)
    383   {
    384     if (size > ((size + offset) & 0xFFFFFFFFFFFFFFFFU))
    385       return NULL;
    386   }
    387   res = response_create_basic (sc, size, (MHD_FreeCallback)NULL, NULL);
    388   if (NULL != res)
    389   {
    390     res->cntn_dtype = mhd_RESPONSE_CONTENT_DATA_FILE;
    391     res->cntn.file.fd = fd;
    392     res->cntn.file.offset = offset;
    393 #ifdef mhd_USE_SENDFILE
    394     res->cntn.file.use_sf = (size < MHD_SIZE_UNKNOWN);
    395 #endif
    396     res->cntn.file.is_pipe = false; /* Not necessary */
    397   }
    398   return res;
    399 }
    400 
    401 
    402 MHD_EXTERN_
    403 MHD_FN_PAR_FD_READ_ (2) struct MHD_Response *
    404 MHD_response_from_fd (enum MHD_HTTP_StatusCode sc,
    405                       int fd,
    406                       uint_fast64_t offset,
    407                       uint_fast64_t size)
    408 {
    409   struct MHD_Response *res =
    410     response_from_fd_inner (sc, fd, offset, size);
    411   if (NULL == res)
    412     close (fd);
    413   return res;
    414 }
    415 
    416 
    417 MHD_EXTERN_
    418 MHD_FN_PAR_FD_READ_ (2) struct MHD_Response *
    419 MHD_response_from_pipe (enum MHD_HTTP_StatusCode sc,
    420                         int fd)
    421 {
    422   struct MHD_Response *restrict res;
    423   res = response_create_basic (sc,
    424                                MHD_SIZE_UNKNOWN,
    425                                (MHD_FreeCallback)NULL,
    426                                NULL);
    427   if (NULL != res)
    428   {
    429     res->cntn_dtype = mhd_RESPONSE_CONTENT_DATA_FILE;
    430     res->cntn.file.fd = fd;
    431     res->cntn.file.offset = 0; /* Not necessary */
    432 #ifdef mhd_USE_SENDFILE
    433     res->cntn.file.use_sf = false; /* Not necessary */
    434 #endif
    435     res->cntn.file.is_pipe = true;
    436   }
    437   else
    438     close (fd);
    439   return res;
    440 }
    441 
    442 
    443 /**
    444  * Create special internal response for sending error reply
    445  * @param sc the HTTP status code
    446  * @param cntn_len the length of the @a cntn
    447  * @param cntn the content of the response, could be NULL
    448  * @param spec_hdr_len the length of the @a spec_hdr
    449  * @param spec_hdr the special header line, without last CRLF,
    450  *                 if not NULL it will be deallocated by free().
    451  * @return the pointer to the created object if succeed,
    452  *         NULL otherwise
    453  *
    454  */
    455 MHD_INTERNAL
    456 MHD_FN_PAR_CSTR_ (3) MHD_FN_PAR_CSTR_ (5) struct MHD_Response *
    457 mhd_response_special_for_error (unsigned int sc,
    458                                 size_t cntn_len,
    459                                 const char *cntn,
    460                                 size_t spec_hdr_len,
    461                                 char *spec_hdr)
    462 {
    463   struct MHD_Response *restrict res;
    464 
    465   mhd_assert (100 <= sc);
    466   mhd_assert (600 > sc);
    467   mhd_assert ((NULL != cntn) || (0 == cntn_len));
    468   mhd_assert ((NULL != spec_hdr) || (0 == spec_hdr_len));
    469 
    470   res = (struct MHD_Response *)
    471         mhd_calloc (1,
    472                     sizeof(struct MHD_Response));
    473   if (NULL == res)
    474     return NULL;
    475 
    476 #ifndef HAVE_NULL_PTR_ALL_ZEROS
    477   mhd_DLINKEDL_INIT_LIST (res, headers);
    478   res->free.cb = NULL;
    479   res->free.cls = NULL;
    480   res->special_resp.spec_hdr = NULL;
    481 #endif /* ! HAVE_NULL_PTR_ALL_ZEROS */
    482   res->sc = (enum MHD_HTTP_StatusCode)sc;
    483   res->cntn_size = cntn_len;
    484   res->cntn_dtype = mhd_RESPONSE_CONTENT_DATA_BUFFER;
    485   res->cntn.buf = (const unsigned char *)((0 != cntn_len) ? cntn : "");
    486   res->cfg.close_forced = true;
    487   res->cfg.int_err_resp = true;
    488   res->special_resp.spec_hdr_len = spec_hdr_len;
    489   res->special_resp.spec_hdr = spec_hdr;
    490   res->frozen = true;
    491 
    492   return res;
    493 }