httpd.c: reworked to eliminate select error; "not a socket"

This commit is contained in:
F. Duncanh
2026-03-23 18:24:47 -04:00
parent f9cae992c1
commit 5de48c3d7b

View File

@@ -12,7 +12,7 @@
* Lesser General Public License for more details. * Lesser General Public License for more details.
* *
*=================================================================== *===================================================================
* modified by fduncanh 2022 * modified by fduncanh 2022-2026
*/ */
#include <stdlib.h> #include <stdlib.h>
@@ -43,6 +43,7 @@ struct http_connection_s {
void *user_data; void *user_data;
connection_type_t type; connection_type_t type;
http_request_t *request; http_request_t *request;
int pending_remove;
}; };
typedef struct http_connection_s http_connection_t; typedef struct http_connection_s http_connection_t;
@@ -171,6 +172,14 @@ httpd_init(logger_t *logger, httpd_callbacks_t *callbacks, int nohold)
free(httpd); free(httpd);
return NULL; return NULL;
} }
for (int i = 0; i < httpd->max_connections; i++) {
httpd->connections[i].connected = 0;
httpd->connections[i].socket_fd = -1;
httpd->connections[i].user_data = NULL;
httpd->connections[i].type = CONNECTION_TYPE_UNKNOWN;
httpd->connections[i].request = NULL;
httpd->connections[i].pending_remove = 0;
}
/* Use the logger provided */ /* Use the logger provided */
httpd->logger = logger; httpd->logger = logger;
@@ -199,11 +208,14 @@ static void
httpd_remove_connection(httpd_t *httpd, http_connection_t *connection, int sock_err) httpd_remove_connection(httpd_t *httpd, http_connection_t *connection, int sock_err)
{ {
int socket_fd = connection->socket_fd; int socket_fd = connection->socket_fd;
connection->socket_fd = 0;
if (sock_err) { if (sock_err) {
logger_log(httpd->logger, LOGGER_ERR, "httpd: recv error %d on socket %d: %s", logger_log(httpd->logger, LOGGER_ERR, "httpd: recv error %d on socket %d: %s",
sock_err, socket_fd, SOCKET_ERROR_STRING(sock_err)); sock_err, socket_fd, SOCKET_ERROR_STRING(sock_err));
} }
if (connection->pending_remove) {
logger_log(httpd->logger, LOGGER_DEBUG, "connection removal was requested");
connection->pending_remove = 0;
}
if (connection->request) { if (connection->request) {
http_request_destroy(connection->request); http_request_destroy(connection->request);
connection->request = NULL; connection->request = NULL;
@@ -214,11 +226,12 @@ httpd_remove_connection(httpd_t *httpd, http_connection_t *connection, int sock_
httpd->callbacks.conn_destroy(connection->user_data); httpd->callbacks.conn_destroy(connection->user_data);
connection->user_data = NULL; connection->user_data = NULL;
} }
if (socket_fd) { if (socket_fd != -1) {
if (!sock_err) { if (!sock_err) {
shutdown(socket_fd, SHUT_WR); shutdown(socket_fd, SHUT_WR);
} }
int ret = CLOSESOCKET(socket_fd); int ret = CLOSESOCKET(socket_fd);
connection->socket_fd = -1;
if (ret == -1) { if (ret == -1) {
logger_log(httpd->logger, LOGGER_ERR, "httpd error in CLOSESOCKET: %d %s", errno, SOCKET_ERROR_STRING(errno)); logger_log(httpd->logger, LOGGER_ERR, "httpd error in CLOSESOCKET: %d %s", errno, SOCKET_ERROR_STRING(errno));
} else { } else {
@@ -259,6 +272,7 @@ httpd_add_connection(httpd_t *httpd, int fd, unsigned char *local, int local_len
httpd->connections[i].socket_fd = fd; httpd->connections[i].socket_fd = fd;
httpd->connections[i].connected = 1; httpd->connections[i].connected = 1;
httpd->connections[i].user_data = user_data; httpd->connections[i].user_data = user_data;
httpd->connections[i].pending_remove = 0;
httpd->connections[i].type = CONNECTION_TYPE_UNKNOWN; //should not be necessary ... httpd->connections[i].type = CONNECTION_TYPE_UNKNOWN; //should not be necessary ...
return 0; return 0;
} }
@@ -327,7 +341,7 @@ httpd_remove_known_connections(httpd_t *httpd) {
if (!connection->connected || connection->type == CONNECTION_TYPE_UNKNOWN) { if (!connection->connected || connection->type == CONNECTION_TYPE_UNKNOWN) {
continue; continue;
} }
httpd_remove_connection(httpd, connection, 0); connection->pending_remove = 1;
} }
} }
@@ -338,7 +352,7 @@ httpd_remove_connections_by_type(httpd_t *httpd, connection_type_t type) {
if (!connection->connected || connection->type != type) { if (!connection->connected || connection->type != type) {
continue; continue;
} }
httpd_remove_connection(httpd, connection, 0); connection->pending_remove = 1;
} }
} }
@@ -356,10 +370,8 @@ httpd_thread(void *arg)
while (1) { while (1) {
fd_set rfds; fd_set rfds;
struct timeval tv; struct timeval tv;
int nfds=0; int nfds = 0;
int ret = 0; int nfds_set = 0;
int new_request = 0;
int recv_datalen = 0;
MUTEX_LOCK(httpd->run_mutex); MUTEX_LOCK(httpd->run_mutex);
if (!httpd->running) { if (!httpd->running) {
@@ -393,6 +405,10 @@ httpd_thread(void *arg)
if (!httpd->connections[i].connected) { if (!httpd->connections[i].connected) {
continue; continue;
} }
if (httpd->connections[i].pending_remove) {
httpd_remove_connection(httpd, &httpd->connections[i], 0);
continue;
}
socket_fd = httpd->connections[i].socket_fd; socket_fd = httpd->connections[i].socket_fd;
FD_SET(socket_fd, &rfds); FD_SET(socket_fd, &rfds);
if (nfds <= socket_fd) { if (nfds <= socket_fd) {
@@ -400,11 +416,11 @@ httpd_thread(void *arg)
} }
} }
ret = select(nfds, &rfds, NULL, NULL, &tv); nfds_set = select(nfds, &rfds, NULL, NULL, &tv);
if (ret == 0) { if (nfds_set == 0) {
/* Timeout happened */ /* Timeout happened */
continue; continue;
} else if (ret == -1) { } else if (nfds_set == -1) {
int sock_err = SOCKET_GET_ERROR(); int sock_err = SOCKET_GET_ERROR();
logger_log(httpd->logger, LOGGER_ERR, logger_log(httpd->logger, LOGGER_ERR,
"httpd error in select: %d %s", sock_err, SOCKET_ERROR_STRING(sock_err)); "httpd error in select: %d %s", sock_err, SOCKET_ERROR_STRING(sock_err));
@@ -432,6 +448,8 @@ httpd_thread(void *arg)
} }
} }
for (int i = 0; i < httpd->max_connections; i++) { for (int i = 0; i < httpd->max_connections; i++) {
int recv_datalen = 0;
int new_request = 0;
http_connection_t *connection = &httpd->connections[i]; http_connection_t *connection = &httpd->connections[i];
if (!connection->connected) { if (!connection->connected) {
@@ -481,15 +499,12 @@ httpd_thread(void *arg)
int readstart = 0; int readstart = 0;
new_request = 0; new_request = 0;
while (readstart < 8) { while (readstart < 8) {
if (!connection->socket_fd) {
break;
}
int ret = recv(connection->socket_fd, buffer + readstart, sizeof(buffer) - readstart, 0); int ret = recv(connection->socket_fd, buffer + readstart, sizeof(buffer) - readstart, 0);
if (ret == 0) { if (ret == 0) {
logger_log(httpd->logger, LOGGER_DEBUG, "client closed connection on socket %d", logger_log(httpd->logger, LOGGER_DEBUG, "client closed connection on socket %d",
connection->socket_fd); connection->socket_fd);
httpd_remove_connection(httpd, connection, 0); httpd_remove_connection(httpd, connection, 0);
continue; break;
} else if (ret == -1) { } else if (ret == -1) {
if (errno == SOCKET_ERRORNAME(EAGAIN) || errno == SOCKET_ERRORNAME(EWOULDBLOCK) || errno == SOCKET_ERRORNAME(EINTR)) { if (errno == SOCKET_ERRORNAME(EAGAIN) || errno == SOCKET_ERRORNAME(EWOULDBLOCK) || errno == SOCKET_ERRORNAME(EINTR)) {
continue; continue;
@@ -502,15 +517,14 @@ httpd_thread(void *arg)
recv_datalen = readstart; recv_datalen = readstart;
} }
} }
if (!connection->socket_fd) { if (connection->socket_fd == -1) {
/* connection was recently removed */ /* connection was removed */
continue; continue;
} }
if (!memcmp(buffer, http, 8) || !memcmp(buffer, event, 8)) { if (!memcmp(buffer, http, 8) || !memcmp(buffer, event, 8)) {
http_request_set_reverse(connection->request); http_request_set_reverse(connection->request);
} }
} else { } else {
if (connection->socket_fd) {
int ret = recv(connection->socket_fd, buffer, sizeof(buffer), 0); int ret = recv(connection->socket_fd, buffer, sizeof(buffer), 0);
if (ret == 0) { if (ret == 0) {
httpd_remove_connection(httpd, connection, 0); httpd_remove_connection(httpd, connection, 0);
@@ -520,15 +534,11 @@ httpd_thread(void *arg)
continue; continue;
} else { } else {
httpd_remove_connection(httpd, connection, SOCKET_GET_ERROR()); httpd_remove_connection(httpd, connection, SOCKET_GET_ERROR());
break; continue;
} }
} else { } else {
recv_datalen = ret; recv_datalen = ret;
} }
} else {
/* connection was recently removed */
continue;
}
} }
if (http_request_is_reverse(connection->request)) { if (http_request_is_reverse(connection->request)) {
/* this is a response from the client to a /* this is a response from the client to a