libmicrohttpd2

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

stream_process_reply.c (48906B)


      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) 2014-2024 Evgeny Grin (Karlson2k)
      5   Copyright (C) 2007-2020 Daniel Pittman and Christian Grothoff
      6 
      7   GNU libmicrohttpd is free software; you can redistribute it and/or
      8   modify it under the terms of the GNU Lesser General Public
      9   License as published by the Free Software Foundation; either
     10   version 2.1 of the License, or (at your option) any later version.
     11 
     12   GNU libmicrohttpd is distributed in the hope that it will be useful,
     13   but WITHOUT ANY WARRANTY; without even the implied warranty of
     14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     15   Lesser General Public License for more details.
     16 
     17   Alternatively, you can redistribute GNU libmicrohttpd and/or
     18   modify it under the terms of the GNU General Public License as
     19   published by the Free Software Foundation; either version 2 of
     20   the License, or (at your option) any later version, together
     21   with the eCos exception, as follows:
     22 
     23     As a special exception, if other files instantiate templates or
     24     use macros or inline functions from this file, or you compile this
     25     file and link it with other works to produce a work based on this
     26     file, this file does not by itself cause the resulting work to be
     27     covered by the GNU General Public License. However the source code
     28     for this file must still be made available in accordance with
     29     section (3) of the GNU General Public License v2.
     30 
     31     This exception does not invalidate any other reasons why a work
     32     based on this file might be covered by the GNU General Public
     33     License.
     34 
     35   You should have received copies of the GNU Lesser General Public
     36   License and the GNU General Public License along with this library;
     37   if not, see <https://www.gnu.org/licenses/>.
     38 */
     39 
     40 /**
     41  * @file src/mhd2/stream_process_reply.h
     42  * @brief  The implementation of internal functions for forming and sending
     43  *         replies for requests
     44  * @author Karlson2k (Evgeny Grin)
     45  *
     46  * Based on the MHD v0.x code by Daniel Pittman, Christian Grothoff and other
     47  * contributors.
     48  */
     49 
     50 #include "mhd_sys_options.h"
     51 
     52 #include "sys_bool_type.h"
     53 #include "sys_base_types.h"
     54 
     55 #include "mhd_assert.h"
     56 #include "mhd_unreachable.h"
     57 #include "mhd_predict.h"
     58 
     59 #include <string.h>
     60 #ifdef HAVE_TIME_H
     61 #  include <time.h>
     62 #endif
     63 
     64 #include "mhd_daemon.h"
     65 #include "mhd_response.h"
     66 #include "mhd_reply.h"
     67 #include "mhd_connection.h"
     68 
     69 #include "daemon_logger.h"
     70 
     71 #include "mhd_str.h"
     72 #include "http_status_str.h"
     73 #include "stream_process_reply.h"
     74 #include "stream_funcs.h"
     75 #include "request_get_value.h"
     76 #ifdef MHD_SUPPORT_AUTH_DIGEST
     77 #  include "mhd_digest_auth_data.h"
     78 #  include "auth_digest.h"
     79 #endif
     80 
     81 #include "mhd_read_file.h"
     82 
     83 #include "mhd_public_api.h"
     84 
     85 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ void
     86 mhd_stream_call_dcc_cleanup_if_needed (struct MHD_Connection *restrict c)
     87 {
     88   if (mhd_DCC_ACTION_CONTINUE != c->rp.app_act.act)
     89     return;
     90   if (NULL == c->rp.app_act.data.cntnue.iov_data)
     91     return;
     92 
     93   mhd_assert (mhd_RESPONSE_CONTENT_DATA_CALLBACK == \
     94               c->rp.response->cntn_dtype);
     95 
     96   if (NULL != c->rp.app_act.data.cntnue.iov_data->iov_fcb)
     97   {
     98     c->rp.app_act.data.cntnue.iov_data->iov_fcb (
     99       c->rp.app_act.data.cntnue.iov_data->iov_fcb_cls);
    100   }
    101 
    102   c->rp.app_act.data.cntnue.iov_data = NULL;
    103 }
    104 
    105 
    106 /**
    107  * This enum type describes requirements for reply body and reply bode-specific
    108  * headers (namely Content-Length, Transfer-Encoding).
    109  */
    110 enum replyBodyUse
    111 {
    112   /**
    113    * No reply body allowed.
    114    * Reply body headers 'Content-Length:' or 'Transfer-Encoding: chunked' are
    115    * not allowed as well.
    116    */
    117   RP_BODY_NONE = 0,
    118 
    119   /**
    120    * Do not send reply body.
    121    * Reply body headers 'Content-Length:' or 'Transfer-Encoding: chunked' are
    122    * allowed, but optional.
    123    */
    124   RP_BODY_HEADERS_ONLY = 1,
    125 
    126   /**
    127    * Send reply body and
    128    * reply body headers 'Content-Length:' or 'Transfer-Encoding: chunked'.
    129    * Reply body headers are required.
    130    */
    131   RP_BODY_SEND = 2
    132 };
    133 
    134 
    135 /**
    136  * Is it allowed to reuse the connection?
    137  * The TCP stream can be reused for the next requests if the connection
    138  * is HTTP 1.1 and the "Connection" header either does not exist or
    139  * is not set to "close", or if the connection is HTTP 1.0 and the
    140  * "Connection" header is explicitly set to "keep-alive".
    141  * If no HTTP version is specified (or if it is not 1.0 or 1.1), the connection
    142  * is definitively closed.  If the "Connection" header is not exactly "close"
    143  * or "keep-alive", connection is reused if is it HTTP/1.1.
    144  * If response has HTTP/1.0 flag or has "Connection: close" header
    145  * then connection must be closed.
    146  * If full request has not been read then connection must be closed
    147  * as well as more client data may be sent.
    148  *
    149  * @param c the connection to check for re-use
    150  * @return mhd_CONN_KEEPALIVE_POSSIBLE if (based on the request and
    151  *         the response) a connection could be reused,
    152  *         MHD_CONN_MUST_CLOSE if connection must be closed after sending
    153  *         complete reply,
    154  *         mhd_CONN_MUST_UPGRADE if connection must be upgraded.
    155  */
    156 static MHD_FN_PAR_NONNULL_ALL_ enum mhd_ConnReuse
    157 get_conn_reuse (struct MHD_Connection *c)
    158 {
    159   const struct MHD_Response *const restrict rp = c->rp.response;
    160 
    161   mhd_assert (NULL != rp);
    162   if (mhd_CONN_MUST_CLOSE == c->conn_reuse)
    163     return mhd_CONN_MUST_CLOSE;
    164 
    165   mhd_assert ((!c->stop_with_error) || (c->discard_request));
    166   if ((c->sk.state.rmt_shut_wr) || (c->discard_request))
    167     return mhd_CONN_MUST_CLOSE;
    168 
    169   if (rp->cfg.close_forced)
    170     return mhd_CONN_MUST_CLOSE;
    171 
    172   mhd_assert ((MHD_SIZE_UNKNOWN != rp->cntn_size) \
    173               || (!rp->cfg.mode_1_0));
    174 
    175   if (!MHD_HTTP_VERSION_IS_1X (c->rq.http_ver))
    176     return mhd_CONN_MUST_CLOSE;
    177 
    178   if (rp->cfg.mode_1_0
    179       && !mhd_stream_has_header_token_st (c,
    180                                           MHD_HTTP_HEADER_CONNECTION,
    181                                           "keep-alive"))
    182     return mhd_CONN_MUST_CLOSE;
    183 
    184 #if 0 // def MHD_SUPPORT_UPGRADE // TODO: Implement upgrade support
    185   /* TODO: Move below the next check when MHD stops closing connections
    186    * when response is queued in first callback */
    187   if (NULL != r->upgrade_handler)
    188   {
    189     /* No "close" token is enforced by 'add_response_header_connection()' */
    190     mhd_assert (0 == (r->flags_auto & MHD_RAF_HAS_CONNECTION_CLOSE));
    191     /* Valid HTTP version is enforced by 'MHD_queue_response()' */
    192     mhd_assert (MHD_IS_HTTP_VER_SUPPORTED (c->rq.http_ver));
    193     mhd_assert (!c->stop_with_error);
    194     return mhd_CONN_MUST_UPGRADE;
    195   }
    196 #endif /* MHD_SUPPORT_UPGRADE */
    197 
    198   return mhd_CONN_KEEPALIVE_POSSIBLE;
    199 }
    200 
    201 
    202 /**
    203  * Check whether reply body must be used.
    204  *
    205  * If reply body is needed, it could be zero-sized.
    206  *
    207  * @param c the connection to check
    208  * @param rcode the response code
    209  * @return enum value indicating whether response body can be used and
    210  *         whether response body length headers are allowed or required.
    211  * @sa is_reply_body_header_needed()
    212  */
    213 static enum replyBodyUse
    214 is_reply_body_needed (struct MHD_Connection *restrict c,
    215                       uint_fast16_t rcode)
    216 {
    217   mhd_assert (100 <= rcode);
    218   mhd_assert (999 >= rcode);
    219 
    220   if (199 >= rcode)
    221     return RP_BODY_NONE;
    222 
    223   if (MHD_HTTP_STATUS_NO_CONTENT == rcode)
    224     return RP_BODY_NONE;
    225 
    226 #if 0
    227   /* This check is not needed as upgrade handler is used only with code 101 */
    228 #  ifdef MHD_SUPPORT_UPGRADE
    229   if (NULL != rp.response->upgrade_handler)
    230     return RP_BODY_NONE;
    231 #  endif /* MHD_SUPPORT_UPGRADE */
    232 #endif
    233 
    234 #if 0
    235   /* CONNECT is not supported by MHD */
    236   /* Successful responses for connect requests are filtered by
    237    * MHD_queue_response() */
    238   if ((mhd_HTTP_METHOD_CONNECT == c->rq.http_mthd)
    239       && (2 == rcode / 100))
    240     return false; /* Actually pass-through CONNECT is not supported by MHD */
    241 #endif
    242 
    243   /* Reply body headers could be used.
    244    * Check whether reply body itself must be used. */
    245 
    246   if (mhd_HTTP_METHOD_HEAD == c->rq.http_mthd)
    247     return RP_BODY_HEADERS_ONLY;
    248 
    249   if (MHD_HTTP_STATUS_NOT_MODIFIED == rcode)
    250     return RP_BODY_HEADERS_ONLY;
    251 
    252   /* Reply body must be sent.
    253    * The body may have zero length, but body size must be indicated by
    254    * headers ('Content-Length:' or 'Transfer-Encoding: chunked'). */
    255   return RP_BODY_SEND;
    256 }
    257 
    258 
    259 /**
    260  * Setup connection reply properties.
    261  *
    262  * Reply properties include presence of reply body, transfer-encoding
    263  * type and other.
    264  *
    265  * @param c the connection to process
    266  */
    267 static MHD_FN_PAR_NONNULL_ALL_ void
    268 setup_reply_properties (struct MHD_Connection *restrict c)
    269 {
    270   struct MHD_Response *const restrict r = c->rp.response;  /**< a short alias */
    271   enum replyBodyUse use_rp_body;
    272   bool use_chunked;
    273   bool end_by_closing;
    274 
    275   mhd_assert (NULL != r);
    276 
    277   /* ** Adjust reply properties ** */
    278 
    279   c->conn_reuse = get_conn_reuse (c);
    280   use_rp_body = is_reply_body_needed (c, r->sc);
    281   c->rp.props.send_reply_body = (use_rp_body > RP_BODY_HEADERS_ONLY);
    282   c->rp.props.use_reply_body_headers = (use_rp_body >= RP_BODY_HEADERS_ONLY);
    283 
    284 #if 0 // def MHD_SUPPORT_UPGRADE // TODO: upgrade support
    285   mhd_assert ((NULL == r->upgrade_handler)
    286               || (RP_BODY_NONE == use_rp_body));
    287 #endif /* MHD_SUPPORT_UPGRADE */
    288 
    289   use_chunked = false;
    290   end_by_closing = false;
    291   if (c->rp.props.use_reply_body_headers)
    292   {
    293     if (r->cfg.chunked)
    294     {
    295       mhd_assert (!r->cfg.mode_1_0);
    296       use_chunked = MHD_HTTP_VERSION_IS_LIKE_11 (c->rq.http_ver);
    297     }
    298     if ((MHD_SIZE_UNKNOWN == r->cntn_size)
    299         && (!use_chunked)
    300         && (c->rp.props.send_reply_body))
    301     {
    302       /* End of the stream is indicated by closure */
    303       end_by_closing = true;
    304     }
    305   }
    306 
    307   if (end_by_closing)
    308   {
    309     mhd_assert (mhd_CONN_MUST_UPGRADE != c->conn_reuse);
    310     /* End of the stream is indicated by closure */
    311     c->conn_reuse = mhd_CONN_MUST_CLOSE;
    312   }
    313 
    314   c->rp.props.chunked = use_chunked;
    315   c->rp.props.end_by_closing = end_by_closing;
    316 
    317   if ((!c->rp.props.send_reply_body) || (0 == r->cntn_size))
    318     c->rp.cntn_loc = mhd_REPLY_CNTN_LOC_NOWHERE;
    319   else if (c->rp.props.chunked)
    320     c->rp.cntn_loc = mhd_REPLY_CNTN_LOC_CONN_BUF;
    321   else
    322   {
    323     switch (r->cntn_dtype)
    324     {
    325     case mhd_RESPONSE_CONTENT_DATA_BUFFER:
    326       c->rp.cntn_loc = mhd_REPLY_CNTN_LOC_RESP_BUF;
    327       break;
    328     case mhd_RESPONSE_CONTENT_DATA_IOVEC:
    329       c->rp.cntn_loc = mhd_REPLY_CNTN_LOC_IOV;
    330       break;
    331     case mhd_RESPONSE_CONTENT_DATA_FILE:
    332       if (mhd_C_HAS_TLS (c))
    333       {
    334         c->rp.cntn_loc = mhd_REPLY_CNTN_LOC_CONN_BUF;
    335         break;
    336       }
    337 #ifdef mhd_USE_SENDFILE
    338       if (r->cntn.file.use_sf)
    339       {
    340         c->rp.cntn_loc = mhd_REPLY_CNTN_LOC_FILE;
    341         break;
    342       }
    343 #endif
    344       c->rp.cntn_loc = mhd_REPLY_CNTN_LOC_CONN_BUF;
    345       break;
    346     case mhd_RESPONSE_CONTENT_DATA_CALLBACK:
    347       c->rp.cntn_loc = mhd_REPLY_CNTN_LOC_CONN_BUF;
    348       break;
    349     case mhd_RESPONSE_CONTENT_DATA_INVALID:
    350     default:
    351       mhd_UNREACHABLE ();
    352       c->rp.cntn_loc = mhd_REPLY_CNTN_LOC_NOWHERE;
    353       break;
    354     }
    355   }
    356 
    357 #ifndef NDEBUG
    358   c->rp.props.set = true;
    359 #endif /* _DEBUG */
    360 }
    361 
    362 
    363 /**
    364  * Check whether queued response is suitable for @a connection.
    365  * @param c the connection to check
    366  */
    367 static void
    368 check_connection_reply (struct MHD_Connection *restrict c)
    369 {
    370   struct MHD_Response *const restrict r = c->rp.response;  /**< a short alias */
    371 
    372   mhd_assert (c->rp.props.set);
    373 
    374   if ((!c->rp.props.use_reply_body_headers)
    375       && (0 != r->cntn_size))
    376   {
    377     mhd_LOG_PRINT (c->daemon, MHD_SC_REPLY_NOT_EMPTY_RESPONSE,
    378                    mhd_LOG_FMT ("This reply with response code %u " \
    379                                 "cannot use reply content. Non-empty " \
    380                                 "response content is ignored and not used."),
    381                    (unsigned)(c->rp.response->sc));
    382   }
    383   if ((!c->rp.props.use_reply_body_headers)
    384       && (r->cfg.cnt_len_by_app))
    385   {
    386     mhd_LOG_PRINT (c->daemon, MHD_SC_REPLY_CONTENT_LENGTH_NOT_ALLOWED,
    387                    mhd_LOG_FMT ("This reply with response code %u " \
    388                                 "cannot use reply content. Application " \
    389                                 "defined \"Content-Length\" header " \
    390                                 "violates HTTP specification."),
    391                    (unsigned)(c->rp.response->sc));
    392   }
    393 }
    394 
    395 
    396 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_
    397 MHD_FN_PAR_OUT_ (1) bool
    398 mhd_build_date_str (char date[MHD_FN_PAR_FIX_ARR_SIZE_ (29)])
    399 {
    400   static const char *const days[] = {
    401     "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
    402   };
    403   static const char *const mons[] = {
    404     "Jan", "Feb", "Mar", "Apr", "May", "Jun",
    405     "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
    406   };
    407   static const size_t buf_len = 29;
    408   struct tm now;
    409   time_t t;
    410   const char *src;
    411 #if !defined(HAVE_C11_GMTIME_S) && !defined(HAVE_W32_GMTIME_S) && \
    412   !defined(HAVE_GMTIME_R)
    413   struct tm *pNow;
    414 #endif
    415 
    416   if ((time_t)-1 == time (&t))
    417     return false;
    418 #if defined(HAVE_GMTIME_R)
    419   if (NULL == gmtime_r (&t,
    420                         &now))
    421     return false;
    422 #elif defined(HAVE_C11_GMTIME_S)
    423   if (NULL == gmtime_s (&t,
    424                         &now))
    425     return false;
    426 #elif defined(HAVE_W32_GMTIME_S)
    427   if (0 != gmtime_s (&now,
    428                      &t))
    429     return false;
    430 #else
    431   pNow = gmtime (&t);
    432   if (NULL == pNow)
    433     return false;
    434   now = *pNow;
    435 #endif
    436 
    437   /* Day of the week */
    438   src = days[now.tm_wday % 7];
    439   date[0] = src[0];
    440   date[1] = src[1];
    441   date[2] = src[2];
    442   date[3] = ',';
    443   date[4] = ' ';
    444   /* Day of the month */
    445   if (2 != mhd_uint8_to_str_pad ((uint8_t)now.tm_mday, 2,
    446                                  date + 5, buf_len - 5))
    447     return false;
    448   date[7] = ' ';
    449   /* Month */
    450   src = mons[now.tm_mon % 12];
    451   date[8] = src[0];
    452   date[9] = src[1];
    453   date[10] = src[2];
    454   date[11] = ' ';
    455   /* Year */
    456   if (4 != mhd_uint16_to_str ((uint_least16_t)(1900 + now.tm_year), date + 12,
    457                               buf_len - 12))
    458     return false;
    459   date[16] = ' ';
    460   /* Time */
    461   mhd_uint8_to_str_pad ((uint8_t)now.tm_hour, 2, date + 17, buf_len - 17);
    462   date[19] = ':';
    463   mhd_uint8_to_str_pad ((uint8_t)now.tm_min, 2, date + 20, buf_len - 20);
    464   date[22] = ':';
    465   mhd_uint8_to_str_pad ((uint8_t)now.tm_sec, 2, date + 23, buf_len - 23);
    466   date[25] = ' ';
    467   date[26] = 'G';
    468   date[27] = 'M';
    469   date[28] = 'T';
    470 
    471   return true;
    472 }
    473 
    474 
    475 /**
    476  * Produce HTTP DATE header.
    477  * Result is always 37 bytes long (plus one terminating null).
    478  *
    479  * @param[out] header where to write the header, with
    480  *             at least 38 bytes available space.
    481  */
    482 static MHD_FN_PAR_NONNULL_ALL_
    483 MHD_FN_PAR_OUT_ (1) bool
    484 get_date_header (char *header)
    485 {
    486   header[0] = 'D';
    487   header[1] = 'a';
    488   header[2] = 't';
    489   header[3] = 'e';
    490   header[4] = ':';
    491   header[5] = ' ';
    492   if (!mhd_build_date_str (header + 6))
    493   {
    494     header[0] = 0;
    495     return false;
    496   }
    497   header[35] = '\r';
    498   header[36] = '\n';
    499   header[37] = 0;
    500   return true;
    501 }
    502 
    503 
    504 /**
    505  * Append data to the buffer if enough space is available,
    506  * update position.
    507  * @param[out] buf the buffer to append data to
    508  * @param buf_size the size of the @a buffer
    509  * @param[in,out] ppos the pointer to position in the @a buffer
    510  * @param append the data to append
    511  * @param append_size the size of the @a append
    512  * @return true if data has been added and position has been updated,
    513  *         false if not enough space is available
    514  */
    515 static MHD_FN_PAR_NONNULL_ALL_ bool
    516 buffer_append (char *restrict buf,
    517                size_t buf_size,
    518                size_t *restrict ppos,
    519                const char *restrict append,
    520                size_t append_size)
    521 {
    522   mhd_assert (NULL != buf); /* Mute static analyzer */
    523   if (mhd_COND_HARDLY_EVER (*ppos + append_size < append_size)) /* Check for overflow */
    524     return false;
    525   if (buf_size < *ppos + append_size)
    526     return false;
    527   memcpy (buf + *ppos, append, append_size);
    528   *ppos += append_size;
    529   return true;
    530 }
    531 
    532 
    533 /**
    534  * Append CRLF to the buffer if enough space is available,
    535  * update position.
    536  * @param[out] buf the buffer to append data to
    537  * @param buf_size the size of the @a buffer
    538  * @param[in,out] ppos the pointer to position in the @a buffer
    539  * @return true if CRLF has been added and position has been updated,
    540  *         false if not enough space is available
    541  */
    542 mhd_static_inline MHD_FN_PAR_NONNULL_ALL_ bool
    543 buffer_append_crlf (char *restrict buf,
    544                     size_t buf_size,
    545                     size_t *restrict ppos)
    546 {
    547   mhd_assert (NULL != buf); /* Mute static analyzer */
    548   if (mhd_COND_HARDLY_EVER (*ppos + 2 < 2)) /* Check for overflow */
    549     return false;
    550   if (mhd_COND_ALMOST_NEVER (buf_size < *ppos + 2))
    551     return false;
    552   buf[*ppos] = '\r';
    553   buf[*ppos + 1] = '\n';
    554   *ppos += 2;
    555   return true;
    556 }
    557 
    558 
    559 /**
    560  * Add user-defined headers from response object to
    561  * the text buffer.
    562  *
    563  * @param buf the buffer to add headers to
    564  * @param ppos the pointer to the position in the @a buf
    565  * @param buf_size the size of the @a buf
    566  * @param r the response
    567  * @param filter_content_len skip "Content-Length" header if any
    568  * @param add_close add "close" token to the
    569  *                  "Connection:" header (if any), ignored if no "Connection:"
    570  *                  header was added by user or if "close" token is already
    571  *                  present in "Connection:" header
    572  * @param add_keep_alive add "Keep-Alive" token to the
    573  *                       "Connection:" header (if any)
    574  * @return true if succeed,
    575  *         false if buffer is too small
    576  */
    577 static MHD_FN_PAR_NONNULL_ALL_ bool
    578 add_user_headers (char *restrict buf,
    579                   size_t *restrict ppos,
    580                   size_t buf_size,
    581                   struct MHD_Response *restrict r,
    582                   bool filter_content_len,
    583                   bool add_close,
    584                   bool add_keep_alive)
    585 {
    586   struct mhd_ResponseHeader *hdr; /**< Iterates through User-specified headers */
    587   size_t el_size; /**< the size of current element to be added to the @a buf */
    588 
    589   mhd_assert (!add_close || !add_keep_alive);
    590   mhd_assert (!add_keep_alive || !add_close);
    591 
    592   if (r->cfg.has_hdr_conn)
    593   {
    594     add_close = false;          /* No such header */
    595     add_keep_alive = false;     /* No such header */
    596   }
    597 
    598   for (hdr = mhd_DLINKEDL_GET_FIRST (r, headers);
    599        NULL != hdr;
    600        hdr = mhd_DLINKEDL_GET_NEXT (hdr, headers))
    601   {
    602     size_t initial_pos = *ppos;
    603 
    604     if (filter_content_len)
    605     { /* Need to filter-out "Content-Length" */
    606       if (mhd_str_equal_caseless_n_st (MHD_HTTP_HEADER_CONTENT_LENGTH, \
    607                                        hdr->name.cstr,
    608                                        hdr->name.len))
    609       {
    610         /* Reset filter flag  */
    611         filter_content_len = false;
    612         continue; /* Skip "Content-Length" header */
    613       }
    614     }
    615 
    616     /* Add user header */
    617     el_size = hdr->name.len + 2u;
    618     if (mhd_COND_ALMOST_NEVER (2u > el_size)) /* Check for overflow */
    619       return false;
    620     el_size += hdr->value.len;
    621     if (mhd_COND_ALMOST_NEVER (hdr->value.len > el_size)) /* Check for overflow */
    622       return false;
    623     el_size += 2u;
    624     if (mhd_COND_ALMOST_NEVER (2u > el_size)) /* Check for overflow */
    625       return false;
    626     if (mhd_COND_HARDLY_EVER (*ppos + el_size < el_size)) /* Check for overflow */
    627       return false;
    628     if (buf_size < *ppos + el_size)
    629       return false;
    630     memcpy (buf + *ppos, hdr->name.cstr, hdr->name.len);
    631     (*ppos) += hdr->name.len;
    632     buf[(*ppos)++] = ':';
    633     buf[(*ppos)++] = ' ';
    634     if (add_close || add_keep_alive)
    635     {
    636       /* "Connection:" header must be always the first one */
    637       mhd_assert (mhd_str_equal_caseless_n (hdr->name.cstr, \
    638                                             MHD_HTTP_HEADER_CONNECTION, \
    639                                             hdr->name.len));
    640 
    641       if (add_close)
    642       {
    643         static const struct MHD_String cn_add = mhd_MSTR_INIT ("close, ");
    644         el_size += cn_add.len;
    645         if (mhd_COND_ALMOST_NEVER (cn_add.len > el_size)) /* Check for overflow */
    646           return false;
    647         if (mhd_COND_HARDLY_EVER (initial_pos + el_size < el_size)) /* Check for overflow */
    648           return false;
    649         if (buf_size < initial_pos + el_size)
    650           return false;
    651         memcpy (buf + *ppos, cn_add.cstr,
    652                 cn_add.len);
    653         *ppos += cn_add.len;
    654       }
    655       else
    656       {
    657         static const struct MHD_String cn_add = mhd_MSTR_INIT ("Keep-Alive, ");
    658         el_size += cn_add.len;
    659         if (mhd_COND_ALMOST_NEVER (cn_add.len > el_size)) /* Check for overflow */
    660           return false;
    661         if (mhd_COND_HARDLY_EVER (initial_pos + el_size < el_size)) /* Check for overflow */
    662           return false;
    663         if (buf_size < initial_pos + el_size)
    664           return false;
    665         memcpy (buf + *ppos, cn_add.cstr,
    666                 cn_add.len);
    667         *ppos += cn_add.len;
    668       }
    669       add_close = false;
    670       add_keep_alive = false;
    671     }
    672     if (0 != hdr->value.len)
    673       memcpy (buf + *ppos, hdr->value.cstr, hdr->value.len);
    674     *ppos += hdr->value.len;
    675     buf[(*ppos)++] = '\r';
    676     buf[(*ppos)++] = '\n';
    677     mhd_assert (initial_pos + el_size == (*ppos));
    678   }
    679   return true;
    680 }
    681 
    682 
    683 /**
    684  * Append static string to the buffer if enough space is available,
    685  * update position.
    686  * @param[out] buf the buffer to append data to
    687  * @param buf_size the size of the @a buffer
    688  * @param[in,out] ppos the pointer to position in the @a buffer
    689  * @param str the static string to append
    690  * @return true if data has been added and position has been updated,
    691  *         false if not enough space is available
    692  */
    693 #define buffer_append_s(buf, buf_size, ppos, str) \
    694         buffer_append ((buf),(buf_size),(ppos),(str),mhd_SSTR_LEN (str))
    695 
    696 
    697 /**
    698  * Append MHD_String to the buffer if enough space is available,
    699  * update position.
    700  * @param[out] buf the buffer to append data to
    701  * @param buf_size the size of the @a buffer
    702  * @param[in,out] ppos the pointer to position in the @a buffer
    703  * @param pmhdstr the pointer to string to append
    704  * @return true if data has been added and position has been updated,
    705  *         false if not enough space is available
    706  */
    707 #define buffer_append_mstr(buf, buf_size, ppos, pmhdstr) \
    708         buffer_append ((buf),(buf_size),(ppos), \
    709                        (pmhdstr)->cstr, (pmhdstr)->len)
    710 
    711 /**
    712  * Allocate the connection's write buffer and fill it with all of the
    713  * headers from the response.
    714  * Inner version of the function.
    715  *
    716  * @param c the connection to process
    717  * @return 'true' if state has been update,
    718  *         'false' if connection is going to be aborted
    719  */
    720 static MHD_FN_PAR_NONNULL_ALL_ bool
    721 build_header_response_inn (struct MHD_Connection *restrict c)
    722 {
    723   struct MHD_Response *const restrict r = c->rp.response;
    724   char *restrict buf;                            /**< the output buffer */
    725   size_t pos;                                    /**< append offset in the @a buf */
    726   size_t buf_size;                               /**< the size of the @a buf */
    727   size_t el_size;                                /**< the size of current element to be added to the @a buf */
    728   uint_fast16_t rcode;                           /**< the response code */
    729   bool use_conn_close;                           /**< Use "Connection: close" header */
    730   bool use_conn_k_alive;                         /**< Use "Connection: Keep-Alive" header */
    731 
    732   mhd_assert (NULL != r);
    733 
    734   /* ** Adjust response properties ** */
    735   setup_reply_properties (c);
    736 
    737   mhd_assert (c->rp.props.set);
    738   mhd_assert ((mhd_CONN_MUST_CLOSE == c->conn_reuse) \
    739               || (mhd_CONN_KEEPALIVE_POSSIBLE == c->conn_reuse) \
    740               || (mhd_CONN_MUST_UPGRADE == c->conn_reuse));
    741 #if 0 // def MHD_SUPPORT_UPGRADE // TODO: upgrade support
    742   mhd_assert ((NULL == r->upgrade_handler) \
    743               || (mhd_CONN_MUST_UPGRADE == c->keepalive));
    744 #else  /* ! MHD_SUPPORT_UPGRADE */
    745   mhd_assert (mhd_CONN_MUST_UPGRADE != c->conn_reuse);
    746 #endif /* ! MHD_SUPPORT_UPGRADE */
    747   mhd_assert ((!c->rp.props.chunked) || c->rp.props.use_reply_body_headers);
    748   mhd_assert ((!c->rp.props.send_reply_body) \
    749               || c->rp.props.use_reply_body_headers);
    750   mhd_assert ((!c->rp.props.end_by_closing) \
    751               || (mhd_CONN_MUST_CLOSE == c->conn_reuse));
    752 #if 0 // def MHD_SUPPORT_UPGRADE  // TODO: upgrade support
    753   mhd_assert (NULL == r->upgrade_handler \
    754               || !c->rp.props.use_reply_body_headers);
    755 #endif /* MHD_SUPPORT_UPGRADE */
    756 
    757   check_connection_reply (c);
    758 
    759   rcode = (uint_fast16_t)c->rp.response->sc;
    760   if (mhd_CONN_MUST_CLOSE == c->conn_reuse)
    761   {
    762     /* The closure of connection must be always indicated by header
    763      * to avoid hung connections */
    764     use_conn_close = true;
    765     use_conn_k_alive = false;
    766   }
    767   else if (mhd_CONN_KEEPALIVE_POSSIBLE == c->conn_reuse)
    768   {
    769     mhd_assert (!r->cfg.mode_1_0);
    770     use_conn_close = false;
    771     /* Add "Connection: keep-alive" if request is HTTP/1.0 or
    772      * if reply is HTTP/1.0
    773      * For HTTP/1.1 add header only if explicitly requested by app
    774      * (by response flag), as "Keep-Alive" is default for HTTP/1.1. */
    775     if (r->cfg.mode_1_0
    776         || (MHD_HTTP_VERSION_1_0 == c->rq.http_ver))
    777       use_conn_k_alive = true;
    778     else
    779       use_conn_k_alive = false;
    780   }
    781   else
    782   {
    783     use_conn_close = false;
    784     use_conn_k_alive = false;
    785   }
    786 
    787   /* ** Actually build the response header ** */
    788 
    789   /* Get all space available */
    790   mhd_stream_maximize_write_buffer (c);
    791   buf = c->write_buffer;
    792   pos = c->write_buffer_append_offset;
    793   buf_size = c->write_buffer_size;
    794   if (0 == buf_size)
    795     return false;
    796   mhd_assert (NULL != buf);
    797 
    798   // TODO: use pre-calculated header size
    799   /* * The status line * */
    800 
    801   /* The HTTP version */
    802   if (!c->rp.responseIcy)
    803   { /* HTTP reply */
    804     if (!r->cfg.mode_1_0)
    805     { /* HTTP/1.1 reply */
    806       /* Use HTTP/1.1 responses for HTTP/1.0 clients.
    807        * See https://datatracker.ietf.org/doc/html/rfc7230#section-2.6 */
    808       if (!buffer_append_s (buf, buf_size, &pos, MHD_HTTP_VERSION_1_1_STR))
    809         return false;
    810     }
    811     else
    812     { /* HTTP/1.0 reply */
    813       if (!buffer_append_s (buf, buf_size, &pos, MHD_HTTP_VERSION_1_0_STR))
    814         return false;
    815     }
    816   }
    817   else
    818   { /* ICY reply */
    819     if (!buffer_append_s (buf, buf_size, &pos, "ICY"))
    820       return false;
    821   }
    822 
    823   /* The response code */
    824   if (mhd_COND_HARDLY_EVER (pos + 5 < 5)) /* Check for overflow */
    825     return false;
    826   if (buf_size < pos + 5) /* space + code + space */
    827     return false;
    828   buf[pos++] = ' ';
    829   pos += mhd_uint16_to_str ((uint16_t)rcode, buf + pos,
    830                             buf_size - pos);
    831   buf[pos++] = ' ';
    832 
    833   /* The reason phrase */
    834   if (1)
    835   {
    836     const struct MHD_String *stat_str;
    837     stat_str = mhd_HTTP_status_code_to_string_int (rcode);
    838     mhd_assert (0 != stat_str->len);
    839     if (!buffer_append (buf, buf_size, &pos,
    840                         stat_str->cstr,
    841                         stat_str->len))
    842       return false;
    843   }
    844   /* The linefeed */
    845   if (!buffer_append_crlf (buf,
    846                            buf_size,
    847                            &pos))
    848     return false;
    849 
    850   /* * The headers * */
    851 
    852   /* A special custom header */
    853   if (0 != r->special_resp.spec_hdr_len)
    854   {
    855     mhd_assert (r->cfg.int_err_resp);
    856     if (!buffer_append (buf, buf_size, &pos,
    857                         r->special_resp.spec_hdr,
    858                         r->special_resp.spec_hdr_len))
    859       return false;
    860     if (!buffer_append_crlf (buf,
    861                              buf_size,
    862                              &pos))
    863       return false;
    864   }
    865 
    866   /* Main automatic headers */
    867 
    868   /* The "Date:" header */
    869   if ((!r->cfg.has_hdr_date)
    870       && (!c->daemon->req_cfg.suppress_date))
    871   {
    872     /* Additional byte for unused zero-termination */
    873     if (mhd_COND_HARDLY_EVER (pos + 38 < 38)) /* Check for overflow */
    874       return false;
    875     if (buf_size < pos + 38)
    876       return false;
    877     if (get_date_header (buf + pos))
    878       pos += 37;
    879   }
    880   /* The "Connection:" header */
    881   mhd_assert (!use_conn_close || !use_conn_k_alive);
    882   mhd_assert (!use_conn_k_alive || !use_conn_close);
    883   if (!r->cfg.has_hdr_conn)
    884   {
    885     if (use_conn_close)
    886     {
    887       if (!buffer_append_s (buf, buf_size, &pos,
    888                             MHD_HTTP_HEADER_CONNECTION ": close\r\n"))
    889         return false;
    890     }
    891     else if (use_conn_k_alive)
    892     {
    893       if (!buffer_append_s (buf, buf_size, &pos,
    894                             MHD_HTTP_HEADER_CONNECTION ": Keep-Alive\r\n"))
    895         return false;
    896     }
    897     use_conn_close = false;
    898     use_conn_k_alive = false;
    899   }
    900 
    901   /* Special headers */
    902 #ifdef MHD_SUPPORT_AUTH_DIGEST
    903   if (mhd_RESP_HAS_AUTH_DIGEST (r))
    904   {
    905     char noncestr[mhd_AUTH_DIGEST_NONCE_LEN];
    906     const struct mhd_RespAuthDigestHeader *dg_hdr;
    907     if (!mhd_auth_digest_get_new_nonce (c,
    908                                         noncestr))
    909     {
    910       mhd_STREAM_ABORT (c,
    911                         mhd_CONN_CLOSE_NONCE_ERROR,
    912                         "Failed to generate a new nonce for Digest Auth.");
    913       return false;
    914     }
    915     for (dg_hdr = mhd_DLINKEDL_GET_FIRST (r, auth_d_hdrs);
    916          NULL != dg_hdr;
    917          dg_hdr = mhd_DLINKEDL_GET_NEXT (dg_hdr, auth_d_hdrs))
    918     {
    919       size_t nonce_pos;
    920       nonce_pos = pos + dg_hdr->nonce_pos;
    921       if (!buffer_append_mstr (buf, buf_size, &pos, \
    922                                &(dg_hdr->hdr)))
    923         return false;
    924       memcpy (buf + nonce_pos,
    925               noncestr,
    926               sizeof(noncestr));
    927     }
    928   }
    929 #endif /* MHD_SUPPORT_AUTH_DIGEST */
    930 
    931   /* User-defined headers */
    932 
    933   if (!add_user_headers (buf, &pos, buf_size, r,
    934                          !c->rp.props.use_reply_body_headers,
    935                          use_conn_close,
    936                          use_conn_k_alive))
    937     return false;
    938 
    939   /* Other automatic headers */
    940 
    941   if (c->rp.props.use_reply_body_headers)
    942   {
    943     /* Body-specific headers */
    944 
    945     if (c->rp.props.chunked)
    946     { /* Chunked encoding is used */
    947       mhd_assert (!c->rp.props.end_by_closing);
    948       if (!buffer_append_s (buf, buf_size, &pos,
    949                             MHD_HTTP_HEADER_TRANSFER_ENCODING ": " \
    950                             "chunked\r\n"))
    951         return false;
    952     }
    953     else /* Chunked encoding is not used */
    954     {
    955       if ((MHD_SIZE_UNKNOWN != r->cntn_size)
    956           && (!c->rp.props.end_by_closing)
    957           && (!r->cfg.chunked)
    958           && (!r->cfg.head_only))
    959       { /* The size is known and can be indicated by the header */
    960         if (!r->cfg.cnt_len_by_app)
    961         { /* The response does not have app-defined "Content-Length" header */
    962           if (!buffer_append_s (buf, buf_size, &pos,
    963                                 MHD_HTTP_HEADER_CONTENT_LENGTH ": "))
    964             return false;
    965           el_size = mhd_uint64_to_str (r->cntn_size,
    966                                        buf + pos,
    967                                        buf_size - pos);
    968           if (0 == el_size)
    969             return false;
    970           pos += el_size;
    971 
    972           if (!buffer_append_crlf (buf,
    973                                    buf_size,
    974                                    &pos))
    975             return false;
    976         }
    977       }
    978       else
    979       {
    980         mhd_assert ((!c->rp.props.send_reply_body) \
    981                     || (mhd_CONN_MUST_CLOSE == c->conn_reuse));
    982         (void)0;
    983       }
    984     }
    985   }
    986 
    987   /* * Header termination * */
    988   if (!buffer_append_crlf (buf,
    989                            buf_size,
    990                            &pos))
    991     return false;
    992 
    993   c->write_buffer_append_offset = pos;
    994   return true;
    995 }
    996 
    997 
    998 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool
    999 mhd_stream_build_header_response (struct MHD_Connection *restrict c)
   1000 {
   1001   if (!build_header_response_inn (c))
   1002   {
   1003 #ifdef MHD_SUPPORT_AUTH_DIGEST
   1004     if (mhd_HTTP_STAGE_PRE_CLOSING <= c->stage)
   1005       return false; /* Already started closing */
   1006 #else  /* ! MHD_SUPPORT_AUTH_DIGEST */
   1007     mhd_assert (mhd_HTTP_STAGE_START_REPLY == c->stage);
   1008 #endif /* ! MHD_SUPPORT_AUTH_DIGEST */
   1009     mhd_STREAM_ABORT (c,
   1010                       mhd_CONN_CLOSE_NO_POOL_MEM_FOR_REPLY,
   1011                       "No memory in the pool for the reply headers.");
   1012     return false;
   1013   }
   1014   c->stage = mhd_HTTP_STAGE_HEADERS_SENDING;
   1015   return true;
   1016 }
   1017 
   1018 
   1019 /**
   1020  * Pre-process DCC action provided by application.
   1021  * 'abort' and 'suspend' actions are fully processed,
   1022  * 'continue' and 'finish' actions needs to be processed by the caller.
   1023  * @param c the stream to use
   1024  * @param act the action provided by application
   1025  * @return 'true' if action if 'continue' or 'finish' and need to be
   1026  *         processed,
   1027  *         'false' if action is 'suspend' or 'abort' and is already processed.
   1028  */
   1029 static MHD_FN_PAR_NONNULL_ (1) bool
   1030 preprocess_dcc_action (struct MHD_Connection *restrict c,
   1031                        const struct MHD_DynamicContentCreatorAction *act)
   1032 {
   1033   /**
   1034    * The action created for the current request
   1035    */
   1036   struct MHD_DynamicContentCreatorAction *const a =
   1037     &(c->rp.app_act);
   1038 
   1039   if (NULL != act)
   1040   {
   1041     if ((a != act)
   1042         || !mhd_DCC_ACTION_IS_VALID (c->rp.app_act.act)
   1043         || ((MHD_SIZE_UNKNOWN != c->rp.response->cntn_size)
   1044             && (mhd_DCC_ACTION_FINISH == c->rp.app_act.act)))
   1045     {
   1046       mhd_LOG_MSG (c->daemon, MHD_SC_ACTION_INVALID, \
   1047                    "Provided Dynamic Content Creator action is not " \
   1048                    "a correct action generated for the current request.");
   1049       act = NULL;
   1050     }
   1051   }
   1052   if (NULL == act)
   1053     a->act = mhd_DCC_ACTION_ABORT;
   1054 
   1055   switch (a->act)
   1056   {
   1057   case mhd_DCC_ACTION_CONTINUE:
   1058     return true;
   1059   case mhd_DCC_ACTION_FINISH:
   1060     mhd_assert (MHD_SIZE_UNKNOWN == c->rp.response->cntn_size);
   1061     return true;
   1062   case mhd_DCC_ACTION_SUSPEND:
   1063     mhd_assert (0 && "Not implemented yet");
   1064     // TODO: Implement suspend;
   1065     mhd_STREAM_ABORT (c,
   1066                       mhd_CONN_CLOSE_INT_ERROR,
   1067                       "Suspending connection is not implemented yet");
   1068     return false;
   1069   case mhd_DCC_ACTION_ABORT:
   1070     mhd_STREAM_ABORT (c,
   1071                       mhd_CONN_CLOSE_APP_ABORTED,
   1072                       "Dynamic Content Creator requested abort " \
   1073                       "of the request");
   1074     return false;
   1075   case mhd_DCC_ACTION_NO_ACTION:
   1076   default:
   1077     break;
   1078   }
   1079   mhd_UNREACHABLE ();
   1080   mhd_STREAM_ABORT (c,
   1081                     mhd_CONN_CLOSE_INT_ERROR,
   1082                     "Impossible code path");
   1083   return false;
   1084 }
   1085 
   1086 
   1087 /**
   1088  * Read the next portion of the response file into the buffer.
   1089  * The read starts at the response file offset plus the current response
   1090  * content read position. Handles file read errors, does NOT update the
   1091  * response content read position; the caller must update it on success.
   1092  * @param c the stream to use
   1093  * @param r the response to use
   1094  * @param buf_size the size of the @p buf buffer
   1095  * @param[out] buf the buffer to fill with the file data
   1096  * @param[out] size_filled the pointer to variable to get the size of the data
   1097  *                         actually put to the @p buf buffer
   1098  * @return 'true' if succeed (size_filled is updated),
   1099  *         'false' if failed (the stream is closed)
   1100  */
   1101 static MHD_FN_MUST_CHECK_RESULT_ MHD_FN_PAR_NONNULL_ALL_
   1102 MHD_FN_PAR_OUT_SIZE_ (4, 3) MHD_FN_PAR_OUT_ (5) bool
   1103 read_response_file (struct MHD_Connection *restrict c,
   1104                     struct MHD_Response *const restrict r,
   1105                     size_t buf_size,
   1106                     char buf[MHD_FN_PAR_DYN_ARR_SIZE_ (buf_size)],
   1107                     size_t *restrict size_filled)
   1108 {
   1109   mhd_assert (r == c->rp.response);
   1110   mhd_assert (mhd_RESPONSE_CONTENT_DATA_FILE == r->cntn_dtype);
   1111 
   1112   switch (mhd_read_file (r->cntn.file.fd,
   1113                          r->cntn.file.offset + c->rp.rsp_cntn_read_pos,
   1114                          buf_size,
   1115                          buf,
   1116                          size_filled))
   1117   {
   1118   case mhd_FILE_READ_OK:
   1119     return true;
   1120   case mhd_FILE_READ_ERROR:
   1121     mhd_STREAM_ABORT (c, \
   1122                       mhd_CONN_CLOSE_FILE_READ_ERROR, \
   1123                       "Error reading file for file-backed response.");
   1124     return false;
   1125   case mhd_FILE_READ_OFFSET_TOO_LARGE:
   1126     mhd_STREAM_ABORT (c, \
   1127                       mhd_CONN_CLOSE_FILE_OFFSET_TOO_LARGE, \
   1128                       "The offset for file-backed response is too large " \
   1129                       "for this platform.");
   1130     return false;
   1131   case mhd_FILE_READ_EOF:
   1132     if (MHD_SIZE_UNKNOWN == r->cntn_size)
   1133       return true;
   1134     mhd_STREAM_ABORT (c, \
   1135                       mhd_CONN_CLOSE_FILE_TOO_SHORT, \
   1136                       "The file for file-backed response is smaller " \
   1137                       "than specified by application.");
   1138     return false;
   1139   default:
   1140     break;
   1141   }
   1142   mhd_UNREACHABLE ();
   1143   c->rp.cntn_loc = mhd_REPLY_CNTN_LOC_NOWHERE;
   1144   return false;
   1145 }
   1146 
   1147 
   1148 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool
   1149 mhd_stream_prep_unchunked_body (struct MHD_Connection *restrict c)
   1150 {
   1151   struct MHD_Response *const restrict r = c->rp.response;
   1152   uint64_t left_to_send;
   1153 
   1154   mhd_assert (c->rp.props.send_reply_body);
   1155   mhd_assert (c->rp.rsp_cntn_read_pos != r->cntn_size);
   1156 
   1157   mhd_stream_call_dcc_cleanup_if_needed (c);
   1158 
   1159   if (0 == r->cntn_size)
   1160   { /* 0-byte response is always ready */
   1161     c->stage = mhd_HTTP_STAGE_FULL_REPLY_SENT;
   1162     return true;
   1163   }
   1164 
   1165   mhd_assert (mhd_REPLY_CNTN_LOC_NOWHERE != c->rp.cntn_loc);
   1166   mhd_assert (mhd_RESPONSE_CONTENT_DATA_INVALID != r->cntn_dtype);
   1167 
   1168   if (MHD_SIZE_UNKNOWN == r->cntn_size)
   1169     left_to_send = MHD_SIZE_UNKNOWN;
   1170   else
   1171     left_to_send = r->cntn_size - c->rp.rsp_cntn_read_pos;
   1172 
   1173   mhd_assert (0 != left_to_send);
   1174 
   1175   if (mhd_REPLY_CNTN_LOC_RESP_BUF == c->rp.cntn_loc)
   1176   {
   1177     (void)0;  /* Nothing to do, buffers are ready */
   1178   }
   1179   else if (mhd_REPLY_CNTN_LOC_CONN_BUF == c->rp.cntn_loc)
   1180   {
   1181     if (mhd_RESPONSE_CONTENT_DATA_CALLBACK == r->cntn_dtype)
   1182     {
   1183       const struct MHD_DynamicContentCreatorAction *act;
   1184       size_t size_to_fill =
   1185         c->write_buffer_size - c->write_buffer_append_offset;
   1186       size_t filled;
   1187 
   1188       if (size_to_fill > left_to_send)
   1189         size_to_fill = (size_t)left_to_send;
   1190 
   1191       mhd_assert (c->write_buffer_append_offset < c->write_buffer_size);
   1192       mhd_assert (NULL == c->rp.app_act_ctx.connection);
   1193 
   1194       c->rp.app_act_ctx.connection = c;
   1195       c->rp.app_act.act = mhd_DCC_ACTION_NO_ACTION;
   1196 
   1197       act =
   1198         r->cntn.dyn.cb (r->cntn.dyn.cls,
   1199                         &(c->rp.app_act_ctx),
   1200                         c->rp.rsp_cntn_read_pos,
   1201                         (void *)
   1202                         (c->write_buffer + c->write_buffer_append_offset),
   1203                         size_to_fill);
   1204       c->rp.app_act_ctx.connection = NULL; /* Block any attempt to create a new action */
   1205       if (!preprocess_dcc_action (c, act))
   1206         return true;
   1207       if (mhd_DCC_ACTION_FINISH == c->rp.app_act.act)
   1208       {
   1209         mhd_assert (MHD_SIZE_UNKNOWN == r->cntn_size);
   1210         mhd_assert (c->rp.props.end_by_closing);
   1211 
   1212         c->stage = mhd_HTTP_STAGE_FULL_REPLY_SENT;
   1213 
   1214         return true;
   1215       }
   1216       mhd_assert (mhd_DCC_ACTION_CONTINUE == c->rp.app_act.act);
   1217       // TODO: implement iov sending
   1218 
   1219       filled = c->rp.app_act.data.cntnue.buf_data_size;
   1220       if (size_to_fill < filled)
   1221       {
   1222         mhd_STREAM_ABORT (c,
   1223                           mhd_CONN_CLOSE_APP_ERROR,
   1224                           "Closing connection (application returned more data "
   1225                           "than requested).");
   1226         return true;
   1227       }
   1228       c->rp.rsp_cntn_read_pos += filled;
   1229       c->write_buffer_append_offset += filled;
   1230     }
   1231     else if (mhd_RESPONSE_CONTENT_DATA_FILE == r->cntn_dtype)
   1232     {
   1233       size_t size_to_fill =
   1234         c->write_buffer_size - c->write_buffer_append_offset;
   1235       size_t filled;
   1236 
   1237       if (size_to_fill > left_to_send)
   1238         size_to_fill = (size_t)left_to_send;
   1239 
   1240       mhd_assert (c->write_buffer_append_offset < c->write_buffer_size);
   1241 
   1242       if (!read_response_file (c,
   1243                                r,
   1244                                size_to_fill,
   1245                                c->write_buffer + c->write_buffer_append_offset,
   1246                                &filled))
   1247         return true; /* Error, the stream is closed */
   1248       if (0u == filled)
   1249       {
   1250         mhd_assert (MHD_SIZE_UNKNOWN == r->cntn_size);
   1251         c->stage = mhd_HTTP_STAGE_FULL_REPLY_SENT;
   1252 
   1253         return true;
   1254       }
   1255 
   1256       c->rp.rsp_cntn_read_pos += filled;
   1257       c->write_buffer_append_offset += filled;
   1258     }
   1259     else
   1260     {
   1261       mhd_assert (0 && "Impossible value");
   1262       mhd_UNREACHABLE ();
   1263       c->rp.cntn_loc = mhd_REPLY_CNTN_LOC_NOWHERE;
   1264       c->rp.rsp_cntn_read_pos = r->cntn_size;
   1265     }
   1266   }
   1267   else if (mhd_REPLY_CNTN_LOC_IOV == c->rp.cntn_loc)
   1268   {
   1269     size_t copy_size;
   1270 
   1271     mhd_assert (NULL == c->rp.resp_iov.iov);
   1272     mhd_assert (mhd_RESPONSE_CONTENT_DATA_IOVEC == r->cntn_dtype);
   1273 
   1274     copy_size = r->cntn.iovec.cnt * sizeof(mhd_iovec);
   1275     c->rp.resp_iov.iov = (mhd_iovec *)
   1276                          mhd_stream_alloc_memory (c,
   1277                                                   copy_size);
   1278     if (NULL == c->rp.resp_iov.iov)
   1279     {
   1280       /* not enough memory */
   1281       mhd_STREAM_ABORT (c,
   1282                         mhd_CONN_CLOSE_NO_POOL_MEM_FOR_REPLY,
   1283                         "No memory in the pool for the response data.");
   1284       return false;
   1285     }
   1286     memcpy (c->rp.resp_iov.iov,
   1287             &(r->cntn.iovec.iov),
   1288             copy_size);
   1289     c->rp.resp_iov.cnt = r->cntn.iovec.cnt;
   1290     c->rp.resp_iov.sent = 0;
   1291   }
   1292 #if defined(mhd_USE_SENDFILE)
   1293   else if (mhd_REPLY_CNTN_LOC_FILE == c->rp.cntn_loc)
   1294   {
   1295     (void)0;  /* Nothing to do, file should be read directly */
   1296   }
   1297 #endif /* mhd_USE_SENDFILE */
   1298   else
   1299   {
   1300     mhd_assert (0 && "Impossible value");
   1301     mhd_UNREACHABLE ();
   1302     c->rp.cntn_loc = mhd_REPLY_CNTN_LOC_NOWHERE;
   1303     c->rp.rsp_cntn_read_pos = r->cntn_size;
   1304   }
   1305 
   1306   c->stage = mhd_HTTP_STAGE_UNCHUNKED_BODY_READY;
   1307   return false;
   1308 }
   1309 
   1310 
   1311 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool
   1312 mhd_stream_prep_chunked_body (struct MHD_Connection *restrict c)
   1313 {
   1314   size_t filled;
   1315   struct MHD_Response *const restrict r = c->rp.response;
   1316   static const size_t max_chunk = 0xFFFFFF;
   1317   char chunk_hdr[6];            /* 6: max strlen of "FFFFFF" */
   1318   /* "FFFFFF" + "\r\n" */
   1319   static const size_t max_chunk_hdr_len = sizeof(chunk_hdr) + 2;
   1320   /* "FFFFFF" + "\r\n" + "\r\n" (chunk termination) */
   1321   static const size_t max_chunk_overhead = sizeof(chunk_hdr) + 2 + 2;
   1322   size_t chunk_hdr_len;
   1323   uint64_t left_to_send;
   1324   size_t size_to_fill;
   1325 
   1326   mhd_assert (0 == c->write_buffer_append_offset);
   1327   mhd_assert (0 == c->write_buffer_send_offset);
   1328 
   1329   mhd_stream_call_dcc_cleanup_if_needed (c);
   1330 
   1331   /* The buffer must be reasonably large enough */
   1332   if (32 > c->write_buffer_size)
   1333   {
   1334     mhd_STREAM_ABORT (c,
   1335                       mhd_CONN_CLOSE_NO_POOL_MEM_FOR_REPLY,
   1336                       "No memory in the pool for the reply chunked content.");
   1337     return true;
   1338   }
   1339   mhd_assert (max_chunk_overhead < \
   1340               (c->write_buffer_size));
   1341 
   1342   if (MHD_SIZE_UNKNOWN == r->cntn_size)
   1343     left_to_send = MHD_SIZE_UNKNOWN;
   1344   else
   1345     left_to_send = r->cntn_size - c->rp.rsp_cntn_read_pos;
   1346 
   1347   mhd_assert (0 != left_to_send);
   1348   if (0 != left_to_send)
   1349   {
   1350     size_to_fill =
   1351       c->write_buffer_size - max_chunk_overhead;
   1352     /* Limit size for the callback to the max usable size */
   1353     if (max_chunk < size_to_fill)
   1354       size_to_fill = max_chunk;
   1355     if (left_to_send < size_to_fill)
   1356       size_to_fill = (size_t)left_to_send;
   1357   }
   1358   else
   1359     size_to_fill = 0;
   1360 
   1361   if ((0 == left_to_send)
   1362       && (mhd_RESPONSE_CONTENT_DATA_CALLBACK != r->cntn_dtype))
   1363   {
   1364     c->stage = mhd_HTTP_STAGE_CHUNKED_BODY_SENT;
   1365     return true;
   1366   }
   1367   else if (mhd_RESPONSE_CONTENT_DATA_BUFFER == r->cntn_dtype)
   1368   {
   1369     mhd_assert (size_to_fill <= \
   1370                 r->cntn_size - (size_t)c->rp.rsp_cntn_read_pos);
   1371     memcpy (c->write_buffer + max_chunk_hdr_len,
   1372             r->cntn.buf + (size_t)c->rp.rsp_cntn_read_pos,
   1373             size_to_fill);
   1374     filled = size_to_fill;
   1375   }
   1376   else if (mhd_RESPONSE_CONTENT_DATA_CALLBACK == r->cntn_dtype)
   1377   {
   1378     const struct MHD_DynamicContentCreatorAction *act;
   1379 
   1380     mhd_assert (NULL == c->rp.app_act_ctx.connection);
   1381 
   1382     c->rp.app_act_ctx.connection = c;
   1383     c->rp.app_act.act = mhd_DCC_ACTION_NO_ACTION;
   1384 
   1385     act =
   1386       r->cntn.dyn.cb (r->cntn.dyn.cls,
   1387                       &(c->rp.app_act_ctx),
   1388                       c->rp.rsp_cntn_read_pos,
   1389                       (void *)
   1390                       (c->write_buffer + max_chunk_hdr_len),
   1391                       size_to_fill);
   1392     c->rp.app_act_ctx.connection = NULL; /* Block any attempt to create a new action */
   1393     if (!preprocess_dcc_action (c, act))
   1394       return true;
   1395     if (mhd_DCC_ACTION_FINISH == c->rp.app_act.act)
   1396     {
   1397       mhd_assert (MHD_SIZE_UNKNOWN == r->cntn_size);
   1398       c->stage = mhd_HTTP_STAGE_CHUNKED_BODY_SENT;
   1399 
   1400       return true;
   1401     }
   1402     mhd_assert (mhd_DCC_ACTION_CONTINUE == c->rp.app_act.act);
   1403     // TODO: implement iov sending
   1404 
   1405     filled = c->rp.app_act.data.cntnue.buf_data_size;
   1406     if (size_to_fill < filled)
   1407     {
   1408       mhd_STREAM_ABORT (c,
   1409                         mhd_CONN_CLOSE_APP_ERROR,
   1410                         "Closing connection (application returned more data "
   1411                         "than requested).");
   1412       return true;
   1413     }
   1414   }
   1415   else if (mhd_RESPONSE_CONTENT_DATA_FILE == r->cntn_dtype)
   1416   {
   1417     if (!read_response_file (c,
   1418                              r,
   1419                              size_to_fill,
   1420                              c->write_buffer + max_chunk_hdr_len,
   1421                              &filled))
   1422       return true; /* Error, the stream is closed */
   1423     if (0u == filled)
   1424     {
   1425       mhd_assert (MHD_SIZE_UNKNOWN == r->cntn_size);
   1426       c->stage = mhd_HTTP_STAGE_CHUNKED_BODY_SENT;
   1427 
   1428       return true;
   1429     }
   1430   }
   1431   else
   1432   {
   1433     mhd_assert (0 && "Not implemented yet");
   1434     filled = 0;
   1435   }
   1436 
   1437   chunk_hdr_len = mhd_uint32_to_strx ((uint_fast32_t)filled,
   1438                                       chunk_hdr,
   1439                                       sizeof(chunk_hdr));
   1440   mhd_assert (chunk_hdr_len != 0);
   1441   mhd_assert (chunk_hdr_len < sizeof(chunk_hdr));
   1442   c->write_buffer_send_offset = max_chunk_hdr_len - (chunk_hdr_len + 2);
   1443   memcpy (c->write_buffer + c->write_buffer_send_offset,
   1444           chunk_hdr,
   1445           chunk_hdr_len);
   1446   c->write_buffer[max_chunk_hdr_len - 2] = '\r';
   1447   c->write_buffer[max_chunk_hdr_len - 1] = '\n';
   1448   c->write_buffer[max_chunk_hdr_len + filled] = '\r';
   1449   c->write_buffer[max_chunk_hdr_len + filled + 1] = '\n';
   1450   c->write_buffer_append_offset = max_chunk_hdr_len + filled + 2;
   1451   if (0 != filled)
   1452     c->rp.rsp_cntn_read_pos += filled;
   1453   else
   1454     c->rp.rsp_cntn_read_pos = r->cntn_size;
   1455 
   1456   c->stage = mhd_HTTP_STAGE_CHUNKED_BODY_READY;
   1457 
   1458   return false;
   1459 }
   1460 
   1461 
   1462 /**
   1463  * Allocate the connection's write buffer (if necessary) and fill it
   1464  * with response footers.
   1465  * Inner version.
   1466  *
   1467  * @param c the connection
   1468  * @return 'true' if footers formed successfully,
   1469  *         'false' if not enough buffer
   1470  */
   1471 static MHD_FN_PAR_NONNULL_ALL_ bool
   1472 prep_chunked_footer_inn (struct MHD_Connection *restrict c)
   1473 {
   1474   char *buf;           /**< the buffer to write footers to */
   1475   size_t buf_size;     /**< the size of the @a buf */
   1476   size_t used_size;    /**< the used size of the @a buf */
   1477   // struct MHD_HTTP_Res_Header *pos;
   1478 
   1479   mhd_assert (c->rp.props.chunked);
   1480   mhd_assert (mhd_HTTP_STAGE_CHUNKED_BODY_SENT == c->stage);
   1481   mhd_assert (NULL != c->rp.response);
   1482 
   1483   buf_size = mhd_stream_maximize_write_buffer (c);
   1484   /* '5' is the minimal size of chunked footer ("0\r\n\r\n") */
   1485   if (buf_size < 5)
   1486     return false;
   1487   mhd_assert (NULL != c->write_buffer);
   1488   buf = c->write_buffer + c->write_buffer_append_offset;
   1489   mhd_assert (NULL != buf);
   1490   used_size = 0;
   1491   buf[used_size++] = '0';
   1492   buf[used_size++] = '\r';
   1493   buf[used_size++] = '\n';
   1494 
   1495 #if 0 // TODO: use dynamic/connection's footers
   1496   for (pos = c->rp.response->first_header; NULL != pos; pos = pos->next)
   1497   {
   1498     if (MHD_FOOTER_KIND == pos->kind)
   1499     {
   1500       size_t new_used_size; /* resulting size with this header */
   1501       /* '4' is colon, space, linefeeds */
   1502       new_used_size = used_size + pos->header_size + pos->value_size + 4;
   1503       if (new_used_size > buf_size)
   1504         return MHD_NO;
   1505       memcpy (buf + used_size, pos->header, pos->header_size);
   1506       used_size += pos->header_size;
   1507       buf[used_size++] = ':';
   1508       buf[used_size++] = ' ';
   1509       memcpy (buf + used_size, pos->value, pos->value_size);
   1510       used_size += pos->value_size;
   1511       buf[used_size++] = '\r';
   1512       buf[used_size++] = '\n';
   1513       mhd_assert (used_size == new_used_size);
   1514     }
   1515   }
   1516 #endif
   1517 
   1518   if (used_size + 2 > buf_size)
   1519     return false;
   1520   buf[used_size++] = '\r';
   1521   buf[used_size++] = '\n';
   1522 
   1523   c->write_buffer_append_offset += used_size;
   1524   mhd_assert (c->write_buffer_append_offset <= c->write_buffer_size);
   1525 
   1526   return true;
   1527 }
   1528 
   1529 
   1530 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool
   1531 mhd_stream_prep_chunked_footer (struct MHD_Connection *restrict c)
   1532 {
   1533   if (!prep_chunked_footer_inn (c))
   1534   {
   1535     mhd_STREAM_ABORT (c,
   1536                       mhd_CONN_CLOSE_NO_POOL_MEM_FOR_REPLY,
   1537                       "No memory in the pool for the reply chunked footer.");
   1538     return true;
   1539   }
   1540   c->stage = mhd_HTTP_STAGE_FOOTERS_SENDING;
   1541   return false;
   1542 }