libmicrohttpd2

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

stream_process_states.c (21759B)


      1 /* SPDX-License-Identifier: LGPL-2.1-or-later OR (GPL-2.0-or-later WITH eCos-exception-2.0) */
      2 /*
      3   This file is part of GNU libmicrohttpd.
      4   Copyright (C) 2014-2024 Evgeny Grin (Karlson2k)
      5   Copyright (C) 2007-2020 Daniel Pittman and Christian Grothoff
      6 
      7   GNU libmicrohttpd is free software; you can redistribute it and/or
      8   modify it under the terms of the GNU Lesser General Public
      9   License as published by the Free Software Foundation; either
     10   version 2.1 of the License, or (at your option) any later version.
     11 
     12   GNU libmicrohttpd is distributed in the hope that it will be useful,
     13   but WITHOUT ANY WARRANTY; without even the implied warranty of
     14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     15   Lesser General Public License for more details.
     16 
     17   Alternatively, you can redistribute GNU libmicrohttpd and/or
     18   modify it under the terms of the GNU General Public License as
     19   published by the Free Software Foundation; either version 2 of
     20   the License, or (at your option) any later version, together
     21   with the eCos exception, as follows:
     22 
     23     As a special exception, if other files instantiate templates or
     24     use macros or inline functions from this file, or you compile this
     25     file and link it with other works to produce a work based on this
     26     file, this file does not by itself cause the resulting work to be
     27     covered by the GNU General Public License. However the source code
     28     for this file must still be made available in accordance with
     29     section (3) of the GNU General Public License v2.
     30 
     31     This exception does not invalidate any other reasons why a work
     32     based on this file might be covered by the GNU General Public
     33     License.
     34 
     35   You should have received copies of the GNU Lesser General Public
     36   License and the GNU General Public License along with this library;
     37   if not, see <https://www.gnu.org/licenses/>.
     38 */
     39 
     40 /**
     41  * @file src/mhd2/stream_process_states.h
     42  * @brief  The definitions of internal functions for processing
     43  *         stream states
     44  * @author Karlson2k (Evgeny Grin)
     45  *
     46  * Based on the MHD v0.x code by Daniel Pittman, Christian Grothoff and other
     47  * contributors.
     48  */
     49 
     50 #include "mhd_sys_options.h"
     51 
     52 #include "sys_bool_type.h"
     53 #include "sys_base_types.h"
     54 
     55 #include "mhd_assert.h"
     56 #include "mhd_unreachable.h"
     57 
     58 #include "mhd_str_macros.h"
     59 #include "mhd_socket_error_funcs.h"
     60 
     61 #include "mhd_daemon.h"
     62 #include "mhd_connection.h"
     63 #include "mhd_response.h"
     64 
     65 #include "mhd_comm_layer_state.h"
     66 #ifdef MHD_SUPPORT_HTTP2
     67 #  include "h2/h2_comm.h"
     68 #endif
     69 #ifdef MHD_SUPPORT_HTTPS
     70 #  include "mhd_tls_funcs.h"
     71 #endif /* MHD_SUPPORT_HTTPS */
     72 
     73 #include "stream_process_states.h"
     74 #include "stream_funcs.h"
     75 #include "stream_process_request.h"
     76 #include "stream_process_reply.h"
     77 
     78 #include "conn_mark_ready.h"
     79 #include "conn_timeout.h"
     80 
     81 #ifdef MHD_SUPPORT_UPGRADE
     82 #  include "upgrade_proc.h"
     83 #endif /* MHD_SUPPORT_UPGRADE */
     84 
     85 #ifdef MHD_USE_TRACE_SUSPEND_RESUME
     86 #  include <stdio.h>
     87 #endif /* MHD_USE_TRACE_SUSPEND_RESUME */
     88 
     89 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ void
     90 mhd_conn_event_loop_state_update (struct MHD_Connection *restrict c)
     91 {
     92 #ifdef MHD_SUPPORT_HTTPS
     93   mhd_assert (!mhd_C_HAS_TLS (c) \
     94               || (mhd_CONN_STATE_TLS_CONNECTED == c->conn_state));
     95   mhd_assert (mhd_C_HAS_TLS (c) \
     96               || (mhd_CONN_STATE_TCP_CONNECTED == c->conn_state));
     97 #endif /* MHD_SUPPORT_HTTPS */
     98 
     99 #ifdef MHD_SUPPORT_HTTP2
    100   if (mhd_C_IS_HTTP2 (c))
    101   {
    102     mhd_h2_conn_state_update (c);
    103     return;
    104   }
    105 #endif /* MHD_SUPPORT_HTTP2 */
    106 
    107   switch (c->stage)
    108   {
    109   case mhd_HTTP_STAGE_INIT:
    110   case mhd_HTTP_STAGE_REQ_LINE_RECEIVING:
    111     c->event_loop_info = MHD_EVENT_LOOP_INFO_RECV;
    112     break;
    113   case mhd_HTTP_STAGE_REQ_LINE_RECEIVED:
    114     mhd_assert (0 && "Impossible value");
    115     mhd_UNREACHABLE ();
    116     break;
    117   case mhd_HTTP_STAGE_REQ_HEADERS_RECEIVING:
    118     c->event_loop_info = MHD_EVENT_LOOP_INFO_RECV;
    119     break;
    120   case mhd_HTTP_STAGE_HEADERS_RECEIVED:
    121   case mhd_HTTP_STAGE_HEADERS_PROCESSED:
    122     mhd_assert (0 && "Impossible value");
    123     mhd_UNREACHABLE ();
    124     break;
    125   case mhd_HTTP_STAGE_CONTINUE_SENDING:
    126     c->event_loop_info = MHD_EVENT_LOOP_INFO_SEND;
    127     break;
    128   case mhd_HTTP_STAGE_BODY_RECEIVING:
    129     c->event_loop_info = MHD_EVENT_LOOP_INFO_RECV;
    130     break;
    131   case mhd_HTTP_STAGE_BODY_RECEIVED:
    132     mhd_assert (0 && "Impossible value");
    133     mhd_UNREACHABLE ();
    134     break;
    135   case mhd_HTTP_STAGE_FOOTERS_RECEIVING:
    136     c->event_loop_info = MHD_EVENT_LOOP_INFO_RECV;
    137     break;
    138   case mhd_HTTP_STAGE_FOOTERS_RECEIVED:
    139     mhd_assert (0 && "Impossible value");
    140     mhd_UNREACHABLE ();
    141     break;
    142   case mhd_HTTP_STAGE_FULL_REQ_RECEIVED:
    143     mhd_assert (0 && "Should not be possible");
    144     c->event_loop_info = MHD_EVENT_LOOP_INFO_PROCESS;
    145     break;
    146   case mhd_HTTP_STAGE_REQ_RECV_FINISHED:
    147     mhd_assert (0 && "Impossible value");
    148     mhd_UNREACHABLE ();
    149     break;
    150   case mhd_HTTP_STAGE_START_REPLY:
    151     mhd_assert (0 && "Impossible value");
    152     mhd_UNREACHABLE ();
    153     break;
    154   case mhd_HTTP_STAGE_HEADERS_SENDING:
    155     /* headers in buffer, keep writing */
    156     c->event_loop_info = MHD_EVENT_LOOP_INFO_SEND;
    157     break;
    158   case mhd_HTTP_STAGE_HEADERS_SENT:
    159     mhd_assert (0 && "Impossible value");
    160     mhd_UNREACHABLE ();
    161     break;
    162 #ifdef MHD_SUPPORT_UPGRADE
    163   case mhd_HTTP_STAGE_UPGRADE_HEADERS_SENDING:
    164     c->event_loop_info = MHD_EVENT_LOOP_INFO_SEND;
    165     break;
    166 #endif /* MHD_SUPPORT_UPGRADE */
    167   case mhd_HTTP_STAGE_UNCHUNKED_BODY_UNREADY:
    168     mhd_assert (0 && "Should not be possible");
    169     c->event_loop_info = MHD_EVENT_LOOP_INFO_PROCESS;
    170     break;
    171   case mhd_HTTP_STAGE_UNCHUNKED_BODY_READY:
    172     c->event_loop_info = MHD_EVENT_LOOP_INFO_SEND;
    173     break;
    174   case mhd_HTTP_STAGE_CHUNKED_BODY_UNREADY:
    175     mhd_assert (0 && "Should not be possible");
    176     c->event_loop_info = MHD_EVENT_LOOP_INFO_PROCESS;
    177     break;
    178   case mhd_HTTP_STAGE_CHUNKED_BODY_READY:
    179     c->event_loop_info = MHD_EVENT_LOOP_INFO_SEND;
    180     break;
    181   case mhd_HTTP_STAGE_CHUNKED_BODY_SENT:
    182     mhd_assert (0 && "Impossible value");
    183     mhd_UNREACHABLE ();
    184     break;
    185   case mhd_HTTP_STAGE_FOOTERS_SENDING:
    186     c->event_loop_info = MHD_EVENT_LOOP_INFO_SEND;
    187     break;
    188   case mhd_HTTP_STAGE_FULL_REPLY_SENT:
    189     mhd_assert (0 && "Impossible value");
    190     mhd_UNREACHABLE ();
    191     break;
    192 #ifdef MHD_SUPPORT_UPGRADE
    193   case mhd_HTTP_STAGE_UPGRADING:
    194     mhd_assert (0 && "Impossible value");
    195     mhd_UNREACHABLE ();
    196     break;
    197   case mhd_HTTP_STAGE_UPGRADED:
    198     mhd_assert (0 && "Should not be possible");
    199     c->event_loop_info = MHD_EVENT_LOOP_INFO_UPGRADED;
    200     break;
    201   case mhd_HTTP_STAGE_UPGRADED_CLEANING:
    202     mhd_assert (0 && "Should be unreachable");
    203     c->event_loop_info = MHD_EVENT_LOOP_INFO_CLEANUP;
    204     break;
    205 #endif /* MHD_SUPPORT_UPGRADE */
    206   case mhd_HTTP_STAGE_PRE_CLOSING:
    207     mhd_assert (0 && "Should be unreachable");
    208     c->event_loop_info = MHD_EVENT_LOOP_INFO_CLEANUP;
    209     break;
    210   case mhd_HTTP_STAGE_CLOSED:
    211     mhd_assert (0 && "Should be unreachable");
    212     c->event_loop_info = MHD_EVENT_LOOP_INFO_CLEANUP;
    213     break;
    214   default:
    215     mhd_UNREACHABLE ();
    216     break;
    217   }
    218 }
    219 
    220 
    221 /**
    222  * Process HTTP communication layer states and events
    223  * @param c the connection to process
    224  * @return #mhd_COMM_LAYER_PROCESSING if setting up HTTP connection is
    225  *                                    in progress,
    226  *         #mhd_COMM_LAYER_OK if HTTP communication can be performed now,
    227  *         #mhd_COMM_LAYER_BROKEN if connection is broken and should be closed.
    228  */
    229 mhd_static_inline enum mhd_CommLayerState
    230 process_http_comm_layer (struct MHD_Connection *restrict c)
    231 {
    232 #ifdef MHD_SUPPORT_HTTP2
    233   if (mhd_HTTP_LAYER_CONNECTED == c->h_layer.state)
    234     return mhd_COMM_LAYER_OK; /* Shortcut for the most common case */
    235 
    236   switch (c->h_layer.state)
    237   {
    238   case mhd_HTTP_LAYER_PREFACE:
    239     return mhd_h2_process_preface (c);
    240   case mhd_HTTP_LAYER_CONNECTED:
    241     mhd_UNREACHABLE (); /* Handled above */
    242     return mhd_COMM_LAYER_OK;
    243   case mhd_HTTP_LAYER_CLOSING:
    244   case mhd_HTTP_LAYER_CLOSED:
    245     return mhd_COMM_LAYER_OK;
    246   case mhd_HTTP_LAYER_BROKEN:
    247     return mhd_COMM_LAYER_BROKEN;
    248   default:
    249     break;
    250   }
    251 
    252   mhd_UNREACHABLE ();
    253   return mhd_COMM_LAYER_BROKEN;
    254 
    255 #else  /* ! MHD_SUPPORT_HTTP2 */
    256   (void)c;  /* Unused in HTTP/1.x-only modes */
    257   return mhd_COMM_LAYER_OK;
    258 #endif /* ! MHD_SUPPORT_HTTP2 */
    259 }
    260 
    261 
    262 /**
    263  * Finalise resuming of the connection
    264  * @param c the connection to resume
    265  */
    266 static MHD_FN_PAR_NONNULL_ALL_ void
    267 finish_resume (struct MHD_Connection *restrict c)
    268 {
    269   mhd_assert (c->resuming);
    270   c->resuming = false;
    271 
    272 #ifdef MHD_USE_TRACE_SUSPEND_RESUME
    273   fprintf (stderr,
    274            "%%%%%%    Resumed connection, FD: %2llu\n",
    275            (unsigned long long)c->sk.fd);
    276 #endif /* MHD_USE_TRACE_SUSPEND_RESUME */
    277   mhd_assert (!c->suspended);
    278   mhd_assert (MHD_EVENT_LOOP_INFO_PROCESS == c->event_loop_info);
    279 }
    280 
    281 
    282 /* always return 'false' */
    283 static MHD_FN_PAR_NONNULL_ALL_ bool
    284 handle_early_peer_shutdown (struct MHD_Connection *restrict c,
    285                             bool has_unfinished_process)
    286 {
    287   mhd_conn_start_closing (c,
    288                           has_unfinished_process ?
    289                           mhd_CONN_CLOSE_CLIENT_SHUTDOWN_EARLY :
    290                           mhd_CONN_CLOSE_HTTP_COMPLETED,
    291                           NULL);
    292   return false;
    293 }
    294 
    295 
    296 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool
    297 mhd_conn_process_data (struct MHD_Connection *restrict c)
    298 {
    299   struct MHD_Daemon *const d = c->daemon;
    300   bool daemon_closing;
    301 
    302   if (c->suspended)
    303   {
    304     mhd_assert (!mhd_C_IS_HTTP2 (c));
    305     return true;
    306   }
    307 
    308   mhd_assert ((!mhd_SCKT_NET_ST_HAS_FLAG (c->sk.ready,
    309                                           mhd_SOCKET_NET_STATE_ERROR_READY))
    310               || (mhd_SOCKET_ERR_NO_ERROR != c->sk.state.discnt_err));
    311 
    312   if (mhd_SOCKET_ERR_NO_ERROR == c->sk.state.discnt_err)
    313   {
    314     switch (process_http_comm_layer (c))
    315     {
    316     case mhd_COMM_LAYER_OK:
    317       break;        /* Process HTTP data */
    318     case mhd_COMM_LAYER_PROCESSING:
    319       mhd_assert (!c->resuming);
    320       if (c->sk.state.rmt_shut_wr)
    321       {
    322         bool unfinished;
    323         unfinished = (0 != c->read_buffer_offset);
    324 #ifdef MHD_SUPPORT_HTTPS
    325         if (!unfinished
    326             && mhd_C_HAS_TLS (c))
    327           unfinished =
    328             (mhd_TLS_ALPN_PROT_HTTP2 == mhd_tls_conn_get_alpn_prot (c->tls));
    329 #endif /* MHD_SUPPORT_HTTPS */
    330         return handle_early_peer_shutdown (c,
    331                                            unfinished);
    332       }
    333       return true;  /* Too early for HTTP */
    334     case mhd_COMM_LAYER_BROKEN:
    335       mhd_assert (c->dbg.closing_started);
    336       return false; /* Connection is broken */
    337     default:
    338       mhd_UNREACHABLE ();
    339       return false;
    340     }
    341   }
    342 
    343 #ifdef MHD_SUPPORT_HTTP2
    344   if (mhd_C_IS_HTTP2 (c))
    345   {
    346     if (!mhd_h2_conn_process_data (c))
    347       return false;
    348     mhd_conn_event_loop_state_update (c);
    349     mhd_conn_mark_ready_update (c);
    350     return true;
    351   }
    352 #endif /* MHD_SUPPORT_HTTP2 */
    353 
    354   mhd_assert (mhd_D_IS_HTTP1_ENABLED (d) || (!mhd_C_IS_HTTP2 (c)) \
    355               || c->stop_with_error);
    356 
    357   if (c->resuming)
    358   {
    359     mhd_assert (!mhd_C_IS_HTTP2 (c));
    360     finish_resume (c);
    361   }
    362 
    363   if ((c->sk.state.rmt_shut_wr) && (mhd_HTTP_STAGE_START_REPLY > c->stage))
    364   {
    365     if (0 == c->read_buffer_offset)
    366     { /* Read buffer is empty, connection state is actual */
    367       return handle_early_peer_shutdown (c,
    368                                          (mhd_HTTP_STAGE_INIT != c->stage));
    369     }
    370   }
    371 
    372   if (mhd_SOCKET_ERR_NO_ERROR != c->sk.state.discnt_err)
    373   {
    374     mhd_assert (mhd_SOCKET_ERR_IS_HARD (c->sk.state.discnt_err));
    375     mhd_conn_start_closing_skt_err (c);
    376     return false;
    377   }
    378 
    379   daemon_closing = (mhd_DAEMON_STATE_STOPPING == d->state);
    380 #ifdef MHD_SUPPORT_THREADS
    381   daemon_closing = daemon_closing || d->threading.stop_requested;
    382 #endif /* MHD_SUPPORT_THREADS */
    383   if (daemon_closing)
    384   {
    385     mhd_conn_start_closing_d_shutdown (c);
    386     return false;
    387   }
    388 
    389   mhd_assert (!c->suspended);
    390 
    391   while (!0)
    392   {
    393 #ifdef MHD_SUPPORT_HTTPS
    394     mhd_assert (!mhd_C_HAS_TLS (c) \
    395                 || (mhd_CONN_STATE_TLS_CONNECTED == c->conn_state));
    396     mhd_assert (mhd_C_HAS_TLS (c) \
    397                 || (mhd_CONN_STATE_TCP_CONNECTED == c->conn_state));
    398 #endif /* MHD_SUPPORT_HTTPS */
    399     switch (c->stage)
    400     {
    401     case mhd_HTTP_STAGE_INIT:
    402     case mhd_HTTP_STAGE_REQ_LINE_RECEIVING:
    403       if (mhd_stream_get_request_line (c))
    404       {
    405         mhd_assert (mhd_HTTP_STAGE_REQ_LINE_RECEIVING < c->stage);
    406         mhd_assert ((MHD_HTTP_VERSION_IS_1X (c->rq.http_ver)) \
    407                     || (c->discard_request)
    408                     || (mhd_HTTP_STAGE_PRE_CLOSING <= c->stage));
    409         continue;
    410       }
    411       mhd_assert (mhd_HTTP_STAGE_REQ_LINE_RECEIVING >= c->stage);
    412       break;
    413     case mhd_HTTP_STAGE_REQ_LINE_RECEIVED:
    414       mhd_stream_switch_to_rq_headers_proc (c);
    415       mhd_assert (mhd_HTTP_STAGE_REQ_LINE_RECEIVED != c->stage);
    416       continue;
    417     case mhd_HTTP_STAGE_REQ_HEADERS_RECEIVING:
    418       if (mhd_stream_get_request_headers (c, false))
    419       {
    420         mhd_assert (mhd_HTTP_STAGE_REQ_HEADERS_RECEIVING < c->stage);
    421         mhd_assert ((mhd_HTTP_STAGE_HEADERS_RECEIVED == c->stage) \
    422                     || (c->discard_request));
    423         continue;
    424       }
    425       mhd_assert (mhd_HTTP_STAGE_REQ_HEADERS_RECEIVING == c->stage);
    426       break;
    427     case mhd_HTTP_STAGE_HEADERS_RECEIVED:
    428       mhd_stream_parse_request_headers (c);
    429       mhd_assert (c->stage != mhd_HTTP_STAGE_HEADERS_RECEIVED);
    430       continue;
    431     case mhd_HTTP_STAGE_HEADERS_PROCESSED:
    432       if (mhd_stream_call_app_request_cb (c))
    433       {
    434         mhd_assert (mhd_HTTP_STAGE_HEADERS_PROCESSED < c->stage);
    435         continue;
    436       }
    437       // TODO: add assert
    438       break;
    439     case mhd_HTTP_STAGE_CONTINUE_SENDING:
    440       if (c->continue_message_write_offset ==
    441           mhd_SSTR_LEN (mdh_HTTP_1_1_100_CONTINUE_REPLY))
    442       {
    443 #ifdef MHD_SUPPORT_UPGRADE
    444         c->rp.sent_100_cntn = true;
    445 #endif /* MHD_SUPPORT_UPGRADE */
    446         c->stage = mhd_HTTP_STAGE_BODY_RECEIVING;
    447         continue;
    448       }
    449       break;
    450     case mhd_HTTP_STAGE_BODY_RECEIVING:
    451       mhd_assert (c->rq.cntn.recv_size < c->rq.cntn.cntn_size);
    452       mhd_assert (!c->discard_request);
    453       mhd_assert (NULL == c->rp.response);
    454       if (0 == c->read_buffer_offset)
    455         break; /* Need more data to process */
    456 
    457       if (mhd_stream_process_request_body (c))
    458         continue;
    459       mhd_assert (!c->discard_request);
    460       mhd_assert (NULL == c->rp.response);
    461       break;
    462     case mhd_HTTP_STAGE_BODY_RECEIVED:
    463       mhd_assert (!c->discard_request);
    464       mhd_assert (NULL == c->rp.response);
    465       mhd_assert (c->rq.have_chunked_upload);
    466       /* Reset counter variables reused for footers */
    467       c->rq.num_cr_sp_replaced = 0;
    468       c->rq.skipped_broken_lines = 0;
    469       mhd_stream_reset_rq_hdr_proc_state (c);
    470       c->stage = mhd_HTTP_STAGE_FOOTERS_RECEIVING;
    471       continue;
    472     case mhd_HTTP_STAGE_FOOTERS_RECEIVING:
    473       mhd_assert (c->rq.have_chunked_upload);
    474       if (mhd_stream_get_request_headers (c, true))
    475       {
    476         mhd_assert (mhd_HTTP_STAGE_FOOTERS_RECEIVING < c->stage);
    477         mhd_assert ((mhd_HTTP_STAGE_FOOTERS_RECEIVED == c->stage) \
    478                     || (c->discard_request));
    479         continue;
    480       }
    481       mhd_assert (mhd_HTTP_STAGE_FOOTERS_RECEIVING == c->stage);
    482       break;
    483     case mhd_HTTP_STAGE_FOOTERS_RECEIVED:
    484       mhd_assert (c->rq.have_chunked_upload);
    485       c->stage = mhd_HTTP_STAGE_FULL_REQ_RECEIVED;
    486       continue;
    487     case mhd_HTTP_STAGE_FULL_REQ_RECEIVED:
    488       if (mhd_stream_call_app_final_upload_cb (c))
    489       {
    490         mhd_assert (mhd_HTTP_STAGE_FOOTERS_RECEIVING != c->stage);
    491         continue;
    492       }
    493       break;
    494     case mhd_HTTP_STAGE_REQ_RECV_FINISHED:
    495       if (mhd_stream_process_req_recv_finished (c))
    496         continue;
    497       break;
    498     // TODO: add stage for setup and full request buffers cleanup
    499     case mhd_HTTP_STAGE_START_REPLY:
    500       mhd_assert (NULL != c->rp.response);
    501       mhd_stream_switch_from_recv_to_send (c);
    502       if (!mhd_stream_build_header_response (c))
    503         continue;
    504       mhd_assert (mhd_HTTP_STAGE_START_REPLY != c->stage);
    505       break;
    506     case mhd_HTTP_STAGE_HEADERS_SENDING:
    507       /* no default action, wait for sending all the headers */
    508       break;
    509     case mhd_HTTP_STAGE_HEADERS_SENT:
    510       if (c->rp.props.send_reply_body)
    511       {
    512         if (c->rp.props.chunked)
    513           c->stage = mhd_HTTP_STAGE_CHUNKED_BODY_UNREADY;
    514         else
    515           c->stage = mhd_HTTP_STAGE_UNCHUNKED_BODY_UNREADY;
    516       }
    517       else
    518         c->stage = mhd_HTTP_STAGE_FULL_REPLY_SENT;
    519       continue;
    520 #ifdef MHD_SUPPORT_UPGRADE
    521     case mhd_HTTP_STAGE_UPGRADE_HEADERS_SENDING:
    522       if (!mhd_upgrade_try_start_upgrading (c))
    523         break;
    524       continue;
    525 #endif /* MHD_SUPPORT_UPGRADE */
    526     case mhd_HTTP_STAGE_UNCHUNKED_BODY_READY:
    527       mhd_assert (c->rp.props.send_reply_body);
    528       mhd_assert (!c->rp.props.chunked);
    529       /* nothing to do here, send the data */
    530       break;
    531     case mhd_HTTP_STAGE_UNCHUNKED_BODY_UNREADY:
    532       mhd_assert (c->rp.props.send_reply_body);
    533       mhd_assert (!c->rp.props.chunked);
    534       if (0 == c->rp.response->cntn_size)
    535       { /* a shortcut */
    536         c->stage = mhd_HTTP_STAGE_FULL_REPLY_SENT;
    537         continue;
    538       }
    539       if (mhd_stream_prep_unchunked_body (c))
    540         continue;
    541       break;
    542     case mhd_HTTP_STAGE_CHUNKED_BODY_READY:
    543       mhd_assert (c->rp.props.send_reply_body);
    544       mhd_assert (c->rp.props.chunked);
    545       /* nothing to do here */
    546       break;
    547     case mhd_HTTP_STAGE_CHUNKED_BODY_UNREADY:
    548       mhd_assert (c->rp.props.send_reply_body);
    549       mhd_assert (c->rp.props.chunked);
    550       if ((0 == c->rp.response->cntn_size)
    551           || (c->rp.rsp_cntn_read_pos ==
    552               c->rp.response->cntn_size))
    553       {
    554         c->stage = mhd_HTTP_STAGE_CHUNKED_BODY_SENT;
    555         continue;
    556       }
    557       if (mhd_stream_prep_chunked_body (c))
    558         continue;
    559       break;
    560     case mhd_HTTP_STAGE_CHUNKED_BODY_SENT:
    561       mhd_assert (c->rp.props.send_reply_body);
    562       mhd_assert (c->rp.props.chunked);
    563       mhd_assert (c->write_buffer_send_offset <= \
    564                   c->write_buffer_append_offset);
    565       mhd_stream_call_dcc_cleanup_if_needed (c);
    566       if (mhd_stream_prep_chunked_footer (c))
    567         continue;
    568       break;
    569     case mhd_HTTP_STAGE_FOOTERS_SENDING:
    570       mhd_assert (c->rp.props.send_reply_body);
    571       mhd_assert (c->rp.props.chunked);
    572       /* no default action */
    573       break;
    574     case mhd_HTTP_STAGE_FULL_REPLY_SENT:
    575       // FIXME: support MHD_HTTP_STATUS_PROCESSING ?
    576       /* Reset connection after complete reply */
    577       mhd_stream_finish_req_serving ( \
    578         c,
    579         mhd_CONN_KEEPALIVE_POSSIBLE == c->conn_reuse
    580         && !c->discard_request
    581         && !c->sk.state.rmt_shut_wr);
    582       continue;
    583 #ifdef MHD_SUPPORT_UPGRADE
    584     case mhd_HTTP_STAGE_UPGRADING:
    585       if (mhd_upgrade_finish_switch_to_upgraded (c))
    586         return true;     /* Do not close connection */
    587       mhd_assert (mhd_HTTP_STAGE_PRE_CLOSING == c->stage);
    588       continue;
    589     case mhd_HTTP_STAGE_UPGRADED:
    590       mhd_assert (0 && "Should be unreachable");
    591       mhd_UNREACHABLE ();
    592       break;
    593     case mhd_HTTP_STAGE_UPGRADED_CLEANING:
    594       mhd_assert (0 && "Should be unreachable");
    595       mhd_UNREACHABLE ();
    596       break;
    597 #endif /* MHD_SUPPORT_UPGRADE */
    598     case mhd_HTTP_STAGE_PRE_CLOSING:
    599       return false;
    600     case mhd_HTTP_STAGE_CLOSED:
    601       mhd_assert (0 && "Should be unreachable");
    602       mhd_UNREACHABLE ();
    603       break;
    604     default:
    605       mhd_assert (0 && "Impossible value");
    606       mhd_UNREACHABLE ();
    607       break;
    608     }
    609 
    610     mhd_assert (mhd_HTTP_STAGE_CLOSED != c->stage);
    611 
    612     if (mhd_HTTP_STAGE_PRE_CLOSING == c->stage)
    613     {
    614       mhd_assert (0 && "Pre-closing should be already caught in the loop");
    615       mhd_UNREACHABLE ();
    616       return false;
    617     }
    618 
    619     if (c->suspended)
    620     {
    621       /* Do not perform any network activity while suspended */
    622       c->event_loop_info = MHD_EVENT_LOOP_INFO_PROCESS;
    623 
    624       mhd_conn_mark_unready (c, d);
    625       mhd_conn_deinit_activity_timeout (c);
    626 #ifdef MHD_USE_TRACE_SUSPEND_RESUME
    627       fprintf (stderr,
    628                "%%%%%%  Connection suspended, FD: %2llu\n",
    629                (unsigned long long)c->sk.fd);
    630 #endif /* MHD_USE_TRACE_SUSPEND_RESUME */
    631       return true;
    632     }
    633 
    634     if ((c->sk.state.rmt_shut_wr) && (mhd_HTTP_STAGE_START_REPLY > c->stage))
    635     {
    636       mhd_conn_start_closing (c,
    637                               (mhd_HTTP_STAGE_INIT == c->stage) ?
    638                               mhd_CONN_CLOSE_HTTP_COMPLETED :
    639                               mhd_CONN_CLOSE_CLIENT_SHUTDOWN_EARLY,
    640                               NULL);
    641       return false;
    642     }
    643 
    644     if (0 != (c->sk.ready & mhd_SOCKET_NET_STATE_ERROR_READY))
    645     {
    646       mhd_assert (0 && "Should be handled earlier");
    647       mhd_conn_start_closing_skt_err (c);
    648       return false;
    649     }
    650 
    651     mhd_conn_event_loop_state_update (c);
    652 
    653     if (0 != (MHD_EVENT_LOOP_INFO_RECV & c->event_loop_info))
    654     {
    655       /* Check whether the space is available to receive data */
    656       switch (mhd_stream_check_and_grow_read_buffer_space (c))
    657       {
    658       case mhd_CONN_BUFF_GROW_ERR_CONN_CLOSE:
    659         mhd_assert (c->discard_request);
    660         return false;
    661       case mhd_CONN_BUFF_GROW_ERR_REPLY:
    662         continue; /* Process error reply */
    663       case mhd_CONN_BUFF_GROW_OK:
    664         break;
    665       default:
    666         mhd_UNREACHABLE ();
    667         break;
    668       }
    669     }
    670 
    671     /* Current MHD design assumes that data must be always processes when
    672      * available. If it is not possible, connection must be suspended. */
    673     mhd_assert (MHD_EVENT_LOOP_INFO_PROCESS != c->event_loop_info);
    674 
    675     /* Sockets errors must be already handled */
    676     mhd_assert (0 == (c->sk.ready & mhd_SOCKET_NET_STATE_ERROR_READY));
    677 
    678     mhd_conn_mark_ready_update (c);
    679 
    680     break;
    681   }
    682 
    683   return true;
    684 }