libmicrohttpd2

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

conn_data_send.c (15488B)


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