libmicrohttpd2

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

stream_funcs.c (39688B)


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