Update.
This commit is contained in:
parent
43fcdc4e26
commit
a9a6abb531
91
rproxy.c
91
rproxy.c
@ -1697,6 +1697,7 @@ void accept_new_connection(int listener_fd) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void close_connection(int fd) {
|
void close_connection(int fd) {
|
||||||
if (fd < 0 || fd >= MAX_FDS) return;
|
if (fd < 0 || fd >= MAX_FDS) return;
|
||||||
|
|
||||||
@ -1706,34 +1707,12 @@ void close_connection(int fd) {
|
|||||||
// Prevent double-closing
|
// Prevent double-closing
|
||||||
if (conn->fd == -1) return;
|
if (conn->fd == -1) return;
|
||||||
|
|
||||||
connection_t *pair = conn->pair;
|
int pair_fd = -1;
|
||||||
|
if (conn->pair && conn->pair->fd != -1) {
|
||||||
// --- START: ROBUST STATE & PAIR MANAGEMENT FIX ---
|
pair_fd = conn->pair->fd;
|
||||||
|
conn->pair->pair = NULL;
|
||||||
// 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
|
// Record request end if needed
|
||||||
if (conn->vhost_stats && conn->request_start_time > 0) {
|
if (conn->vhost_stats && conn->request_start_time > 0) {
|
||||||
monitor_record_request_end(conn->vhost_stats, conn->request_start_time);
|
monitor_record_request_end(conn->vhost_stats, conn->request_start_time);
|
||||||
@ -1748,7 +1727,7 @@ void close_connection(int fd) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
log_debug("Closing connection on fd %d, remaining: %d", fd, monitor.active_connections);
|
log_debug("Closing connection on fd %d, pair %d, remaining: %d", fd, pair_fd, monitor.active_connections);
|
||||||
|
|
||||||
// Remove from epoll before closing
|
// Remove from epoll before closing
|
||||||
epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fd, NULL);
|
epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fd, NULL);
|
||||||
@ -1771,7 +1750,13 @@ void close_connection(int fd) {
|
|||||||
memset(conn, 0, sizeof(connection_t));
|
memset(conn, 0, sizeof(connection_t));
|
||||||
conn->type = CONN_TYPE_UNUSED;
|
conn->type = CONN_TYPE_UNUSED;
|
||||||
conn->fd = -1;
|
conn->fd = -1;
|
||||||
|
|
||||||
|
// Close the paired connection
|
||||||
|
if (pair_fd != -1) {
|
||||||
|
close_connection(pair_fd);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -2114,13 +2099,10 @@ void handle_client_read(connection_t *conn) {
|
|||||||
static void handle_forwarding(connection_t *conn) {
|
static void handle_forwarding(connection_t *conn) {
|
||||||
connection_t *pair = conn->pair;
|
connection_t *pair = conn->pair;
|
||||||
if (!pair || pair->fd == -1) {
|
if (!pair || pair->fd == -1) {
|
||||||
// This is an orphaned connection. If it's a client, reset it to read new requests.
|
|
||||||
// Otherwise, it's a dead upstream connection that should be closed.
|
|
||||||
if (conn->type == CONN_TYPE_CLIENT) {
|
if (conn->type == CONN_TYPE_CLIENT) {
|
||||||
log_info("[ROUTING-ORPHAN] Client fd=%d lost upstream, resetting to READING_HEADERS", conn->fd);
|
log_info("[ROUTING-ORPHAN] Client fd=%d lost upstream, resetting to READING_HEADERS", conn->fd);
|
||||||
conn->state = CLIENT_STATE_READING_HEADERS;
|
conn->state = CLIENT_STATE_READING_HEADERS;
|
||||||
conn->pair = NULL;
|
conn->pair = NULL;
|
||||||
// Process any data that was received while it was orphaned.
|
|
||||||
if (buffer_available_read(&conn->read_buf) > 0) {
|
if (buffer_available_read(&conn->read_buf) > 0) {
|
||||||
handle_client_read(conn);
|
handle_client_read(conn);
|
||||||
}
|
}
|
||||||
@ -2130,13 +2112,48 @@ static void handle_forwarding(connection_t *conn) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 1. Read data from the source connection.
|
|
||||||
int bytes_read = do_read(conn);
|
int bytes_read = do_read(conn);
|
||||||
|
if (bytes_read <= 0 && (errno != EAGAIN && errno != EWOULDBLOCK)) {
|
||||||
|
close_connection(conn->fd);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// 2. Forward any data we just read to the paired connection.
|
// --- START: THE CHECKPOINT FIX FOR RACE CONDITION ---
|
||||||
|
if (bytes_read > 0 && conn->type == CONN_TYPE_CLIENT) {
|
||||||
|
// Before forwarding, we MUST check if the data is a new pipelined request.
|
||||||
|
char *data_start = conn->read_buf.data + conn->read_buf.head;
|
||||||
|
size_t data_len = buffer_available_read(&conn->read_buf);
|
||||||
|
|
||||||
|
// A simple but highly effective check for the start of a new HTTP request.
|
||||||
|
if (data_len > 4 && (
|
||||||
|
strncmp(data_start, "GET ", 4) == 0 ||
|
||||||
|
strncmp(data_start, "POST ", 5) == 0 ||
|
||||||
|
strncmp(data_start, "PUT ", 4) == 0 ||
|
||||||
|
strncmp(data_start, "DELETE ", 7) == 0 ||
|
||||||
|
strncmp(data_start, "HEAD ", 5) == 0 ||
|
||||||
|
strncmp(data_start, "OPTIONS ", 8) == 0
|
||||||
|
)) {
|
||||||
|
log_info("[STATE-FIX] Checkpoint: Pipelined request detected from client fd=%d. Halting forwarding.", conn->fd);
|
||||||
|
|
||||||
|
// This is a new request. We must stop forwarding immediately.
|
||||||
|
// 1. Close the connection for the PREVIOUS request.
|
||||||
|
close_connection(pair->fd);
|
||||||
|
|
||||||
|
// 2. Reset the client's state to parsing mode.
|
||||||
|
conn->state = CLIENT_STATE_READING_HEADERS;
|
||||||
|
conn->pair = NULL; // The link to the old upstream is now severed.
|
||||||
|
|
||||||
|
// 3. Immediately process the new request that's already in the buffer.
|
||||||
|
handle_client_read(conn);
|
||||||
|
return; // Stop execution in this function.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// --- END: THE CHECKPOINT FIX ---
|
||||||
|
|
||||||
|
// If the checkpoint is passed, the data is part of the current request body
|
||||||
|
// (or is data from the upstream), so we forward it as intended.
|
||||||
size_t data_to_forward = buffer_available_read(&conn->read_buf);
|
size_t data_to_forward = buffer_available_read(&conn->read_buf);
|
||||||
if (data_to_forward > 0) {
|
if (data_to_forward > 0) {
|
||||||
// --- SYNTAX FIX IS HERE ---
|
|
||||||
if (buffer_ensure_capacity(&pair->write_buf, pair->write_buf.tail + data_to_forward) < 0) {
|
if (buffer_ensure_capacity(&pair->write_buf, pair->write_buf.tail + data_to_forward) < 0) {
|
||||||
log_error("Failed to grow write buffer for forwarding");
|
log_error("Failed to grow write buffer for forwarding");
|
||||||
close_connection(conn->fd);
|
close_connection(conn->fd);
|
||||||
@ -2147,18 +2164,12 @@ static void handle_forwarding(connection_t *conn) {
|
|||||||
conn->read_buf.data + conn->read_buf.head,
|
conn->read_buf.data + conn->read_buf.head,
|
||||||
data_to_forward);
|
data_to_forward);
|
||||||
pair->write_buf.tail += data_to_forward;
|
pair->write_buf.tail += data_to_forward;
|
||||||
// --- END OF SYNTAX FIX ---
|
|
||||||
buffer_consume(&conn->read_buf, data_to_forward);
|
buffer_consume(&conn->read_buf, data_to_forward);
|
||||||
|
|
||||||
modify_epoll(pair->fd, EPOLLIN | EPOLLOUT);
|
modify_epoll(pair->fd, EPOLLIN | EPOLLOUT);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 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)) {
|
|
||||||
close_connection(conn->fd);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user