From 43fcdc4e264c60cefdd6ea7af335b9b39a9fc127 Mon Sep 17 00:00:00 2001 From: retoor Date: Thu, 25 Sep 2025 19:31:04 +0200 Subject: [PATCH] Update. --- rproxy.c | 90 +++++++++++++++++++++++++------------------------------- 1 file changed, 40 insertions(+), 50 deletions(-) diff --git a/rproxy.c b/rproxy.c index d876c42..c0b224c 100644 --- a/rproxy.c +++ b/rproxy.c @@ -1706,12 +1706,34 @@ void close_connection(int fd) { // Prevent double-closing if (conn->fd == -1) return; - int pair_fd = -1; - if (conn->pair && conn->pair->fd != -1) { - pair_fd = conn->pair->fd; - conn->pair->pair = NULL; + connection_t *pair = conn->pair; + + // --- START: ROBUST STATE & PAIR MANAGEMENT FIX --- + + // Decouple the pair immediately to prevent circular logic. + if (pair) { + pair->pair = NULL; } + // If the connection being closed is a CLIENT, we must also close its upstream pair. + if (conn->type == CONN_TYPE_CLIENT && pair) { + close_connection(pair->fd); + } + + // If the connection being closed is an UPSTREAM, its client pair must be + // reset to handle the next keep-alive request. This is the core of the fix. + if (conn->type == CONN_TYPE_UPSTREAM && pair && pair->type == CONN_TYPE_CLIENT) { + log_info("[STATE-FIX] Upstream fd=%d closed. Resetting client fd=%d for next request.", fd, pair->fd); + pair->state = CLIENT_STATE_READING_HEADERS; + + // A new request might have been read into the buffer due to the race condition. + // We must attempt to process it now to serve it correctly. + if (buffer_available_read(&pair->read_buf) > 0) { + handle_client_read(pair); + } + } + // --- END: ROBUST STATE & PAIR MANAGEMENT FIX --- + // Record request end if needed if (conn->vhost_stats && conn->request_start_time > 0) { monitor_record_request_end(conn->vhost_stats, conn->request_start_time); @@ -1726,7 +1748,7 @@ void close_connection(int fd) { } } - log_debug("Closing connection on fd %d, pair %d, remaining: %d", fd, pair_fd, monitor.active_connections); + log_debug("Closing connection on fd %d, remaining: %d", fd, monitor.active_connections); // Remove from epoll before closing epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fd, NULL); @@ -1749,13 +1771,11 @@ void close_connection(int fd) { memset(conn, 0, sizeof(connection_t)); conn->type = CONN_TYPE_UNUSED; conn->fd = -1; - - // Close the paired connection - if (pair_fd != -1) { - close_connection(pair_fd); - } } + + + void connect_to_upstream(connection_t *client, const char *data, size_t data_len) { if (!client || !data) return; @@ -2110,15 +2130,16 @@ static void handle_forwarding(connection_t *conn) { return; } - // 1. Read data from the source connection (either client or upstream). + // 1. Read data from the source connection. int bytes_read = do_read(conn); - // 2. Forward any data we just read to the paired connection's write buffer. + // 2. Forward any data we just read to the paired connection. size_t data_to_forward = buffer_available_read(&conn->read_buf); if (data_to_forward > 0) { + // --- SYNTAX FIX IS HERE --- if (buffer_ensure_capacity(&pair->write_buf, pair->write_buf.tail + data_to_forward) < 0) { log_error("Failed to grow write buffer for forwarding"); - close_connection(conn->fd); // Close the pair of connections on memory error. + close_connection(conn->fd); return; } @@ -2126,52 +2147,21 @@ static void handle_forwarding(connection_t *conn) { conn->read_buf.data + conn->read_buf.head, data_to_forward); pair->write_buf.tail += data_to_forward; + // --- END OF SYNTAX FIX --- buffer_consume(&conn->read_buf, data_to_forward); - // Make sure the event loop knows the pair is ready to be written to. modify_epoll(pair->fd, EPOLLIN | EPOLLOUT); } - // 3. Handle connection state changes (EOF or errors). + // 3. On any read error or EOF, close the connection. + // The new logic in `close_connection` will correctly handle the state of the pair. if (bytes_read <= 0 && (errno != EAGAIN && errno != EWOULDBLOCK)) { - if (bytes_read < 0) { // A real read error occurred. - log_debug("Read error on fd=%d while forwarding. Closing connection.", conn->fd); - close_connection(conn->fd); // This will recursively close the pair. - return; - } - - // --- ROBUST FIX FOR KEEP-ALIVE RACE CONDITION --- - // At this point, bytes_read is 0, meaning one side has closed its connection (EOF). - - log_info("[ROUTING-EOF] EOF on fd=%d (type=%s)", conn->fd, - (conn->type == CONN_TYPE_CLIENT) ? "CLIENT" : "UPSTREAM"); - - if (conn->type == CONN_TYPE_UPSTREAM) { - // **This is the critical fix.** The upstream server finished its response. - // The client connection must be immediately reset to handle the next keep-alive request. - if (pair->type == CONN_TYPE_CLIENT) { - log_info("[ROUTING-KEEPALIVE] Upstream fd=%d finished. Resetting client fd=%d.", conn->fd, pair->fd); - - // Reset client state to stop forwarding and prepare for new headers. - pair->state = CLIENT_STATE_READING_HEADERS; - pair->pair = NULL; // Decouple from this now-finished upstream connection. - - // Check for pipelined requests that may have already arrived. - if (buffer_available_read(&pair->read_buf) > 0) { - handle_client_read(pair); - } - } - // The upstream connection's job is done. Close it, leaving the client open. - close_connection(conn->fd); - - } else if (conn->type == CONN_TYPE_CLIENT) { - // The client has closed its side of the connection. - // In this case, we perform a full teardown of both connections. - close_connection(conn->fd); - } + close_connection(conn->fd); } } + + static void handle_ssl_handshake(connection_t *conn) { if (!conn->ssl || conn->ssl_handshake_done) return;