libmicrohttpd2

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

conn_data_send.c (15470B)


      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-2025 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/conn_data_send.c
     42  * @brief  The implementation of data sending functions for connection
     43  * @author Karlson2k (Evgeny Grin)
     44  *
     45  * Based on the MHD v0.x code by Daniel Pittman, Christian Grothoff, Evgeny Grin
     46  * and other contributors.
     47  */
     48 
     49 #include "mhd_sys_options.h"
     50 
     51 #include "conn_data_send.h"
     52 #include "sys_bool_type.h"
     53 #include "sys_base_types.h"
     54 #include "mhd_str_macros.h"
     55 
     56 #include "mhd_assert.h"
     57 #include "mhd_unreachable.h"
     58 
     59 #include "mhd_connection.h"
     60 #include "mhd_response.h"
     61 
     62 #include "mhd_socket_error.h"
     63 
     64 #include "mhd_socket_error_funcs.h"
     65 
     66 #include "conn_timeout.h"
     67 #include "stream_funcs.h"
     68 
     69 #include "mhd_send.h"
     70 
     71 
     72 /**
     73  * Check if we are done sending the write-buffer.
     74  * If so, transition into "next_state".
     75  *
     76  * @param connection connection to check write status for
     77  * @param next_stage the next state to transition to
     78  * @return #MHD_NO if we are not done, #MHD_YES if we are
     79  */
     80 static MHD_FN_PAR_NONNULL_ALL_ bool
     81 check_write_done (struct MHD_Connection *restrict connection,
     82                   enum mhd_HttpStage next_stage)
     83 {
     84   // TODO: integrate into states processing
     85   if ( (connection->write_buffer_append_offset !=
     86         connection->write_buffer_send_offset)
     87        /* || data_in_tls_buffers == true  */
     88        )
     89     return false;
     90   connection->write_buffer_append_offset = 0;
     91   connection->write_buffer_send_offset = 0;
     92   connection->stage = next_stage;
     93   return true;
     94 }
     95 
     96 
     97 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ void
     98 mhd_conn_data_send (struct MHD_Connection *c)
     99 {
    100   static const char http_100_continue_msg[] =
    101     mdh_HTTP_1_1_100_CONTINUE_REPLY;
    102   static const size_t http_100_continue_msg_len =
    103     mhd_SSTR_LEN (mdh_HTTP_1_1_100_CONTINUE_REPLY);
    104   enum mhd_SocketError res;
    105   size_t sent;
    106 
    107   mhd_assert (mhd_SOCKET_ERR_NO_ERROR == c->sk.state.discnt_err);
    108 
    109   // TODO: assert check suspended
    110 
    111   // TODO: MOVE out STATES PROCESSING
    112 
    113   res = mhd_SOCKET_ERR_INTERNAL; /* Mute compiler warning */
    114   mhd_assert (mhd_SOCKET_ERR_INTERNAL == res);  /* Mute analyser warning */
    115 
    116 #ifdef MHD_SUPPORT_HTTP2
    117   if (mhd_C_IS_HTTP2 (c))
    118   {
    119     res = mhd_send_data (c,
    120                          c->write_buffer_append_offset
    121                          - c->write_buffer_send_offset,
    122                          c->write_buffer
    123                          + c->write_buffer_send_offset,
    124                          true,
    125                          &sent);
    126     if (mhd_SOCKET_ERR_NO_ERROR == res)
    127       c->write_buffer_send_offset += sent;
    128   }
    129   else
    130 #endif /* MHD_SUPPORT_HTTP2 */
    131   switch (c->stage)
    132   {
    133   case mhd_HTTP_STAGE_CONTINUE_SENDING:
    134     res = mhd_send_data (c,
    135                          http_100_continue_msg_len
    136                          - c->continue_message_write_offset,
    137                          http_100_continue_msg
    138                          + c->continue_message_write_offset,
    139                          true,
    140                          &sent);
    141     if (mhd_SOCKET_ERR_NO_ERROR == res)
    142       c->continue_message_write_offset += sent;
    143     break;
    144   case mhd_HTTP_STAGE_HEADERS_SENDING:
    145     if (1)
    146     {
    147       struct MHD_Response *const restrict resp = c->rp.response;
    148       const size_t wb_ready = c->write_buffer_append_offset
    149                               - c->write_buffer_send_offset;
    150       mhd_assert (c->write_buffer_append_offset >= \
    151                   c->write_buffer_send_offset);
    152       mhd_assert (NULL != resp);
    153       mhd_assert ((mhd_CONN_MUST_UPGRADE != c->conn_reuse) || \
    154                   (! c->rp.props.send_reply_body));
    155 
    156       // TODO: support body generating alongside with header sending
    157 
    158       if ((c->rp.props.send_reply_body) &&
    159           (mhd_REPLY_CNTN_LOC_RESP_BUF == c->rp.cntn_loc))
    160       {
    161         /* Send response headers alongside the response body, if the body
    162          * data is available. */
    163         bool complete_response = true;
    164         size_t body_size = (size_t) resp->cntn_size;
    165         mhd_assert (mhd_RESPONSE_CONTENT_DATA_BUFFER == resp->cntn_dtype);
    166         mhd_assert (! c->rp.props.chunked);
    167         mhd_assert (MHD_SIZE_UNKNOWN != resp->cntn_size);
    168 
    169         if (resp->cntn_size != body_size)
    170         {
    171           body_size = (size_t) ~((size_t) 0);
    172           complete_response = false;
    173         }
    174 
    175         res = mhd_send_hdr_and_body (c,
    176                                      wb_ready,
    177                                      c->write_buffer
    178                                      + c->write_buffer_send_offset,
    179                                      false,
    180                                      body_size,
    181                                      (const char *) resp->cntn.buf,
    182                                      complete_response,
    183                                      &sent);
    184       }
    185       else
    186       {
    187         /* This is response for HEAD request or reply body is not allowed
    188          * for any other reason or reply body is dynamically generated. */
    189         /* Do not send the body data even if it's available. */
    190         res = mhd_send_hdr_and_body (c,
    191                                      wb_ready,
    192                                      c->write_buffer
    193                                      + c->write_buffer_send_offset,
    194                                      false,
    195                                      0,
    196                                      NULL,
    197                                      ((0 == resp->cntn_size) ||
    198                                       (! c->rp.props.send_reply_body)),
    199                                      &sent);
    200       }
    201       if (mhd_SOCKET_ERR_NO_ERROR == res)
    202       {
    203         mhd_assert (mhd_HTTP_STAGE_HEADERS_SENDING == c->stage);
    204 
    205         if (sent > wb_ready)
    206         {
    207           /* The complete header and some response data have been sent,
    208            * update both offsets. */
    209           mhd_assert (0 == c->rp.rsp_cntn_read_pos);
    210           mhd_assert (! c->rp.props.chunked);
    211           mhd_assert (c->rp.props.send_reply_body);
    212           mhd_assert (MHD_SIZE_UNKNOWN != resp->cntn_size);
    213           c->stage = mhd_HTTP_STAGE_UNCHUNKED_BODY_READY;
    214           c->write_buffer_send_offset += wb_ready;
    215           c->rp.rsp_cntn_read_pos = sent - wb_ready;
    216           if (c->rp.rsp_cntn_read_pos == c->rp.response->cntn_size)
    217             c->stage = mhd_HTTP_STAGE_FULL_REPLY_SENT;
    218         }
    219         else
    220         {
    221           c->write_buffer_send_offset += sent;
    222           // TODO: move it to data processing
    223           check_write_done (c,
    224                             mhd_HTTP_STAGE_HEADERS_SENT);
    225         }
    226       }
    227     }
    228     break;
    229   case mhd_HTTP_STAGE_UNCHUNKED_BODY_READY:
    230   case mhd_HTTP_STAGE_CHUNKED_BODY_READY:
    231     if (1)
    232     {
    233       struct MHD_Response *const restrict resp = c->rp.response;
    234       mhd_assert (c->rp.props.send_reply_body);
    235       mhd_assert (c->rp.rsp_cntn_read_pos <= resp->cntn_size);
    236       mhd_assert ((mhd_HTTP_STAGE_CHUNKED_BODY_READY != c->stage) || \
    237                   (mhd_REPLY_CNTN_LOC_CONN_BUF == c->rp.cntn_loc));
    238       if (mhd_REPLY_CNTN_LOC_RESP_BUF == c->rp.cntn_loc)
    239       {
    240         bool complete_response = true;
    241         const uint_fast64_t send_left =
    242           resp->cntn_size - c->rp.rsp_cntn_read_pos;
    243         size_t send_size = (size_t) send_left;
    244 
    245         mhd_assert (MHD_SIZE_UNKNOWN != resp->cntn_size);
    246         mhd_assert (mhd_HTTP_STAGE_UNCHUNKED_BODY_READY == c->stage);
    247         mhd_assert (mhd_RESPONSE_CONTENT_DATA_BUFFER == resp->cntn_dtype);
    248         mhd_assert (c->rp.rsp_cntn_read_pos < resp->cntn_size);
    249 
    250         if (send_left != send_size)
    251         {
    252           send_size = (size_t) ~((size_t) 0);
    253           complete_response = false;
    254           mhd_assert (send_left >= send_size);
    255         }
    256 
    257         res = mhd_send_data (c,
    258                              send_size,
    259                              (const char *) resp->cntn.buf
    260                              + c->rp.rsp_cntn_read_pos,
    261                              complete_response,
    262                              &sent);
    263         mhd_assert ((mhd_SOCKET_ERR_NO_ERROR != res) ||
    264                     sent <= send_size);
    265       }
    266       else if (mhd_REPLY_CNTN_LOC_CONN_BUF == c->rp.cntn_loc)
    267       {
    268         mhd_assert (c->write_buffer_append_offset > \
    269                     c->write_buffer_send_offset);
    270 
    271         res = mhd_send_data (c,
    272                              c->write_buffer_append_offset
    273                              - c->write_buffer_send_offset,
    274                              c->write_buffer + c->write_buffer_send_offset,
    275                              true,
    276                              &sent);
    277         mhd_assert ((mhd_SOCKET_ERR_NO_ERROR != res) || \
    278                     sent <= (c->write_buffer_append_offset \
    279                              - c->write_buffer_send_offset));
    280       }
    281       else if (mhd_REPLY_CNTN_LOC_IOV == c->rp.cntn_loc)
    282       {
    283         mhd_assert (mhd_RESPONSE_CONTENT_DATA_IOVEC == resp->cntn_dtype);
    284 
    285         res = mhd_send_iovec (c,
    286                               &c->rp.resp_iov,
    287                               true,
    288                               &sent);
    289       }
    290 #if defined(mhd_USE_SENDFILE)
    291       else if (mhd_REPLY_CNTN_LOC_FILE == c->rp.cntn_loc)
    292       {
    293         mhd_assert (mhd_RESPONSE_CONTENT_DATA_FILE == resp->cntn_dtype);
    294 
    295         res = mhd_send_sendfile (c, &sent);
    296         if (mhd_SOCKET_ERR_INTR == res)
    297         {
    298           if (! c->rp.response->cntn.file.use_sf)
    299           { /* Switch to filereader */
    300             mhd_assert (! c->rp.props.chunked);
    301             c->rp.cntn_loc = mhd_REPLY_CNTN_LOC_CONN_BUF;
    302             c->stage = mhd_HTTP_STAGE_UNCHUNKED_BODY_UNREADY;
    303           }
    304         }
    305       }
    306 #endif /* mhd_USE_SENDFILE */
    307       else
    308       {
    309         mhd_assert (0 && "Should be unreachable");
    310         sent = 0; /* Mute compiler warning */
    311         res = mhd_SOCKET_ERR_INTERNAL;
    312       }
    313 
    314       if (mhd_SOCKET_ERR_NO_ERROR == res)
    315       {
    316         if (mhd_REPLY_CNTN_LOC_CONN_BUF == c->rp.cntn_loc)
    317         {
    318           enum mhd_HttpStage next_stage;
    319           c->write_buffer_send_offset += sent;
    320           // TODO: move it to data processing
    321           if (mhd_HTTP_STAGE_CHUNKED_BODY_READY == c->stage)
    322             next_stage =
    323               (c->rp.response->cntn_size == c->rp.rsp_cntn_read_pos) ?
    324               mhd_HTTP_STAGE_CHUNKED_BODY_SENT :
    325               mhd_HTTP_STAGE_CHUNKED_BODY_UNREADY;
    326           else
    327             next_stage =
    328               (c->rp.rsp_cntn_read_pos == resp->cntn_size) ?
    329               mhd_HTTP_STAGE_FULL_REPLY_SENT :
    330               mhd_HTTP_STAGE_UNCHUNKED_BODY_UNREADY;
    331           check_write_done (c,
    332                             next_stage);
    333         }
    334         else
    335         {
    336           c->rp.rsp_cntn_read_pos += sent;
    337           mhd_assert (resp->cntn_size >= c->rp.rsp_cntn_read_pos);
    338           if (c->rp.rsp_cntn_read_pos == resp->cntn_size)
    339             c->stage = mhd_HTTP_STAGE_FULL_REPLY_SENT;
    340         }
    341       }
    342     }
    343     break;
    344   case mhd_HTTP_STAGE_FOOTERS_SENDING:
    345     res = mhd_send_data (c,
    346                          c->write_buffer_append_offset
    347                          - c->write_buffer_send_offset,
    348                          c->write_buffer
    349                          + c->write_buffer_send_offset,
    350                          true,
    351                          &sent);
    352     if (mhd_SOCKET_ERR_NO_ERROR == res)
    353     {
    354       c->write_buffer_send_offset += sent;
    355       // TODO: move it to data processing
    356       check_write_done (c,
    357                         mhd_HTTP_STAGE_FULL_REPLY_SENT);
    358     }
    359     break;
    360 #ifdef MHD_SUPPORT_UPGRADE
    361   case mhd_HTTP_STAGE_UPGRADE_HEADERS_SENDING:
    362     res = mhd_send_data (c,
    363                          c->write_buffer_append_offset
    364                          - c->write_buffer_send_offset,
    365                          c->write_buffer
    366                          + c->write_buffer_send_offset,
    367                          true,
    368                          &sent);
    369     if (mhd_SOCKET_ERR_NO_ERROR == res)
    370       c->write_buffer_send_offset += sent;
    371     break;
    372 #endif /* MHD_SUPPORT_UPGRADE */
    373   case mhd_HTTP_STAGE_INIT:
    374   case mhd_HTTP_STAGE_REQ_LINE_RECEIVING:
    375   case mhd_HTTP_STAGE_REQ_LINE_RECEIVED:
    376   case mhd_HTTP_STAGE_REQ_HEADERS_RECEIVING:
    377   case mhd_HTTP_STAGE_HEADERS_RECEIVED:
    378   case mhd_HTTP_STAGE_HEADERS_PROCESSED:
    379   case mhd_HTTP_STAGE_BODY_RECEIVING:
    380   case mhd_HTTP_STAGE_BODY_RECEIVED:
    381   case mhd_HTTP_STAGE_FOOTERS_RECEIVING:
    382   case mhd_HTTP_STAGE_FOOTERS_RECEIVED:
    383   case mhd_HTTP_STAGE_FULL_REQ_RECEIVED:
    384   case mhd_HTTP_STAGE_REQ_RECV_FINISHED:
    385   case mhd_HTTP_STAGE_START_REPLY:
    386   case mhd_HTTP_STAGE_HEADERS_SENT:
    387   case mhd_HTTP_STAGE_UNCHUNKED_BODY_UNREADY:
    388   case mhd_HTTP_STAGE_CHUNKED_BODY_UNREADY:
    389   case mhd_HTTP_STAGE_CHUNKED_BODY_SENT:
    390   case mhd_HTTP_STAGE_FULL_REPLY_SENT:
    391   case mhd_HTTP_STAGE_PRE_CLOSING:
    392   case mhd_HTTP_STAGE_CLOSED:
    393 #ifdef MHD_SUPPORT_UPGRADE
    394   case mhd_HTTP_STAGE_UPGRADING:
    395   case mhd_HTTP_STAGE_UPGRADED:
    396   case mhd_HTTP_STAGE_UPGRADED_CLEANING:
    397 #endif /* MHD_SUPPORT_UPGRADE */
    398     mhd_assert (0 && "Should be unreachable");
    399     mhd_UNREACHABLE ();
    400     res = mhd_SOCKET_ERR_INTERNAL;
    401     break;
    402   default:
    403     mhd_assert (0 && "Impossible value");
    404     mhd_UNREACHABLE ();
    405     res = mhd_SOCKET_ERR_INTERNAL;
    406     break;
    407   }
    408 
    409   if (mhd_SOCKET_ERR_NO_ERROR == res)
    410   {
    411     mhd_conn_update_activity_mark (c);
    412   }
    413   else if (mhd_SOCKET_ERR_IS_HARD (res))
    414   {
    415     if (mhd_SOCKET_ERR_NO_ERROR == c->sk.state.discnt_err)
    416       c->sk.state.discnt_err = res;
    417     c->sk.ready =
    418       (enum mhd_SocketNetState) (((unsigned int) c->sk.ready)
    419                                  | mhd_SOCKET_NET_STATE_ERROR_READY);
    420   }
    421 
    422   if (mhd_SCKT_NET_ST_HAS_FLAG (c->sk.ready,
    423                                 mhd_SOCKET_NET_STATE_ERROR_READY) &&
    424       (mhd_SOCKET_ERR_NO_ERROR == c->sk.state.discnt_err))
    425     c->sk.state.discnt_err = mhd_socket_error_get_from_socket (c->sk.fd);
    426 }