libmicrohttpd2

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

response_from.c (14362B)


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