libmicrohttpd2

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

commit f9578650704c2260173f5e4aac9c956a9e5a9d6a
parent a73a6be16f6fe1da7b3043cf5c77afd19c312737
Author: Evgeny Grin (Karlson2k) <k2k@drgrin.dev>
Date:   Mon, 30 Sep 2024 02:46:33 +0100

Finished implementation of HTTP Upgrade + some fixes

Diffstat:
Msrc/include/microhttpd2.h | 19+++++++++----------
Msrc/include/microhttpd2_main.h.in | 17++++++++---------
Msrc/include/microhttpd2_preamble.h.in | 2+-
Msrc/mhd2/Makefile.am | 2+-
Msrc/mhd2/conn_data_send.c | 349++++++++++++++++++++++++++++++++++++++++++++-----------------------------------
Msrc/mhd2/events_process.c | 11++++++++++-
Msrc/mhd2/mhd_action.h | 4++--
Msrc/mhd2/mhd_connection.h | 2+-
Msrc/mhd2/mhd_sockets_macros.h | 19+++++++++++++++++++
Msrc/mhd2/mhd_upgrade.h | 2+-
Msrc/mhd2/stream_funcs.c | 41++++++++++++++++++++++-------------------
Dsrc/mhd2/upgrade_net.c | 531-------------------------------------------------------------------------------
Msrc/mhd2/upgrade_proc.c | 4++--
Asrc/mhd2/upgraded_net.c | 537+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
14 files changed, 809 insertions(+), 731 deletions(-)

