libmicrohttpd2

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

stream_process_reply.c (48553B)


      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     mhd_STREAM_ABORT (c, \
   1133                       mhd_CONN_CLOSE_FILE_TOO_SHORT, \
   1134                       "The file for file-backed response is smaller " \
   1135                       "than specified by application.");
   1136     return false;
   1137   default:
   1138     break;
   1139   }
   1140   mhd_UNREACHABLE ();
   1141   c->rp.cntn_loc = mhd_REPLY_CNTN_LOC_NOWHERE;
   1142   return false;
   1143 }
   1144 
   1145 
   1146 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool
   1147 mhd_stream_prep_unchunked_body (struct MHD_Connection *restrict c)
   1148 {
   1149   struct MHD_Response *const restrict r = c->rp.response;
   1150   uint64_t left_to_send;
   1151 
   1152   mhd_assert (c->rp.props.send_reply_body);
   1153   mhd_assert (c->rp.rsp_cntn_read_pos != r->cntn_size);
   1154 
   1155   mhd_stream_call_dcc_cleanup_if_needed (c);
   1156 
   1157   if (0 == r->cntn_size)
   1158   { /* 0-byte response is always ready */
   1159     c->stage = mhd_HTTP_STAGE_FULL_REPLY_SENT;
   1160     return true;
   1161   }
   1162 
   1163   mhd_assert (mhd_REPLY_CNTN_LOC_NOWHERE != c->rp.cntn_loc);
   1164   mhd_assert (mhd_RESPONSE_CONTENT_DATA_INVALID != r->cntn_dtype);
   1165 
   1166   if (MHD_SIZE_UNKNOWN == r->cntn_size)
   1167     left_to_send = MHD_SIZE_UNKNOWN;
   1168   else
   1169     left_to_send = r->cntn_size - c->rp.rsp_cntn_read_pos;
   1170 
   1171   mhd_assert (0 != left_to_send);
   1172 
   1173   if (mhd_REPLY_CNTN_LOC_RESP_BUF == c->rp.cntn_loc)
   1174   {
   1175     (void) 0; /* Nothing to do, buffers are ready */
   1176   }
   1177   else if (mhd_REPLY_CNTN_LOC_CONN_BUF == c->rp.cntn_loc)
   1178   {
   1179     if (mhd_RESPONSE_CONTENT_DATA_CALLBACK == r->cntn_dtype)
   1180     {
   1181       const struct MHD_DynamicContentCreatorAction *act;
   1182       size_t size_to_fill =
   1183         c->write_buffer_size - c->write_buffer_append_offset;
   1184       size_t filled;
   1185 
   1186       if (size_to_fill > left_to_send)
   1187         size_to_fill = (size_t) left_to_send;
   1188 
   1189       mhd_assert (c->write_buffer_append_offset < c->write_buffer_size);
   1190       mhd_assert (NULL == c->rp.app_act_ctx.connection);
   1191 
   1192       c->rp.app_act_ctx.connection = c;
   1193       c->rp.app_act.act = mhd_DCC_ACTION_NO_ACTION;
   1194 
   1195       act =
   1196         r->cntn.dyn.cb (r->cntn.dyn.cls,
   1197                         &(c->rp.app_act_ctx),
   1198                         c->rp.rsp_cntn_read_pos,
   1199                         (void *)
   1200                         (c->write_buffer + c->write_buffer_append_offset),
   1201                         size_to_fill);
   1202       c->rp.app_act_ctx.connection = NULL; /* Block any attempt to create a new action */
   1203       if (! preprocess_dcc_action (c, act))
   1204         return true;
   1205       if (mhd_DCC_ACTION_FINISH == c->rp.app_act.act)
   1206       {
   1207         mhd_assert (MHD_SIZE_UNKNOWN == r->cntn_size);
   1208         mhd_assert (c->rp.props.end_by_closing);
   1209 
   1210         c->stage = mhd_HTTP_STAGE_FULL_REPLY_SENT;
   1211 
   1212         return true;
   1213       }
   1214       mhd_assert (mhd_DCC_ACTION_CONTINUE == c->rp.app_act.act);
   1215       // TODO: implement iov sending
   1216 
   1217       filled = c->rp.app_act.data.cntnue.buf_data_size;
   1218       if (size_to_fill < filled)
   1219       {
   1220         mhd_STREAM_ABORT (c,
   1221                           mhd_CONN_CLOSE_APP_ERROR,
   1222                           "Closing connection (application returned more data "
   1223                           "than requested).");
   1224         return true;
   1225       }
   1226       c->rp.rsp_cntn_read_pos += filled;
   1227       c->write_buffer_append_offset += filled;
   1228     }
   1229     else if (mhd_RESPONSE_CONTENT_DATA_FILE == r->cntn_dtype)
   1230     {
   1231       size_t size_to_fill =
   1232         c->write_buffer_size - c->write_buffer_append_offset;
   1233       size_t filled;
   1234 
   1235       if (size_to_fill > left_to_send)
   1236         size_to_fill = (size_t) left_to_send;
   1237 
   1238       if (! read_response_file (c,
   1239                                 r,
   1240                                 size_to_fill,
   1241                                 c->write_buffer + c->write_buffer_append_offset,
   1242                                 &filled))
   1243         return true; /* Error, the stream is closed */
   1244 
   1245       c->rp.rsp_cntn_read_pos += filled;
   1246       c->write_buffer_append_offset += filled;
   1247     }
   1248     else
   1249     {
   1250       mhd_assert (0 && "Impossible value");
   1251       mhd_UNREACHABLE ();
   1252       c->rp.cntn_loc = mhd_REPLY_CNTN_LOC_NOWHERE;
   1253       c->rp.rsp_cntn_read_pos = r->cntn_size;
   1254     }
   1255   }
   1256   else if (mhd_REPLY_CNTN_LOC_IOV == c->rp.cntn_loc)
   1257   {
   1258     size_t copy_size;
   1259 
   1260     mhd_assert (NULL == c->rp.resp_iov.iov);
   1261     mhd_assert (mhd_RESPONSE_CONTENT_DATA_IOVEC == r->cntn_dtype);
   1262 
   1263     copy_size = r->cntn.iovec.cnt * sizeof(mhd_iovec);
   1264     c->rp.resp_iov.iov = (mhd_iovec *)
   1265                          mhd_stream_alloc_memory (c,
   1266                                                   copy_size);
   1267     if (NULL == c->rp.resp_iov.iov)
   1268     {
   1269       /* not enough memory */
   1270       mhd_STREAM_ABORT (c,
   1271                         mhd_CONN_CLOSE_NO_POOL_MEM_FOR_REPLY,
   1272                         "No memory in the pool for the response data.");
   1273       return false;
   1274     }
   1275     memcpy (c->rp.resp_iov.iov,
   1276             &(r->cntn.iovec.iov),
   1277             copy_size);
   1278     c->rp.resp_iov.cnt = r->cntn.iovec.cnt;
   1279     c->rp.resp_iov.sent = 0;
   1280   }
   1281 #if defined(mhd_USE_SENDFILE)
   1282   else if (mhd_REPLY_CNTN_LOC_FILE == c->rp.cntn_loc)
   1283   {
   1284     (void) 0; /* Nothing to do, file should be read directly */
   1285   }
   1286 #endif /* mhd_USE_SENDFILE */
   1287   else
   1288   {
   1289     mhd_assert (0 && "Impossible value");
   1290     mhd_UNREACHABLE ();
   1291     c->rp.cntn_loc = mhd_REPLY_CNTN_LOC_NOWHERE;
   1292     c->rp.rsp_cntn_read_pos = r->cntn_size;
   1293   }
   1294 
   1295   c->stage = mhd_HTTP_STAGE_UNCHUNKED_BODY_READY;
   1296   return false;
   1297 }
   1298 
   1299 
   1300 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool
   1301 mhd_stream_prep_chunked_body (struct MHD_Connection *restrict c)
   1302 {
   1303   size_t filled;
   1304   struct MHD_Response *const restrict r = c->rp.response;
   1305   static const size_t max_chunk = 0xFFFFFF;
   1306   char chunk_hdr[6];            /* 6: max strlen of "FFFFFF" */
   1307   /* "FFFFFF" + "\r\n" */
   1308   static const size_t max_chunk_hdr_len = sizeof(chunk_hdr) + 2;
   1309   /* "FFFFFF" + "\r\n" + "\r\n" (chunk termination) */
   1310   static const size_t max_chunk_overhead = sizeof(chunk_hdr) + 2 + 2;
   1311   size_t chunk_hdr_len;
   1312   uint64_t left_to_send;
   1313   size_t size_to_fill;
   1314 
   1315   mhd_assert (0 == c->write_buffer_append_offset);
   1316   mhd_assert (0 == c->write_buffer_send_offset);
   1317 
   1318   mhd_stream_call_dcc_cleanup_if_needed (c);
   1319 
   1320   /* The buffer must be reasonably large enough */
   1321   if (32 > c->write_buffer_size)
   1322   {
   1323     mhd_STREAM_ABORT (c,
   1324                       mhd_CONN_CLOSE_NO_POOL_MEM_FOR_REPLY,
   1325                       "No memory in the pool for the reply chunked content.");
   1326     return true;
   1327   }
   1328   mhd_assert (max_chunk_overhead < \
   1329               (c->write_buffer_size));
   1330 
   1331   if (MHD_SIZE_UNKNOWN == r->cntn_size)
   1332     left_to_send = MHD_SIZE_UNKNOWN;
   1333   else
   1334     left_to_send = r->cntn_size - c->rp.rsp_cntn_read_pos;
   1335 
   1336   mhd_assert (0 != left_to_send);
   1337   if (0 != left_to_send)
   1338   {
   1339     size_to_fill =
   1340       c->write_buffer_size - max_chunk_overhead;
   1341     /* Limit size for the callback to the max usable size */
   1342     if (max_chunk < size_to_fill)
   1343       size_to_fill = max_chunk;
   1344     if (left_to_send < size_to_fill)
   1345       size_to_fill = (size_t) left_to_send;
   1346   }
   1347   else
   1348     size_to_fill = 0;
   1349 
   1350   if ((0 == left_to_send) &&
   1351       (mhd_RESPONSE_CONTENT_DATA_CALLBACK != r->cntn_dtype))
   1352   {
   1353     c->stage = mhd_HTTP_STAGE_CHUNKED_BODY_SENT;
   1354     return true;
   1355   }
   1356   else if (mhd_RESPONSE_CONTENT_DATA_BUFFER == r->cntn_dtype)
   1357   {
   1358     mhd_assert (size_to_fill <= \
   1359                 r->cntn_size - (size_t) c->rp.rsp_cntn_read_pos);
   1360     memcpy (c->write_buffer + max_chunk_hdr_len,
   1361             r->cntn.buf + (size_t) c->rp.rsp_cntn_read_pos,
   1362             size_to_fill);
   1363     filled = size_to_fill;
   1364   }
   1365   else if (mhd_RESPONSE_CONTENT_DATA_CALLBACK == r->cntn_dtype)
   1366   {
   1367     const struct MHD_DynamicContentCreatorAction *act;
   1368 
   1369     mhd_assert (NULL == c->rp.app_act_ctx.connection);
   1370 
   1371     c->rp.app_act_ctx.connection = c;
   1372     c->rp.app_act.act = mhd_DCC_ACTION_NO_ACTION;
   1373 
   1374     act =
   1375       r->cntn.dyn.cb (r->cntn.dyn.cls,
   1376                       &(c->rp.app_act_ctx),
   1377                       c->rp.rsp_cntn_read_pos,
   1378                       (void *)
   1379                       (c->write_buffer + max_chunk_hdr_len),
   1380                       size_to_fill);
   1381     c->rp.app_act_ctx.connection = NULL; /* Block any attempt to create a new action */
   1382     if (! preprocess_dcc_action (c, act))
   1383       return true;
   1384     if (mhd_DCC_ACTION_FINISH == c->rp.app_act.act)
   1385     {
   1386       mhd_assert (MHD_SIZE_UNKNOWN == r->cntn_size);
   1387       c->stage = mhd_HTTP_STAGE_CHUNKED_BODY_SENT;
   1388 
   1389       return true;
   1390     }
   1391     mhd_assert (mhd_DCC_ACTION_CONTINUE == c->rp.app_act.act);
   1392     // TODO: implement iov sending
   1393 
   1394     filled = c->rp.app_act.data.cntnue.buf_data_size;
   1395     if (size_to_fill < filled)
   1396     {
   1397       mhd_STREAM_ABORT (c,
   1398                         mhd_CONN_CLOSE_APP_ERROR,
   1399                         "Closing connection (application returned more data "
   1400                         "than requested).");
   1401       return true;
   1402     }
   1403   }
   1404   else if (mhd_RESPONSE_CONTENT_DATA_FILE == r->cntn_dtype)
   1405   {
   1406     if (! read_response_file (c,
   1407                               r,
   1408                               size_to_fill,
   1409                               c->write_buffer + max_chunk_hdr_len,
   1410                               &filled))
   1411       return true; /* Error, the stream is closed */
   1412   }
   1413   else
   1414   {
   1415     mhd_assert (0 && "Not implemented yet");
   1416     filled = 0;
   1417   }
   1418 
   1419   chunk_hdr_len = mhd_uint32_to_strx ((uint_fast32_t) filled,
   1420                                       chunk_hdr,
   1421                                       sizeof(chunk_hdr));
   1422   mhd_assert (chunk_hdr_len != 0);
   1423   mhd_assert (chunk_hdr_len < sizeof(chunk_hdr));
   1424   c->write_buffer_send_offset = max_chunk_hdr_len - (chunk_hdr_len + 2);
   1425   memcpy (c->write_buffer + c->write_buffer_send_offset,
   1426           chunk_hdr,
   1427           chunk_hdr_len);
   1428   c->write_buffer[max_chunk_hdr_len - 2] = '\r';
   1429   c->write_buffer[max_chunk_hdr_len - 1] = '\n';
   1430   c->write_buffer[max_chunk_hdr_len + filled] = '\r';
   1431   c->write_buffer[max_chunk_hdr_len + filled + 1] = '\n';
   1432   c->write_buffer_append_offset = max_chunk_hdr_len + filled + 2;
   1433   if (0 != filled)
   1434     c->rp.rsp_cntn_read_pos += filled;
   1435   else
   1436     c->rp.rsp_cntn_read_pos = r->cntn_size;
   1437 
   1438   c->stage = mhd_HTTP_STAGE_CHUNKED_BODY_READY;
   1439 
   1440   return false;
   1441 }
   1442 
   1443 
   1444 /**
   1445  * Allocate the connection's write buffer (if necessary) and fill it
   1446  * with response footers.
   1447  * Inner version.
   1448  *
   1449  * @param c the connection
   1450  * @return 'true' if footers formed successfully,
   1451  *         'false' if not enough buffer
   1452  */
   1453 static MHD_FN_PAR_NONNULL_ALL_ bool
   1454 prep_chunked_footer_inn (struct MHD_Connection *restrict c)
   1455 {
   1456   char *buf;           /**< the buffer to write footers to */
   1457   size_t buf_size;     /**< the size of the @a buf */
   1458   size_t used_size;    /**< the used size of the @a buf */
   1459   // struct MHD_HTTP_Res_Header *pos;
   1460 
   1461   mhd_assert (c->rp.props.chunked);
   1462   mhd_assert (mhd_HTTP_STAGE_CHUNKED_BODY_SENT == c->stage);
   1463   mhd_assert (NULL != c->rp.response);
   1464 
   1465   buf_size = mhd_stream_maximize_write_buffer (c);
   1466   /* '5' is the minimal size of chunked footer ("0\r\n\r\n") */
   1467   if (buf_size < 5)
   1468     return false;
   1469   mhd_assert (NULL != c->write_buffer);
   1470   buf = c->write_buffer + c->write_buffer_append_offset;
   1471   mhd_assert (NULL != buf);
   1472   used_size = 0;
   1473   buf[used_size++] = '0';
   1474   buf[used_size++] = '\r';
   1475   buf[used_size++] = '\n';
   1476 
   1477 #if 0 // TODO: use dynamic/connection's footers
   1478   for (pos = c->rp.response->first_header; NULL != pos; pos = pos->next)
   1479   {
   1480     if (MHD_FOOTER_KIND == pos->kind)
   1481     {
   1482       size_t new_used_size; /* resulting size with this header */
   1483       /* '4' is colon, space, linefeeds */
   1484       new_used_size = used_size + pos->header_size + pos->value_size + 4;
   1485       if (new_used_size > buf_size)
   1486         return MHD_NO;
   1487       memcpy (buf + used_size, pos->header, pos->header_size);
   1488       used_size += pos->header_size;
   1489       buf[used_size++] = ':';
   1490       buf[used_size++] = ' ';
   1491       memcpy (buf + used_size, pos->value, pos->value_size);
   1492       used_size += pos->value_size;
   1493       buf[used_size++] = '\r';
   1494       buf[used_size++] = '\n';
   1495       mhd_assert (used_size == new_used_size);
   1496     }
   1497   }
   1498 #endif
   1499 
   1500   if (used_size + 2 > buf_size)
   1501     return false;
   1502   buf[used_size++] = '\r';
   1503   buf[used_size++] = '\n';
   1504 
   1505   c->write_buffer_append_offset += used_size;
   1506   mhd_assert (c->write_buffer_append_offset <= c->write_buffer_size);
   1507 
   1508   return true;
   1509 }
   1510 
   1511 
   1512 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool
   1513 mhd_stream_prep_chunked_footer (struct MHD_Connection *restrict c)
   1514 {
   1515   if (! prep_chunked_footer_inn (c))
   1516   {
   1517     mhd_STREAM_ABORT (c,
   1518                       mhd_CONN_CLOSE_NO_POOL_MEM_FOR_REPLY,
   1519                       "No memory in the pool for the reply chunked footer.");
   1520     return true;
   1521   }
   1522   c->stage = mhd_HTTP_STAGE_FOOTERS_SENDING;
   1523   return false;
   1524 }