mod capability;
mod concurrency;
mod event_processing;
mod helpers;
mod introspection;
mod lifecycle;
mod limits;
mod locks;
mod messaging;
mod push_config;
mod shutdown;
pub use shutdown::ShutdownReport;
use std::collections::HashMap;
use std::sync::Arc;
use std::time::{Duration, Instant};
use a2a_protocol_types::agent_card::AgentCard;
use a2a_protocol_types::task::TaskId;
use crate::error::ServerResult;
use crate::executor::AgentExecutor;
use crate::interceptor::ServerInterceptorChain;
use crate::metrics::Metrics;
use crate::push::{PushConfigStore, PushSender};
use crate::store::TaskStore;
use crate::streaming::{EventQueueManager, InMemoryQueueReader};
use crate::tenant_config::PerTenantConfig;
use crate::tenant_resolver::TenantResolver;
pub use limits::HandlerLimits;
pub use a2a_protocol_types::responses::SendMessageResponse;
pub struct RequestHandler {
pub(crate) executor: Arc<dyn AgentExecutor>,
pub(crate) task_store: Arc<dyn TaskStore>,
pub(crate) push_config_store: Arc<dyn PushConfigStore>,
pub(crate) push_sender: Option<Arc<dyn PushSender>>,
pub(crate) event_queue_manager: EventQueueManager,
pub(crate) interceptors: ServerInterceptorChain,
pub(crate) agent_card: Option<AgentCard>,
pub(crate) executor_timeout: Option<Duration>,
pub(crate) metrics: Arc<dyn Metrics>,
pub(crate) limits: HandlerLimits,
pub(crate) tenant_resolver: Option<Arc<dyn TenantResolver>>,
pub(crate) tenant_config: Option<PerTenantConfig>,
pub(crate) require_resolved_tenant: bool,
pub(crate) allow_unauthenticated_extended_card: bool,
pub(crate) required_extensions: Vec<String>,
pub(crate) declared_extensions: Vec<String>,
pub(crate) cancellation_tokens: Arc<tokio::sync::RwLock<HashMap<TaskId, CancellationEntry>>>,
pub(crate) context_locks:
Arc<tokio::sync::RwLock<HashMap<String, Arc<tokio::sync::Mutex<()>>>>>,
pub(crate) tenant_slots: Arc<tokio::sync::RwLock<HashMap<String, Arc<tokio::sync::Semaphore>>>>,
}
#[derive(Debug, Clone)]
pub(crate) struct CancellationEntry {
pub(crate) token: tokio_util::sync::CancellationToken,
pub(crate) created_at: Instant,
}
impl RequestHandler {
#[must_use]
pub fn tenant_resolver(&self) -> Option<&dyn TenantResolver> {
self.tenant_resolver.as_deref()
}
#[must_use]
pub const fn tenant_config(&self) -> Option<&PerTenantConfig> {
self.tenant_config.as_ref()
}
pub(crate) fn tenant_limits(&self) -> Option<&crate::tenant_config::TenantLimits> {
let config = self.tenant_config.as_ref()?;
Some(config.get(&crate::store::tenant::TenantContext::current()))
}
pub(crate) async fn resolve_tenant(
&self,
method: &str,
headers: Option<&HashMap<String, String>>,
client_tenant: Option<&str>,
) -> ServerResult<String> {
let Some(resolver) = self.tenant_resolver.as_deref() else {
return Ok(client_tenant.unwrap_or_default().to_owned());
};
let call_ctx = crate::handler::helpers::build_call_context(method, headers);
let derived = resolver.resolve(&call_ctx).await;
if self.require_resolved_tenant && derived.is_none() {
return Err(crate::error::ServerError::InvalidParams(
"no tenant could be determined for this request and strict \
multi-tenancy is enabled"
.to_owned(),
));
}
let authoritative = derived.unwrap_or_default();
if let Some(client) = client_tenant {
if !client.is_empty() && client != authoritative {
return Err(crate::error::ServerError::InvalidParams(format!(
"request tenant '{client}' does not match the authenticated tenant"
)));
}
}
Ok(authoritative)
}
}
impl std::fmt::Debug for RequestHandler {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("RequestHandler")
.field("push_sender", &self.push_sender.is_some())
.field("event_queue_manager", &self.event_queue_manager)
.field("interceptors", &self.interceptors)
.field("agent_card", &self.agent_card.is_some())
.field("metrics", &"<dyn Metrics>")
.field("tenant_resolver", &self.tenant_resolver.is_some())
.field("tenant_config", &self.tenant_config)
.finish_non_exhaustive()
}
}
#[derive(Debug)]
#[allow(clippy::large_enum_variant)]
pub enum SendMessageResult {
Response(SendMessageResponse),
Stream(InMemoryQueueReader),
}
#[cfg(test)]
mod tenant_limits_tests;
#[cfg(test)]
mod tests;