feat: process buffered client data after upstream connection closes to prevent data loss

Add logic in connection_close to detect remaining buffered data on client read buffer when an upstream connection is closed, and invoke handle_client_read to process it. Also add a forward declaration for handle_client_read and include debug logging for routing decisions. A new test validates that pipelined requests buffered before upstream close are correctly handled.
This commit is contained in:
2026-01-27 15:19:36 +00:00
parent f75581963a
commit 69ab2f4fd9
3 changed files with 110 additions and 1 deletions
+20 -1
View File
@@ -36,6 +36,8 @@ connection_t connections[MAX_FDS];
int epoll_fd = -1;
time_t cached_time = 0;
static void handle_client_read(connection_t *conn);
void connection_update_cached_time(void) {
cached_time = time(NULL);
}
@@ -246,6 +248,13 @@ void connection_close(int fd) {
pair->splice_pipe[0] = -1;
pair->splice_pipe[1] = -1;
pair->can_splice = 0;
memset(&pair->request, 0, sizeof(http_request_t));
if (buffer_available_read(&pair->read_buf) > 0) {
log_debug("Processing buffered data on client fd %d after upstream close", pair->fd);
handle_client_read(pair);
}
} else if (conn->type == CONN_TYPE_CLIENT && pair->type == CONN_TYPE_UPSTREAM) {
log_debug("Client fd %d is closing. Closing orphaned upstream pair fd %d.", fd, pair->fd);
pair->pair = NULL;
@@ -776,7 +785,13 @@ static void handle_client_read(connection_t *conn) {
clock_gettime(CLOCK_MONOTONIC, &ts);
conn->request_start_time = ts.tv_sec + ts.tv_nsec / 1e9;
if (http_uri_is_internal_route(conn->request.uri)) {
log_debug("[ROUTING-DEBUG] fd=%d state=%d method=%s uri=%s host=%s",
conn->fd, conn->state, conn->request.method, conn->request.uri, conn->request.host);
int is_internal = http_uri_is_internal_route(conn->request.uri);
log_debug("[ROUTING-DEBUG] fd=%d is_internal=%d for uri=%s", conn->fd, is_internal, conn->request.uri);
if (is_internal) {
char normalized_uri[2048];
http_normalize_uri_path(conn->request.uri, normalized_uri, sizeof(normalized_uri));
@@ -805,6 +820,9 @@ static void handle_client_read(connection_t *conn) {
conn->state = CLIENT_STATE_CLOSING;
}
return;
} else {
log_debug("[ROUTING-DEBUG] fd=%d NOT internal route, will forward to vhost. uri=%s host=%s",
conn->fd, conn->request.uri, conn->request.host);
}
route_config_t *route = config_find_route(conn->request.host);
@@ -899,6 +917,7 @@ static void handle_forwarding(connection_t *conn) {
log_info("ROUTING-ORPHAN: Client fd=%d lost upstream, resetting to READING_HEADERS", conn->fd);
conn->state = CLIENT_STATE_READING_HEADERS;
conn->pair = NULL;
memset(&conn->request, 0, sizeof(http_request_t));
if (buffer_available_read(&conn->read_buf) > 0) {
handle_client_read(conn);
}