mirror of
https://github.com/espressif/esp-idf.git
synced 2026-04-27 19:13:21 +00:00
refactor(tcp_transport): move connection-closed socket polling from WS to base transport layer
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2020-2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2020-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -136,6 +136,27 @@ void esp_transport_capture_errno(esp_transport_handle_t t, int sock_errno);
|
||||
*/
|
||||
void esp_transport_set_errors(esp_transport_handle_t t, const esp_tls_error_handle_t error_handle);
|
||||
|
||||
/**
|
||||
* @brief Polls the underlying socket until the connection is closed or the timeout expires.
|
||||
*
|
||||
* This is an internal helper used by higher-level transports (e.g. WebSocket) that need to wait
|
||||
* for a clean TCP connection teardown after completing their own application-level close handshake.
|
||||
* It selects on the read and error sets of the socket and distinguishes between:
|
||||
* - Clean closure by FIN (recv returns 0 on a readable socket)
|
||||
* - RST-based closure (ENOTCONN / ECONNRESET / ECONNABORTED on the error set)
|
||||
* - Unexpected conditions (socket readable with actual data, or unrecognised errno)
|
||||
*
|
||||
* @param[in] t A base transport handle (TCP or SSL) whose socket is to be monitored.
|
||||
* Must NOT be a WS transport handle; pass ws->parent from the WS layer.
|
||||
* @param[in] timeout_ms Timeout in milliseconds (-1 to wait forever).
|
||||
*
|
||||
* @return
|
||||
* - 1 Connection terminated: either clean FIN or expected RST errno
|
||||
* - 0 Timeout: no activity within timeout_ms
|
||||
* - -1 Error: socket unexpectedly readable with data, unrecognised errno, or invalid handle
|
||||
*/
|
||||
int esp_transport_poll_connection_closed(esp_transport_handle_t t, int timeout_ms);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2015-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -519,6 +519,46 @@ static int base_get_socket(esp_transport_handle_t t)
|
||||
return INVALID_SOCKET;
|
||||
}
|
||||
|
||||
int esp_transport_poll_connection_closed(esp_transport_handle_t t, int timeout_ms)
|
||||
{
|
||||
int sock = base_get_socket(t);
|
||||
if (sock < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct timeval timeout;
|
||||
fd_set readset;
|
||||
fd_set errset;
|
||||
FD_ZERO(&readset);
|
||||
FD_ZERO(&errset);
|
||||
FD_SET(sock, &readset);
|
||||
FD_SET(sock, &errset);
|
||||
|
||||
int ret = select(sock + 1, &readset, NULL, &errset, esp_transport_utils_ms_to_timeval(timeout_ms, &timeout));
|
||||
if (ret > 0) {
|
||||
if (FD_ISSET(sock, &readset)) {
|
||||
uint8_t buffer;
|
||||
if (recv(sock, &buffer, 1, MSG_PEEK) <= 0) {
|
||||
// Socket readable but zero bytes available: clean closure by FIN
|
||||
return 1;
|
||||
}
|
||||
ESP_LOGW(TAG, "poll_connection_closed: unexpected data readable on socket=%d", sock);
|
||||
} else if (FD_ISSET(sock, &errset)) {
|
||||
int sock_errno = 0;
|
||||
uint32_t optlen = sizeof(sock_errno);
|
||||
getsockopt(sock, SOL_SOCKET, SO_ERROR, &sock_errno, &optlen);
|
||||
ESP_LOGD(TAG, "poll_connection_closed select error %d, errno = %s, fd = %d", sock_errno, strerror(sock_errno), sock);
|
||||
if (sock_errno == ENOTCONN || sock_errno == ECONNRESET || sock_errno == ECONNABORTED) {
|
||||
// RST-based closure: treat as expected connection termination
|
||||
return 1;
|
||||
}
|
||||
ESP_LOGE(TAG, "poll_connection_closed: unexpected errno=%d on socket=%d", sock_errno, sock);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
return ret; // 0 on timeout, -1 on select error
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ESP_TLS_USE_DS_PERIPHERAL
|
||||
void esp_transport_ssl_set_ds_data(esp_transport_handle_t t, void *ds_data)
|
||||
{
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
#include <unistd.h>
|
||||
#include <ctype.h>
|
||||
#include <sys/random.h>
|
||||
#include <sys/socket.h>
|
||||
#include <arpa/inet.h>
|
||||
#include "esp_log.h"
|
||||
#include "esp_transport.h"
|
||||
@@ -18,7 +17,6 @@
|
||||
#include "esp_transport_internal.h"
|
||||
#include "errno.h"
|
||||
#include "esp_tls_crypto.h"
|
||||
#include <arpa/inet.h>
|
||||
|
||||
static const char *TAG = "transport_ws";
|
||||
|
||||
@@ -815,9 +813,9 @@ void esp_transport_ws_set_path(esp_transport_handle_t t, const char *path)
|
||||
static int ws_get_socket(esp_transport_handle_t t)
|
||||
{
|
||||
if (t) {
|
||||
transport_ws_t *ws = t->data;
|
||||
if (ws && ws->parent && ws->parent->_get_socket) {
|
||||
return ws->parent->_get_socket(ws->parent);
|
||||
transport_ws_t *ws = esp_transport_get_context_data(t);
|
||||
if (ws) {
|
||||
return esp_transport_get_socket(ws->parent);
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
@@ -1121,7 +1119,7 @@ static int esp_transport_ws_handle_control_frames(esp_transport_handle_t t, char
|
||||
|
||||
// control frame handled correctly, reset the flag indicating new header received
|
||||
ws->frame_state.header_received = false;
|
||||
int ret = esp_transport_ws_poll_connection_closed(t, timeout_ms);
|
||||
int ret = esp_transport_poll_connection_closed(ws->parent, timeout_ms);
|
||||
if (ret == 0) {
|
||||
ESP_LOGW(TAG, "Connection cannot be terminated gracefully within timeout=%d", timeout_ms);
|
||||
return -1;
|
||||
@@ -1144,38 +1142,6 @@ static int esp_transport_ws_handle_control_frames(esp_transport_handle_t t, char
|
||||
|
||||
int esp_transport_ws_poll_connection_closed(esp_transport_handle_t t, int timeout_ms)
|
||||
{
|
||||
struct timeval timeout;
|
||||
int sock = esp_transport_get_socket(t);
|
||||
fd_set readset;
|
||||
fd_set errset;
|
||||
FD_ZERO(&readset);
|
||||
FD_ZERO(&errset);
|
||||
FD_SET(sock, &readset);
|
||||
FD_SET(sock, &errset);
|
||||
|
||||
int ret = select(sock + 1, &readset, NULL, &errset, esp_transport_utils_ms_to_timeval(timeout_ms, &timeout));
|
||||
if (ret > 0) {
|
||||
if (FD_ISSET(sock, &readset)) {
|
||||
uint8_t buffer;
|
||||
if (recv(sock, &buffer, 1, MSG_PEEK) <= 0) {
|
||||
// socket is readable, but reads zero bytes -- connection cleanly closed by FIN flag
|
||||
return 1;
|
||||
}
|
||||
ESP_LOGW(TAG, "esp_transport_ws_poll_connection_closed: unexpected data readable on socket=%d", sock);
|
||||
} else if (FD_ISSET(sock, &errset)) {
|
||||
int sock_errno = 0;
|
||||
uint32_t optlen = sizeof(sock_errno);
|
||||
getsockopt(sock, SOL_SOCKET, SO_ERROR, &sock_errno, &optlen);
|
||||
ESP_LOGD(TAG, "esp_transport_ws_poll_connection_closed select error %d, errno = %s, fd = %d", sock_errno, strerror(sock_errno), sock);
|
||||
if (sock_errno == ENOTCONN || sock_errno == ECONNRESET || sock_errno == ECONNABORTED) {
|
||||
// the three err codes above might be caused by connection termination by RTS flag
|
||||
// which we still assume as expected closing sequence of ws-transport connection
|
||||
return 1;
|
||||
}
|
||||
ESP_LOGE(TAG, "esp_transport_ws_poll_connection_closed: unexpected errno=%d on socket=%d", sock_errno, sock);
|
||||
}
|
||||
return -1; // indicates error: socket unexpectedly reads an actual data, or unexpected errno code
|
||||
}
|
||||
return ret;
|
||||
|
||||
transport_ws_t *ws = esp_transport_get_context_data(t);
|
||||
return esp_transport_poll_connection_closed(ws->parent, timeout_ms);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user