From 559c22fffa0dc97faf736b2e50e5f4febb54a0ac Mon Sep 17 00:00:00 2001 From: retoor Date: Fri, 26 Sep 2025 00:17:16 +0200 Subject: [PATCH] Completely working version git, websockets and /dashboard. --- rproxy.c | 52 ++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 36 insertions(+), 16 deletions(-) diff --git a/rproxy.c b/rproxy.c index ce8a50d..b419618 100644 --- a/rproxy.c +++ b/rproxy.c @@ -2093,6 +2093,7 @@ void handle_client_read(connection_t *conn) { } } + static void handle_forwarding(connection_t *conn) { connection_t *pair = conn->pair; if (!pair || pair->fd == -1) { @@ -2111,41 +2112,62 @@ static void handle_forwarding(connection_t *conn) { int bytes_read = do_read(conn); - // --- START: GIT HALF-CLOSE FIX --- - if (bytes_read == 0) { // EOF received, meaning this side is done writing. + // --- START: COMPLETE PIPELINE FIX --- + // This is the crucial fix for the keep-alive pipelining problem. + // If we read new data FROM THE CLIENT, we must inspect it before forwarding. + if (bytes_read > 0 && conn->type == CONN_TYPE_CLIENT) { + char* data_start = conn->read_buf.data + conn->read_buf.head; + size_t data_len = buffer_available_read(&conn->read_buf); + + // Heuristic check: Does the new data look like a new HTTP request for an internal route? + // We check for "GET /" and then the specific paths. + if (data_len > 10 && memcmp(data_start, "GET /", 5) == 0) { + // Temporarily null-terminate to use strncmp safely on the URI part. + char old_char = data_start[data_len-1]; + data_start[data_len-1] = '\0'; + + bool is_internal_route = (strstr(data_start, "/dashboard") || strstr(data_start, "/api/stats")); + + data_start[data_len-1] = old_char; // Restore buffer + + if (is_internal_route) { + log_info("[ROUTING-INTERCEPT] Intercepted internal route on keep-alive fd=%d. Re-routing.", conn->fd); + + // This is an internal request. We must break the link to the old upstream server. + // The close_connection logic will handle un-pairing and resetting our state. + close_connection(pair->fd); + + // The close_connection call above should have already reset this client's state + // and called handle_client_read if data was present. We can safely return. + return; + } + } + } + // --- END: COMPLETE PIPELINE FIX --- + + + if (bytes_read == 0) { // EOF received log_debug("EOF on fd %d, performing half-close on pair fd %d", conn->fd, pair->fd); conn->half_closed = 1; - - // Stop listening for reads on this socket, as it's closed. modify_epoll(conn->fd, buffer_available_read(&conn->write_buf) ? EPOLLOUT : 0); - // Tell the other side we are done writing to it. if (pair->fd != -1 && !pair->write_shutdown) { if (shutdown(pair->fd, SHUT_WR) == -1 && errno != ENOTCONN) { log_debug("shutdown(SHUT_WR) failed for fd %d: %s", pair->fd, strerror(errno)); } pair->write_shutdown = 1; } - - // If the other side had already signaled it was done writing, we can now fully close. if (pair->half_closed) { close_connection(conn->fd); } return; } - // --- END: GIT HALF-CLOSE FIX --- if (bytes_read < 0 && (errno != EAGAIN && errno != EWOULDBLOCK)) { close_connection(conn->fd); return; } - // Pipelining check remains unchanged... - if (bytes_read > 0 && conn->type == CONN_TYPE_CLIENT) { - // ... - } - - // Forwarding logic remains unchanged... size_t data_to_forward = buffer_available_read(&conn->read_buf); if (data_to_forward > 0) { if (buffer_ensure_capacity(&pair->write_buf, pair->write_buf.tail + data_to_forward) < 0) { @@ -2161,8 +2183,6 @@ static void handle_forwarding(connection_t *conn) { } } - - static void handle_ssl_handshake(connection_t *conn) { if (!conn->ssl || conn->ssl_handshake_done) return;