feat: gRPC keepalive — ping/pong handler + HTTP/2 keepalive

- proto: add Ping/Pong message types for application-level keepalive
- service: echo Pong back on Ping in run_session message loop
- mod: configure HTTP/2 keepalive on server builder (15s interval,
  5s timeout) to detect half-open connections
This commit is contained in:
2026-03-24 18:07:02 +00:00
parent ecf0ead7c7
commit d58bbfce66
2 changed files with 11 additions and 1 deletions

View File

@@ -12,6 +12,7 @@ pub use proto::code_agent_server::{CodeAgent, CodeAgentServer};
pub use proto::*; pub use proto::*;
use std::sync::Arc; use std::sync::Arc;
use std::time::Duration;
use tonic::transport::Server; use tonic::transport::Server;
use tracing::{error, info}; use tracing::{error, info};
@@ -57,7 +58,9 @@ pub async fn start_server(state: Arc<GrpcState>) -> anyhow::Result<()> {
let svc = service::CodeAgentService::new(state); let svc = service::CodeAgentService::new(state);
let mut builder = Server::builder(); let mut builder = Server::builder()
.http2_keepalive_interval(Some(Duration::from_secs(15)))
.http2_keepalive_timeout(Some(Duration::from_secs(5)));
if dev_mode { if dev_mode {
info!(%addr, "Starting gRPC server (dev mode — no auth)"); info!(%addr, "Starting gRPC server (dev mode — no auth)");

View File

@@ -303,6 +303,13 @@ async fn run_session(
Some(client_message::Payload::Start(_)) => { Some(client_message::Payload::Start(_)) => {
warn!("Received duplicate StartSession — ignoring"); warn!("Received duplicate StartSession — ignoring");
} }
Some(client_message::Payload::Ping(p)) => {
let _ = tx.send(Ok(ServerMessage {
payload: Some(server_message::Payload::Pong(Pong {
timestamp: p.timestamp,
})),
})).await;
}
// ToolResult and Approval are handled by the orchestrator bridge // ToolResult and Approval are handled by the orchestrator bridge
_ => continue, _ => continue,
} }