libmicrohttpd2

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

stream_process_states.c (21251B)


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