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 }