Update.
This commit is contained in:
parent
ce81738292
commit
dd30b5d18d
99
rproxy.c
99
rproxy.c
@ -2094,40 +2094,33 @@ 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) {
|
||||||
// If this is a client with no pair, reset it to reading headers
|
// 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;
|
||||||
// Clear old request data to avoid state confusion
|
// Process any data that was received while it was orphaned.
|
||||||
memset(&conn->request, 0, sizeof(http_request_t));
|
|
||||||
conn->request.keep_alive = 1; // Default for HTTP/1.1
|
|
||||||
conn->request.content_length = -1;
|
|
||||||
// Try to read any pending requests
|
|
||||||
if (buffer_available_read(&conn->read_buf) > 0) {
|
if (buffer_available_read(&conn->read_buf) > 0) {
|
||||||
log_info("[ROUTING-ORPHAN] Processing buffered data on orphaned client fd=%d", conn->fd);
|
|
||||||
handle_client_read(conn);
|
handle_client_read(conn);
|
||||||
}
|
}
|
||||||
return;
|
} else {
|
||||||
}
|
|
||||||
close_connection(conn->fd);
|
close_connection(conn->fd);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read from this connection
|
// 1. Read data from the source connection (either client or upstream).
|
||||||
int bytes_read = do_read(conn);
|
int bytes_read = do_read(conn);
|
||||||
|
|
||||||
// Forward any data we have
|
// 2. Forward any data we just read to the paired connection's write buffer.
|
||||||
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) {
|
||||||
size_t available_write = buffer_available_write(&pair->write_buf);
|
|
||||||
if (available_write < data_to_forward) {
|
|
||||||
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");
|
log_error("Failed to grow write buffer for forwarding");
|
||||||
close_connection(conn->fd);
|
close_connection(conn->fd); // Close the pair of connections on memory error.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
memcpy(pair->write_buf.data + pair->write_buf.tail,
|
memcpy(pair->write_buf.data + pair->write_buf.tail,
|
||||||
conn->read_buf.data + conn->read_buf.head,
|
conn->read_buf.data + conn->read_buf.head,
|
||||||
@ -2135,58 +2128,48 @@ static void handle_forwarding(connection_t *conn) {
|
|||||||
pair->write_buf.tail += data_to_forward;
|
pair->write_buf.tail += data_to_forward;
|
||||||
buffer_consume(&conn->read_buf, data_to_forward);
|
buffer_consume(&conn->read_buf, data_to_forward);
|
||||||
|
|
||||||
// Enable write events on the pair
|
// Make sure the event loop knows the pair is ready to be written to.
|
||||||
modify_epoll(pair->fd, EPOLLIN | EPOLLOUT);
|
modify_epoll(pair->fd, EPOLLIN | EPOLLOUT);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle connection closure
|
// 3. Handle connection state changes (EOF or errors).
|
||||||
if (bytes_read == 0) {
|
if (bytes_read <= 0 && (errno != EAGAIN && errno != EWOULDBLOCK)) {
|
||||||
// EOF received
|
if (bytes_read < 0) { // A real read error occurred.
|
||||||
log_info("[ROUTING-EOF] EOF on fd=%d (type=%d), keep-alive=%d, is_websocket=%d",
|
log_debug("Read error on fd=%d while forwarding. Closing connection.", conn->fd);
|
||||||
conn->fd, conn->type, conn->request.keep_alive, conn->request.is_websocket);
|
close_connection(conn->fd); // This will recursively close the pair.
|
||||||
|
|
||||||
// For client connections with keep-alive, don't close immediately
|
|
||||||
if (conn->type == CONN_TYPE_CLIENT && conn->request.keep_alive && !conn->request.is_websocket) {
|
|
||||||
// Close the upstream connection
|
|
||||||
if (pair) {
|
|
||||||
log_info("[ROUTING-KEEPALIVE] Closing upstream fd=%d, keeping client fd=%d alive", pair->fd, conn->fd);
|
|
||||||
close_connection(pair->fd);
|
|
||||||
conn->pair = NULL;
|
|
||||||
}
|
|
||||||
// Reset client to reading headers for next request
|
|
||||||
conn->state = CLIENT_STATE_READING_HEADERS;
|
|
||||||
conn->half_closed = 0;
|
|
||||||
conn->write_shutdown = 0;
|
|
||||||
// Clear request data
|
|
||||||
memset(&conn->request, 0, sizeof(http_request_t));
|
|
||||||
conn->request.keep_alive = 1; // Default for HTTP/1.1
|
|
||||||
conn->request.content_length = -1;
|
|
||||||
|
|
||||||
// CRITICAL FIX: Process any buffered data (pipelined requests)
|
|
||||||
if (buffer_available_read(&conn->read_buf) > 0) {
|
|
||||||
log_info("[ROUTING-PIPELINE] Processing buffered pipelined request on fd=%d (%zu bytes)",
|
|
||||||
conn->fd, buffer_available_read(&conn->read_buf));
|
|
||||||
handle_client_read(conn);
|
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Regular half-close for non-keep-alive connections
|
// --- ROBUST FIX FOR KEEP-ALIVE RACE CONDITION ---
|
||||||
if (!conn->half_closed) {
|
// At this point, bytes_read is 0, meaning one side has closed its connection (EOF).
|
||||||
conn->half_closed = 1;
|
|
||||||
shutdown(conn->fd, SHUT_RD);
|
log_info("[ROUTING-EOF] EOF on fd=%d (type=%s)", conn->fd,
|
||||||
if (pair && !pair->write_shutdown) {
|
(conn->type == CONN_TYPE_CLIENT) ? "CLIENT" : "UPSTREAM");
|
||||||
shutdown(pair->fd, SHUT_WR);
|
|
||||||
pair->write_shutdown = 1;
|
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);
|
||||||
}
|
}
|
||||||
// If both sides are closed, close the connection
|
}
|
||||||
if (pair && pair->half_closed) {
|
// 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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (bytes_read < 0 && errno != EAGAIN && errno != EWOULDBLOCK) {
|
|
||||||
close_connection(conn->fd);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void handle_ssl_handshake(connection_t *conn) {
|
static void handle_ssl_handshake(connection_t *conn) {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user