1pub mod agent_card;
5pub mod executor;
6pub mod handler;
7pub mod jsonrpc;
8pub mod middleware;
9pub mod pagination;
10pub mod push;
11pub mod rest;
12pub mod sse;
13pub mod task_store;
14
15#[cfg(feature = "fuzzing")]
18#[doc(hidden)]
19pub mod fuzz_support {
20 use a2a::A2AError;
21
22 pub fn validate_push_url(url: &str) -> Result<(), A2AError> {
23 crate::push::sender::validate_push_url(url)
24 }
25
26 pub fn is_blocked_ip(ip: std::net::IpAddr) -> bool {
27 crate::push::sender::is_blocked_ip(ip)
28 }
29}
30
31#[cfg(any(feature = "rustls-tls", feature = "rustls-no-provider"))]
32pub mod tls;
33
34pub use agent_card::{AgentCardProducer, StaticAgentCard, WELL_KNOWN_AGENT_CARD_PATH};
35pub use executor::{AgentExecutor, ExecutorContext};
36pub use handler::{
37 DefaultRequestHandler, ExtendedAgentCardResolver, RequestAuthorizer, RequestHandler,
38};
39pub use middleware::{CallContext, CallInterceptor, InterceptedHandler, ServiceParams, User};
40pub use push::{HttpPushSender, HttpPushSenderConfig, InMemoryPushConfigStore, PushConfigStore};
41pub use task_store::{InMemoryTaskStore, TaskStore};
42
43pub(crate) fn sanitized_internal_error(detail: impl std::fmt::Display) -> a2a::A2AError {
55 tracing::error!(error = %detail, "internal error; returning a generic message to the client");
56 a2a::A2AError::internal("Internal error")
57}
58
59#[cfg(test)]
60pub(crate) mod test_util {
61 use std::collections::HashMap;
62 use std::sync::{Arc, Mutex};
63
64 use a2a::*;
65 use async_trait::async_trait;
66 use futures::stream::BoxStream;
67
68 use crate::handler::RequestHandler;
69 use crate::middleware::ServiceParams;
70
71 pub fn install_crypto_provider() {
72 #[cfg(feature = "rustls-tls")]
73 {
74 let _ = rustls::crypto::aws_lc_rs::default_provider().install_default();
75 }
76 }
77
78 pub(crate) type CapturedParams = Arc<Mutex<HashMap<&'static str, ServiceParams>>>;
79
80 pub(crate) struct CapturingHandler {
87 captured: CapturedParams,
88 }
89
90 impl CapturingHandler {
91 pub fn new() -> (Arc<Self>, CapturedParams) {
92 let captured = Arc::new(Mutex::new(HashMap::new()));
93 let handler = Arc::new(CapturingHandler {
94 captured: captured.clone(),
95 });
96 (handler, captured)
97 }
98
99 fn record(&self, method: &'static str, params: &ServiceParams) {
100 self.captured.lock().unwrap().insert(method, params.clone());
101 }
102 }
103
104 #[async_trait]
105 impl RequestHandler for CapturingHandler {
106 async fn send_message(
107 &self,
108 params: &ServiceParams,
109 _req: SendMessageRequest,
110 ) -> Result<SendMessageResponse, A2AError> {
111 self.record("send_message", params);
112 Err(A2AError::internal("stub"))
113 }
114
115 async fn send_streaming_message(
116 &self,
117 params: &ServiceParams,
118 _req: SendMessageRequest,
119 ) -> Result<BoxStream<'static, Result<StreamResponse, A2AError>>, A2AError> {
120 self.record("send_streaming_message", params);
121 Ok(Box::pin(futures::stream::empty()))
122 }
123
124 async fn get_task(
125 &self,
126 params: &ServiceParams,
127 _req: GetTaskRequest,
128 ) -> Result<Task, A2AError> {
129 self.record("get_task", params);
130 Err(A2AError::task_not_found("stub"))
131 }
132
133 async fn list_tasks(
134 &self,
135 params: &ServiceParams,
136 _req: ListTasksRequest,
137 ) -> Result<ListTasksResponse, A2AError> {
138 self.record("list_tasks", params);
139 Ok(ListTasksResponse {
140 tasks: Vec::new(),
141 next_page_token: String::new(),
142 page_size: 0,
143 total_size: 0,
144 })
145 }
146
147 async fn cancel_task(
148 &self,
149 params: &ServiceParams,
150 _req: CancelTaskRequest,
151 ) -> Result<Task, A2AError> {
152 self.record("cancel_task", params);
153 Err(A2AError::task_not_found("stub"))
154 }
155
156 async fn subscribe_to_task(
157 &self,
158 params: &ServiceParams,
159 _req: SubscribeToTaskRequest,
160 ) -> Result<BoxStream<'static, Result<StreamResponse, A2AError>>, A2AError> {
161 self.record("subscribe_to_task", params);
162 Ok(Box::pin(futures::stream::empty()))
163 }
164
165 async fn create_push_config(
166 &self,
167 params: &ServiceParams,
168 req: TaskPushNotificationConfig,
169 ) -> Result<TaskPushNotificationConfig, A2AError> {
170 self.record("create_push_config", params);
171 Ok(req)
172 }
173
174 async fn get_push_config(
175 &self,
176 params: &ServiceParams,
177 _req: GetTaskPushNotificationConfigRequest,
178 ) -> Result<TaskPushNotificationConfig, A2AError> {
179 self.record("get_push_config", params);
180 Err(A2AError::internal("stub"))
181 }
182
183 async fn list_push_configs(
184 &self,
185 params: &ServiceParams,
186 _req: ListTaskPushNotificationConfigsRequest,
187 ) -> Result<ListTaskPushNotificationConfigsResponse, A2AError> {
188 self.record("list_push_configs", params);
189 Ok(ListTaskPushNotificationConfigsResponse {
190 configs: Vec::new(),
191 next_page_token: None,
192 })
193 }
194
195 async fn delete_push_config(
196 &self,
197 params: &ServiceParams,
198 _req: DeleteTaskPushNotificationConfigRequest,
199 ) -> Result<(), A2AError> {
200 self.record("delete_push_config", params);
201 Ok(())
202 }
203
204 async fn get_extended_agent_card(
205 &self,
206 params: &ServiceParams,
207 _req: GetExtendedAgentCardRequest,
208 ) -> Result<AgentCard, A2AError> {
209 self.record("get_extended_agent_card", params);
210 Err(A2AError::internal("stub"))
211 }
212 }
213
214 #[track_caller]
216 pub(crate) fn assert_header_captured(
217 captured: &CapturedParams,
218 method: &'static str,
219 header: &str,
220 expected: &str,
221 ) {
222 let captured = captured.lock().unwrap();
223 let params = captured
224 .get(method)
225 .unwrap_or_else(|| panic!("{method} should have been called; captured={captured:?}"));
226 assert_eq!(
227 params.get(header),
228 Some(&vec![expected.to_string()]),
229 "{header} should be propagated to {method}; got {params:?}",
230 );
231 }
232}