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 }