libmicrohttpd2

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

stream_funcs.c (41664B)


      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) 2022-2024 Evgeny Grin (Karlson2k)
      5 
      6   GNU libmicrohttpd is free software; you can redistribute it and/or
      7   modify it under the terms of the GNU Lesser General Public
      8   License as published by the Free Software Foundation; either
      9   version 2.1 of the License, or (at your option) any later version.
     10 
     11   GNU libmicrohttpd is distributed in the hope that it will be useful,
     12   but WITHOUT ANY WARRANTY; without even the implied warranty of
     13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     14   Lesser General Public License for more details.
     15 
     16   Alternatively, you can redistribute GNU libmicrohttpd and/or
     17   modify it under the terms of the GNU General Public License as
     18   published by the Free Software Foundation; either version 2 of
     19   the License, or (at your option) any later version, together
     20   with the eCos exception, as follows:
     21 
     22     As a special exception, if other files instantiate templates or
     23     use macros or inline functions from this file, or you compile this
     24     file and link it with other works to produce a work based on this
     25     file, this file does not by itself cause the resulting work to be
     26     covered by the GNU General Public License. However the source code
     27     for this file must still be made available in accordance with
     28     section (3) of the GNU General Public License v2.
     29 
     30     This exception does not invalidate any other reasons why a work
     31     based on this file might be covered by the GNU General Public
     32     License.
     33 
     34   You should have received copies of the GNU Lesser General Public
     35   License and the GNU General Public License along with this library;
     36   if not, see <https://www.gnu.org/licenses/>.
     37 */
     38 
     39 /**
     40  * @file src/mhd2/stream_funcs.c
     41  * @brief  The definition of the stream internal functions
     42  * @author Karlson2k (Evgeny Grin)
     43  */
     44 
     45 #include "mhd_sys_options.h"
     46 
     47 #include "stream_funcs.h"
     48 
     49 #include "mhd_assert.h"
     50 #include "mhd_unreachable.h"
     51 
     52 #ifdef mhd_DEBUG_CONN_ADD_CLOSE
     53 #  include <stdio.h>
     54 #endif /* mhd_DEBUG_CONN_ADD_CLOSE */
     55 #include <string.h>
     56 #include "extr_events_funcs.h"
     57 #ifdef MHD_SUPPORT_EPOLL
     58 #  include <sys/epoll.h>
     59 #endif
     60 #include "sys_malloc.h"
     61 
     62 #include "mhd_daemon.h"
     63 #include "mhd_connection.h"
     64 #include "mhd_response.h"
     65 #include "mempool_funcs.h"
     66 #include "mhd_str.h"
     67 #include "mhd_str_macros.h"
     68 
     69 #include "mhd_sockets_funcs.h"
     70 
     71 #include "request_get_value.h"
     72 #include "response_destroy.h"
     73 #include "mhd_mono_clock.h"
     74 #include "daemon_logger.h"
     75 #include "daemon_funcs.h"
     76 #include "conn_mark_ready.h"
     77 #include "stream_process_reply.h"
     78 #include "extr_events_funcs.h"
     79 
     80 #ifdef MHD_SUPPORT_HTTPS
     81 #  include "mhd_tls_funcs.h"
     82 #endif
     83 
     84 #include "mhd_public_api.h"
     85 
     86 
     87 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ void *
     88 mhd_stream_alloc_memory (struct MHD_Connection *restrict c,
     89                          size_t size)
     90 {
     91   struct mhd_MemoryPool *const restrict pool = c->pool;     /* a short alias */
     92   size_t need_to_be_freed = 0; /**< The required amount of additional free memory */
     93   void *res;
     94 
     95   res = mhd_pool_try_alloc (pool,
     96                             size,
     97                             &need_to_be_freed);
     98   if (NULL != res)
     99     return res;
    100 
    101   if (mhd_pool_is_resizable_inplace (pool,
    102                                      c->write_buffer,
    103                                      c->write_buffer_size))
    104   {
    105     if (c->write_buffer_size - c->write_buffer_append_offset >=
    106         need_to_be_freed)
    107     {
    108       char *buf;
    109       const size_t new_buf_size = c->write_buffer_size - need_to_be_freed;
    110       buf = (char *) mhd_pool_reallocate (pool,
    111                                           c->write_buffer,
    112                                           c->write_buffer_size,
    113                                           new_buf_size);
    114       mhd_assert (c->write_buffer == buf);
    115       mhd_assert (c->write_buffer_append_offset <= new_buf_size);
    116       mhd_assert (c->write_buffer_send_offset <= new_buf_size);
    117       c->write_buffer_size = new_buf_size;
    118       c->write_buffer = buf;
    119     }
    120     else
    121       return NULL;
    122   }
    123   else if (mhd_pool_is_resizable_inplace (pool,
    124                                           c->read_buffer,
    125                                           c->read_buffer_size))
    126   {
    127     if (c->read_buffer_size - c->read_buffer_offset >= need_to_be_freed)
    128     {
    129       char *buf;
    130       const size_t new_buf_size = c->read_buffer_size - need_to_be_freed;
    131       buf = (char *) mhd_pool_reallocate (pool,
    132                                           c->read_buffer,
    133                                           c->read_buffer_size,
    134                                           new_buf_size);
    135       mhd_assert (c->read_buffer == buf);
    136       mhd_assert (c->read_buffer_offset <= new_buf_size);
    137       c->read_buffer_size = new_buf_size;
    138       c->read_buffer = buf;
    139     }
    140     else
    141       return NULL;
    142   }
    143   else
    144     return NULL;
    145   res = mhd_pool_allocate (pool, size, true);
    146   mhd_assert (NULL != res); /* It has been checked that pool has enough space */
    147   return res;
    148 }
    149 
    150 
    151 /**
    152  * Shrink stream read buffer to the zero size of free space in the buffer
    153  * @param c the connection whose read buffer is being manipulated
    154  */
    155 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ void
    156 mhd_stream_shrink_read_buffer (struct MHD_Connection *restrict c)
    157 {
    158   void *new_buf;
    159 
    160   if ((NULL == c->read_buffer) || (0 == c->read_buffer_size))
    161   {
    162     mhd_assert (0 == c->read_buffer_size);
    163     mhd_assert (0 == c->read_buffer_offset);
    164     return;
    165   }
    166 
    167   mhd_assert (c->read_buffer_offset <= c->read_buffer_size);
    168   if (0 == c->read_buffer_offset)
    169   {
    170     mhd_pool_deallocate (c->pool, c->read_buffer, c->read_buffer_size);
    171     c->read_buffer = NULL;
    172     c->read_buffer_size = 0;
    173   }
    174   else
    175   {
    176     mhd_assert (mhd_pool_is_resizable_inplace (c->pool, c->read_buffer, \
    177                                                c->read_buffer_size));
    178     new_buf = mhd_pool_reallocate (c->pool, c->read_buffer, c->read_buffer_size,
    179                                    c->read_buffer_offset);
    180     mhd_assert (c->read_buffer == new_buf);
    181     c->read_buffer = (char *) new_buf;
    182     c->read_buffer_size = c->read_buffer_offset;
    183   }
    184 }
    185 
    186 
    187 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ size_t
    188 mhd_stream_maximize_write_buffer (struct MHD_Connection *restrict c)
    189 {
    190   struct mhd_MemoryPool *const restrict pool = c->pool;
    191   void *new_buf;
    192   size_t new_size;
    193   size_t free_size;
    194 
    195   mhd_assert ((NULL != c->write_buffer) || (0 == c->write_buffer_size));
    196   mhd_assert (c->write_buffer_append_offset >= c->write_buffer_send_offset);
    197   mhd_assert (c->write_buffer_size >= c->write_buffer_append_offset);
    198 
    199   free_size = mhd_pool_get_free (pool);
    200   if (0 != free_size)
    201   {
    202     new_size = c->write_buffer_size + free_size;
    203     /* This function must not move the buffer position.
    204      * mhd_pool_reallocate () may return the new position only if buffer was
    205      * allocated 'from_end' or is not the last allocation,
    206      * which should not happen. */
    207     mhd_assert ((NULL == c->write_buffer) || \
    208                 mhd_pool_is_resizable_inplace (pool, c->write_buffer, \
    209                                                c->write_buffer_size));
    210     new_buf = mhd_pool_reallocate (pool,
    211                                    c->write_buffer,
    212                                    c->write_buffer_size,
    213                                    new_size);
    214     mhd_assert ((c->write_buffer == new_buf) || (NULL == c->write_buffer));
    215     c->write_buffer = (char *) new_buf;
    216     c->write_buffer_size = new_size;
    217     if (c->write_buffer_send_offset == c->write_buffer_append_offset)
    218     {
    219       /* All data have been sent, reset offsets to zero. */
    220       c->write_buffer_send_offset = 0;
    221       c->write_buffer_append_offset = 0;
    222     }
    223   }
    224 
    225   return c->write_buffer_size - c->write_buffer_append_offset;
    226 }
    227 
    228 
    229 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ void
    230 mhd_stream_release_write_buffer (struct MHD_Connection *restrict c)
    231 {
    232   struct mhd_MemoryPool *const restrict pool = c->pool;
    233 
    234   mhd_assert ((NULL != c->write_buffer) || (0 == c->write_buffer_size));
    235   mhd_assert (c->write_buffer_append_offset == c->write_buffer_send_offset);
    236   mhd_assert (c->write_buffer_size >= c->write_buffer_append_offset);
    237 
    238   mhd_pool_deallocate (pool, c->write_buffer, c->write_buffer_size);
    239   c->write_buffer_send_offset = 0;
    240   c->write_buffer_append_offset = 0;
    241   c->write_buffer_size = 0;
    242   c->write_buffer = NULL;
    243 
    244 }
    245 
    246 
    247 #ifndef MHD_MAX_REASONABLE_HEADERS_SIZE_
    248 /**
    249  * A reasonable headers size (excluding request line) that should be sufficient
    250  * for most requests.
    251  * If incoming data buffer free space is not enough to process the complete
    252  * header (the request line and all headers) and the headers size is larger than
    253  * this size then the status code 431 "Request Header Fields Too Large" is
    254  * returned to the client.
    255  * The larger headers are processed by MHD if enough space is available.
    256  */
    257 #  define MHD_MAX_REASONABLE_HEADERS_SIZE_ (6 * 1024)
    258 #endif /* ! MHD_MAX_REASONABLE_HEADERS_SIZE_ */
    259 
    260 #ifndef MHD_MAX_REASONABLE_REQ_TARGET_SIZE_
    261 /**
    262  * A reasonable request target (the request URI) size that should be sufficient
    263  * for most requests.
    264  * If incoming data buffer free space is not enough to process the complete
    265  * header (the request line and all headers) and the request target size is
    266  * larger than this size then the status code 414 "URI Too Long" is
    267  * returned to the client.
    268  * The larger request targets are processed by MHD if enough space is available.
    269  * The value chosen according to RFC 9112 Section 3, paragraph 5
    270  */
    271 #  define MHD_MAX_REASONABLE_REQ_TARGET_SIZE_ 8000
    272 #endif /* ! MHD_MAX_REASONABLE_REQ_TARGET_SIZE_ */
    273 
    274 #ifndef MHD_MIN_REASONABLE_HEADERS_SIZE_
    275 /**
    276  * A reasonable headers size (excluding request line) that should be sufficient
    277  * for basic simple requests.
    278  * When no space left in the receiving buffer try to avoid replying with
    279  * the status code 431 "Request Header Fields Too Large" if headers size
    280  * is smaller then this value.
    281  */
    282 #  define MHD_MIN_REASONABLE_HEADERS_SIZE_ 26
    283 #endif /* ! MHD_MIN_REASONABLE_HEADERS_SIZE_ */
    284 
    285 #ifndef MHD_MIN_REASONABLE_REQ_TARGET_SIZE_
    286 /**
    287  * A reasonable request target (the request URI) size that should be sufficient
    288  * for basic simple requests.
    289  * When no space left in the receiving buffer try to avoid replying with
    290  * the status code 414 "URI Too Long" if the request target size is smaller then
    291  * this value.
    292  */
    293 #  define MHD_MIN_REASONABLE_REQ_TARGET_SIZE_ 40
    294 #endif /* ! MHD_MIN_REASONABLE_REQ_TARGET_SIZE_ */
    295 
    296 #ifndef MHD_MIN_REASONABLE_REQ_METHOD_SIZE_
    297 /**
    298  * A reasonable request method string size that should be sufficient
    299  * for basic simple requests.
    300  * When no space left in the receiving buffer try to avoid replying with
    301  * the status code 501 "Not Implemented" if the request method size is
    302  * smaller then this value.
    303  */
    304 #  define MHD_MIN_REASONABLE_REQ_METHOD_SIZE_ 16
    305 #endif /* ! MHD_MIN_REASONABLE_REQ_METHOD_SIZE_ */
    306 
    307 #ifndef MHD_MIN_REASONABLE_REQ_CHUNK_LINE_LENGTH_
    308 /**
    309  * A reasonable minimal chunk line length.
    310  * When no space left in the receiving buffer reply with 413 "Content Too Large"
    311  * if the chunk line length is larger than this value.
    312  */
    313 #  define MHD_MIN_REASONABLE_REQ_CHUNK_LINE_LENGTH_ 4
    314 #endif /* ! MHD_MIN_REASONABLE_REQ_CHUNK_LINE_LENGTH_ */
    315 
    316 
    317 MHD_INTERNAL
    318 MHD_FN_PAR_NONNULL_ (1) MHD_FN_PAR_IN_SIZE_ (4,3) unsigned int
    319 mhd_stream_get_no_space_err_status_code (struct MHD_Connection *restrict c,
    320                                          enum MHD_ProcRecvDataStage stage,
    321                                          size_t add_element_size,
    322                                          const char *restrict add_element)
    323 {
    324   size_t method_size;
    325   size_t uri_size;
    326   size_t opt_headers_size;
    327   size_t host_field_line_size;
    328 
    329   mhd_assert (mhd_HTTP_STAGE_REQ_LINE_RECEIVED < c->stage);
    330   mhd_assert (MHD_PROC_RECV_HEADERS <= stage);
    331   mhd_assert ((0 == add_element_size) || (NULL != add_element));
    332 
    333   c->rq.too_large = true;
    334 
    335   if (mhd_HTTP_STAGE_HEADERS_RECEIVED > c->stage)
    336   {
    337     mhd_assert (NULL != c->rq.field_lines.start);
    338     opt_headers_size =
    339       (size_t) ((c->read_buffer + c->read_buffer_offset)
    340                 - c->rq.field_lines.start);
    341   }
    342   else
    343     opt_headers_size = c->rq.field_lines.size;
    344 
    345   /* The read buffer is fully used by the request line, the field lines
    346      (headers) and internal information.
    347      The return status code works as a suggestion for the client to reduce
    348      one of the request elements. */
    349 
    350   if ((MHD_PROC_RECV_BODY_CHUNKED == stage) &&
    351       (MHD_MIN_REASONABLE_REQ_CHUNK_LINE_LENGTH_ < add_element_size))
    352   {
    353     /* Request could be re-tried easily with smaller chunk sizes */
    354     return MHD_HTTP_STATUS_CONTENT_TOO_LARGE;
    355   }
    356 
    357   host_field_line_size = 0;
    358   /* The "Host:" field line is mandatory.
    359      The total size of the field lines (headers) cannot be smaller than
    360      the size of the "Host:" field line. */
    361   if ((MHD_PROC_RECV_HEADERS == stage)
    362       && (0 != add_element_size))
    363   {
    364     static const size_t header_host_key_len =
    365       mhd_SSTR_LEN (MHD_HTTP_HEADER_HOST);
    366     const bool is_host_header =
    367       (header_host_key_len + 1 <= add_element_size)
    368       && ( (0 == add_element[header_host_key_len])
    369            || (':' == add_element[header_host_key_len]) )
    370       && mhd_str_equal_caseless_bin_n (MHD_HTTP_HEADER_HOST,
    371                                        add_element,
    372                                        header_host_key_len);
    373     if (is_host_header)
    374     {
    375       const bool is_parsed = ! (
    376         (mhd_HTTP_STAGE_HEADERS_RECEIVED > c->stage) &&
    377         (add_element_size == c->read_buffer_offset) &&
    378         (c->read_buffer == add_element) );
    379       size_t actual_element_size;
    380 
    381       mhd_assert (! is_parsed || (0 == add_element[header_host_key_len]));
    382       /* The actual size should be larger due to CRLF or LF chars,
    383          however the exact termination sequence is not known here and
    384          as perfect precision is not required, to simplify the code
    385          assume the minimal length. */
    386       if (is_parsed)
    387         actual_element_size = add_element_size + 1;  /* "1" for LF */
    388       else
    389         actual_element_size = add_element_size;
    390 
    391       host_field_line_size = actual_element_size;
    392       mhd_assert (opt_headers_size >= actual_element_size);
    393       opt_headers_size -= actual_element_size;
    394     }
    395   }
    396   if (0 == host_field_line_size)
    397   {
    398     static const size_t host_field_name_len =
    399       mhd_SSTR_LEN (MHD_HTTP_HEADER_HOST);
    400     struct MHD_StringNullable host_value;
    401 
    402     if (mhd_request_get_value_n (&(c->rq),
    403                                  MHD_VK_HEADER,
    404                                  host_field_name_len,
    405                                  MHD_HTTP_HEADER_HOST,
    406                                  &host_value))
    407     {
    408       /* Calculate the minimal size of the field line: no space between
    409          colon and the field value, line terminated by LR */
    410       host_field_line_size =
    411         host_field_name_len + host_value.len + 2; /* "2" for ':' and LF */
    412 
    413       /* The "Host:" field could be added by application */
    414       if (opt_headers_size >= host_field_line_size)
    415       {
    416         opt_headers_size -= host_field_line_size;
    417         /* Take into account typical space after colon and CR at the end of the line */
    418         if (opt_headers_size >= 2)
    419           opt_headers_size -= 2;
    420       }
    421       else
    422         host_field_line_size = 0; /* No "Host:" field line set by the client */
    423     }
    424   }
    425 
    426   uri_size = c->rq.req_target_len;
    427   if (mhd_HTTP_METHOD_OTHER != c->rq.http_mthd)
    428     method_size = 0; /* Do not recommend shorter request method */
    429   else
    430   {
    431     mhd_assert (NULL != c->rq.method.cstr);
    432     method_size = c->rq.method.len;
    433     mhd_assert (method_size == strlen (c->rq.method.cstr));
    434   }
    435 
    436   if ((size_t) MHD_MAX_REASONABLE_HEADERS_SIZE_ < opt_headers_size)
    437   {
    438     /* Typically the easiest way to reduce request header size is
    439        a removal of some optional headers. */
    440     if (opt_headers_size > (uri_size / 8))
    441     {
    442       if ((opt_headers_size / 2) > method_size)
    443         return MHD_HTTP_STATUS_REQUEST_HEADER_FIELDS_TOO_LARGE;
    444       else
    445         return MHD_HTTP_STATUS_NOT_IMPLEMENTED; /* The length of the HTTP request method is unreasonably large */
    446     }
    447     else
    448     { /* Request target is MUCH larger than headers */
    449       if ((uri_size / 16) > method_size)
    450         return MHD_HTTP_STATUS_URI_TOO_LONG;
    451       else
    452         return MHD_HTTP_STATUS_NOT_IMPLEMENTED; /* The length of the HTTP request method is unreasonably large */
    453     }
    454   }
    455   if ((size_t) MHD_MAX_REASONABLE_REQ_TARGET_SIZE_ < uri_size)
    456   {
    457     /* If request target size if larger than maximum reasonable size
    458        recommend client to reduce the request target size (length). */
    459     if ((uri_size / 16) > method_size)
    460       return MHD_HTTP_STATUS_URI_TOO_LONG;     /* Request target is MUCH larger than headers */
    461     else
    462       return MHD_HTTP_STATUS_NOT_IMPLEMENTED;  /* The length of the HTTP request method is unreasonably large */
    463   }
    464 
    465   /* The read buffer is too small to handle reasonably large requests */
    466 
    467   if ((size_t) MHD_MIN_REASONABLE_HEADERS_SIZE_ < opt_headers_size)
    468   {
    469     /* Recommend application to retry with minimal headers */
    470     if ((opt_headers_size * 4) > uri_size)
    471     {
    472       if (opt_headers_size > method_size)
    473         return MHD_HTTP_STATUS_REQUEST_HEADER_FIELDS_TOO_LARGE;
    474       else
    475         return MHD_HTTP_STATUS_NOT_IMPLEMENTED; /* The length of the HTTP request method is unreasonably large */
    476     }
    477     else
    478     { /* Request target is significantly larger than headers */
    479       if (uri_size > method_size * 4)
    480         return MHD_HTTP_STATUS_URI_TOO_LONG;
    481       else
    482         return MHD_HTTP_STATUS_NOT_IMPLEMENTED; /* The length of the HTTP request method is unreasonably large */
    483     }
    484   }
    485   if ((size_t) MHD_MIN_REASONABLE_REQ_TARGET_SIZE_ < uri_size)
    486   {
    487     /* Recommend application to retry with a shorter request target */
    488     if (uri_size > method_size * 4)
    489       return MHD_HTTP_STATUS_URI_TOO_LONG;
    490     else
    491       return MHD_HTTP_STATUS_NOT_IMPLEMENTED; /* The length of the HTTP request method is unreasonably large */
    492   }
    493 
    494   if ((size_t) MHD_MIN_REASONABLE_REQ_METHOD_SIZE_ < method_size)
    495   {
    496     /* The request target (URI) and headers are (reasonably) very small.
    497        Some non-standard long request method is used. */
    498     /* The last resort response as it means "the method is not supported
    499        by the server for any URI". */
    500     return MHD_HTTP_STATUS_NOT_IMPLEMENTED;
    501   }
    502 
    503   /* The almost impossible situation: all elements are small, but cannot
    504      fit the buffer. The application set the buffer size to
    505      critically low value? */
    506 
    507   if ((1 < opt_headers_size) || (1 < uri_size))
    508   {
    509     if (opt_headers_size >= uri_size)
    510       return MHD_HTTP_STATUS_REQUEST_HEADER_FIELDS_TOO_LARGE;
    511     else
    512       return MHD_HTTP_STATUS_URI_TOO_LONG;
    513   }
    514 
    515   /* Nothing to reduce in the request.
    516      Reply with some status. */
    517   if (0 != host_field_line_size)
    518     return MHD_HTTP_STATUS_REQUEST_HEADER_FIELDS_TOO_LARGE;
    519 
    520   return MHD_HTTP_STATUS_URI_TOO_LONG;
    521 }
    522 
    523 
    524 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ void
    525 mhd_stream_switch_from_recv_to_send (struct MHD_Connection *c)
    526 {
    527   /* Read buffer is not needed for this request, shrink it.*/
    528   mhd_stream_shrink_read_buffer (c);
    529 }
    530 
    531 
    532 /**
    533  * Finish request serving.
    534  * The stream will be re-used or closed.
    535  *
    536  * @param c the connection to use.
    537  */
    538 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ void
    539 mhd_stream_finish_req_serving (struct MHD_Connection *restrict c,
    540                                bool reuse)
    541 {
    542   struct MHD_Daemon *const restrict d = c->daemon;
    543 
    544   if (! reuse)
    545   {
    546     mhd_assert (! c->stop_with_error || (NULL == c->rp.response) || \
    547                 (c->rp.response->cfg.int_err_resp));
    548 
    549     /* Next function will notify client and set connection
    550      * state to "PRE-CLOSING" */
    551     /* Later response and memory pool will be destroyed */
    552     mhd_conn_start_closing (c,
    553                             c->stop_with_error ?
    554                             mhd_CONN_CLOSE_ERR_REPLY_SENT :
    555                             mhd_CONN_CLOSE_HTTP_COMPLETED,
    556                             NULL);
    557   }
    558   else
    559   {
    560     /* Reset connection to process the next request */
    561     size_t new_read_buf_size;
    562     mhd_assert (! c->stop_with_error);
    563     mhd_assert (! c->discard_request);
    564     mhd_assert (NULL == c->rq.cntn.lbuf.data);
    565 
    566 #if 0 // TODO: notification callback
    567     if ( (NULL != d->notify_completed) &&
    568          (c->rq.app_aware) )
    569       d->notify_completed (d->notify_completed_cls,
    570                            c,
    571                            &c->rq.app_context,
    572                            MHD_REQUEST_ENDED_COMPLETED_OK);
    573     c->rq.app_aware = false;
    574 #endif
    575 
    576     mhd_stream_call_dcc_cleanup_if_needed (c);
    577     if (NULL != c->rp.resp_iov.iov)
    578     {
    579       free (c->rp.resp_iov.iov);
    580       c->rp.resp_iov.iov = NULL;
    581     }
    582 
    583     if (NULL != c->rp.response)
    584       mhd_response_dec_use_count (c->rp.response);
    585     c->rp.response = NULL;
    586 
    587     c->conn_reuse = mhd_CONN_KEEPALIVE_POSSIBLE;
    588     c->stage = mhd_HTTP_STAGE_INIT;
    589     c->event_loop_info = MHD_EVENT_LOOP_INFO_RECV; /* Dummy state, real state set later */
    590 
    591     // TODO: move request reset to special function
    592     memset (&c->rq, 0, sizeof(c->rq));
    593 
    594     // TODO: move reply reset to special function
    595     /* iov (if any) will be deallocated by mhd_pool_reset */
    596     memset (&c->rp, 0, sizeof(c->rp));
    597 
    598 #ifndef HAVE_NULL_PTR_ALL_ZEROS
    599     // TODO: move request reset to special function
    600     mhd_DLINKEDL_INIT_LIST (&(c->rq), fields);
    601 #ifdef MHD_SUPPORT_POST_PARSER
    602     mhd_DLINKEDL_INIT_LIST (&(c->rq), post_fields);
    603 #endif /* MHD_SUPPORT_POST_PARSER */
    604     c->rq.version = NULL;
    605     c->rq.url = NULL;
    606     c->rq.field_lines.start = NULL;
    607     c->rq.app_context = NULL;
    608     c->rq.hdrs.rq_line.rq_tgt = NULL;
    609     c->rq.hdrs.rq_line.rq_tgt_qmark = NULL;
    610 
    611     // TODO: move reply reset to special function
    612     c->rp.app_act_ctx.connection = NULL;
    613     c->rp.response = NULL;
    614     c->rp.resp_iov.iov = NULL;
    615 #endif /* ! HAVE_NULL_PTR_ALL_ZEROS */
    616 
    617     c->write_buffer = NULL;
    618     c->write_buffer_size = 0;
    619     c->write_buffer_send_offset = 0;
    620     c->write_buffer_append_offset = 0;
    621     c->continue_message_write_offset = 0;
    622 
    623     /* Reset the read buffer to the starting size,
    624        preserving the bytes we have already read. */
    625     new_read_buf_size = d->conns.cfg.mem_pool_size / 2;
    626     if (c->read_buffer_offset > new_read_buf_size)
    627       new_read_buf_size = c->read_buffer_offset;
    628 
    629     c->read_buffer
    630       = (char *) mhd_pool_reset (c->pool,
    631                                  c->read_buffer,
    632                                  c->read_buffer_offset,
    633                                  new_read_buf_size);
    634     c->read_buffer_size = new_read_buf_size;
    635   }
    636   c->rq.app_context = NULL;
    637 }
    638 
    639 
    640 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool
    641 mhd_stream_is_timeout_expired (struct MHD_Connection *restrict c)
    642 {
    643   const uint_fast64_t timeout = c->connection_timeout_ms;
    644   uint_fast64_t now;
    645   uint_fast64_t since_actv;
    646 
    647   mhd_assert (! c->suspended);
    648 
    649   if (0 == timeout)
    650     return false;
    651 
    652   now = mhd_monotonic_msec_counter (); // TODO: Get and use timer value one time only per round
    653   since_actv = now - c->last_activity;
    654   /* Keep the next lines in sync with #connection_get_wait() to avoid
    655    * undesired side-effects like busy-waiting. */
    656   if (timeout < since_actv)
    657   {
    658     const uint_fast64_t jump_back = c->last_activity - now;
    659     if (jump_back < since_actv)
    660     {
    661       /* Very unlikely that it is more than quarter-million years pause.
    662        * More likely that system clock jumps back. */
    663       if (4000 >= jump_back)
    664       {
    665         c->last_activity = now; /* Avoid repetitive messages.
    666                                    Warn: the order of connections sorted
    667                                    by timeout is not updated. */
    668         mhd_LOG_PRINT (c->daemon, MHD_SC_SYS_CLOCK_JUMP_BACK_CORRECTED, \
    669                        mhd_LOG_FMT ("Detected system clock %u " \
    670                                     "milliseconds jump back."),
    671                        (unsigned int) jump_back);
    672         return false;
    673       }
    674       mhd_LOG_PRINT (c->daemon, MHD_SC_SYS_CLOCK_JUMP_BACK_LARGE, \
    675                      mhd_LOG_FMT ("Detected too large system clock %" \
    676                                   PRIuFAST64 " milliseconds jump back"),
    677                      jump_back);
    678     }
    679     return true;
    680   }
    681   return false;
    682 }
    683 
    684 
    685 /**
    686  * Update last activity mark to the current time..
    687  * @param c the connection to update
    688  */
    689 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ void
    690 mhd_stream_update_activity_mark (struct MHD_Connection *restrict c)
    691 {
    692   struct MHD_Daemon *const restrict d = c->daemon;
    693 #if defined(MHD_SUPPORT_THREADS)
    694   mhd_assert (! mhd_D_HAS_WORKERS (d));
    695 #endif /* MHD_SUPPORT_THREADS */
    696 
    697   mhd_assert (! c->suspended);
    698 
    699   if (0 == c->connection_timeout_ms)
    700     return;  /* Skip update of activity for connections
    701                without timeout timer. */
    702 
    703   c->last_activity = mhd_monotonic_msec_counter (); // TODO: Get and use time value one time per round
    704   if (mhd_D_HAS_THR_PER_CONN (d))
    705     return; /* each connection has personal timeout */
    706 
    707   if (c->connection_timeout_ms != d->conns.cfg.timeout_ms)
    708     return; /* custom timeout, no need to move it in "normal" DLL */
    709 
    710   /* move connection to head of timeout list (by remove + add operation) */
    711   mhd_DLINKEDL_DEL_D (&(d->conns.def_timeout), c, by_timeout);
    712   mhd_DLINKEDL_INS_FIRST_D (&(d->conns.def_timeout), c, by_timeout);
    713 }
    714 
    715 
    716 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ void
    717 mhd_stream_resumed_activity_mark (struct MHD_Connection *restrict c)
    718 {
    719   struct MHD_Daemon *const restrict d = c->daemon;
    720 #if defined(MHD_SUPPORT_THREADS)
    721   mhd_assert (! mhd_D_HAS_WORKERS (d));
    722 #endif /* MHD_SUPPORT_THREADS */
    723 
    724   mhd_assert (! c->suspended);
    725   mhd_assert (c->resuming);
    726 
    727   /* Update of activity for connections without timeout timer unless
    728    * no timeout is set */
    729   if (0 != c->connection_timeout_ms)
    730     c->last_activity = mhd_monotonic_msec_counter (); // TODO: Get and use time value one time per round
    731 
    732   if (mhd_D_HAS_THR_PER_CONN (d))
    733     return; /* each connection has personal timeout */
    734 
    735   if (c->connection_timeout_ms == d->conns.cfg.timeout_ms)
    736     mhd_DLINKEDL_INS_FIRST_D (&(d->conns.def_timeout), c, by_timeout);
    737   else
    738     mhd_DLINKEDL_INS_FIRST_D (&(d->conns.cust_timeout), c, by_timeout);
    739 }
    740 
    741 
    742 MHD_INTERNAL
    743 MHD_FN_PAR_NONNULL_ALL_ void
    744 mhd_conn_remove_from_timeout_lists (struct MHD_Connection *restrict c)
    745 {
    746   if (mhd_D_HAS_THR_PER_CONN (c->daemon))
    747     return;
    748 
    749   if (c->connection_timeout_ms == c->daemon->conns.cfg.timeout_ms)
    750     mhd_DLINKEDL_DEL_D (&(c->daemon->conns.def_timeout), \
    751                         c, by_timeout);
    752   else
    753     mhd_DLINKEDL_DEL_D (&(c->daemon->conns.cust_timeout), \
    754                         c, by_timeout);
    755 }
    756 
    757 
    758 /* return 'true' is lingering needed, 'false' is lingering is not needed */
    759 static MHD_FN_PAR_NONNULL_ALL_ bool
    760 conn_start_socket_closing (struct MHD_Connection *restrict c,
    761                            bool close_hard)
    762 {
    763   bool need_lingering;
    764   /* Make changes on the socket early to let the kernel and the remote
    765    * to process the changes in parallel. */
    766   if (close_hard)
    767   {
    768     /* Use abortive closing, send RST to remote to indicate a problem */
    769     (void) mhd_socket_set_hard_close (c->sk.fd);
    770     c->stage = mhd_HTTP_STAGE_PRE_CLOSING;
    771     c->event_loop_info = MHD_EVENT_LOOP_INFO_CLEANUP;
    772 
    773     return false;
    774   }
    775 
    776   mhd_assert (c->sk.state.rmt_shut_wr || \
    777               ! mhd_SOCKET_ERR_IS_HARD (c->sk.state.discnt_err));
    778 
    779   need_lingering = ! c->sk.state.rmt_shut_wr;
    780   if (need_lingering)
    781   {
    782 #ifdef MHD_SUPPORT_HTTPS
    783     if (mhd_C_HAS_TLS (c))
    784     {
    785       if ((0 != (((unsigned int) c->sk.ready)
    786                  & mhd_SOCKET_NET_STATE_SEND_READY))
    787           || c->sk.props.is_nonblck)
    788         need_lingering =
    789           (mhd_TLS_PROCED_FAILED != mhd_tls_conn_shutdown (c->tls));
    790     }
    791     else
    792 #endif /* MHD_SUPPORT_HTTPS */
    793     if (1)
    794     {
    795       need_lingering = mhd_socket_shut_wr (c->sk.fd);
    796       if (need_lingering)
    797         need_lingering = (! c->sk.state.rmt_shut_wr); /* Skip as already closed */
    798     }
    799   }
    800 
    801   return need_lingering;
    802 }
    803 
    804 
    805 #ifdef MHD_SUPPORT_HTTP2
    806 
    807 static MHD_FN_PAR_NONNULL_ALL_ void
    808 conn_h2_start_closing (struct MHD_Connection *restrict c,
    809                        bool close_hard)
    810 {
    811   mhd_assert (mhd_C_IS_HTTP2 (c));
    812   mhd_assert (c->h2.dbg.h2_deinited);
    813   mhd_assert (! c->rq.app_aware);
    814 
    815   conn_start_socket_closing (c,
    816                              close_hard);
    817 
    818   mhd_conn_remove_from_timeout_lists (c);
    819 
    820 #ifndef NDEBUG
    821   c->dbg.closing_started = true;
    822 #endif
    823 }
    824 
    825 
    826 #endif /* MHD_SUPPORT_HTTP2 */
    827 
    828 
    829 MHD_INTERNAL
    830 MHD_FN_PAR_NONNULL_ (1) MHD_FN_PAR_CSTR_ (3) void
    831 mhd_conn_start_closing (struct MHD_Connection *restrict c,
    832                         enum mhd_ConnCloseReason reason,
    833                         const char *log_msg)
    834 {
    835   bool close_hard;
    836   enum MHD_RequestEndedCode end_code;
    837   enum MHD_StatusCode sc;
    838   bool reply_sending_aborted;
    839 
    840 #ifdef mhd_DEBUG_CONN_ADD_CLOSE
    841   fprintf (stderr,
    842            "&&& mhd_conn_start_closing([FD: %2llu], %u, %s%s%s)...\n",
    843            (unsigned long long) c->sk.fd,
    844            (unsigned int) reason,
    845            log_msg ? "\"" : "",
    846            log_msg ? log_msg : "[NULL]",
    847            log_msg ? "\"" : "");
    848 #endif /* mhd_DEBUG_CONN_ADD_CLOSE */
    849 
    850 #ifdef MHD_SUPPORT_HTTP2
    851   if (mhd_C_IS_HTTP2 (c))
    852   {
    853     mhd_assert ((mhd_CONN_CLOSE_DAEMON_SHUTDOWN == reason) ||
    854                 (mhd_CONN_CLOSE_H2_CLOSE_SOFT == reason) ||
    855                 (mhd_CONN_CLOSE_H2_CLOSE_HARD == reason));
    856     mhd_assert (NULL == log_msg);
    857     conn_h2_start_closing (c,
    858                            reason != mhd_CONN_CLOSE_H2_CLOSE_SOFT);
    859     return;
    860   }
    861 #endif /* MHD_SUPPORT_HTTP2 */
    862 
    863   reply_sending_aborted =
    864     ((mhd_HTTP_STAGE_HEADERS_SENDING <= c->stage)
    865      && (mhd_HTTP_STAGE_FULL_REPLY_SENT > c->stage));
    866   sc = MHD_SC_INTERNAL_ERROR;
    867   switch (reason)
    868   {
    869   case mhd_CONN_CLOSE_CLIENT_HTTP_ERR_ABORT_CONN:
    870     close_hard = true;
    871     end_code = MHD_REQUEST_ENDED_HTTP_PROTOCOL_ERROR;
    872     sc = MHD_SC_REQ_MALFORMED;
    873     mhd_assert (! reply_sending_aborted);
    874     break;
    875   case mhd_CONN_CLOSE_NO_POOL_MEM_FOR_REQUEST:
    876     close_hard = true;
    877     end_code = MHD_REQUEST_ENDED_NO_RESOURCES;
    878     mhd_assert (! reply_sending_aborted);
    879     break;
    880   case mhd_CONN_CLOSE_CLIENT_SHUTDOWN_EARLY:
    881     close_hard = true;
    882     end_code = MHD_REQUEST_ENDED_CLIENT_ABORT;
    883     sc = MHD_SC_CLIENT_SHUTDOWN_EARLY;
    884     mhd_assert (! reply_sending_aborted);
    885     break;
    886   case mhd_CONN_CLOSE_H2_PREFACE_MISSING:
    887     close_hard = true;
    888     end_code = MHD_REQUEST_ENDED_HTTP_PROTOCOL_ERROR;
    889     sc = MHD_SC_ALPN_H2_NO_PREFACE;
    890     break;
    891   case mhd_CONN_CLOSE_NO_POOL_MEM_FOR_REPLY:
    892     close_hard = true;
    893     end_code = (! c->stop_with_error || c->rq.too_large) ?
    894                MHD_REQUEST_ENDED_NO_RESOURCES :
    895                MHD_REQUEST_ENDED_HTTP_PROTOCOL_ERROR;
    896     sc = MHD_SC_REPLY_POOL_ALLOCATION_FAILURE;
    897     if (reply_sending_aborted && (NULL == log_msg))
    898       log_msg = mhd_MSG4LOG ("Response aborted due to insufficient memory " \
    899                              "in the connection pool");
    900     break;
    901   case mhd_CONN_CLOSE_NO_MEM_FOR_ERR_RESPONSE:
    902     close_hard = true;
    903     end_code = c->rq.too_large ?
    904                MHD_REQUEST_ENDED_NO_RESOURCES :
    905                MHD_REQUEST_ENDED_HTTP_PROTOCOL_ERROR;
    906     sc = MHD_SC_ERR_RESPONSE_ALLOCATION_FAILURE;
    907     break;
    908   case mhd_CONN_CLOSE_APP_ERROR:
    909     close_hard = true;
    910     end_code = MHD_REQUEST_ENDED_BY_APP_ERROR;
    911     sc = MHD_SC_APPLICATION_DATA_GENERATION_FAILURE_CLOSED;
    912     if (reply_sending_aborted && (NULL == log_msg))
    913       log_msg = mhd_MSG4LOG ("Response aborted due to application reply " \
    914                              "generation failure");
    915     break;
    916   case mhd_CONN_CLOSE_APP_ABORTED:
    917     close_hard = true;
    918     end_code = MHD_REQUEST_ENDED_BY_APP_ABORT;
    919     sc = MHD_SC_APPLICATION_CALLBACK_ABORT_ACTION;
    920     if (reply_sending_aborted && (NULL == log_msg))
    921       log_msg = mhd_MSG4LOG ("Application aborted reply sending");
    922     break;
    923   case mhd_CONN_CLOSE_FILE_OFFSET_TOO_LARGE:
    924     close_hard = true;
    925     end_code = MHD_REQUEST_ENDED_FILE_ERROR;
    926     sc = MHD_SC_REPLY_FILE_OFFSET_TOO_LARGE;
    927     if (reply_sending_aborted && (NULL == log_msg))
    928       log_msg = mhd_MSG4LOG ("Response aborted because OS failed " \
    929                              "to read too large response file");
    930     break;
    931   case mhd_CONN_CLOSE_FILE_READ_ERROR:
    932     close_hard = true;
    933     end_code = MHD_REQUEST_ENDED_FILE_ERROR;
    934     sc = MHD_SC_REPLY_FILE_READ_ERROR;
    935     if (reply_sending_aborted && (NULL == log_msg))
    936       log_msg = mhd_MSG4LOG ("Response aborted because OS failed " \
    937                              "to read response file");
    938     break;
    939   case mhd_CONN_CLOSE_FILE_TOO_SHORT:
    940     close_hard = true;
    941     end_code = MHD_REQUEST_ENDED_BY_APP_ERROR;
    942     sc = MHD_SC_REPLY_FILE_TOO_SHORT;
    943     if (reply_sending_aborted && (NULL == log_msg))
    944       log_msg = mhd_MSG4LOG ("Response aborted because response file is "
    945                              "shorter that expected");
    946     break;
    947 #ifdef MHD_SUPPORT_AUTH_DIGEST
    948   case mhd_CONN_CLOSE_NONCE_ERROR:
    949     close_hard = true;
    950     end_code = MHD_REQUEST_ENDED_NONCE_ERROR;
    951     sc = MHD_SC_REPLY_NONCE_ERROR;
    952     mhd_assert (! reply_sending_aborted);
    953     break;
    954 #endif /* MHD_SUPPORT_AUTH_DIGEST */
    955 
    956   case mhd_CONN_CLOSE_INT_ERROR:
    957     close_hard = true;
    958     end_code = MHD_REQUEST_ENDED_NO_RESOURCES;
    959     if (reply_sending_aborted && (NULL == log_msg))
    960       log_msg = mhd_MSG4LOG ("Response aborted due to MHD internal error");
    961     break;
    962   case mhd_CONN_CLOSE_EXTR_EVENT_REG_FAILED:
    963     close_hard = true;
    964     end_code = MHD_REQUEST_ENDED_BY_EXT_EVENT_ERROR;
    965     sc = MHD_SC_EXTR_EVENT_REG_FAILED;
    966     if (reply_sending_aborted && (NULL == log_msg))
    967       log_msg = mhd_MSG4LOG ("Response aborted due to external event " \
    968                              "registration failure");
    969     break;
    970   case mhd_CONN_CLOSE_NO_SYS_RESOURCES:
    971     close_hard = true;
    972     end_code = MHD_REQUEST_ENDED_NO_RESOURCES;
    973     sc = MHD_SC_NO_SYS_RESOURCES;
    974     if (reply_sending_aborted && (NULL == log_msg))
    975       log_msg = mhd_MSG4LOG ("Response aborted due to lack of " \
    976                              "system resources");
    977     break;
    978   case mhd_CONN_CLOSE_SOCKET_ERR:
    979     close_hard = true;
    980     switch (c->sk.state.discnt_err)
    981     {
    982     case mhd_SOCKET_ERR_NOMEM:
    983       end_code = MHD_REQUEST_ENDED_NO_RESOURCES;
    984       sc = MHD_SC_NO_SYS_RESOURCES;
    985       if (reply_sending_aborted && (NULL == log_msg))
    986         log_msg = mhd_MSG4LOG ("Response aborted because system closed " \
    987                                "socket due to lack of system resources");
    988       break;
    989     case mhd_SOCKET_ERR_REMT_DISCONN:
    990       close_hard = false;
    991       end_code = (mhd_HTTP_STAGE_INIT == c->stage) ?
    992                  MHD_REQUEST_ENDED_COMPLETED_OK /* Not used */
    993                  : MHD_REQUEST_ENDED_CLIENT_ABORT;
    994       if (reply_sending_aborted)
    995       {
    996         sc = MHD_SC_CLIENT_CLOSED_CONN_EARLY;
    997         if (NULL == log_msg)
    998           log_msg = mhd_MSG4LOG ("Response aborted because remote client " \
    999                                  "closed connection early");
   1000       }
   1001       break;
   1002     case mhd_SOCKET_ERR_CONNRESET:
   1003       end_code = MHD_REQUEST_ENDED_CLIENT_ABORT;
   1004       sc = MHD_SC_CONNECTION_RESET;
   1005       if (reply_sending_aborted && (NULL == log_msg))
   1006         log_msg = mhd_MSG4LOG ("Response aborted due to aborted connection");
   1007       break;
   1008     case mhd_SOCKET_ERR_CONN_BROKEN:
   1009     case mhd_SOCKET_ERR_NOTCONN:
   1010     case mhd_SOCKET_ERR_TLS:
   1011     case mhd_SOCKET_ERR_PIPE:
   1012     case mhd_SOCKET_ERR_NOT_CHECKED:
   1013     case mhd_SOCKET_ERR_BADF:
   1014     case mhd_SOCKET_ERR_INVAL:
   1015     case mhd_SOCKET_ERR_OPNOTSUPP:
   1016     case mhd_SOCKET_ERR_NOTSOCK:
   1017     case mhd_SOCKET_ERR_OTHER:
   1018     case mhd_SOCKET_ERR_INTERNAL:
   1019     case mhd_SOCKET_ERR_NO_ERROR:
   1020       end_code = MHD_REQUEST_ENDED_CONNECTION_ERROR;
   1021       sc = MHD_SC_CONNECTION_BROKEN;
   1022       if (reply_sending_aborted && (NULL == log_msg))
   1023         log_msg = mhd_MSG4LOG ("Response aborted due to broken connection");
   1024       break;
   1025     case mhd_SOCKET_ERR_AGAIN:
   1026     case mhd_SOCKET_ERR_INTR:
   1027     default:
   1028       mhd_UNREACHABLE ();
   1029       break;
   1030     }
   1031     break;
   1032   case mhd_CONN_CLOSE_DAEMON_SHUTDOWN:
   1033     close_hard = true;
   1034     end_code = MHD_REQUEST_ENDED_DAEMON_SHUTDOWN;
   1035     break;
   1036 
   1037   case mhd_CONN_CLOSE_TIMEDOUT:
   1038     if (mhd_HTTP_STAGE_INIT == c->stage)
   1039     {
   1040       close_hard = false;
   1041       end_code = MHD_REQUEST_ENDED_COMPLETED_OK; /* Not used */
   1042       break;
   1043     }
   1044     close_hard = true;
   1045     end_code = MHD_REQUEST_ENDED_TIMEOUT_REACHED;
   1046     sc = MHD_SC_CONNECTION_TIMEOUT;
   1047     if (reply_sending_aborted && (NULL == log_msg))
   1048       log_msg = mhd_MSG4LOG ("Response aborted due to sending timeout");
   1049     break;
   1050 
   1051   case mhd_CONN_CLOSE_ERR_REPLY_SENT:
   1052     close_hard = false;
   1053     end_code = c->rq.too_large ?
   1054                MHD_REQUEST_ENDED_NO_RESOURCES :
   1055                MHD_REQUEST_ENDED_HTTP_PROTOCOL_ERROR;
   1056     break;
   1057 #ifdef MHD_SUPPORT_UPGRADE
   1058   case mhd_CONN_CLOSE_UPGRADE:
   1059     close_hard = false;
   1060     end_code = MHD_REQUEST_ENDED_COMPLETED_OK_UPGRADE;
   1061     break;
   1062 #endif /* MHD_SUPPORT_UPGRADE */
   1063   case mhd_CONN_CLOSE_HTTP_COMPLETED:
   1064     close_hard = false;
   1065     end_code = MHD_REQUEST_ENDED_COMPLETED_OK;
   1066     break;
   1067 
   1068 #ifdef MHD_SUPPORT_HTTP2
   1069   case mhd_CONN_CLOSE_H2_CLOSE_SOFT:
   1070   case mhd_CONN_CLOSE_H2_CLOSE_HARD:
   1071 #endif /* MHD_SUPPORT_HTTP2 */
   1072   default:
   1073     mhd_assert (0 && "Unreachable code");
   1074     mhd_UNREACHABLE ();
   1075     end_code = MHD_REQUEST_ENDED_COMPLETED_OK;
   1076     close_hard = false;
   1077     break;
   1078   }
   1079 
   1080   mhd_assert ((NULL == log_msg) || (MHD_SC_INTERNAL_ERROR != sc));
   1081 
   1082 #ifdef MHD_SUPPORT_UPGRADE
   1083   if (mhd_CONN_CLOSE_UPGRADE == reason)
   1084   {
   1085     mhd_assert (mhd_HTTP_STAGE_UPGRADING == c->stage);
   1086     c->event_loop_info = MHD_EVENT_LOOP_INFO_UPGRADED;
   1087   }
   1088   else
   1089 #endif /* MHD_SUPPORT_UPGRADE */
   1090   if (1)
   1091   {
   1092     if (conn_start_socket_closing (c,
   1093                                    close_hard))
   1094     {
   1095       (void) 0; // TODO: start local lingering phase
   1096       c->stage = mhd_HTTP_STAGE_PRE_CLOSING; // TODO: start local lingering phase
   1097       c->event_loop_info = MHD_EVENT_LOOP_INFO_CLEANUP; // TODO: start local lingering phase
   1098     }
   1099     else
   1100     {  /* No need / not possible to linger */
   1101       c->stage = mhd_HTTP_STAGE_PRE_CLOSING;
   1102       c->event_loop_info = MHD_EVENT_LOOP_INFO_CLEANUP;
   1103     }
   1104   }
   1105 
   1106 #ifdef MHD_SUPPORT_LOG_FUNCTIONALITY
   1107   if (NULL != log_msg)
   1108   {
   1109     mhd_LOG_MSG (c->daemon, sc, log_msg);
   1110   }
   1111 #else  /* ! MHD_SUPPORT_LOG_FUNCTIONALITY */
   1112   (void) log_msg; /* Mute compiler warning */
   1113   (void) sc;      /* Mute compiler warning */
   1114 #endif /* ! MHD_SUPPORT_LOG_FUNCTIONALITY */
   1115 
   1116 #if 0 // TODO: notification callback
   1117   mhd_assert ((mhd_HTTP_STAGE_INIT != c->stage) || (! c->rq.app_aware));
   1118   if ( (NULL != d->notify_completed) &&
   1119        (c->rq.app_aware) )
   1120     d->notify_completed (d->notify_completed_cls,
   1121                          c,
   1122                          &c->rq.app_context,
   1123                          MHD_REQUEST_ENDED_COMPLETED_OK);
   1124 #else
   1125   (void) end_code;
   1126 #endif
   1127   c->rq.app_aware = false;
   1128 
   1129   if (! c->suspended)
   1130   {
   1131     mhd_assert (! c->resuming);
   1132     mhd_conn_remove_from_timeout_lists (c);
   1133   }
   1134 
   1135 #ifndef NDEBUG
   1136   c->dbg.closing_started = true;
   1137 #endif
   1138 }
   1139 
   1140 
   1141 MHD_INTERNAL
   1142 MHD_FN_PAR_NONNULL_ (1) void
   1143 mhd_conn_pre_clean_part1 (struct MHD_Connection *restrict c)
   1144 {
   1145   // TODO: support suspended connections
   1146   mhd_conn_mark_unready (c, c->daemon);
   1147 
   1148   mhd_stream_call_dcc_cleanup_if_needed (c);
   1149   if (NULL != c->rq.cntn.lbuf.data)
   1150     mhd_daemon_free_lbuf (c->daemon, &(c->rq.cntn.lbuf));
   1151 
   1152   if (mhd_WM_INT_HAS_EXT_EVENTS (c->daemon->wmode_int))
   1153   {
   1154     struct MHD_Daemon *const d = c->daemon;
   1155     if (NULL != c->extr_event.app_cntx)
   1156     {
   1157       c->extr_event.app_cntx =
   1158         mhd_daemon_extr_event_reg (d,
   1159                                    c->sk.fd,
   1160                                    MHD_FD_STATE_NONE,
   1161                                    c->extr_event.app_cntx,
   1162                                    (struct MHD_EventUpdateContext *) c);
   1163       if (NULL != c->extr_event.app_cntx)
   1164         mhd_log_extr_event_dereg_failed (d);
   1165     }
   1166   }
   1167 #ifdef MHD_SUPPORT_EPOLL
   1168   else if (mhd_POLL_TYPE_EPOLL == c->daemon->events.poll_type)
   1169   {
   1170     struct epoll_event event;
   1171 
   1172     event.events = 0;
   1173     event.data.ptr = NULL;
   1174     if (0 != epoll_ctl (c->daemon->events.data.epoll.e_fd,
   1175                         EPOLL_CTL_DEL,
   1176                         c->sk.fd,
   1177                         &event))
   1178     {
   1179       mhd_LOG_MSG (c->daemon, MHD_SC_EPOLL_CTL_REMOVE_FAILED,
   1180                    "Failed to remove connection socket from epoll.");
   1181     }
   1182   }
   1183 #endif /* MHD_SUPPORT_EPOLL */
   1184 }
   1185 
   1186 
   1187 MHD_INTERNAL
   1188 MHD_FN_PAR_NONNULL_ (1) void
   1189 mhd_conn_pre_clean (struct MHD_Connection *restrict c)
   1190 {
   1191 #ifdef mhd_DEBUG_CONN_ADD_CLOSE
   1192   fprintf (stderr,
   1193            "&&&    Closing connection, FD: %2llu\n",
   1194            (unsigned long long) c->sk.fd);
   1195 #endif /* mhd_DEBUG_CONN_ADD_CLOSE */
   1196 
   1197   mhd_assert (c->dbg.closing_started);
   1198   mhd_assert (! c->dbg.pre_cleaned);
   1199 
   1200 #ifdef MHD_SUPPORT_UPGRADE
   1201   if (NULL == c->upgr.c)
   1202 #endif
   1203   mhd_conn_pre_clean_part1 (c);
   1204 
   1205   if (NULL != c->rp.resp_iov.iov)
   1206   {
   1207     free (c->rp.resp_iov.iov);
   1208     c->rp.resp_iov.iov = NULL;
   1209   }
   1210   if (NULL != c->rp.response)
   1211     mhd_response_dec_use_count (c->rp.response);
   1212   c->rp.response = NULL;
   1213 
   1214   mhd_assert (NULL != c->pool);
   1215   c->read_buffer_offset = 0;
   1216   c->read_buffer_size = 0;
   1217   c->read_buffer = NULL;
   1218   c->write_buffer_send_offset = 0;
   1219   c->write_buffer_append_offset = 0;
   1220   c->write_buffer_size = 0;
   1221   c->write_buffer = NULL;
   1222   // TODO: call in the thread where it was allocated for thread-per-connection
   1223   mhd_pool_destroy (c->pool);
   1224   c->pool = NULL;
   1225 
   1226   c->stage = mhd_HTTP_STAGE_CLOSED;
   1227 #ifndef NDEBUG
   1228   c->dbg.pre_cleaned = true;
   1229 #endif
   1230 }