libmicrohttpd2

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

conn_data_process.c (6940B)


      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) 2015-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/data_process.c
     42  * @brief  The implementation of data receiving, sending and processing
     43  *         functions for connection
     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 "conn_data_process.h"
     53 #include "sys_bool_type.h"
     54 #include "sys_base_types.h"
     55 
     56 #include "mhd_assert.h"
     57 #include "mhd_assume.h"
     58 #include "mhd_unreachable.h"
     59 #include "mhd_constexpr.h"
     60 
     61 #include <string.h>
     62 
     63 #include "mhd_daemon.h"
     64 #include "mhd_connection.h"
     65 
     66 #include "mhd_socket_error_funcs.h"
     67 
     68 #include "daemon_logger.h"
     69 
     70 #include "mhd_comm_layer_state.h"
     71 #ifdef MHD_SUPPORT_HTTPS
     72 #  include "conn_tls_check.h"
     73 #endif /* MHD_SUPPORT_HTTPS */
     74 
     75 #include "conn_data_recv.h"
     76 #include "conn_data_send.h"
     77 #include "stream_process_states.h"
     78 #include "mhd_comm_layer_state.h"
     79 
     80 
     81 mhd_static_inline enum mhd_CommLayerState
     82 process_conn_layer (struct MHD_Connection *restrict c)
     83 {
     84 #ifdef MHD_SUPPORT_HTTPS
     85   if (mhd_C_HAS_TLS (c))
     86     return mhd_conn_tls_check (c);
     87 #endif /* MHD_SUPPORT_HTTPS */
     88 
     89   return mhd_COMM_LAYER_OK;
     90 }
     91 
     92 
     93 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool
     94 mhd_conn_process_recv_send_data (struct MHD_Connection *restrict c)
     95 {
     96   bool send_ready_state_known;
     97   bool has_sock_err;
     98   bool data_processed;
     99 
    100   data_processed = false;
    101 
    102   mhd_assert (! c->suspended);
    103   if (c->resuming)
    104   {
    105     /* Fully resume the connection + call app callbacks for the data */
    106     if (! mhd_conn_process_data (c))
    107       return false;
    108 
    109     data_processed = true;
    110   }
    111 
    112   switch (process_conn_layer (c))
    113   {
    114   case mhd_COMM_LAYER_OK:
    115     break;        /* Connected, the data */
    116   case mhd_COMM_LAYER_PROCESSING:
    117     return true;  /* Not yet fully connected, too early for the data */
    118   case mhd_COMM_LAYER_BROKEN:
    119     return false; /* Connection is broken */
    120   default:
    121     mhd_UNREACHABLE ();
    122     return false;
    123   }
    124 
    125   /* The "send-ready" state is known if system polling call is edge-triggered
    126      (it always checks for both send- and recv-ready) or if connection needs
    127      sending (therefore "send-ready" was explicitly checked by sockets polling
    128      call). */
    129   send_ready_state_known =
    130     ((mhd_D_IS_USING_EDGE_TRIG (c->daemon)) ||
    131      (0 != (MHD_EVENT_LOOP_INFO_SEND & c->event_loop_info)));
    132   has_sock_err =
    133     (0 != (mhd_SOCKET_NET_STATE_ERROR_READY & c->sk.ready));
    134   mhd_assert (mhd_SOCKET_ERR_NO_ERROR == c->sk.state.discnt_err);
    135 
    136   if (0 != (MHD_EVENT_LOOP_INFO_RECV & c->event_loop_info))
    137   {
    138     bool use_recv;
    139     use_recv = (0 != (mhd_SOCKET_NET_STATE_RECV_READY
    140                       & (c->sk.ready | mhd_C_HAS_TLS_DATA_IN (c))));
    141     use_recv = use_recv ||
    142                (has_sock_err && c->sk.props.is_nonblck);
    143 
    144     if (use_recv)
    145     {
    146       mhd_conn_data_recv (c, has_sock_err);
    147       mhd_assert (! has_sock_err ||
    148                   (mhd_SOCKET_ERR_NO_ERROR != c->sk.state.discnt_err));
    149       if (! mhd_C_IS_HTTP2 (c))
    150       {
    151         if (! mhd_conn_process_data (c))
    152           return false;
    153         data_processed = true;
    154         mhd_ASSUME (mhd_SOCKET_ERR_NO_ERROR == c->sk.state.discnt_err);
    155       }
    156     }
    157   }
    158 
    159   if (0 != (MHD_EVENT_LOOP_INFO_SEND & c->event_loop_info))
    160   {
    161     bool use_send;
    162     /* Perform sending if:
    163      * + connection is ready for sending or
    164      * + just formed send data, connection send ready status is not known and
    165      *   connection socket is non-blocking
    166      * + detected network error on the connection, to check for the error */
    167     /* Assuming that after finishing receiving phase, connection send system
    168        buffers should have some space as sending was performed before receiving
    169        or has not been performed yet. */
    170     use_send = (0 != (mhd_SOCKET_NET_STATE_SEND_READY & c->sk.ready));
    171 
    172     /* Do not try to send if connection is broken when receiving */
    173     use_send = use_send && (mhd_SOCKET_ERR_NO_ERROR == c->sk.state.discnt_err);
    174 
    175     if (! mhd_C_IS_HTTP2 (c))
    176     {
    177       use_send = use_send ||
    178                  (data_processed && (! send_ready_state_known)
    179                   && c->sk.props.is_nonblck);
    180       use_send = use_send ||
    181                  (has_sock_err && c->sk.props.is_nonblck);
    182     }
    183 
    184     if (use_send)
    185     {
    186       mhd_conn_data_send (c);
    187       mhd_assert (! has_sock_err ||
    188                   (mhd_SOCKET_ERR_NO_ERROR != c->sk.state.discnt_err));
    189       if (! mhd_C_IS_HTTP2 (c))
    190       {
    191         if (! mhd_conn_process_data (c))
    192           return false;
    193         data_processed = true;
    194         mhd_ASSUME (mhd_SOCKET_ERR_NO_ERROR == c->sk.state.discnt_err);
    195       }
    196     }
    197   }
    198 
    199   if (mhd_SCKT_NET_ST_HAS_FLAG (c->sk.ready,
    200                                 mhd_SOCKET_NET_STATE_ERROR_READY) &&
    201       (mhd_SOCKET_ERR_NO_ERROR == c->sk.state.discnt_err))
    202   {
    203     c->sk.state.discnt_err = mhd_socket_error_get_from_socket (c->sk.fd);
    204     mhd_ASSUME (mhd_SOCKET_ERR_NO_ERROR != c->sk.state.discnt_err);
    205   }
    206 
    207   if (! data_processed ||
    208       mhd_C_IS_HTTP2 (c) ||
    209       (mhd_SOCKET_ERR_NO_ERROR != c->sk.state.discnt_err))
    210     return mhd_conn_process_data (c);
    211 
    212   return true;
    213 }