libmicrohttpd2

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

stream_process_states.c (20872B)


      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 
     70 #include "stream_process_states.h"
     71 #include "stream_funcs.h"
     72 #include "stream_process_request.h"
     73 #include "stream_process_reply.h"
     74 
     75 #include "conn_mark_ready.h"
     76 #include "conn_timeout.h"
     77 
     78 #ifdef MHD_SUPPORT_UPGRADE
     79 #  include "upgrade_proc.h"
     80 #endif /* MHD_SUPPORT_UPGRADE */
     81 
     82 #ifdef mhd_DEBUG_SUSPEND_RESUME
     83 #  include <stdio.h>
     84 #endif /* mhd_DEBUG_SUSPEND_RESUME */
     85 
     86 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ void
     87 mhd_conn_event_loop_state_update (struct MHD_Connection *restrict c)
     88 {
     89 #ifdef MHD_SUPPORT_HTTPS
     90   mhd_assert (! mhd_C_HAS_TLS (c) || \
     91               (mhd_CONN_STATE_TLS_CONNECTED == c->conn_state));
     92   mhd_assert (mhd_C_HAS_TLS (c) || \
     93               (mhd_CONN_STATE_TCP_CONNECTED == c->conn_state));
     94 #endif /* MHD_SUPPORT_HTTPS */
     95 
     96 #ifdef MHD_SUPPORT_HTTP2
     97   if (mhd_C_IS_HTTP2 (c))
     98   {
     99     mhd_h2_conn_state_update (c);
    100     return;
    101   }
    102 #endif /* MHD_SUPPORT_HTTP2 */
    103 
    104   switch (c->stage)
    105   {
    106   case mhd_HTTP_STAGE_INIT:
    107   case mhd_HTTP_STAGE_REQ_LINE_RECEIVING:
    108     c->event_loop_info = MHD_EVENT_LOOP_INFO_RECV;
    109     break;
    110   case mhd_HTTP_STAGE_REQ_LINE_RECEIVED:
    111     mhd_assert (0 && "Impossible value");
    112     mhd_UNREACHABLE ();
    113     break;
    114   case mhd_HTTP_STAGE_REQ_HEADERS_RECEIVING:
    115     c->event_loop_info = MHD_EVENT_LOOP_INFO_RECV;
    116     break;
    117   case mhd_HTTP_STAGE_HEADERS_RECEIVED:
    118   case mhd_HTTP_STAGE_HEADERS_PROCESSED:
    119     mhd_assert (0 && "Impossible value");
    120     mhd_UNREACHABLE ();
    121     break;
    122   case mhd_HTTP_STAGE_CONTINUE_SENDING:
    123     c->event_loop_info = MHD_EVENT_LOOP_INFO_SEND;
    124     break;
    125   case mhd_HTTP_STAGE_BODY_RECEIVING:
    126     c->event_loop_info = MHD_EVENT_LOOP_INFO_RECV;
    127     break;
    128   case mhd_HTTP_STAGE_BODY_RECEIVED:
    129     mhd_assert (0 && "Impossible value");
    130     mhd_UNREACHABLE ();
    131     break;
    132   case mhd_HTTP_STAGE_FOOTERS_RECEIVING:
    133     c->event_loop_info = MHD_EVENT_LOOP_INFO_RECV;
    134     break;
    135   case mhd_HTTP_STAGE_FOOTERS_RECEIVED:
    136     mhd_assert (0 && "Impossible value");
    137     mhd_UNREACHABLE ();
    138     break;
    139   case mhd_HTTP_STAGE_FULL_REQ_RECEIVED:
    140     mhd_assert (0 && "Should not be possible");
    141     c->event_loop_info = MHD_EVENT_LOOP_INFO_PROCESS;
    142     break;
    143   case mhd_HTTP_STAGE_REQ_RECV_FINISHED:
    144     mhd_assert (0 && "Impossible value");
    145     mhd_UNREACHABLE ();
    146     break;
    147   case mhd_HTTP_STAGE_START_REPLY:
    148     mhd_assert (0 && "Impossible value");
    149     mhd_UNREACHABLE ();
    150     break;
    151   case mhd_HTTP_STAGE_HEADERS_SENDING:
    152     /* headers in buffer, keep writing */
    153     c->event_loop_info = MHD_EVENT_LOOP_INFO_SEND;
    154     break;
    155   case mhd_HTTP_STAGE_HEADERS_SENT:
    156     mhd_assert (0 && "Impossible value");
    157     mhd_UNREACHABLE ();
    158     break;
    159 #ifdef MHD_SUPPORT_UPGRADE
    160   case mhd_HTTP_STAGE_UPGRADE_HEADERS_SENDING:
    161     c->event_loop_info = MHD_EVENT_LOOP_INFO_SEND;
    162     break;
    163 #endif /* MHD_SUPPORT_UPGRADE */
    164   case mhd_HTTP_STAGE_UNCHUNKED_BODY_UNREADY:
    165     mhd_assert (0 && "Should not be possible");
    166     c->event_loop_info = MHD_EVENT_LOOP_INFO_PROCESS;
    167     break;
    168   case mhd_HTTP_STAGE_UNCHUNKED_BODY_READY:
    169     c->event_loop_info = MHD_EVENT_LOOP_INFO_SEND;
    170     break;
    171   case mhd_HTTP_STAGE_CHUNKED_BODY_UNREADY:
    172     mhd_assert (0 && "Should not be possible");
    173     c->event_loop_info = MHD_EVENT_LOOP_INFO_PROCESS;
    174     break;
    175   case mhd_HTTP_STAGE_CHUNKED_BODY_READY:
    176     c->event_loop_info = MHD_EVENT_LOOP_INFO_SEND;
    177     break;
    178   case mhd_HTTP_STAGE_CHUNKED_BODY_SENT:
    179     mhd_assert (0 && "Impossible value");
    180     mhd_UNREACHABLE ();
    181     break;
    182   case mhd_HTTP_STAGE_FOOTERS_SENDING:
    183     c->event_loop_info = MHD_EVENT_LOOP_INFO_SEND;
    184     break;
    185   case mhd_HTTP_STAGE_FULL_REPLY_SENT:
    186     mhd_assert (0 && "Impossible value");
    187     mhd_UNREACHABLE ();
    188     break;
    189 #ifdef MHD_SUPPORT_UPGRADE
    190   case mhd_HTTP_STAGE_UPGRADING:
    191     mhd_assert (0 && "Impossible value");
    192     mhd_UNREACHABLE ();
    193     break;
    194   case mhd_HTTP_STAGE_UPGRADED:
    195     mhd_assert (0 && "Should not be possible");
    196     c->event_loop_info = MHD_EVENT_LOOP_INFO_UPGRADED;
    197     break;
    198   case mhd_HTTP_STAGE_UPGRADED_CLEANING:
    199     mhd_assert (0 && "Should be unreachable");
    200     c->event_loop_info = MHD_EVENT_LOOP_INFO_CLEANUP;
    201     break;
    202 #endif /* MHD_SUPPORT_UPGRADE */
    203   case mhd_HTTP_STAGE_PRE_CLOSING:
    204     mhd_assert (0 && "Should be unreachable");
    205     c->event_loop_info = MHD_EVENT_LOOP_INFO_CLEANUP;
    206     break;
    207   case mhd_HTTP_STAGE_CLOSED:
    208     mhd_assert (0 && "Should be unreachable");
    209     c->event_loop_info = MHD_EVENT_LOOP_INFO_CLEANUP;
    210     break;
    211   default:
    212     mhd_UNREACHABLE ();
    213     break;
    214   }
    215 }
    216 
    217 
    218 /**
    219  * Process HTTP communication layer states and events
    220  * @param c the connection to process
    221  * @return #mhd_COMM_LAYER_PROCESSING if setting up HTTP connection is
    222  *                                    in progress,
    223  *         #mhd_COMM_LAYER_OK if HTTP communication can be performed now,
    224  *         #mhd_COMM_LAYER_BROKEN if connection is broken and should be closed.
    225  */
    226 mhd_static_inline enum mhd_CommLayerState
    227 process_http_comm_layer (struct MHD_Connection *restrict c)
    228 {
    229 #ifdef MHD_SUPPORT_HTTP2
    230   if (mhd_HTTP_LAYER_CONNECTED == c->h_layer.state)
    231     return mhd_COMM_LAYER_OK; /* Shortcut for the most common case */
    232 
    233   switch (c->h_layer.state)
    234   {
    235   case mhd_HTTP_LAYER_PREFACE:
    236     return mhd_h2_process_preface (c);
    237   case mhd_HTTP_LAYER_CONNECTED:
    238     mhd_UNREACHABLE (); /* Handled above */
    239     return mhd_COMM_LAYER_OK;
    240   case mhd_HTTP_LAYER_CLOSING:
    241   case mhd_HTTP_LAYER_CLOSED:
    242     return mhd_COMM_LAYER_OK;
    243   case mhd_HTTP_LAYER_BROKEN:
    244     return mhd_COMM_LAYER_BROKEN;
    245   default:
    246     break;
    247   }
    248 
    249   mhd_UNREACHABLE ();
    250   return mhd_COMM_LAYER_BROKEN;
    251 
    252 #else  /* ! MHD_SUPPORT_HTTP2 */
    253   (void) c; /* Unused in HTTP/1.x-only modes */
    254   return mhd_COMM_LAYER_OK;
    255 #endif /* ! MHD_SUPPORT_HTTP2 */
    256 }
    257 
    258 
    259 /**
    260  * Finalise resuming of the connection
    261  * @param c the connection to resume
    262  */
    263 static MHD_FN_PAR_NONNULL_ALL_ void
    264 finish_resume (struct MHD_Connection *restrict c)
    265 {
    266   mhd_assert (c->resuming);
    267   c->resuming = false;
    268 
    269 #ifdef mhd_DEBUG_SUSPEND_RESUME
    270   fprintf (stderr,
    271            "%%%%%%    Resumed connection, FD: %2llu\n",
    272            (unsigned long long) c->sk.fd);
    273 #endif /* mhd_DEBUG_SUSPEND_RESUME */
    274   mhd_assert (! c->suspended);
    275   mhd_assert (MHD_EVENT_LOOP_INFO_PROCESS == c->event_loop_info);
    276 }
    277 
    278 
    279 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool
    280 mhd_conn_process_data (struct MHD_Connection *restrict c)
    281 {
    282   struct MHD_Daemon *const d = c->daemon;
    283   bool daemon_closing;
    284 
    285   if (c->suspended)
    286   {
    287     mhd_assert (! mhd_C_IS_HTTP2 (c));
    288     return true;
    289   }
    290 
    291   switch (process_http_comm_layer (c))
    292   {
    293   case mhd_COMM_LAYER_OK:
    294     break;        /* Process HTTP data */
    295   case mhd_COMM_LAYER_PROCESSING:
    296     mhd_assert (! c->resuming);
    297     return true;  /* Too early for HTTP */
    298   case mhd_COMM_LAYER_BROKEN:
    299     mhd_assert (c->dbg.closing_started);
    300     return false; /* Connection is broken */
    301   default:
    302     mhd_UNREACHABLE ();
    303     return false;
    304   }
    305 
    306   if (c->resuming)
    307   {
    308     mhd_assert (! mhd_C_IS_HTTP2 (c));
    309     finish_resume (c);
    310   }
    311 
    312 #ifdef MHD_SUPPORT_HTTP2
    313   if (mhd_C_IS_HTTP2 (c))
    314   {
    315     if (! mhd_h2_conn_process_data (c))
    316       return false;
    317     mhd_conn_event_loop_state_update (c);
    318     mhd_conn_mark_ready_update (c);
    319     return true;
    320   }
    321 #endif /* MHD_SUPPORT_HTTP2 */
    322 
    323   mhd_assert (mhd_D_IS_HTTP1_ENABLED (d) || (! mhd_C_IS_HTTP2 (c)) || \
    324               c->stop_with_error);
    325 
    326   if ((c->sk.state.rmt_shut_wr) && (mhd_HTTP_STAGE_START_REPLY > c->stage))
    327   {
    328     if (0 == c->read_buffer_offset)
    329     { /* Read buffer is empty, connection state is actual */
    330       mhd_conn_start_closing (c,
    331                               (mhd_HTTP_STAGE_INIT == c->stage) ?
    332                               mhd_CONN_CLOSE_HTTP_COMPLETED :
    333                               mhd_CONN_CLOSE_CLIENT_SHUTDOWN_EARLY,
    334                               NULL);
    335       return false;
    336     }
    337   }
    338 
    339   mhd_assert ((! mhd_SCKT_NET_ST_HAS_FLAG (c->sk.ready,
    340                                            mhd_SOCKET_NET_STATE_ERROR_READY))
    341               || (mhd_SOCKET_ERR_NO_ERROR == c->sk.state.discnt_err));
    342 
    343   if (mhd_SOCKET_ERR_NO_ERROR != c->sk.state.discnt_err)
    344   {
    345     mhd_assert (mhd_SOCKET_ERR_IS_HARD (c->sk.state.discnt_err));
    346     mhd_conn_start_closing_skt_err (c);
    347     return false;
    348   }
    349 
    350   daemon_closing = (mhd_DAEMON_STATE_STOPPING == d->state);
    351 #ifdef MHD_SUPPORT_THREADS
    352   daemon_closing = daemon_closing || d->threading.stop_requested;
    353 #endif /* MHD_SUPPORT_THREADS */
    354   if (daemon_closing)
    355   {
    356     mhd_conn_start_closing_d_shutdown (c);
    357     return false;
    358   }
    359 
    360   mhd_assert (! c->suspended);
    361 
    362   while (! 0)
    363   {
    364 #ifdef MHD_SUPPORT_HTTPS
    365     mhd_assert (! mhd_C_HAS_TLS (c) || \
    366                 (mhd_CONN_STATE_TLS_CONNECTED == c->conn_state));
    367     mhd_assert (mhd_C_HAS_TLS (c) || \
    368                 (mhd_CONN_STATE_TCP_CONNECTED == c->conn_state));
    369 #endif /* MHD_SUPPORT_HTTPS */
    370     switch (c->stage)
    371     {
    372     case mhd_HTTP_STAGE_INIT:
    373     case mhd_HTTP_STAGE_REQ_LINE_RECEIVING:
    374       if (mhd_stream_get_request_line (c))
    375       {
    376         mhd_assert (mhd_HTTP_STAGE_REQ_LINE_RECEIVING < c->stage);
    377         mhd_assert ((MHD_HTTP_VERSION_IS_SUPPORTED (c->rq.http_ver)) \
    378                     || (c->discard_request)
    379                     || (mhd_HTTP_STAGE_PRE_CLOSING <= c->stage));
    380         continue;
    381       }
    382       mhd_assert (mhd_HTTP_STAGE_REQ_LINE_RECEIVING >= c->stage);
    383       break;
    384     case mhd_HTTP_STAGE_REQ_LINE_RECEIVED:
    385       mhd_stream_switch_to_rq_headers_proc (c);
    386       mhd_assert (mhd_HTTP_STAGE_REQ_LINE_RECEIVED != c->stage);
    387       continue;
    388     case mhd_HTTP_STAGE_REQ_HEADERS_RECEIVING:
    389       if (mhd_stream_get_request_headers (c, false))
    390       {
    391         mhd_assert (mhd_HTTP_STAGE_REQ_HEADERS_RECEIVING < c->stage);
    392         mhd_assert ((mhd_HTTP_STAGE_HEADERS_RECEIVED == c->stage) || \
    393                     (c->discard_request));
    394         continue;
    395       }
    396       mhd_assert (mhd_HTTP_STAGE_REQ_HEADERS_RECEIVING == c->stage);
    397       break;
    398     case mhd_HTTP_STAGE_HEADERS_RECEIVED:
    399       mhd_stream_parse_request_headers (c);
    400       mhd_assert (c->stage != mhd_HTTP_STAGE_HEADERS_RECEIVED);
    401       continue;
    402     case mhd_HTTP_STAGE_HEADERS_PROCESSED:
    403       if (mhd_stream_call_app_request_cb (c))
    404       {
    405         mhd_assert (mhd_HTTP_STAGE_HEADERS_PROCESSED < c->stage);
    406         continue;
    407       }
    408       // TODO: add assert
    409       break;
    410     case mhd_HTTP_STAGE_CONTINUE_SENDING:
    411       if (c->continue_message_write_offset ==
    412           mhd_SSTR_LEN (mdh_HTTP_1_1_100_CONTINUE_REPLY))
    413       {
    414 #ifdef MHD_SUPPORT_UPGRADE
    415         c->rp.sent_100_cntn = true;
    416 #endif /* MHD_SUPPORT_UPGRADE */
    417         c->stage = mhd_HTTP_STAGE_BODY_RECEIVING;
    418         continue;
    419       }
    420       break;
    421     case mhd_HTTP_STAGE_BODY_RECEIVING:
    422       mhd_assert (c->rq.cntn.recv_size < c->rq.cntn.cntn_size);
    423       mhd_assert (! c->discard_request);
    424       mhd_assert (NULL == c->rp.response);
    425       if (0 == c->read_buffer_offset)
    426         break; /* Need more data to process */
    427 
    428       if (mhd_stream_process_request_body (c))
    429         continue;
    430       mhd_assert (! c->discard_request);
    431       mhd_assert (NULL == c->rp.response);
    432       break;
    433     case mhd_HTTP_STAGE_BODY_RECEIVED:
    434       mhd_assert (! c->discard_request);
    435       mhd_assert (NULL == c->rp.response);
    436       mhd_assert (c->rq.have_chunked_upload);
    437       /* Reset counter variables reused for footers */
    438       c->rq.num_cr_sp_replaced = 0;
    439       c->rq.skipped_broken_lines = 0;
    440       mhd_stream_reset_rq_hdr_proc_state (c);
    441       c->stage = mhd_HTTP_STAGE_FOOTERS_RECEIVING;
    442       continue;
    443     case mhd_HTTP_STAGE_FOOTERS_RECEIVING:
    444       mhd_assert (c->rq.have_chunked_upload);
    445       if (mhd_stream_get_request_headers (c, true))
    446       {
    447         mhd_assert (mhd_HTTP_STAGE_FOOTERS_RECEIVING < c->stage);
    448         mhd_assert ((mhd_HTTP_STAGE_FOOTERS_RECEIVED == c->stage) || \
    449                     (c->discard_request));
    450         continue;
    451       }
    452       mhd_assert (mhd_HTTP_STAGE_FOOTERS_RECEIVING == c->stage);
    453       break;
    454     case mhd_HTTP_STAGE_FOOTERS_RECEIVED:
    455       mhd_assert (c->rq.have_chunked_upload);
    456       c->stage = mhd_HTTP_STAGE_FULL_REQ_RECEIVED;
    457       continue;
    458     case mhd_HTTP_STAGE_FULL_REQ_RECEIVED:
    459       if (mhd_stream_call_app_final_upload_cb (c))
    460       {
    461         mhd_assert (mhd_HTTP_STAGE_FOOTERS_RECEIVING != c->stage);
    462         continue;
    463       }
    464       break;
    465     case mhd_HTTP_STAGE_REQ_RECV_FINISHED:
    466       if (mhd_stream_process_req_recv_finished (c))
    467         continue;
    468       break;
    469     // TODO: add stage for setup and full request buffers cleanup
    470     case mhd_HTTP_STAGE_START_REPLY:
    471       mhd_assert (NULL != c->rp.response);
    472       mhd_stream_switch_from_recv_to_send (c);
    473       if (! mhd_stream_build_header_response (c))
    474         continue;
    475       mhd_assert (mhd_HTTP_STAGE_START_REPLY != c->stage);
    476       break;
    477     case mhd_HTTP_STAGE_HEADERS_SENDING:
    478       /* no default action, wait for sending all the headers */
    479       break;
    480     case mhd_HTTP_STAGE_HEADERS_SENT:
    481       if (c->rp.props.send_reply_body)
    482       {
    483         if (c->rp.props.chunked)
    484           c->stage = mhd_HTTP_STAGE_CHUNKED_BODY_UNREADY;
    485         else
    486           c->stage = mhd_HTTP_STAGE_UNCHUNKED_BODY_UNREADY;
    487       }
    488       else
    489         c->stage = mhd_HTTP_STAGE_FULL_REPLY_SENT;
    490       continue;
    491 #ifdef MHD_SUPPORT_UPGRADE
    492     case mhd_HTTP_STAGE_UPGRADE_HEADERS_SENDING:
    493       if (! mhd_upgrade_try_start_upgrading (c))
    494         break;
    495       continue;
    496 #endif /* MHD_SUPPORT_UPGRADE */
    497     case mhd_HTTP_STAGE_UNCHUNKED_BODY_READY:
    498       mhd_assert (c->rp.props.send_reply_body);
    499       mhd_assert (! c->rp.props.chunked);
    500       /* nothing to do here, send the data */
    501       break;
    502     case mhd_HTTP_STAGE_UNCHUNKED_BODY_UNREADY:
    503       mhd_assert (c->rp.props.send_reply_body);
    504       mhd_assert (! c->rp.props.chunked);
    505       if (0 == c->rp.response->cntn_size)
    506       { /* a shortcut */
    507         c->stage = mhd_HTTP_STAGE_FULL_REPLY_SENT;
    508         continue;
    509       }
    510       if (mhd_stream_prep_unchunked_body (c))
    511         continue;
    512       break;
    513     case mhd_HTTP_STAGE_CHUNKED_BODY_READY:
    514       mhd_assert (c->rp.props.send_reply_body);
    515       mhd_assert (c->rp.props.chunked);
    516       /* nothing to do here */
    517       break;
    518     case mhd_HTTP_STAGE_CHUNKED_BODY_UNREADY:
    519       mhd_assert (c->rp.props.send_reply_body);
    520       mhd_assert (c->rp.props.chunked);
    521       if ( (0 == c->rp.response->cntn_size) ||
    522            (c->rp.rsp_cntn_read_pos ==
    523             c->rp.response->cntn_size) )
    524       {
    525         c->stage = mhd_HTTP_STAGE_CHUNKED_BODY_SENT;
    526         continue;
    527       }
    528       if (mhd_stream_prep_chunked_body (c))
    529         continue;
    530       break;
    531     case mhd_HTTP_STAGE_CHUNKED_BODY_SENT:
    532       mhd_assert (c->rp.props.send_reply_body);
    533       mhd_assert (c->rp.props.chunked);
    534       mhd_assert (c->write_buffer_send_offset <= \
    535                   c->write_buffer_append_offset);
    536       mhd_stream_call_dcc_cleanup_if_needed (c);
    537       if (mhd_stream_prep_chunked_footer (c))
    538         continue;
    539       break;
    540     case mhd_HTTP_STAGE_FOOTERS_SENDING:
    541       mhd_assert (c->rp.props.send_reply_body);
    542       mhd_assert (c->rp.props.chunked);
    543       /* no default action */
    544       break;
    545     case mhd_HTTP_STAGE_FULL_REPLY_SENT:
    546       // FIXME: support MHD_HTTP_STATUS_PROCESSING ?
    547       /* Reset connection after complete reply */
    548       mhd_stream_finish_req_serving ( \
    549         c,
    550         mhd_CONN_KEEPALIVE_POSSIBLE == c->conn_reuse
    551         && ! c->discard_request
    552         && ! c->sk.state.rmt_shut_wr);
    553       continue;
    554 #ifdef MHD_SUPPORT_UPGRADE
    555     case mhd_HTTP_STAGE_UPGRADING:
    556       if (mhd_upgrade_finish_switch_to_upgraded (c))
    557         return true;     /* Do not close connection */
    558       mhd_assert (mhd_HTTP_STAGE_PRE_CLOSING == c->stage);
    559       continue;
    560     case mhd_HTTP_STAGE_UPGRADED:
    561       mhd_assert (0 && "Should be unreachable");
    562       mhd_UNREACHABLE ();
    563       break;
    564     case mhd_HTTP_STAGE_UPGRADED_CLEANING:
    565       mhd_assert (0 && "Should be unreachable");
    566       mhd_UNREACHABLE ();
    567       break;
    568 #endif /* MHD_SUPPORT_UPGRADE */
    569     case mhd_HTTP_STAGE_PRE_CLOSING:
    570       return false;
    571     case mhd_HTTP_STAGE_CLOSED:
    572       mhd_assert (0 && "Should be unreachable");
    573       mhd_UNREACHABLE ();
    574       break;
    575     default:
    576       mhd_assert (0 && "Impossible value");
    577       mhd_UNREACHABLE ();
    578       break;
    579     }
    580 
    581     mhd_assert (mhd_HTTP_STAGE_CLOSED != c->stage);
    582 
    583     if (mhd_HTTP_STAGE_PRE_CLOSING == c->stage)
    584     {
    585       mhd_assert (0 && "Pre-closing should be already caught in the loop");
    586       mhd_UNREACHABLE ();
    587       return false;
    588     }
    589 
    590     if (c->suspended)
    591     {
    592       /* Do not perform any network activity while suspended */
    593       c->event_loop_info = MHD_EVENT_LOOP_INFO_PROCESS;
    594 
    595       mhd_conn_mark_unready (c, d);
    596       mhd_conn_deinit_activity_timeout (c);
    597   #ifdef mhd_DEBUG_SUSPEND_RESUME
    598       fprintf (stderr,
    599                "%%%%%%  Connection suspended, FD: %2llu\n",
    600                (unsigned long long) c->sk.fd);
    601   #endif /* mhd_DEBUG_SUSPEND_RESUME */
    602       return true;
    603     }
    604 
    605     if ((c->sk.state.rmt_shut_wr) && (mhd_HTTP_STAGE_START_REPLY > c->stage))
    606     {
    607       mhd_conn_start_closing (c,
    608                               (mhd_HTTP_STAGE_INIT == c->stage) ?
    609                               mhd_CONN_CLOSE_HTTP_COMPLETED :
    610                               mhd_CONN_CLOSE_CLIENT_SHUTDOWN_EARLY,
    611                               NULL);
    612       return false;
    613     }
    614 
    615     if (0 != (c->sk.ready & mhd_SOCKET_NET_STATE_ERROR_READY))
    616     {
    617       mhd_assert (0 && "Should be handled earlier");
    618       mhd_conn_start_closing_skt_err (c);
    619       return false;
    620     }
    621 
    622     mhd_conn_event_loop_state_update (c);
    623 
    624     if (0 != (MHD_EVENT_LOOP_INFO_RECV & c->event_loop_info))
    625     {
    626       /* Check whether the space is available to receive data */
    627       switch (mhd_stream_check_and_grow_read_buffer_space (c))
    628       {
    629       case mhd_CONN_BUFF_GROW_ERR_CONN_CLOSE:
    630         mhd_assert (c->discard_request);
    631         return false;
    632       case mhd_CONN_BUFF_GROW_ERR_REPLY:
    633         continue; /* Process error reply */
    634       case mhd_CONN_BUFF_GROW_OK:
    635         break;
    636       default:
    637         mhd_UNREACHABLE ();
    638         break;
    639       }
    640     }
    641 
    642     /* Current MHD design assumes that data must be always processes when
    643      * available. If it is not possible, connection must be suspended. */
    644     mhd_assert (MHD_EVENT_LOOP_INFO_PROCESS != c->event_loop_info);
    645 
    646     /* Sockets errors must be already handled */
    647     mhd_assert (0 == (c->sk.ready & mhd_SOCKET_NET_STATE_ERROR_READY));
    648 
    649     mhd_conn_mark_ready_update (c);
    650 
    651     break;
    652   }
    653 
    654   return true;
    655 }