1mod response;
13
14use std::collections::HashMap;
15use std::convert::Infallible;
16use std::sync::Arc;
17
18use bytes::Bytes;
19use http_body_util::combinators::BoxBody;
20use hyper::body::Incoming;
21
22use a2a_protocol_types::jsonrpc::{
23 JsonRpcError, JsonRpcErrorResponse, JsonRpcId, JsonRpcRequest, JsonRpcSuccessResponse,
24 JsonRpcVersion,
25};
26
27use crate::agent_card::StaticAgentCardHandler;
28use crate::dispatch::cors::CorsConfig;
29use crate::error::ServerError;
30use crate::handler::{RequestHandler, SendMessageResult};
31use crate::serve::Dispatcher;
32use crate::streaming::build_sse_response;
33
34use response::{
35 error_response, error_response_bytes, extract_headers, json_response, parse_error_response,
36 parse_params, read_body_limited, success_response, success_response_bytes,
37};
38
39pub struct JsonRpcDispatcher {
47 handler: Arc<RequestHandler>,
48 card_handler: Option<StaticAgentCardHandler>,
49 cors: Option<CorsConfig>,
50 config: super::DispatchConfig,
51}
52
53impl JsonRpcDispatcher {
54 #[must_use]
57 pub fn new(handler: Arc<RequestHandler>) -> Self {
58 Self::with_config(handler, super::DispatchConfig::default())
59 }
60
61 #[must_use]
63 pub fn with_config(handler: Arc<RequestHandler>, config: super::DispatchConfig) -> Self {
64 let card_handler = handler
65 .agent_card
66 .as_ref()
67 .and_then(|card| StaticAgentCardHandler::new(card).ok());
68 Self {
69 handler,
70 card_handler,
71 cors: None,
72 config,
73 }
74 }
75
76 #[must_use]
81 pub fn with_cors(mut self, cors: CorsConfig) -> Self {
82 self.cors = Some(cors);
83 self
84 }
85
86 pub async fn dispatch(
93 &self,
94 req: hyper::Request<Incoming>,
95 ) -> hyper::Response<BoxBody<Bytes, Infallible>> {
96 if req.method() == "OPTIONS" {
98 if let Some(ref cors) = self.cors {
99 return cors.preflight_response();
100 }
101 return json_response(204, Vec::new());
102 }
103
104 if req.method() == "GET" && req.uri().path() == "/.well-known/agent-card.json" {
107 let mut resp = self.card_handler.as_ref().map_or_else(
108 || json_response(404, br#"{"error":"agent card not configured"}"#.to_vec()),
109 |h| h.handle(&req).map(http_body_util::BodyExt::boxed),
110 );
111 if let Some(ref cors) = self.cors {
112 cors.apply_headers(&mut resp);
113 }
114 return resp;
115 }
116
117 let requested_extensions = req
122 .headers()
123 .get(a2a_protocol_types::A2A_EXTENSIONS_HEADER)
124 .and_then(|v| v.to_str().ok())
125 .map(str::to_owned);
126
127 let mut resp = Box::pin(self.dispatch_inner(req)).await;
132 if let Some(hval) = self
133 .handler
134 .activated_extensions_header_value(requested_extensions.as_deref())
135 && let Ok(v) = hyper::header::HeaderValue::from_str(&hval)
136 {
137 resp.headers_mut()
138 .insert(a2a_protocol_types::A2A_EXTENSIONS_HEADER, v);
139 }
140 if let Some(ref cors) = self.cors {
141 cors.apply_headers(&mut resp);
142 }
143 resp
144 }
145
146 #[allow(clippy::too_many_lines)]
148 async fn dispatch_inner(
149 &self,
150 req: hyper::Request<Incoming>,
151 ) -> hyper::Response<BoxBody<Bytes, Infallible>> {
152 if let Some(ct) = req.headers().get("content-type") {
154 let ct_str = ct.to_str().unwrap_or("");
155 if !ct_str.starts_with("application/json")
156 && !ct_str.starts_with(a2a_protocol_types::A2A_CONTENT_TYPE)
157 {
158 return error_response(
166 None,
167 &ServerError::Protocol(
168 a2a_protocol_types::error::A2aError::content_type_not_supported(format!(
169 "unsupported Content-Type: {ct_str}; expected application/json or application/a2a+json"
170 )),
171 ),
172 );
173 }
174 }
175
176 let version_value = req
180 .headers()
181 .get(a2a_protocol_types::A2A_VERSION_HEADER)
182 .and_then(|v| v.to_str().ok());
183 if let Err(err) =
184 super::validate_version_header(version_value, self.config.require_version_header)
185 {
186 return error_response(None, &ServerError::Protocol(err));
187 }
188
189 let headers = extract_headers(req.headers());
191
192 let body_bytes = match read_body_limited(
194 req.into_body(),
195 self.config.max_request_body_size,
196 self.config.body_read_timeout,
197 )
198 .await
199 {
200 Ok(bytes) => bytes,
201 Err(msg) => return parse_error_response(None, &msg),
202 };
203
204 let raw: serde_json::Value = match serde_json::from_slice(&body_bytes) {
206 Ok(v) => v,
207 Err(e) => return parse_error_response(None, &e.to_string()),
208 };
209
210 if raw.is_array() {
211 let serde_json::Value::Array(items) = raw else {
213 unreachable!()
214 };
215 if items.is_empty() {
216 return parse_error_response(None, "empty batch request");
217 }
218 if items.len() > self.config.max_batch_size {
220 return parse_error_response(
221 None,
222 &format!(
223 "batch too large: {} requests exceeds {} limit",
224 items.len(),
225 self.config.max_batch_size
226 ),
227 );
228 }
229 let mut responses: Vec<serde_json::Value> = Vec::with_capacity(items.len());
230 for item in items {
231 let rpc_req: JsonRpcRequest = match serde_json::from_value(item) {
232 Ok(r) => r,
233 Err(e) => {
234 let err_resp = JsonRpcErrorResponse::new(
236 None,
237 JsonRpcError::new(
238 a2a_protocol_types::error::ErrorCode::ParseError.as_i32(),
239 format!("Parse error: {e}"),
240 ),
241 );
242 if let Ok(v) = serde_json::to_value(&err_resp) {
243 responses.push(v);
244 }
245 continue;
246 }
247 };
248 let resp_body = self.dispatch_single_request(&rpc_req, &headers).await;
249 if let Ok(v) = serde_json::from_slice::<serde_json::Value>(&resp_body) {
250 responses.push(v);
251 }
252 }
253 let body = serde_json::to_vec(&responses).unwrap_or_default();
254 json_response(200, body)
255 } else {
256 let rpc_req: JsonRpcRequest = match serde_json::from_value(raw) {
258 Ok(r) => r,
259 Err(e) => return parse_error_response(None, &e.to_string()),
260 };
261 self.dispatch_single_request_http(&rpc_req, &headers).await
262 }
263 }
264
265 #[allow(clippy::too_many_lines)]
269 async fn dispatch_single_request_http(
270 &self,
271 rpc_req: &JsonRpcRequest,
272 headers: &HashMap<String, String>,
273 ) -> hyper::Response<BoxBody<Bytes, Infallible>> {
274 let id = rpc_req.id.to_response_id();
275 trace_info!(method = %rpc_req.method, "dispatching JSON-RPC request");
276
277 match rpc_req.method.as_str() {
279 "SendStreamingMessage" => {
280 return self.dispatch_send_message(id, rpc_req, true, headers).await;
281 }
282 "SubscribeToTask" => {
283 return match parse_params::<a2a_protocol_types::params::TaskIdParams>(rpc_req) {
284 Ok(p) => match self.handler.on_resubscribe(p, Some(headers)).await {
285 Ok(reader) => build_sse_response(
286 reader,
287 Some(self.config.sse_keep_alive_interval),
288 Some(self.config.sse_channel_capacity),
289 Some(id.clone()),
292 ),
293 Err(e) => error_response(id, &e),
294 },
295 Err(e) => error_response(id, &e),
296 };
297 }
298 _ => {}
299 }
300
301 let body = self.dispatch_single_request(rpc_req, headers).await;
302 json_response(200, body)
303 }
304
305 #[allow(clippy::too_many_lines)]
309 async fn dispatch_single_request(
310 &self,
311 rpc_req: &JsonRpcRequest,
312 headers: &HashMap<String, String>,
313 ) -> Vec<u8> {
314 let id = rpc_req.id.to_response_id();
315
316 match rpc_req.method.as_str() {
317 "SendMessage" => {
318 match self
319 .dispatch_send_message_inner(id.clone(), rpc_req, false, headers)
320 .await
321 {
322 Ok(resp) => serde_json::to_vec(&resp).unwrap_or_default(),
323 Err(body) => body,
324 }
325 }
326 "SendStreamingMessage" => {
327 let err = ServerError::InvalidParams(
329 "SendStreamingMessage not supported in batch requests".into(),
330 );
331 let a2a_err = err.to_a2a_error();
332 let resp = JsonRpcErrorResponse::new(
333 id,
334 JsonRpcError::new(a2a_err.code.as_i32(), a2a_err.message),
335 );
336 serde_json::to_vec(&resp).unwrap_or_default()
337 }
338 "GetTask" => {
339 match parse_params::<a2a_protocol_types::params::TaskQueryParams>(rpc_req) {
340 Ok(p) => match self.handler.on_get_task(p, Some(headers)).await {
341 Ok(r) => success_response_bytes(id, &r),
342 Err(e) => error_response_bytes(id, &e),
343 },
344 Err(e) => error_response_bytes(id, &e),
345 }
346 }
347 "ListTasks" => {
348 match parse_params::<a2a_protocol_types::params::ListTasksParams>(rpc_req) {
349 Ok(p) => match self.handler.on_list_tasks(p, Some(headers)).await {
350 Ok(r) => success_response_bytes(id, &r),
351 Err(e) => error_response_bytes(id, &e),
352 },
353 Err(e) => error_response_bytes(id, &e),
354 }
355 }
356 "CancelTask" => {
357 match parse_params::<a2a_protocol_types::params::CancelTaskParams>(rpc_req) {
358 Ok(p) => match self.handler.on_cancel_task(p, Some(headers)).await {
359 Ok(r) => success_response_bytes(id, &r),
360 Err(e) => error_response_bytes(id, &e),
361 },
362 Err(e) => error_response_bytes(id, &e),
363 }
364 }
365 "SubscribeToTask" => {
366 let err = ServerError::InvalidParams(
367 "SubscribeToTask not supported in batch requests".into(),
368 );
369 error_response_bytes(id, &err)
370 }
371 "CreateTaskPushNotificationConfig" => {
372 match parse_params::<a2a_protocol_types::push::TaskPushNotificationConfig>(rpc_req)
373 {
374 Ok(p) => match self.handler.on_set_push_config(p, Some(headers)).await {
375 Ok(r) => success_response_bytes(id, &r),
376 Err(e) => error_response_bytes(id, &e),
377 },
378 Err(e) => error_response_bytes(id, &e),
379 }
380 }
381 "GetTaskPushNotificationConfig" => {
382 match parse_params::<a2a_protocol_types::params::GetPushConfigParams>(rpc_req) {
383 Ok(p) => match self.handler.on_get_push_config(p, Some(headers)).await {
384 Ok(r) => success_response_bytes(id, &r),
385 Err(e) => error_response_bytes(id, &e),
386 },
387 Err(e) => error_response_bytes(id, &e),
388 }
389 }
390 "ListTaskPushNotificationConfigs" => {
391 match parse_params::<a2a_protocol_types::params::ListPushConfigsParams>(rpc_req) {
392 Ok(p) => match self
393 .handler
394 .on_list_push_configs(&p.task_id, p.tenant.as_deref(), Some(headers))
395 .await
396 {
397 Ok(configs) => {
398 let resp = a2a_protocol_types::responses::ListPushConfigsResponse {
399 configs,
400 next_page_token: None,
401 };
402 success_response_bytes(id, &resp)
403 }
404 Err(e) => error_response_bytes(id, &e),
405 },
406 Err(e) => error_response_bytes(id, &e),
407 }
408 }
409 "DeleteTaskPushNotificationConfig" => {
410 match parse_params::<a2a_protocol_types::params::DeletePushConfigParams>(rpc_req) {
411 Ok(p) => match self.handler.on_delete_push_config(p, Some(headers)).await {
412 Ok(()) => success_response_bytes(id, &serde_json::json!({})),
413 Err(e) => error_response_bytes(id, &e),
414 },
415 Err(e) => error_response_bytes(id, &e),
416 }
417 }
418 "GetExtendedAgentCard" => {
419 match self.handler.on_get_extended_agent_card(Some(headers)).await {
420 Ok(r) => success_response_bytes(id, &r),
421 Err(e) => error_response_bytes(id, &e),
422 }
423 }
424 other => {
425 let err = ServerError::MethodNotFound(other.to_owned());
426 error_response_bytes(id, &err)
427 }
428 }
429 }
430
431 async fn dispatch_send_message_inner(
434 &self,
435 id: JsonRpcId,
436 rpc_req: &JsonRpcRequest,
437 streaming: bool,
438 headers: &HashMap<String, String>,
439 ) -> Result<JsonRpcSuccessResponse<serde_json::Value>, Vec<u8>> {
440 let params = match parse_params::<a2a_protocol_types::params::MessageSendParams>(rpc_req) {
441 Ok(p) => p,
442 Err(e) => return Err(error_response_bytes(id, &e)),
443 };
444 match self
445 .handler
446 .on_send_message(params, streaming, Some(headers))
447 .await
448 {
449 Ok(SendMessageResult::Response(resp)) => {
450 let result = serde_json::to_value(&resp).unwrap_or(serde_json::Value::Null);
451 Ok(JsonRpcSuccessResponse {
452 jsonrpc: JsonRpcVersion,
453 id,
454 result,
455 })
456 }
457 Ok(SendMessageResult::Stream(_)) => {
458 let err = ServerError::Internal("unexpected stream response".into());
460 Err(error_response_bytes(id, &err))
461 }
462 Err(e) => Err(error_response_bytes(id, &e)),
463 }
464 }
465
466 async fn dispatch_send_message(
467 &self,
468 id: JsonRpcId,
469 rpc_req: &JsonRpcRequest,
470 streaming: bool,
471 headers: &HashMap<String, String>,
472 ) -> hyper::Response<BoxBody<Bytes, Infallible>> {
473 let params = match parse_params::<a2a_protocol_types::params::MessageSendParams>(rpc_req) {
474 Ok(p) => p,
475 Err(e) => return error_response(id, &e),
476 };
477 match self
478 .handler
479 .on_send_message(params, streaming, Some(headers))
480 .await
481 {
482 Ok(SendMessageResult::Response(resp)) => success_response(id, &resp),
483 Ok(SendMessageResult::Stream(reader)) => build_sse_response(
484 reader,
485 Some(self.config.sse_keep_alive_interval),
486 Some(self.config.sse_channel_capacity),
487 Some(id.clone()),
489 ),
490 Err(e) => error_response(id, &e),
491 }
492 }
493}
494
495impl std::fmt::Debug for JsonRpcDispatcher {
496 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
497 f.debug_struct("JsonRpcDispatcher").finish()
498 }
499}
500
501impl Dispatcher for JsonRpcDispatcher {
504 fn dispatch(
505 &self,
506 req: hyper::Request<Incoming>,
507 ) -> std::pin::Pin<
508 Box<dyn std::future::Future<Output = crate::serve::DispatchResponse> + Send + '_>,
509 > {
510 Box::pin(self.dispatch(req))
511 }
512}