diff --git a/src/include/microhttpd2.h b/src/include/microhttpd2.h @@ -1439,7 +1439,7 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_StatusCode MHD_SC_DAEMON_DESTROYED_WITH_UNCLOSED_UPGRADED = 60160 , /** - * The provided pointer to 'struct MHD_UpgradeHandle' is invalid + * The provided pointer to 'struct MHD_UpgradedHandle' is invalid */ MHD_SC_UPGRADED_HANDLE_INVALID = 60161 , @@ -6628,7 +6628,7 @@ MHD_FN_PAR_NONNULL_ (3) MHD_FN_PAR_OUT_SIZE_ (3,2); * actions relating to MHD responses that "upgrade" * the HTTP protocol (i.e. to WebSockets). */ -struct MHD_UpgradeHandle; +struct MHD_UpgradedHandle; #ifndef MHD_UPGRADEHANDLER_DEFINED @@ -6657,9 +6657,10 @@ struct MHD_UpgradeHandle; * perform the close() action on the @a sock. */ typedef void -(*MHD_UpgradeHandler)(void *cls, +(MHD_FN_PAR_NONNULL_ (2) MHD_FN_PAR_NONNULL_ (3) + *MHD_UpgradeHandler)(void *cls, struct MHD_Request *MHD_RESTRICT request, - struct MHD_UpgradeHandle *MHD_RESTRICT urh); + struct MHD_UpgradedHandle *MHD_RESTRICT urh); #define MHD_UPGRADEHANDLER_DEFINED 1 #endif /* ! MHD_UPGRADEHANDLER_DEFINED */ @@ -6772,7 +6773,7 @@ MHD_FN_PAR_IN_SIZE_ (6,5); * by this MHD build or platform */ MHD_EXTERN_ enum MHD_StatusCode -MHD_upgraded_recv (struct MHD_UpgradeHandle *MHD_RESTRICT urh, +MHD_upgraded_recv (struct MHD_UpgradedHandle *MHD_RESTRICT urh, size_t recv_buf_size, void *MHD_RESTRICT recv_buf, size_t *MHD_RESTRICT received_size, @@ -6801,9 +6802,7 @@ MHD_FN_PAR_OUT_ (4); * @param max_wait_millisec the maximum wait time for the data, * non-blocking operation if set to zero, * wait indefinitely if larger or equal to - * #MHD_WAIT_INDEFINITELY, - * the function may return earlier if waiting is - * interrupted or by other reasons + * #MHD_WAIT_INDEFINITELY * @param more_data_to_come set to #MHD_YES if the provided data in * the @a send_buf is part of a larger data package, * like an incomplete message or streamed @@ -6828,7 +6827,7 @@ MHD_FN_PAR_OUT_ (4); * by this MHD build or platform */ MHD_EXTERN_ enum MHD_StatusCode -MHD_upgraded_send (struct MHD_UpgradeHandle *MHD_RESTRICT urh, +MHD_upgraded_send (struct MHD_UpgradedHandle *MHD_RESTRICT urh, size_t send_buf_size, const void *MHD_RESTRICT send_buf, size_t *MHD_RESTRICT sent_size, @@ -6851,7 +6850,7 @@ MHD_FN_PAR_OUT_ (4); * error code otherwise */ MHD_EXTERN_ enum MHD_StatusCode -MHD_upgraded_close (struct MHD_UpgradeHandle *urh) +MHD_upgraded_close (struct MHD_UpgradedHandle *urh) MHD_FN_PAR_NONNULL_ (1); diff --git a/src/include/microhttpd2_main.h.in b/src/include/microhttpd2_main.h.in @@ -2204,7 +2204,7 @@ MHD_FN_PAR_NONNULL_ (3) MHD_FN_PAR_OUT_SIZE_ (3,2); * actions relating to MHD responses that "upgrade" * the HTTP protocol (i.e. to WebSockets). */ -struct MHD_UpgradeHandle; +struct MHD_UpgradedHandle; #ifndef MHD_UPGRADEHANDLER_DEFINED @@ -2233,9 +2233,10 @@ struct MHD_UpgradeHandle; * perform the close() action on the @a sock. */ typedef void -(*MHD_UpgradeHandler)(void *cls, +(MHD_FN_PAR_NONNULL_ (2) MHD_FN_PAR_NONNULL_ (3) + *MHD_UpgradeHandler)(void *cls, struct MHD_Request *MHD_RESTRICT request, - struct MHD_UpgradeHandle *MHD_RESTRICT urh); + struct MHD_UpgradedHandle *MHD_RESTRICT urh); #define MHD_UPGRADEHANDLER_DEFINED 1 #endif /* ! MHD_UPGRADEHANDLER_DEFINED */ @@ -2348,7 +2349,7 @@ MHD_FN_PAR_IN_SIZE_ (6,5); * by this MHD build or platform */ MHD_EXTERN_ enum MHD_StatusCode -MHD_upgraded_recv (struct MHD_UpgradeHandle *MHD_RESTRICT urh, +MHD_upgraded_recv (struct MHD_UpgradedHandle *MHD_RESTRICT urh, size_t recv_buf_size, void *MHD_RESTRICT recv_buf, size_t *MHD_RESTRICT received_size, @@ -2377,9 +2378,7 @@ MHD_FN_PAR_OUT_ (4); * @param max_wait_millisec the maximum wait time for the data, * non-blocking operation if set to zero, * wait indefinitely if larger or equal to - * #MHD_WAIT_INDEFINITELY, - * the function may return earlier if waiting is - * interrupted or by other reasons + * #MHD_WAIT_INDEFINITELY * @param more_data_to_come set to #MHD_YES if the provided data in * the @a send_buf is part of a larger data package, * like an incomplete message or streamed @@ -2404,7 +2403,7 @@ MHD_FN_PAR_OUT_ (4); * by this MHD build or platform */ MHD_EXTERN_ enum MHD_StatusCode -MHD_upgraded_send (struct MHD_UpgradeHandle *MHD_RESTRICT urh, +MHD_upgraded_send (struct MHD_UpgradedHandle *MHD_RESTRICT urh, size_t send_buf_size, const void *MHD_RESTRICT send_buf, size_t *MHD_RESTRICT sent_size, @@ -2427,7 +2426,7 @@ MHD_FN_PAR_OUT_ (4); * error code otherwise */ MHD_EXTERN_ enum MHD_StatusCode -MHD_upgraded_close (struct MHD_UpgradeHandle *urh) +MHD_upgraded_close (struct MHD_UpgradedHandle *urh) MHD_FN_PAR_NONNULL_ (1); diff --git a/src/include/microhttpd2_preamble.h.in b/src/include/microhttpd2_preamble.h.in @@ -1439,7 +1439,7 @@ enum MHD_FIXED_ENUM_MHD_SET_ MHD_StatusCode MHD_SC_DAEMON_DESTROYED_WITH_UNCLOSED_UPGRADED = 60160 , /** - * The provided pointer to 'struct MHD_UpgradeHandle' is invalid + * The provided pointer to 'struct MHD_UpgradedHandle' is invalid */ MHD_SC_UPGRADED_HANDLE_INVALID = 60161 , diff --git a/src/mhd2/Makefile.am b/src/mhd2/Makefile.am @@ -102,7 +102,7 @@ upgrade_files = \ mhd_upgrade.h \ upgrade_prep.c upgrade_prep.h \ upgrade_proc.c upgrade_proc.h \ - upgrade_net.c + upgraded_net.c if HAVE_POST_PARSER libmicrohttpd2_la_SOURCES += $(post_parser_files) diff --git a/src/mhd2/conn_data_send.c b/src/mhd2/conn_data_send.c @@ -91,8 +91,9 @@ mhd_conn_data_send (struct MHD_Connection *restrict c) res = mhd_SOCKET_ERR_INTERNAL; - if (MHD_CONNECTION_CONTINUE_SENDING == c->state) + switch (c->state) { + case MHD_CONNECTION_CONTINUE_SENDING: res = mhd_send_data (c, http_100_continue_msg_len - c->continue_message_write_offset, @@ -102,177 +103,176 @@ mhd_conn_data_send (struct MHD_Connection *restrict c) &sent); if (mhd_SOCKET_ERR_NO_ERROR == res) c->continue_message_write_offset += sent; - } - else if (MHD_CONNECTION_HEADERS_SENDING == c->state) - { - struct MHD_Response *const restrict resp = c->rp.response; - const size_t wb_ready = c->write_buffer_append_offset - - c->write_buffer_send_offset; - mhd_assert (c->write_buffer_append_offset >= \ - c->write_buffer_send_offset); - mhd_assert (NULL != resp); - mhd_assert ((mhd_CONN_MUST_UPGRADE != c->conn_reuse) || \ - (! c->rp.props.send_reply_body)); - - // TODO: support body generating alongside with header sending - - if ((c->rp.props.send_reply_body) && - (mhd_REPLY_CNTN_LOC_RESP_BUF == c->rp.cntn_loc)) + break; + case MHD_CONNECTION_HEADERS_SENDING: + if (1) { - /* Send response headers alongside the response body, if the body - * data is available. */ - mhd_assert (mhd_RESPONSE_CONTENT_DATA_BUFFER == resp->cntn_dtype); - mhd_assert (! c->rp.props.chunked); + struct MHD_Response *const restrict resp = c->rp.response; + const size_t wb_ready = c->write_buffer_append_offset + - c->write_buffer_send_offset; + mhd_assert (c->write_buffer_append_offset >= \ + c->write_buffer_send_offset); + mhd_assert (NULL != resp); + mhd_assert ((mhd_CONN_MUST_UPGRADE != c->conn_reuse) || \ + (! c->rp.props.send_reply_body)); - res = mhd_send_hdr_and_body (c, - wb_ready, - c->write_buffer - + c->write_buffer_send_offset, - false, - resp->cntn_size, - (const char *) resp->cntn.buf, - true, - &sent); - } - else - { - /* This is response for HEAD request or reply body is not allowed - * for any other reason or reply body is dynamically generated. */ - /* Do not send the body data even if it's available. */ - res = mhd_send_hdr_and_body (c, - wb_ready, - c->write_buffer - + c->write_buffer_send_offset, - false, - 0, - NULL, - ((0 == resp->cntn_size) || - (! c->rp.props.send_reply_body)), - &sent); - } - if (mhd_SOCKET_ERR_NO_ERROR == res) - { - mhd_assert (MHD_CONNECTION_HEADERS_SENDING == c->state); + // TODO: support body generating alongside with header sending - if (sent > wb_ready) + if ((c->rp.props.send_reply_body) && + (mhd_REPLY_CNTN_LOC_RESP_BUF == c->rp.cntn_loc)) { - /* The complete header and some response data have been sent, - * update both offsets. */ - mhd_assert (0 == c->rp.rsp_cntn_read_pos); + /* Send response headers alongside the response body, if the body + * data is available. */ + mhd_assert (mhd_RESPONSE_CONTENT_DATA_BUFFER == resp->cntn_dtype); mhd_assert (! c->rp.props.chunked); - mhd_assert (c->rp.props.send_reply_body); - c->state = MHD_CONNECTION_UNCHUNKED_BODY_READY; - c->write_buffer_send_offset += wb_ready; - c->rp.rsp_cntn_read_pos = sent - wb_ready; - if (c->rp.rsp_cntn_read_pos == c->rp.response->cntn_size) - c->state = MHD_CONNECTION_FULL_REPLY_SENT; + + res = mhd_send_hdr_and_body (c, + wb_ready, + c->write_buffer + + c->write_buffer_send_offset, + false, + resp->cntn_size, + (const char *) resp->cntn.buf, + true, + &sent); } else { - c->write_buffer_send_offset += sent; - // TODO: move it to data processing - check_write_done (c, - MHD_CONNECTION_HEADERS_SENT); + /* This is response for HEAD request or reply body is not allowed + * for any other reason or reply body is dynamically generated. */ + /* Do not send the body data even if it's available. */ + res = mhd_send_hdr_and_body (c, + wb_ready, + c->write_buffer + + c->write_buffer_send_offset, + false, + 0, + NULL, + ((0 == resp->cntn_size) || + (! c->rp.props.send_reply_body)), + &sent); } - - - } - - } - else if ((MHD_CONNECTION_UNCHUNKED_BODY_READY == c->state) || - (MHD_CONNECTION_CHUNKED_BODY_READY == c->state)) - { - struct MHD_Response *const restrict resp = c->rp.response; - mhd_assert (c->rp.props.send_reply_body); - mhd_assert (c->rp.rsp_cntn_read_pos < resp->cntn_size); - mhd_assert ((MHD_CONNECTION_CHUNKED_BODY_READY != c->state) || \ - (mhd_REPLY_CNTN_LOC_CONN_BUF == c->rp.cntn_loc)); - if (mhd_REPLY_CNTN_LOC_RESP_BUF == c->rp.cntn_loc) - { - mhd_assert (mhd_RESPONSE_CONTENT_DATA_BUFFER == resp->cntn_dtype); - - res = mhd_send_data (c, - c->rp.rsp_cntn_read_pos - resp->cntn_size, - (const char *) resp->cntn.buf - + c->rp.rsp_cntn_read_pos, - true, - &sent); - } - else if (mhd_REPLY_CNTN_LOC_CONN_BUF == c->rp.cntn_loc) - { - mhd_assert (c->write_buffer_append_offset > \ - c->write_buffer_send_offset); - - res = mhd_send_data (c, - c->write_buffer_append_offset - - c->write_buffer_send_offset, - c->write_buffer + c->write_buffer_send_offset, - true, - &sent); - } - else if (mhd_REPLY_CNTN_LOC_IOV == c->rp.cntn_loc) - { - mhd_assert (mhd_RESPONSE_CONTENT_DATA_IOVEC == resp->cntn_dtype); - - res = mhd_send_iovec (c, - &c->rp.resp_iov, - true, - &sent); - } -#if defined(MHD_USE_SENDFILE) - else if (mhd_REPLY_CNTN_LOC_FILE == c->rp.cntn_loc) - { - mhd_assert (mhd_RESPONSE_CONTENT_DATA_FILE == resp->cntn_dtype); - - res = mhd_send_sendfile (c, &sent); - if (mhd_SOCKET_ERR_INTR == res) + if (mhd_SOCKET_ERR_NO_ERROR == res) { - if (! c->rp.response->cntn.file.use_sf) - { /* Switch to filereader */ + mhd_assert (MHD_CONNECTION_HEADERS_SENDING == c->state); + + if (sent > wb_ready) + { + /* The complete header and some response data have been sent, + * update both offsets. */ + mhd_assert (0 == c->rp.rsp_cntn_read_pos); mhd_assert (! c->rp.props.chunked); - c->rp.cntn_loc = mhd_REPLY_CNTN_LOC_CONN_BUF; - c->state = MHD_CONNECTION_UNCHUNKED_BODY_UNREADY; + mhd_assert (c->rp.props.send_reply_body); + c->state = MHD_CONNECTION_UNCHUNKED_BODY_READY; + c->write_buffer_send_offset += wb_ready; + c->rp.rsp_cntn_read_pos = sent - wb_ready; + if (c->rp.rsp_cntn_read_pos == c->rp.response->cntn_size) + c->state = MHD_CONNECTION_FULL_REPLY_SENT; + } + else + { + c->write_buffer_send_offset += sent; + // TODO: move it to data processing + check_write_done (c, + MHD_CONNECTION_HEADERS_SENT); } } } -#endif /* MHD_USE_SENDFILE */ - else + break; + case MHD_CONNECTION_UNCHUNKED_BODY_READY: + case MHD_CONNECTION_CHUNKED_BODY_READY: + if (1) { - mhd_assert (0 && "Should be unreachable"); - res = mhd_SOCKET_ERR_INTERNAL; - } + struct MHD_Response *const restrict resp = c->rp.response; + mhd_assert (c->rp.props.send_reply_body); + mhd_assert (c->rp.rsp_cntn_read_pos < resp->cntn_size); + mhd_assert ((MHD_CONNECTION_CHUNKED_BODY_READY != c->state) || \ + (mhd_REPLY_CNTN_LOC_CONN_BUF == c->rp.cntn_loc)); + if (mhd_REPLY_CNTN_LOC_RESP_BUF == c->rp.cntn_loc) + { + mhd_assert (mhd_RESPONSE_CONTENT_DATA_BUFFER == resp->cntn_dtype); + + res = mhd_send_data (c, + c->rp.rsp_cntn_read_pos - resp->cntn_size, + (const char *) resp->cntn.buf + + c->rp.rsp_cntn_read_pos, + true, + &sent); + } + else if (mhd_REPLY_CNTN_LOC_CONN_BUF == c->rp.cntn_loc) + { + mhd_assert (c->write_buffer_append_offset > \ + c->write_buffer_send_offset); + + res = mhd_send_data (c, + c->write_buffer_append_offset + - c->write_buffer_send_offset, + c->write_buffer + c->write_buffer_send_offset, + true, + &sent); + } + else if (mhd_REPLY_CNTN_LOC_IOV == c->rp.cntn_loc) + { + mhd_assert (mhd_RESPONSE_CONTENT_DATA_IOVEC == resp->cntn_dtype); - if (mhd_SOCKET_ERR_NO_ERROR == res) - { - if (mhd_REPLY_CNTN_LOC_CONN_BUF == c->rp.cntn_loc) + res = mhd_send_iovec (c, + &c->rp.resp_iov, + true, + &sent); + } + #if defined(MHD_USE_SENDFILE) + else if (mhd_REPLY_CNTN_LOC_FILE == c->rp.cntn_loc) { - enum MHD_CONNECTION_STATE next_state; - c->write_buffer_send_offset += sent; - // TODO: move it to data processing - if (MHD_CONNECTION_CHUNKED_BODY_READY == c->state) - next_state = - (c->rp.response->cntn_size == c->rp.rsp_cntn_read_pos) ? - MHD_CONNECTION_CHUNKED_BODY_SENT : - MHD_CONNECTION_CHUNKED_BODY_UNREADY; - else - next_state = - (c->rp.rsp_cntn_read_pos == resp->cntn_size) ? - MHD_CONNECTION_FULL_REPLY_SENT : - MHD_CONNECTION_UNCHUNKED_BODY_UNREADY; - check_write_done (c, - next_state); + mhd_assert (mhd_RESPONSE_CONTENT_DATA_FILE == resp->cntn_dtype); + + res = mhd_send_sendfile (c, &sent); + if (mhd_SOCKET_ERR_INTR == res) + { + if (! c->rp.response->cntn.file.use_sf) + { /* Switch to filereader */ + mhd_assert (! c->rp.props.chunked); + c->rp.cntn_loc = mhd_REPLY_CNTN_LOC_CONN_BUF; + c->state = MHD_CONNECTION_UNCHUNKED_BODY_UNREADY; + } + } } + #endif /* MHD_USE_SENDFILE */ else { - c->rp.rsp_cntn_read_pos += sent; - if (c->rp.rsp_cntn_read_pos == resp->cntn_size) - c->state = MHD_CONNECTION_FULL_REPLY_SENT; + mhd_assert (0 && "Should be unreachable"); + res = mhd_SOCKET_ERR_INTERNAL; } - } - } - else if (MHD_CONNECTION_FOOTERS_SENDING == c->state) - { + if (mhd_SOCKET_ERR_NO_ERROR == res) + { + if (mhd_REPLY_CNTN_LOC_CONN_BUF == c->rp.cntn_loc) + { + enum MHD_CONNECTION_STATE next_state; + c->write_buffer_send_offset += sent; + // TODO: move it to data processing + if (MHD_CONNECTION_CHUNKED_BODY_READY == c->state) + next_state = + (c->rp.response->cntn_size == c->rp.rsp_cntn_read_pos) ? + MHD_CONNECTION_CHUNKED_BODY_SENT : + MHD_CONNECTION_CHUNKED_BODY_UNREADY; + else + next_state = + (c->rp.rsp_cntn_read_pos == resp->cntn_size) ? + MHD_CONNECTION_FULL_REPLY_SENT : + MHD_CONNECTION_UNCHUNKED_BODY_UNREADY; + check_write_done (c, + next_state); + } + else + { + c->rp.rsp_cntn_read_pos += sent; + if (c->rp.rsp_cntn_read_pos == resp->cntn_size) + c->state = MHD_CONNECTION_FULL_REPLY_SENT; + } + } + } + break; + case MHD_CONNECTION_FOOTERS_SENDING: res = mhd_send_data (c, c->write_buffer_append_offset - c->write_buffer_send_offset, @@ -287,11 +287,54 @@ mhd_conn_data_send (struct MHD_Connection *restrict c) check_write_done (c, MHD_CONNECTION_FULL_REPLY_SENT); } - } - else - { + break; +#ifdef MHD_UPGRADE_SUPPORT + case MHD_CONNECTION_UPGRADE_HEADERS_SENDING: + res = mhd_send_data (c, + c->write_buffer_append_offset + - c->write_buffer_send_offset, + c->write_buffer + + c->write_buffer_send_offset, + true, + &sent); + if (mhd_SOCKET_ERR_NO_ERROR == res) + c->write_buffer_send_offset += sent; + break; +#endif /* MHD_UPGRADE_SUPPORT */ + case MHD_CONNECTION_INIT: + case MHD_CONNECTION_REQ_LINE_RECEIVING: + case MHD_CONNECTION_REQ_LINE_RECEIVED: + case MHD_CONNECTION_REQ_HEADERS_RECEIVING: + case MHD_CONNECTION_HEADERS_RECEIVED: + case MHD_CONNECTION_HEADERS_PROCESSED: + case MHD_CONNECTION_BODY_RECEIVING: + case MHD_CONNECTION_BODY_RECEIVED: + case MHD_CONNECTION_FOOTERS_RECEIVING: + case MHD_CONNECTION_FOOTERS_RECEIVED: + case MHD_CONNECTION_FULL_REQ_RECEIVED: + case MHD_CONNECTION_REQ_RECV_FINISHED: + case MHD_CONNECTION_START_REPLY: + case MHD_CONNECTION_HEADERS_SENT: + case MHD_CONNECTION_UNCHUNKED_BODY_UNREADY: + case MHD_CONNECTION_CHUNKED_BODY_UNREADY: + case MHD_CONNECTION_CHUNKED_BODY_SENT: + case MHD_CONNECTION_FULL_REPLY_SENT: + case MHD_CONNECTION_PRE_CLOSING: + case MHD_CONNECTION_CLOSED: +#ifdef MHD_UPGRADE_SUPPORT + case MHD_CONNECTION_UPGRADING: + case MHD_CONNECTION_UPGRADED: + case MHD_CONNECTION_UPGRADED_CLEANING: +#endif /* MHD_UPGRADE_SUPPORT */ mhd_assert (0 && "Should be unreachable"); + MHD_UNREACHABLE_; + res = mhd_SOCKET_ERR_INTERNAL; + break; + default: + mhd_assert (0 && "Impossible value"); + MHD_UNREACHABLE_; res = mhd_SOCKET_ERR_INTERNAL; + break; } if (mhd_SOCKET_ERR_NO_ERROR == res) diff --git a/src/mhd2/events_process.c b/src/mhd2/events_process.c @@ -694,6 +694,10 @@ poll_update_fds (struct MHD_Daemon *restrict d, unsigned int i_s; unsigned int i_c; struct MHD_Connection *restrict c; +#ifndef NDEBUG + unsigned int num_skipped = 0; +#endif /* ! NDEBUG */ + mhd_assert (mhd_POLL_TYPE_POLL == d->events.poll_type); i_s = 0; @@ -722,7 +726,12 @@ poll_update_fds (struct MHD_Daemon *restrict d, unsigned short events; /* 'unsigned' for correct bits manipulations */ if (is_conn_excluded_from_http_comm (c)) + { +#ifndef NDEBUG + ++num_skipped; +#endif /* ! NDEBUG */ continue; + } mhd_assert ((i_c - i_s) < d->conns.cfg.count_limit); mhd_assert (i_c < d->dbg.num_events_elements); @@ -739,7 +748,7 @@ poll_update_fds (struct MHD_Daemon *restrict d, d->events.data.poll.fds[i_c].events = (short) events; ++i_c; } - mhd_assert (d->conns.count == (i_c - i_s)); + mhd_assert ((d->conns.count - num_skipped) == (i_c - i_s)); mhd_assert (i_c <= d->dbg.num_events_elements); return i_c; } diff --git a/src/mhd2/mhd_action.h b/src/mhd2/mhd_action.h @@ -213,14 +213,14 @@ struct mhd_PostParseActionData #ifdef MHD_UPGRADE_SUPPORT -struct MHD_UpgradeHandle; /* forward declaration */ +struct MHD_UpgradedHandle; /* forward declaration */ #ifndef MHD_UPGRADEHANDLER_DEFINED typedef void (*MHD_UpgradeHandler)(void *cls, struct MHD_Request *MHD_RESTRICT request, - struct MHD_UpgradeHandle *MHD_RESTRICT urh); + struct MHD_UpgradedHandle *MHD_RESTRICT urh); #define MHD_UPGRADEHANDLER_DEFINED 1 #endif /* ! MHD_UPGRADEHANDLER_DEFINED */ diff --git a/src/mhd2/mhd_connection.h b/src/mhd2/mhd_connection.h @@ -458,7 +458,7 @@ struct MHD_Connection /** * The data for handling HTTP-Upgraded connection */ - struct MHD_UpgradeHandle upgr; + struct MHD_UpgradedHandle upgr; /** * Double-linke list of HTTP-Upgraded connections waiting for clean-up diff --git a/src/mhd2/mhd_sockets_macros.h b/src/mhd2/mhd_sockets_macros.h @@ -97,6 +97,15 @@ # define mhd_SCKT_GET_LERR() (WSAGetLastError ()) #endif +/** + * Set last socket error + */ +#if defined(MHD_POSIX_SOCKETS) +# define mhd_SCKT_SET_LERR(err) do { errno = (err); } while (0) +#elif defined(MHD_WINSOCK_SOCKETS) +# define mhd_SCKT_SET_LERR(err) WSASetLastError ((err)) +#endif + #if defined(MHD_POSIX_SOCKETS) # if defined(EAGAIN) && defined(EWOULDBLOCK) && \ ((EWOULDBLOCK + 0) != (EAGAIN + 0)) @@ -217,6 +226,16 @@ # define mhd_SCKT_ERR_IS_PIPE(err) (WSAESHUTDOWN == (err)) #endif +#if defined(MHD_POSIX_SOCKETS) +# ifdef EINPROGRESS +# define mhd_SCKT_ERR_IS_INPROGRESS(err) (EINPROGRESS == (err)) +# else +# define mhd_SCKT_ERR_IS_INPROGRESS(err) ((void) (err), ! ! 0) +# endif +#elif defined(MHD_WINSOCK_SOCKETS) +# define mhd_SCKT_ERR_IS_INPROGRESS(err) (WSAEINPROGRESS == (err)) +#endif + /** * Check whether is given socket error is type of "incoming connection * was disconnected before 'accept()' is called". diff --git a/src/mhd2/mhd_upgrade.h b/src/mhd2/mhd_upgrade.h @@ -37,7 +37,7 @@ struct MHD_Connection; /* forward declaration */ /** * The data for "HTTP-upgraded" connection */ -struct MHD_UpgradeHandle +struct MHD_UpgradedHandle { /** * The pointer to the "connection" object diff --git a/src/mhd2/stream_funcs.c b/src/mhd2/stream_funcs.c @@ -806,7 +806,7 @@ mhd_conn_start_closing (struct MHD_Connection *restrict c, #ifdef MHD_UPGRADE_SUPPORT if (mhd_CONN_CLOSE_UPGRADE == reason) { - c->state = MHD_CONNECTION_UPGRADING; + mhd_assert (MHD_CONNECTION_UPGRADING == c->state); c->event_loop_info = MHD_EVENT_LOOP_INFO_UPGRADED; } else @@ -883,6 +883,24 @@ mhd_conn_pre_clean_part1 (struct MHD_Connection *restrict c) mhd_stream_call_dcc_cleanup_if_needed (c); if (NULL != c->rq.cntn.lbuf.data) mhd_daemon_free_lbuf (c->daemon, &(c->rq.cntn.lbuf)); + +#ifdef MHD_USE_EPOLL + if (mhd_POLL_TYPE_EPOLL == c->daemon->events.poll_type) + { + struct epoll_event event; + + event.events = 0; + event.data.ptr = NULL; + if (0 != epoll_ctl (c->daemon->events.data.epoll.e_fd, + EPOLL_CTL_DEL, + c->socket_fd, + &event)) + { + mhd_LOG_MSG (c->daemon, MHD_SC_EPOLL_CTL_REMOVE_FAILED, + "Failed to remove connection socket from epoll."); + } + } +#endif /* MHD_USE_EPOLL */ } @@ -895,6 +913,9 @@ mhd_conn_pre_clean (struct MHD_Connection *restrict c) mhd_assert (c->dbg.closing_started); mhd_assert (! c->dbg.pre_cleaned); +#ifdef MHD_UPGRADE_SUPPORT + if (NULL == c->upgr.c) +#endif mhd_conn_pre_clean_part1 (c); if (NULL != c->rp.resp_iov.iov) @@ -918,24 +939,6 @@ mhd_conn_pre_clean (struct MHD_Connection *restrict c) mhd_pool_destroy (c->pool); c->pool = NULL; -#ifdef MHD_USE_EPOLL - if (mhd_POLL_TYPE_EPOLL == c->daemon->events.poll_type) - { - struct epoll_event event; - - event.events = 0; - event.data.ptr = NULL; - if (0 != epoll_ctl (c->daemon->events.data.epoll.e_fd, - EPOLL_CTL_DEL, - c->socket_fd, - &event)) - { - mhd_LOG_MSG (c->daemon, MHD_SC_EPOLL_CTL_REMOVE_FAILED, - "Failed to remove connection socket from epoll."); - } - } -#endif /* MHD_USE_EPOLL */ - c->state = MHD_CONNECTION_CLOSED; #ifndef NDEBUG c->dbg.pre_cleaned = true; diff --git a/src/mhd2/upgrade_net.c b/src/mhd2/upgrade_net.c @@ -1,531 +0,0 @@ -/* - This file is part of GNU libmicrohttpd - Copyright (C) 2024 Evgeny Grin (Karlson2k) & Christian Grothoff - - GNU libmicrohttpd is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - GNU libmicrohttpd is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - -*/ - -/** - * @file src/mhd2/upgrade_net.c - * @brief The implementation of functions for network data exchange - * for HTTP Upgraded connections - * @author Karlson2k (Evgeny Grin) - * @author Christian Grothoff - */ - -#include "mhd_sys_options.h" - -#include "sys_bool_type.h" -#include "sys_base_types.h" - -#include "sys_poll.h" -#ifndef MHD_USE_POLL -# include "sys_select.h" -#endif -#include "mhd_limits.h" - -#include "mhd_sockets_macros.h" - -#include "mhd_upgrade.h" -#include "mhd_connection.h" -#include "mhd_locks.h" - -#include "mhd_recv.h" -#include "mhd_send.h" -#include "mhd_mono_clock.h" - -#include "mhd_public_api.h" - - -#if ! defined (MHD_USE_POLL) && \ - (defined(MHD_POSIX_SOCKETS) || ! defined(MHD_USE_SELECT)) -# if defined(_WIN32) || defined(HAVE_NANOSLEEP) || defined(HAVE_USLEEP) -# define mhd_HAVE_MHD_SLEEP 1 - -/** - * Pause execution for specified number of milliseconds. - * - * @param millisec the number of milliseconds to sleep - */ -static void -mhd_sleep (uint_fast32_t millisec) -{ -#if defined(_WIN32) - Sleep (millisec); -#elif defined(HAVE_NANOSLEEP) - struct timespec slp = {millisec / 1000, (millisec % 1000) * 1000000}; - struct timespec rmn; - int num_retries = 0; - while (0 != nanosleep (&slp, &rmn)) - { - if (EINTR != errno) - externalErrorExit (); - if (num_retries++ > 8) - break; - slp = rmn; - } -#elif defined(HAVE_USLEEP) - uint64_t us = millisec * 1000; - do - { - uint64_t this_sleep; - if (999999 < us) - this_sleep = 999999; - else - this_sleep = us; - /* Ignore return value as it could be void */ - usleep (this_sleep); - us -= this_sleep; - } while (us > 0); -#endif -} - - -#endif /* _WIN32 || HAVE_NANOSLEEP || HAVE_USLEEP */ -#endif /* ! MHD_USE_POLL) && (MHD_POSIX_SOCKETS || ! MHD_USE_SELECT) */ - - -MHD_EXTERN_ -MHD_FN_PAR_NONNULL_ALL_ -MHD_FN_PAR_OUT_SIZE_ (3,2) -MHD_FN_PAR_OUT_ (4) enum MHD_StatusCode -MHD_upgraded_recv (struct MHD_UpgradeHandle *MHD_RESTRICT urh, - size_t recv_buf_size, - void *MHD_RESTRICT recv_buf, - size_t *MHD_RESTRICT received_size, - uint_fast64_t max_wait_millisec) -{ - struct MHD_Connection *restrict c = urh->c; -#if defined(MHD_USE_POLL) || defined(MHD_USE_SELECT) - const MHD_Socket socket_fd = c->socket_fd; -#endif /* MHD_USE_POLL || MHD_USE_SELECT */ - char *restrict buf_char = (char *) recv_buf; - size_t last_block_size; - enum mhd_SocketError res; - - *received_size = 0; - - if (&(c->upgr) != urh) - return MHD_SC_UPGRADED_HANDLE_INVALID; - if (MHD_CONNECTION_UPGRADED != c->state) - return MHD_SC_UPGRADED_HANDLE_INVALID; - - if (0 == recv_buf_size) - return MHD_SC_OK; - - if (NULL != c->read_buffer) - { - mhd_mutex_lock_chk (&(urh->lock)); - if (0 != c->read_buffer_offset) /* Re-check under the lock */ - { - if (recv_buf_size < c->read_buffer_offset) - { - memcpy (buf_char, c->read_buffer, recv_buf_size); - last_block_size = recv_buf_size; - c->read_buffer += recv_buf_size; - c->read_buffer_offset -= recv_buf_size; - c->read_buffer_size -= recv_buf_size; - } - else - { - /* recv_buf_size >= c->read_buffer_offset */ - memcpy (buf_char, c->read_buffer, c->read_buffer_offset); - last_block_size = c->read_buffer_offset; - c->read_buffer_offset = 0; - c->read_buffer_size = 0; - /* Do not deallocate the read buffer to save the time under the lock. - The connection memory pool will not be used anyway. */ - c->read_buffer = NULL; - } - } - mhd_mutex_unlock_chk (&(urh->lock)); - *received_size = last_block_size; - if (recv_buf_size == last_block_size) - return MHD_SC_OK; - } - - last_block_size = 0; - res = mhd_recv (c, - recv_buf_size - *received_size, - buf_char + *received_size, - &last_block_size); - if (mhd_SOCKET_ERR_NO_ERROR == res) - { - if (0 == last_block_size) - c->sk_rmt_shut_wr = true; - *received_size += last_block_size; - return MHD_SC_OK; - } - else if (0 != *received_size) - return MHD_SC_OK; - - if (! mhd_SOCKET_ERR_IS_HARD (res)) - { - while (0 != max_wait_millisec) - { -#if defined(MHD_USE_POLL) - if (1) - { - struct pollfd fds[1]; - int poll_wait; - int poll_res; - int wait_err; - - if (MHD_WAIT_INDEFINITELY <= max_wait_millisec) - poll_wait = -1; - else - { - poll_wait = (int) max_wait_millisec; - if ((max_wait_millisec != (uint_fast64_t) poll_wait) || - (0 > poll_wait)) - poll_wait = INT_MAX; - } - fds[0].fd = socket_fd; - fds[0].events = POLLIN; - - poll_res = mhd_poll (fds, - 1, - poll_wait); - if ((0 >= poll_res) && - (0 != *received_size)) - return MHD_SC_OK; - if (0 == poll_res) - return MHD_SC_UPGRADED_NET_TIMEOUT; - - wait_err = mhd_SCKT_GET_LERR (); - if (! mhd_SCKT_ERR_IS_EAGAIN (wait_err) && - ! mhd_SCKT_ERR_IS_EINTR (wait_err) && - ! mhd_SCKT_ERR_IS_LOW_RESOURCES (wait_err)) - return MHD_SC_UPGRADED_NET_HARD_ERROR; - max_wait_millisec = 0; /* Re-try only one time */ - } -#else /* ! MHD_USE_POLL */ - bool use_select; -# if defined(MHD_USE_SELECT) -# ifdef MHD_POSIX_SOCKETS - use_select = socket_fd < FD_SETSIZE; -# else /* MHD_WINSOCK_SOCKETS */ - use_select = true; -# endif /* MHD_WINSOCK_SOCKETS */ - if (use_select) - { - fd_set rfds; - int sel_res; - int wait_err; - struct timeval tmvl; - -# ifdef MHD_POSIX_SOCKETS - tmvl.tv_sec = (long) (max_wait_millisec / 1000); -# else /* MHD_WINSOCK_SOCKETS */ - tmvl.tv_sec = (long) (max_wait_millisec / 1000); -# endif /* MHD_WINSOCK_SOCKETS */ - if ((max_wait_millisec / 1000 != (uint_fast64_t) tmvl.tv_sec) || - (0 > tmvl.tv_sec)) - { - tmvl.tv_sec = mhd_TIMEVAL_TV_SEC_MAX; - tmvl.tv_usec = 0; - } - else - tmvl.tv_usec = (int) (max_wait_millisec % 1000); - FD_ZERO (&rfds); - FD_SET (socket_fd, &rfds); - - sel_res = select (c->socket_fd + 1, - &rfds, - NULL, - NULL, - (MHD_WAIT_INDEFINITELY <= max_wait_millisec) ? - NULL : &tmvl); - - if ((0 >= sel_res) && - (0 != *received_size)) - return MHD_SC_OK; - if (0 == sel_res) - return MHD_SC_UPGRADED_NET_TIMEOUT; - - wait_err = mhd_SCKT_GET_LERR (); - if (! mhd_SCKT_ERR_IS_EAGAIN (wait_err) && - ! mhd_SCKT_ERR_IS_EINTR (wait_err) && - ! mhd_SCKT_ERR_IS_LOW_RESOURCES (wait_err)) - return MHD_SC_UPGRADED_NET_HARD_ERROR; - max_wait_millisec = 0; /* Re-try only one time */ - } -# else /* ! MHD_USE_SELECT */ - use_select = false; -# endif /* ! MHD_USE_SELECT */ -# if ! defined(MHD_WINSOCK_SOCKETS) || ! defined(MHD_USE_SELECT) - if (! use_select) - { -# ifndef mhd_HAVE_MHD_SLEEP - return MHD_SC_UPGRADED_WAITING_NOT_SUPPORTED; -# else /* mhd_HAVE_MHD_SLEEP */ - uint_fast32_t wait_millisec = max_wait_millisec; - - if (wait_millisec > 100) - wait_millisec = 100; - mhd_sleep (wait_millisec); - if (MHD_WAIT_INDEFINITELY > max_wait_millisec) - max_wait_millisec -= wait_millisec; -# endif /* mhd_HAVE_MHD_SLEEP */ - } -# endif /* ! MHD_WINSOCK_SOCKETS) || ! MHD_USE_SELECT */ -#endif /* ! MHD_USE_POLL */ - last_block_size = 0; - res = mhd_recv (c, - recv_buf_size - *received_size, - buf_char + *received_size, - &last_block_size); - if (mhd_SOCKET_ERR_NO_ERROR == res) - { - if (0 == last_block_size) - c->sk_rmt_shut_wr = true; - *received_size += last_block_size; - return MHD_SC_OK; - } - } - } - if (! mhd_SOCKET_ERR_IS_HARD (res)) - return MHD_SC_UPGRADED_NET_TIMEOUT; - if (mhd_SOCKET_ERR_REMT_DISCONN == res) - return MHD_SC_UPGRADED_NET_CONN_CLOSED; - if (mhd_SOCKET_ERR_TLS == res) - return MHD_SC_UPGRADED_TLS_ERROR; - if (! mhd_SOCKET_ERR_IS_BAD (res)) - return MHD_SC_UPGRADED_NET_CONN_BROKEN; - - return MHD_SC_UPGRADED_NET_HARD_ERROR; -} - - -MHD_EXTERN_ -MHD_FN_PAR_NONNULL_ALL_ -MHD_FN_PAR_IN_SIZE_ (3,2) -MHD_FN_PAR_OUT_ (4) enum MHD_StatusCode -MHD_upgraded_send (struct MHD_UpgradeHandle *MHD_RESTRICT urh, - size_t send_buf_size, - const void *MHD_RESTRICT send_buf, - size_t *MHD_RESTRICT sent_size, - uint_fast64_t max_wait_millisec, - enum MHD_Bool more_data_to_come) -{ - struct MHD_Connection *restrict c = urh->c; -#if defined(MHD_USE_POLL) || defined(MHD_USE_SELECT) - const MHD_Socket socket_fd = c->socket_fd; -#endif /* MHD_USE_POLL || MHD_USE_SELECT */ - const char *restrict buf_char = (const char *) send_buf; - const bool push_data = (MHD_NO == more_data_to_come); - bool finish_time_set; - bool wait_indefinitely; - uint_fast64_t finish_time = 0; - - *sent_size = 0; - - if (&(c->upgr) != urh) - return MHD_SC_UPGRADED_HANDLE_INVALID; - if (MHD_CONNECTION_UPGRADED != c->state) - return MHD_SC_UPGRADED_HANDLE_INVALID; - - finish_time_set = false; - wait_indefinitely = (MHD_WAIT_INDEFINITELY <= max_wait_millisec); - - while (*sent_size != send_buf_size) - { - enum mhd_SocketError res; - size_t last_block_size; - uint_fast64_t wait_left; -#if ! defined(MHD_USE_POLL) - bool use_select; -#endif /* ! MHD_USE_POLL */ - - last_block_size = 0; - res = mhd_send_data (c, - send_buf_size - *sent_size, - buf_char + *sent_size, - push_data, - &last_block_size); - if (mhd_SOCKET_ERR_NO_ERROR == res) - *sent_size += last_block_size; - else if (mhd_SOCKET_ERR_IS_HARD (res)) - { - if (0 != *sent_size) - return MHD_SC_OK; - - if (mhd_SOCKET_ERR_REMT_DISCONN == res) - return MHD_SC_UPGRADED_NET_CONN_CLOSED; - if (mhd_SOCKET_ERR_TLS == res) - return MHD_SC_UPGRADED_TLS_ERROR; - if (! mhd_SOCKET_ERR_IS_BAD (res)) - return MHD_SC_UPGRADED_NET_CONN_BROKEN; - - return MHD_SC_UPGRADED_NET_HARD_ERROR; - } - - if (0 == max_wait_millisec) - { - if (0 != *sent_size) - return MHD_SC_OK; - - return MHD_SC_UPGRADED_NET_TIMEOUT; - } - - if (! wait_indefinitely) - { - uint_fast64_t cur_time; - cur_time = MHD_monotonic_msec_counter (); - - if (! finish_time_set) - { - finish_time = cur_time + max_wait_millisec; - wait_left = max_wait_millisec; - } - else - { - wait_left = finish_time - cur_time; - if (wait_left > cur_time - finish_time) - return MHD_SC_UPGRADED_NET_TIMEOUT; - } - } - -#if defined(MHD_USE_POLL) - if (1) - { - struct pollfd fds[1]; - int poll_wait; - int poll_res; - int wait_err; - - if (wait_indefinitely) - poll_wait = -1; - else - { - poll_wait = (int) wait_left; - if ((wait_left != (uint_fast64_t) poll_wait) || - (0 > poll_wait)) - poll_wait = INT_MAX; - } - fds[0].fd = socket_fd; - fds[0].events = POLLOUT; - - poll_res = mhd_poll (fds, - 1, - poll_wait); - if (0 < poll_res) - continue; - if (0 == poll_res) - { - if (wait_indefinitely || - (INT_MAX == poll_wait)) - continue; - if (0 != *sent_size) - return MHD_SC_OK; - return MHD_SC_UPGRADED_NET_TIMEOUT; - } - - wait_err = mhd_SCKT_GET_LERR (); - if (! mhd_SCKT_ERR_IS_EAGAIN (wait_err) && - ! mhd_SCKT_ERR_IS_EINTR (wait_err) && - ! mhd_SCKT_ERR_IS_LOW_RESOURCES (wait_err)) - return MHD_SC_UPGRADED_NET_HARD_ERROR; - } -#else /* ! MHD_USE_POLL */ -# if defined(MHD_USE_SELECT) -# ifdef MHD_POSIX_SOCKETS - use_select = socket_fd < FD_SETSIZE; -# else /* MHD_WINSOCK_SOCKETS */ - use_select = true; -# endif /* MHD_WINSOCK_SOCKETS */ - if (use_select) - { - fd_set wfds; - int sel_res; - int wait_err; - struct timeval tmvl; - bool max_wait; - - max_wait = false; - if (wait_indefinitely) - { - tmvl.tv_sec = 0; - tmvl.tv_usec = 0; - } - else - { -# ifdef MHD_POSIX_SOCKETS - tmvl.tv_sec = (long) (max_wait_millisec / 1000); -# else /* MHD_WINSOCK_SOCKETS */ - tmvl.tv_sec = (long) (max_wait_millisec / 1000); -# endif /* MHD_WINSOCK_SOCKETS */ - if ((max_wait_millisec / 1000 != (uint_fast64_t) tmvl.tv_sec) || - (0 > tmvl.tv_sec)) - { - tmvl.tv_sec = mhd_TIMEVAL_TV_SEC_MAX; - tmvl.tv_usec = 0; - max_wait = true; - } - else - tmvl.tv_usec = (int) (max_wait_millisec % 1000); - } - FD_ZERO (&wfds); - FD_SET (socket_fd, &wfds); - - sel_res = select (c->socket_fd + 1, - NULL, - &wfds, - NULL, - wait_indefinitely ? NULL : &tmvl); - - if (0 < sel_res) - continue; - if (0 == sel_res) - { - if (wait_indefinitely || - max_wait) - continue; - if (0 != *sent_size) - return MHD_SC_OK; - return MHD_SC_UPGRADED_NET_TIMEOUT; - } - - wait_err = mhd_SCKT_GET_LERR (); - if (! mhd_SCKT_ERR_IS_EAGAIN (wait_err) && - ! mhd_SCKT_ERR_IS_EINTR (wait_err) && - ! mhd_SCKT_ERR_IS_LOW_RESOURCES (wait_err)) - return MHD_SC_UPGRADED_NET_HARD_ERROR; - } -# else /* ! MHD_USE_SELECT */ - use_select = false; -# endif /* ! MHD_USE_SELECT */ -# if ! defined(MHD_WINSOCK_SOCKETS) || ! defined(MHD_USE_SELECT) - if (! use_select) - { -# ifndef mhd_HAVE_MHD_SLEEP - return MHD_SC_UPGRADED_WAITING_NOT_SUPPORTED; -# else /* mhd_HAVE_MHD_SLEEP */ - uint_fast32_t wait_millisec = max_wait_millisec; - - if (wait_millisec > 100) - wait_millisec = 100; - mhd_sleep (wait_millisec); - if (MHD_WAIT_INDEFINITELY > max_wait_millisec) - max_wait_millisec -= wait_millisec; -# endif /* mhd_HAVE_MHD_SLEEP */ - } -# endif /* ! MHD_WINSOCK_SOCKETS) || ! MHD_USE_SELECT */ -#endif /* ! MHD_USE_POLL */ - } - - return MHD_SC_OK; -} diff --git a/src/mhd2/upgrade_proc.c b/src/mhd2/upgrade_proc.c @@ -73,7 +73,7 @@ mhd_upgrade_finish_switch_to_upgraded (struct MHD_Connection *restrict c) mhd_assert (MHD_CONNECTION_UPGRADING == c->state); mhd_assert (NULL != c->write_buffer); mhd_assert ((0 != c->read_buffer_offset) || (NULL == c->read_buffer)); - mhd_assert (c == c->upgr.c); + mhd_assert (NULL == c->upgr.c); pupgr_data = (mhd_ACTION_UPGRADE == c->rq.app_act.head_act.act) ? &(c->rq.app_act.head_act.data.upgrd) : @@ -116,7 +116,7 @@ mhd_upgrade_finish_switch_to_upgraded (struct MHD_Connection *restrict c) MHD_EXTERN_ MHD_FN_PAR_NONNULL_ (1) enum MHD_StatusCode -MHD_upgraded_close (struct MHD_UpgradeHandle *urh) +MHD_upgraded_close (struct MHD_UpgradedHandle *urh) { struct MHD_Connection *const restrict c = urh->c; struct MHD_Daemon *const restrict d = c->daemon; diff --git a/src/mhd2/upgraded_net.c b/src/mhd2/upgraded_net.c @@ -0,0 +1,537 @@ +/* + This file is part of GNU libmicrohttpd + Copyright (C) 2024 Evgeny Grin (Karlson2k) & Christian Grothoff + + GNU libmicrohttpd is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + GNU libmicrohttpd is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +/** + * @file src/mhd2/upgraded_net.c + * @brief The implementation of functions for network data exchange + * for HTTP Upgraded connections + * @author Karlson2k (Evgeny Grin) + * @author Christian Grothoff + */ + +#include "mhd_sys_options.h" + +#include "sys_bool_type.h" +#include "sys_base_types.h" + +#include "sys_poll.h" +#ifndef MHD_USE_POLL +# include "sys_select.h" +#endif +#include "mhd_limits.h" + +#include "mhd_sockets_macros.h" + +#include "mhd_upgrade.h" +#include "mhd_connection.h" +#include "mhd_locks.h" + +#include "mhd_recv.h" +#include "mhd_send.h" +#include "mhd_mono_clock.h" + +#include "mhd_public_api.h" + + +#if ! defined (MHD_USE_POLL) && \ + (defined(MHD_POSIX_SOCKETS) || ! defined(MHD_USE_SELECT)) +# if defined(_WIN32) || defined(HAVE_NANOSLEEP) || defined(HAVE_USLEEP) +# define mhd_HAVE_MHD_SLEEP 1 + +/** + * Pause execution for specified number of milliseconds. + * + * @param millisec the number of milliseconds to sleep + */ +static void +mhd_sleep (uint_fast32_t millisec) +{ +#if defined(_WIN32) + Sleep (millisec); +#elif defined(HAVE_NANOSLEEP) + struct timespec slp = {millisec / 1000, (millisec % 1000) * 1000000}; + struct timespec rmn; + int num_retries = 0; + while (0 != nanosleep (&slp, &rmn)) + { + if (EINTR != errno) + externalErrorExit (); + if (num_retries++ > 8) + break; + slp = rmn; + } +#elif defined(HAVE_USLEEP) + uint64_t us = millisec * 1000; + do + { + uint64_t this_sleep; + if (999999 < us) + this_sleep = 999999; + else + this_sleep = us; + /* Ignore return value as it could be void */ + usleep (this_sleep); + us -= this_sleep; + } while (us > 0); +#endif +} + + +#endif /* _WIN32 || HAVE_NANOSLEEP || HAVE_USLEEP */ +#endif /* ! MHD_USE_POLL) && (MHD_POSIX_SOCKETS || ! MHD_USE_SELECT) */ + + +MHD_EXTERN_ +MHD_FN_PAR_NONNULL_ALL_ +MHD_FN_PAR_OUT_SIZE_ (3,2) +MHD_FN_PAR_OUT_ (4) enum MHD_StatusCode +MHD_upgraded_recv (struct MHD_UpgradedHandle *MHD_RESTRICT urh, + size_t recv_buf_size, + void *MHD_RESTRICT recv_buf, + size_t *MHD_RESTRICT received_size, + uint_fast64_t max_wait_millisec) +{ + struct MHD_Connection *restrict c = urh->c; +#if defined(MHD_USE_POLL) || defined(MHD_USE_SELECT) + const MHD_Socket socket_fd = c->socket_fd; +#endif /* MHD_USE_POLL || MHD_USE_SELECT */ + char *restrict buf_char = (char *) recv_buf; + size_t last_block_size; + enum mhd_SocketError res; + + *received_size = 0; + + if (&(c->upgr) != urh) + return MHD_SC_UPGRADED_HANDLE_INVALID; + if (MHD_CONNECTION_UPGRADED != c->state) + return MHD_SC_UPGRADED_HANDLE_INVALID; + + if (0 == recv_buf_size) + return MHD_SC_OK; + + if (NULL != c->read_buffer) + { + mhd_mutex_lock_chk (&(urh->lock)); + if (0 != c->read_buffer_offset) /* Re-check under the lock */ + { + if (recv_buf_size < c->read_buffer_offset) + { + memcpy (buf_char, c->read_buffer, recv_buf_size); + last_block_size = recv_buf_size; + c->read_buffer += recv_buf_size; + c->read_buffer_offset -= recv_buf_size; + c->read_buffer_size -= recv_buf_size; + } + else + { + /* recv_buf_size >= c->read_buffer_offset */ + memcpy (buf_char, c->read_buffer, c->read_buffer_offset); + last_block_size = c->read_buffer_offset; + c->read_buffer_offset = 0; + c->read_buffer_size = 0; + /* Do not deallocate the read buffer to save the time under the lock. + The connection memory pool will not be used anyway. */ + c->read_buffer = NULL; + } + } + else + last_block_size = 0; + mhd_mutex_unlock_chk (&(urh->lock)); + *received_size = last_block_size; + if (recv_buf_size == last_block_size) + return MHD_SC_OK; + } + + last_block_size = 0; + res = mhd_recv (c, + recv_buf_size - *received_size, + buf_char + *received_size, + &last_block_size); + if (mhd_SOCKET_ERR_NO_ERROR == res) + { + if (0 == last_block_size) + c->sk_rmt_shut_wr = true; + *received_size += last_block_size; + return MHD_SC_OK; + } + else if (0 != *received_size) + return MHD_SC_OK; + + if (! mhd_SOCKET_ERR_IS_HARD (res)) + { + while (0 != max_wait_millisec) + { +#if defined(MHD_USE_POLL) + if (1) + { + struct pollfd fds[1]; + int poll_wait; + int poll_res; + int wait_err; + + if (MHD_WAIT_INDEFINITELY <= max_wait_millisec) + poll_wait = -1; + else + { + poll_wait = (int) max_wait_millisec; + if ((max_wait_millisec != (uint_fast64_t) poll_wait) || + (0 > poll_wait)) + poll_wait = INT_MAX; + } + fds[0].fd = socket_fd; + fds[0].events = POLLIN; + + poll_res = mhd_poll (fds, + 1, + poll_wait); + if ((0 >= poll_res) && + (0 != *received_size)) + return MHD_SC_OK; + else if (0 == poll_res) + return MHD_SC_UPGRADED_NET_TIMEOUT; + else if (0 > poll_res) + { + wait_err = mhd_SCKT_GET_LERR (); + if (! mhd_SCKT_ERR_IS_EAGAIN (wait_err) && + ! mhd_SCKT_ERR_IS_EINTR (wait_err) && + ! mhd_SCKT_ERR_IS_LOW_RESOURCES (wait_err)) + return MHD_SC_UPGRADED_NET_HARD_ERROR; + } + max_wait_millisec = 0; /* Re-try only one time */ + } +#else /* ! MHD_USE_POLL */ +# if defined(MHD_USE_SELECT) + bool use_select; +# ifdef MHD_POSIX_SOCKETS + use_select = (socket_fd < FD_SETSIZE); +# else /* MHD_WINSOCK_SOCKETS */ + use_select = true; +# endif /* MHD_WINSOCK_SOCKETS */ + if (use_select) + { + fd_set rfds; + int sel_res; + int wait_err; + struct timeval tmvl; + +# ifdef MHD_POSIX_SOCKETS + tmvl.tv_sec = (time_t) (max_wait_millisec / 1000); +# else /* MHD_WINSOCK_SOCKETS */ + tmvl.tv_sec = (long) (max_wait_millisec / 1000); +# endif /* MHD_WINSOCK_SOCKETS */ + if ((max_wait_millisec / 1000 != (uint_fast64_t) tmvl.tv_sec) || + (0 > tmvl.tv_sec)) + { + tmvl.tv_sec = mhd_TIMEVAL_TV_SEC_MAX; + tmvl.tv_usec = 0; + } + else + tmvl.tv_usec = (int) (max_wait_millisec % 1000); + FD_ZERO (&rfds); + FD_SET (socket_fd, &rfds); + + sel_res = select (c->socket_fd + 1, + &rfds, + NULL, + NULL, + (MHD_WAIT_INDEFINITELY <= max_wait_millisec) ? + NULL : &tmvl); + + if ((0 >= sel_res) && + (0 != *received_size)) + return MHD_SC_OK; + else if (0 == sel_res) + return MHD_SC_UPGRADED_NET_TIMEOUT; + else if (0 > sel_res) + { + wait_err = mhd_SCKT_GET_LERR (); + if (! mhd_SCKT_ERR_IS_EAGAIN (wait_err) && + ! mhd_SCKT_ERR_IS_EINTR (wait_err) && + ! mhd_SCKT_ERR_IS_LOW_RESOURCES (wait_err)) + return MHD_SC_UPGRADED_NET_HARD_ERROR; + } + max_wait_millisec = 0; /* Re-try only one time */ + } + else /* combined with the next 'if()' */ +# endif /* MHD_USE_SELECT */ + if (1) + { +# ifndef mhd_HAVE_MHD_SLEEP + return MHD_SC_UPGRADED_WAITING_NOT_SUPPORTED; +# else /* mhd_HAVE_MHD_SLEEP */ + uint_fast32_t wait_millisec = (uint_fast32_t) max_wait_millisec; + + if ((wait_millisec != max_wait_millisec) || + (wait_millisec > 100)) + wait_millisec = 100; + mhd_sleep (wait_millisec); + if (MHD_WAIT_INDEFINITELY > max_wait_millisec) + max_wait_millisec -= wait_millisec; +# endif /* mhd_HAVE_MHD_SLEEP */ + } +#endif /* ! MHD_USE_POLL */ + last_block_size = 0; + res = mhd_recv (c, + recv_buf_size - *received_size, + buf_char + *received_size, + &last_block_size); + if (mhd_SOCKET_ERR_NO_ERROR == res) + { + if (0 == last_block_size) + c->sk_rmt_shut_wr = true; + *received_size += last_block_size; + return MHD_SC_OK; + } + } + } + if (! mhd_SOCKET_ERR_IS_HARD (res)) + return MHD_SC_UPGRADED_NET_TIMEOUT; + if (mhd_SOCKET_ERR_REMT_DISCONN == res) + return MHD_SC_UPGRADED_NET_CONN_CLOSED; + if (mhd_SOCKET_ERR_TLS == res) + return MHD_SC_UPGRADED_TLS_ERROR; + if (! mhd_SOCKET_ERR_IS_BAD (res)) + return MHD_SC_UPGRADED_NET_CONN_BROKEN; + + return MHD_SC_UPGRADED_NET_HARD_ERROR; +} + + +MHD_EXTERN_ +MHD_FN_PAR_NONNULL_ALL_ +MHD_FN_PAR_IN_SIZE_ (3,2) +MHD_FN_PAR_OUT_ (4) enum MHD_StatusCode +MHD_upgraded_send (struct MHD_UpgradedHandle *MHD_RESTRICT urh, + size_t send_buf_size, + const void *MHD_RESTRICT send_buf, + size_t *MHD_RESTRICT sent_size, + uint_fast64_t max_wait_millisec, + enum MHD_Bool more_data_to_come) +{ + struct MHD_Connection *restrict c = urh->c; +#if defined(MHD_USE_POLL) || defined(MHD_USE_SELECT) + const MHD_Socket socket_fd = c->socket_fd; +#endif /* MHD_USE_POLL || MHD_USE_SELECT */ + const char *restrict buf_char = (const char *) send_buf; + const bool push_data = (MHD_NO == more_data_to_come); + bool finish_time_set; + bool wait_indefinitely; + uint_fast64_t finish_time = 0; + + *sent_size = 0; + + if (&(c->upgr) != urh) + return MHD_SC_UPGRADED_HANDLE_INVALID; + if (MHD_CONNECTION_UPGRADED != c->state) + return MHD_SC_UPGRADED_HANDLE_INVALID; + + finish_time_set = false; + wait_indefinitely = (MHD_WAIT_INDEFINITELY <= max_wait_millisec); + + while (1) + { + enum mhd_SocketError res; + size_t last_block_size; + uint_fast64_t wait_left; +#if ! defined(MHD_USE_POLL) && defined(MHD_USE_SELECT) + bool use_select; +#endif /* ! MHD_USE_POLL */ + + last_block_size = 0; + res = mhd_send_data (c, + send_buf_size - *sent_size, + buf_char + *sent_size, + push_data, + &last_block_size); + if (mhd_SOCKET_ERR_NO_ERROR == res) + { + *sent_size += last_block_size; + if (send_buf_size == *sent_size) + break; + } + else if (mhd_SOCKET_ERR_IS_HARD (res)) + { + if (0 != *sent_size) + break; + + if (mhd_SOCKET_ERR_REMT_DISCONN == res) + return MHD_SC_UPGRADED_NET_CONN_CLOSED; + if (mhd_SOCKET_ERR_TLS == res) + return MHD_SC_UPGRADED_TLS_ERROR; + if (! mhd_SOCKET_ERR_IS_BAD (res)) + return MHD_SC_UPGRADED_NET_CONN_BROKEN; + + return MHD_SC_UPGRADED_NET_HARD_ERROR; + } + + if (0 == max_wait_millisec) + { + mhd_assert (0 == *sent_size); + + return MHD_SC_UPGRADED_NET_TIMEOUT; + } + + if (! wait_indefinitely) + { + uint_fast64_t cur_time; + cur_time = MHD_monotonic_msec_counter (); + + if (! finish_time_set) + { + finish_time = cur_time + max_wait_millisec; + wait_left = max_wait_millisec; + } + else + { + wait_left = finish_time - cur_time; + if ((wait_left > cur_time - finish_time) || + (0 == wait_left)) + return MHD_SC_UPGRADED_NET_TIMEOUT; + } + } + +#if defined(MHD_USE_POLL) + if (1) + { + struct pollfd fds[1]; + int poll_wait; + int poll_res; + int wait_err; + + if (wait_indefinitely) + poll_wait = -1; + else + { + poll_wait = (int) wait_left; + if ((wait_left != (uint_fast64_t) poll_wait) || + (0 > poll_wait)) + poll_wait = INT_MAX; + } + fds[0].fd = socket_fd; + fds[0].events = POLLOUT; + + poll_res = mhd_poll (fds, + 1, + poll_wait); + if (0 < poll_res) + continue; + if (0 == poll_res) + { + if (wait_indefinitely || + (INT_MAX == poll_wait)) + continue; + if (0 != *sent_size) + return MHD_SC_OK; + return MHD_SC_UPGRADED_NET_TIMEOUT; + } + + mhd_assert (0 > poll_res); + wait_err = mhd_SCKT_GET_LERR (); + if (! mhd_SCKT_ERR_IS_EAGAIN (wait_err) && + ! mhd_SCKT_ERR_IS_EINTR (wait_err) && + ! mhd_SCKT_ERR_IS_LOW_RESOURCES (wait_err)) + return MHD_SC_UPGRADED_NET_HARD_ERROR; + } +#else /* ! MHD_USE_POLL */ +# if defined(MHD_USE_SELECT) +# ifdef MHD_POSIX_SOCKETS + use_select = (socket_fd < FD_SETSIZE); +# else /* MHD_WINSOCK_SOCKETS */ + use_select = true; +# endif /* MHD_WINSOCK_SOCKETS */ + if (use_select) + { + fd_set wfds; + int sel_res; + int wait_err; + struct timeval tmvl; + bool max_wait; + + max_wait = false; + if (wait_indefinitely) + { + tmvl.tv_sec = 0; + tmvl.tv_usec = 0; + } + else + { +# ifdef MHD_POSIX_SOCKETS + tmvl.tv_sec = (time_t) (wait_left / 1000); +# else /* MHD_WINSOCK_SOCKETS */ + tmvl.tv_sec = (long) (wait_left / 1000); +# endif /* MHD_WINSOCK_SOCKETS */ + if ((max_wait_millisec / 1000 != (uint_fast64_t) tmvl.tv_sec) || + (0 > tmvl.tv_sec)) + { + tmvl.tv_sec = mhd_TIMEVAL_TV_SEC_MAX; + tmvl.tv_usec = 0; + max_wait = true; + } + else + tmvl.tv_usec = (int) (max_wait_millisec % 1000); + } + FD_ZERO (&wfds); + FD_SET (socket_fd, &wfds); + + sel_res = select (c->socket_fd + 1, + NULL, + &wfds, + NULL, + wait_indefinitely ? NULL : &tmvl); + + if (0 < sel_res) + continue; + if (0 == sel_res) + { + if (wait_indefinitely || + max_wait) + continue; + if (0 != *sent_size) + return MHD_SC_OK; + return MHD_SC_UPGRADED_NET_TIMEOUT; + } + + mhd_assert (0 > sel_res); + wait_err = mhd_SCKT_GET_LERR (); + if (! mhd_SCKT_ERR_IS_EAGAIN (wait_err) && + ! mhd_SCKT_ERR_IS_EINTR (wait_err) && + ! mhd_SCKT_ERR_IS_LOW_RESOURCES (wait_err)) + return MHD_SC_UPGRADED_NET_HARD_ERROR; + } + else /* combined with the next 'if()' */ +# endif /* MHD_USE_SELECT */ + if (1) + { +# ifndef mhd_HAVE_MHD_SLEEP + return MHD_SC_UPGRADED_WAITING_NOT_SUPPORTED; +# else /* mhd_HAVE_MHD_SLEEP */ + uint_fast32_t wait_millisec = (uint_fast32_t) wait_left; + + if ((wait_millisec != wait_left) || + (wait_millisec > 100)) + wait_millisec = 100; + mhd_sleep (wait_millisec); +# endif /* mhd_HAVE_MHD_SLEEP */ + } +#endif /* ! MHD_USE_POLL */ + } + + return MHD_SC_OK; +}