1use std::sync::Arc;
4
5use a2a::*;
6use a2a_pb::protojson_conv::{self, ProtoJsonPayload};
7use axum::{
8 Json,
9 extract::State,
10 http::{HeaderMap, StatusCode},
11 response::IntoResponse,
12};
13use futures::{StreamExt, stream::BoxStream};
14use serde_json::Value;
15
16use crate::handler::RequestHandler;
17use crate::middleware::{ServiceParams, extract_service_params};
18use crate::sse;
19
20pub struct JsonRpcState<H: RequestHandler> {
22 pub handler: Arc<H>,
23}
24
25impl<H: RequestHandler> Clone for JsonRpcState<H> {
26 fn clone(&self) -> Self {
27 JsonRpcState {
28 handler: self.handler.clone(),
29 }
30 }
31}
32
33pub fn jsonrpc_router<H: RequestHandler>(handler: Arc<H>) -> axum::Router {
38 let state = JsonRpcState { handler };
39 axum::Router::new()
40 .route("/", axum::routing::post(handle_jsonrpc::<H>))
41 .with_state(state)
42}
43
44async fn handle_jsonrpc<H: RequestHandler>(
45 State(state): State<JsonRpcState<H>>,
46 headers: HeaderMap,
47 Json(request): Json<JsonRpcRequest>,
48) -> impl IntoResponse {
49 let params = extract_service_params(&headers);
50 let id = request.id.clone();
51 let method = request.method.as_str();
52
53 if request.jsonrpc != "2.0" {
54 return error_response(id, A2AError::invalid_request("invalid jsonrpc version"));
55 }
56
57 if methods::is_streaming(method) {
58 return handle_streaming_request(&state, ¶ms, &request).await;
59 }
60
61 handle_unary_request(&state, ¶ms, &request).await
62}
63
64async fn handle_unary_request<H: RequestHandler>(
65 state: &JsonRpcState<H>,
66 params: &ServiceParams,
67 request: &JsonRpcRequest,
68) -> axum::response::Response {
69 let id = request.id.clone();
70 let raw_params = request.params.clone().unwrap_or(Value::Null);
71
72 let result: Result<Value, A2AError> = match request.method.as_str() {
73 methods::SEND_MESSAGE => match protojson_conv::from_value::<SendMessageRequest>(raw_params)
74 {
75 Ok(req) => state
76 .handler
77 .send_message(params, req)
78 .await
79 .and_then(|r| protojson_value(&r)),
80 Err(e) => Err(parse_error(e)),
81 },
82 methods::GET_TASK => match protojson_conv::from_value::<GetTaskRequest>(raw_params) {
83 Ok(req) => state
84 .handler
85 .get_task(params, req)
86 .await
87 .and_then(|r| protojson_value(&r)),
88 Err(e) => Err(parse_error(e)),
89 },
90 methods::LIST_TASKS => match protojson_conv::from_value::<ListTasksRequest>(raw_params) {
91 Ok(req) => state
92 .handler
93 .list_tasks(params, req)
94 .await
95 .and_then(|r| protojson_value(&r)),
96 Err(e) => Err(parse_error(e)),
97 },
98 methods::CANCEL_TASK => match protojson_conv::from_value::<CancelTaskRequest>(raw_params) {
99 Ok(req) => state
100 .handler
101 .cancel_task(params, req)
102 .await
103 .and_then(|r| protojson_value(&r)),
104 Err(e) => Err(parse_error(e)),
105 },
106 methods::CREATE_PUSH_CONFIG => match parse_create_push_config_request(raw_params) {
107 Ok(req) => state
108 .handler
109 .create_push_config(params, req)
110 .await
111 .and_then(|r| protojson_value(&r)),
112 Err(e) => Err(parse_error(e)),
113 },
114 methods::GET_PUSH_CONFIG => {
115 match protojson_conv::from_value::<GetTaskPushNotificationConfigRequest>(raw_params) {
116 Ok(req) => state
117 .handler
118 .get_push_config(params, req)
119 .await
120 .and_then(|r| protojson_value(&r)),
121 Err(e) => Err(parse_error(e)),
122 }
123 }
124 methods::LIST_PUSH_CONFIGS => {
125 match protojson_conv::from_value::<ListTaskPushNotificationConfigsRequest>(raw_params) {
126 Ok(req) => state
127 .handler
128 .list_push_configs(params, req)
129 .await
130 .and_then(|r| protojson_value(&r)),
131 Err(e) => Err(parse_error(e)),
132 }
133 }
134 methods::DELETE_PUSH_CONFIG => {
135 match protojson_conv::from_value::<DeleteTaskPushNotificationConfigRequest>(raw_params)
136 {
137 Ok(req) => state
138 .handler
139 .delete_push_config(params, req)
140 .await
141 .map(|_| Value::Null),
142 Err(e) => Err(parse_error(e)),
143 }
144 }
145 methods::GET_EXTENDED_AGENT_CARD => {
146 match protojson_conv::from_value::<GetExtendedAgentCardRequest>(raw_params) {
147 Ok(req) => state
148 .handler
149 .get_extended_agent_card(params, req)
150 .await
151 .and_then(|r| protojson_value(&r)),
152 Err(e) => Err(parse_error(e)),
153 }
154 }
155 "" => Err(A2AError::invalid_request("method is required")),
156 _ => Err(A2AError::method_not_found(&request.method)),
157 };
158
159 match result {
160 Ok(value) => {
161 let resp = JsonRpcResponse::success(id, value);
162 Json(resp).into_response()
163 }
164 Err(e) => error_response(id, e),
165 }
166}
167
168async fn handle_streaming_request<H: RequestHandler>(
169 state: &JsonRpcState<H>,
170 params: &ServiceParams,
171 request: &JsonRpcRequest,
172) -> axum::response::Response {
173 let id = request.id.clone();
174 let raw_params = request.params.clone().unwrap_or(Value::Null);
175
176 match request.method.as_str() {
177 methods::SEND_STREAMING_MESSAGE => {
178 match protojson_conv::from_value::<SendMessageRequest>(raw_params) {
179 Ok(req) => match state.handler.send_streaming_message(params, req).await {
180 Ok(stream) => {
181 sse::sse_jsonrpc_stream(id, protojson_stream(stream)).into_response()
182 }
183 Err(e) => error_response(id, e),
184 },
185 Err(e) => error_response(id, parse_error(e)),
186 }
187 }
188 methods::SUBSCRIBE_TO_TASK => {
189 match protojson_conv::from_value::<SubscribeToTaskRequest>(raw_params) {
190 Ok(req) => match state.handler.subscribe_to_task(params, req).await {
191 Ok(stream) => {
192 sse::sse_jsonrpc_stream(id, protojson_stream(stream)).into_response()
193 }
194 Err(e) => error_response(id, e),
195 },
196 Err(e) => error_response(id, parse_error(e)),
197 }
198 }
199 _ => error_response(id, A2AError::method_not_found(&request.method)),
200 }
201}
202
203fn error_response(id: JsonRpcId, err: A2AError) -> axum::response::Response {
204 let resp = JsonRpcResponse::error(id, err.to_jsonrpc_error());
205 (StatusCode::OK, Json(resp)).into_response()
206}
207
208fn protojson_value<T: ProtoJsonPayload>(value: &T) -> Result<Value, A2AError> {
209 protojson_conv::to_value(value)
210 .map_err(|e| A2AError::internal(format!("failed to serialize ProtoJSON payload: {e}")))
211}
212
213fn protojson_stream(
214 stream: BoxStream<'static, Result<StreamResponse, A2AError>>,
215) -> BoxStream<'static, Result<Value, A2AError>> {
216 Box::pin(stream.map(|item| {
217 item.and_then(|value| {
218 protojson_conv::to_value(&value).map_err(|e| {
219 A2AError::internal(format!("failed to serialize ProtoJSON stream payload: {e}"))
220 })
221 })
222 }))
223}
224
225fn parse_create_push_config_request(
226 raw_params: Value,
227) -> Result<TaskPushNotificationConfig, String> {
228 protojson_conv::from_value::<TaskPushNotificationConfig>(raw_params).map_err(|e| e.to_string())
229}
230
231fn parse_error(e: impl std::fmt::Display) -> A2AError {
232 A2AError {
233 code: error_code::PARSE_ERROR,
234 message: format!("invalid params: {e}"),
235 details: None,
236 }
237}
238
239#[cfg(test)]
240mod tests {
241 use super::*;
242 use crate::InMemoryPushConfigStore;
243 use crate::executor::ExecutorContext;
244 use crate::handler::DefaultRequestHandler;
245 use crate::task_store::InMemoryTaskStore;
246 use a2a_pb::protojson_conv;
247 use axum::body::Body;
248 use axum::http::Request;
249
250 use crate::test_util::{CapturingHandler, assert_header_captured, install_crypto_provider};
251 use futures::stream::BoxStream;
252 use http_body_util::BodyExt;
253 use tower::ServiceExt;
254
255 struct EchoExecutor;
256
257 impl crate::AgentExecutor for EchoExecutor {
258 fn execute(
259 &self,
260 ctx: ExecutorContext,
261 ) -> BoxStream<'static, Result<StreamResponse, A2AError>> {
262 let task = Task {
263 id: ctx.task_id.clone(),
264 context_id: ctx.context_id.clone(),
265 status: TaskStatus {
266 state: TaskState::Completed,
267 message: ctx.message,
268 timestamp: None,
269 },
270 artifacts: None,
271 history: None,
272 metadata: None,
273 };
274 Box::pin(futures::stream::once(async move {
275 Ok(StreamResponse::Task(task))
276 }))
277 }
278
279 fn cancel(
280 &self,
281 ctx: ExecutorContext,
282 ) -> BoxStream<'static, Result<StreamResponse, A2AError>> {
283 let task = Task {
284 id: ctx.task_id.clone(),
285 context_id: ctx.context_id.clone(),
286 status: TaskStatus {
287 state: TaskState::Canceled,
288 message: None,
289 timestamp: None,
290 },
291 artifacts: None,
292 history: None,
293 metadata: None,
294 };
295 Box::pin(futures::stream::once(async move {
296 Ok(StreamResponse::Task(task))
297 }))
298 }
299 }
300
301 fn make_app() -> axum::Router {
302 let handler = Arc::new(DefaultRequestHandler::new(
303 EchoExecutor,
304 InMemoryTaskStore::new(),
305 ));
306 jsonrpc_router(handler)
307 }
308
309 fn make_push_app() -> axum::Router {
310 install_crypto_provider();
311 let handler = Arc::new(
312 DefaultRequestHandler::new(EchoExecutor, InMemoryTaskStore::new())
313 .with_push_config_store(InMemoryPushConfigStore::new()),
314 );
315 jsonrpc_router(handler)
316 }
317
318 async fn post_jsonrpc(app: axum::Router, method: &str, params: Value) -> JsonRpcResponse {
319 let rpc = JsonRpcRequest::new(JsonRpcId::Number(1), method, Some(params));
320 let body = serde_json::to_string(&rpc).unwrap();
321 let req = Request::builder()
322 .uri("/")
323 .method("POST")
324 .header("content-type", "application/json")
325 .body(Body::from(body))
326 .unwrap();
327 let resp = app.oneshot(req).await.unwrap();
328 let body = resp.into_body().collect().await.unwrap().to_bytes();
329 serde_json::from_slice(&body).unwrap()
330 }
331
332 #[tokio::test]
333 async fn test_send_message() {
334 let app = make_app();
335 let params = serde_json::json!({
336 "message": {
337 "messageId": "m1",
338 "role": "ROLE_USER",
339 "parts": [{"text": "hello"}]
340 }
341 });
342 let resp = post_jsonrpc(app, methods::SEND_MESSAGE, params).await;
343 assert!(resp.error.is_none(), "unexpected error: {:?}", resp.error);
344 let result =
345 protojson_conv::from_value::<SendMessageResponse>(resp.result.unwrap()).unwrap();
346 assert!(matches!(result, SendMessageResponse::Task(_)));
347 }
348
349 #[tokio::test]
350 async fn test_send_message_legacy_method_rejected() {
351 let app = make_app();
352 let params = serde_json::json!({
353 "message": {
354 "messageId": "m1",
355 "role": "ROLE_USER",
356 "parts": [{"text": "hello"}]
357 }
358 });
359 let resp = post_jsonrpc(app, "message.send", params).await;
360 assert!(resp.error.is_some(), "unexpected result: {:?}", resp.result);
361 assert_eq!(resp.error.unwrap().code, error_code::METHOD_NOT_FOUND);
362 }
363
364 #[tokio::test]
365 async fn test_get_task_not_found() {
366 let app = make_app();
367 let params = serde_json::json!({"id": "nonexistent"});
368 let resp = post_jsonrpc(app, methods::GET_TASK, params).await;
369 assert!(resp.error.is_some());
370 assert_eq!(resp.error.unwrap().code, error_code::TASK_NOT_FOUND);
371 }
372
373 #[tokio::test]
374 async fn test_invalid_method() {
375 let app = make_app();
376 let resp = post_jsonrpc(app, "unknown.method", Value::Null).await;
377 assert!(resp.error.is_some());
378 assert_eq!(resp.error.unwrap().code, error_code::METHOD_NOT_FOUND);
379 }
380
381 #[tokio::test]
382 async fn test_empty_method() {
383 let app = make_app();
384 let resp = post_jsonrpc(app, "", Value::Null).await;
385 assert!(resp.error.is_some());
386 }
387
388 #[tokio::test]
389 async fn test_invalid_jsonrpc_version() {
390 let app = make_app();
391 let rpc = serde_json::json!({
392 "jsonrpc": "1.0",
393 "id": 1,
394 "method": methods::SEND_MESSAGE,
395 "params": {}
396 });
397 let body = serde_json::to_string(&rpc).unwrap();
398 let req = Request::builder()
399 .uri("/")
400 .method("POST")
401 .header("content-type", "application/json")
402 .body(Body::from(body))
403 .unwrap();
404 let resp = app.oneshot(req).await.unwrap();
405 let body = resp.into_body().collect().await.unwrap().to_bytes();
406 let rpc_resp: JsonRpcResponse = serde_json::from_slice(&body).unwrap();
407 assert!(rpc_resp.error.is_some());
408 }
409
410 #[tokio::test]
411 async fn test_invalid_params() {
412 let app = make_app();
413 let params = serde_json::json!({"bogus": true});
414 let resp = post_jsonrpc(app, methods::SEND_MESSAGE, params).await;
415 assert!(resp.error.is_some());
416 assert_eq!(resp.error.unwrap().code, error_code::PARSE_ERROR);
417 }
418
419 #[tokio::test]
420 async fn test_list_tasks() {
421 let app = make_app();
422 let params = serde_json::json!({});
423 let resp = post_jsonrpc(app, methods::LIST_TASKS, params).await;
424 let result = protojson_conv::from_value::<ListTasksResponse>(resp.result.unwrap()).unwrap();
425 assert!(result.tasks.is_empty());
426 }
427
428 #[tokio::test]
429 async fn test_cancel_task_not_found() {
430 let app = make_app();
431 let params = serde_json::json!({"id": "nonexistent"});
432 let resp = post_jsonrpc(app, methods::CANCEL_TASK, params).await;
433 assert!(resp.error.is_some());
434 }
435
436 #[tokio::test]
437 async fn test_create_push_config() {
438 let app = make_push_app();
439 let params = serde_json::json!({
440 "taskId": "t1",
441 "id": "cfg1",
442 "url": "http://example.com/callback"
443 });
444 let resp = post_jsonrpc(app, methods::CREATE_PUSH_CONFIG, params).await;
445 assert!(resp.error.is_none(), "unexpected error: {:?}", resp.error);
446 let result =
447 serde_json::from_value::<TaskPushNotificationConfig>(resp.result.unwrap()).unwrap();
448 assert_eq!(result.task_id, "t1");
449 assert_eq!(result.id.as_deref(), Some("cfg1"));
450 assert_eq!(result.url, "http://example.com/callback");
451 }
452
453 #[tokio::test]
454 async fn test_create_push_config_accepts_flat_config_object() {
455 let app = make_push_app();
456 let params = serde_json::json!({
457 "taskId": "t1",
458 "id": "cfg1",
459 "url": "http://example.com/callback"
460 });
461 let resp = post_jsonrpc(app, methods::CREATE_PUSH_CONFIG, params).await;
462 assert!(resp.error.is_none(), "unexpected error: {:?}", resp.error);
463 let result =
464 serde_json::from_value::<TaskPushNotificationConfig>(resp.result.unwrap()).unwrap();
465 assert_eq!(result.task_id, "t1");
466 assert_eq!(result.id.as_deref(), Some("cfg1"));
467 assert_eq!(result.url, "http://example.com/callback");
468 }
469
470 #[tokio::test]
471 async fn test_get_push_config() {
472 let app = make_app();
473 let params = serde_json::json!({
474 "taskId": "t1",
475 "id": "cfg1"
476 });
477 let resp = post_jsonrpc(app, methods::GET_PUSH_CONFIG, params).await;
478 assert!(resp.error.is_some() || resp.result.is_some());
479 }
480
481 #[tokio::test]
482 async fn test_list_push_configs() {
483 let app = make_push_app();
484 let params = serde_json::json!({
485 "taskId": "t1"
486 });
487 let resp = post_jsonrpc(app, methods::LIST_PUSH_CONFIGS, params).await;
488 assert!(resp.error.is_none(), "unexpected error: {:?}", resp.error);
489 let result =
490 serde_json::from_value::<ListTaskPushNotificationConfigsResponse>(resp.result.unwrap())
491 .unwrap();
492 assert!(result.configs.is_empty());
493 }
494
495 #[tokio::test]
496 async fn test_delete_push_config() {
497 let app = make_app();
498 let params = serde_json::json!({
499 "taskId": "t1",
500 "id": "cfg1"
501 });
502 let resp = post_jsonrpc(app, methods::DELETE_PUSH_CONFIG, params).await;
503 assert!(resp.error.is_some() || resp.result.is_some());
504 }
505
506 #[tokio::test]
507 async fn test_get_extended_agent_card() {
508 let app = make_app();
509 let params = serde_json::json!({});
510 let resp = post_jsonrpc(app, methods::GET_EXTENDED_AGENT_CARD, params).await;
511 assert!(resp.error.is_some() || resp.result.is_some());
513 }
514
515 #[tokio::test]
516 async fn test_streaming_send_message() {
517 let app = make_app();
518 let body = serde_json::json!({
519 "message": {
520 "messageId": "m1",
521 "role": "ROLE_USER",
522 "parts": [{"text": "hello"}]
523 }
524 });
525 let rpc = JsonRpcRequest::new(
526 JsonRpcId::Number(1),
527 methods::SEND_STREAMING_MESSAGE,
528 Some(body),
529 );
530 let req = Request::builder()
531 .uri("/")
532 .method("POST")
533 .header("content-type", "application/json")
534 .header("accept", "text/event-stream")
535 .body(Body::from(serde_json::to_string(&rpc).unwrap()))
536 .unwrap();
537 let resp = app.oneshot(req).await.unwrap();
538 assert_eq!(resp.status(), StatusCode::OK);
540 }
541
542 #[tokio::test]
543 async fn test_streaming_subscribe_to_task() {
544 let app = make_app();
545 let rpc = JsonRpcRequest::new(
546 JsonRpcId::Number(1),
547 methods::SUBSCRIBE_TO_TASK,
548 Some(serde_json::json!({"id": "t1"})),
549 );
550 let req = Request::builder()
551 .uri("/")
552 .method("POST")
553 .header("content-type", "application/json")
554 .body(Body::from(serde_json::to_string(&rpc).unwrap()))
555 .unwrap();
556 let resp = app.oneshot(req).await.unwrap();
557 assert_eq!(resp.status(), StatusCode::OK);
559 }
560
561 #[tokio::test]
562 async fn test_streaming_send_message_legacy_method_rejected() {
563 let app = make_app();
564 let body = serde_json::json!({
565 "message": {
566 "messageId": "m1",
567 "role": "ROLE_USER",
568 "parts": [{"text": "hello"}]
569 }
570 });
571 let rpc = JsonRpcRequest::new(JsonRpcId::Number(1), "message.stream", Some(body));
572 let req = Request::builder()
573 .uri("/")
574 .method("POST")
575 .header("content-type", "application/json")
576 .header("accept", "text/event-stream")
577 .body(Body::from(serde_json::to_string(&rpc).unwrap()))
578 .unwrap();
579 let resp = app.oneshot(req).await.unwrap();
580 let body = resp.into_body().collect().await.unwrap().to_bytes();
581 let rpc_resp: JsonRpcResponse = serde_json::from_slice(&body).unwrap();
582 assert!(
583 rpc_resp.error.is_some(),
584 "unexpected result: {:?}",
585 rpc_resp.result
586 );
587 assert_eq!(rpc_resp.error.unwrap().code, error_code::METHOD_NOT_FOUND);
588 }
589
590 #[tokio::test]
591 async fn test_send_message_propagates_headers_as_service_params() {
592 let (handler, captured) = CapturingHandler::new();
593 let app = jsonrpc_router(handler);
594
595 let rpc = JsonRpcRequest::new(
596 JsonRpcId::Number(1),
597 methods::SEND_MESSAGE,
598 Some(serde_json::json!({
599 "message": {
600 "messageId": "m1",
601 "role": "ROLE_USER",
602 "parts": [{"text": "hi"}]
603 }
604 })),
605 );
606 let req = Request::builder()
607 .uri("/")
608 .method("POST")
609 .header("content-type", "application/json")
610 .header("authorization", "Bearer jsonrpc-token")
611 .header("x-tenant-id", "acme")
612 .body(Body::from(serde_json::to_string(&rpc).unwrap()))
613 .unwrap();
614 let resp = app.oneshot(req).await.unwrap();
615 assert_eq!(resp.status(), StatusCode::OK);
616
617 assert_header_captured(
618 &captured,
619 "send_message",
620 "authorization",
621 "Bearer jsonrpc-token",
622 );
623 assert_header_captured(&captured, "send_message", "x-tenant-id", "acme");
624 }
625
626 #[tokio::test]
627 async fn test_streaming_send_message_propagates_headers_as_service_params() {
628 let (handler, captured) = CapturingHandler::new();
629 let app = jsonrpc_router(handler);
630
631 let rpc = JsonRpcRequest::new(
632 JsonRpcId::Number(1),
633 methods::SEND_STREAMING_MESSAGE,
634 Some(serde_json::json!({
635 "message": {
636 "messageId": "m1",
637 "role": "ROLE_USER",
638 "parts": [{"text": "hi"}]
639 }
640 })),
641 );
642 let req = Request::builder()
643 .uri("/")
644 .method("POST")
645 .header("content-type", "application/json")
646 .header("accept", "text/event-stream")
647 .header("authorization", "Bearer jsonrpc-streaming-token")
648 .header("x-tenant-id", "acme")
649 .body(Body::from(serde_json::to_string(&rpc).unwrap()))
650 .unwrap();
651 let resp = app.oneshot(req).await.unwrap();
652 assert_eq!(resp.status(), StatusCode::OK);
653 let _ = resp.into_body().collect().await.unwrap().to_bytes();
655
656 assert_header_captured(
657 &captured,
658 "send_streaming_message",
659 "authorization",
660 "Bearer jsonrpc-streaming-token",
661 );
662 assert_header_captured(&captured, "send_streaming_message", "x-tenant-id", "acme");
663 }
664
665 #[tokio::test]
666 async fn test_jsonrpc_unary_methods_propagate_headers_as_service_params() {
667 let cases: &[(&'static str, &'static str, serde_json::Value)] = &[
670 (
671 methods::GET_TASK,
672 "get_task",
673 serde_json::json!({"id": "t1"}),
674 ),
675 (methods::LIST_TASKS, "list_tasks", serde_json::json!({})),
676 (
677 methods::CANCEL_TASK,
678 "cancel_task",
679 serde_json::json!({"id": "t1"}),
680 ),
681 (
682 methods::CREATE_PUSH_CONFIG,
683 "create_push_config",
684 serde_json::json!({"taskId": "t1", "url": "http://example.com/cb"}),
685 ),
686 (
687 methods::GET_PUSH_CONFIG,
688 "get_push_config",
689 serde_json::json!({"taskId": "t1", "id": "cfg1"}),
690 ),
691 (
692 methods::LIST_PUSH_CONFIGS,
693 "list_push_configs",
694 serde_json::json!({"taskId": "t1"}),
695 ),
696 (
697 methods::DELETE_PUSH_CONFIG,
698 "delete_push_config",
699 serde_json::json!({"taskId": "t1", "id": "cfg1"}),
700 ),
701 (
702 methods::GET_EXTENDED_AGENT_CARD,
703 "get_extended_agent_card",
704 serde_json::json!({}),
705 ),
706 ];
707
708 for (rpc_method, handler_method, params) in cases {
709 let (handler, captured) = CapturingHandler::new();
710 let app = jsonrpc_router(handler);
711 let rpc = JsonRpcRequest::new(JsonRpcId::Number(1), *rpc_method, Some(params.clone()));
712 let token = format!("Bearer token-for-{handler_method}");
713 let req = Request::builder()
714 .uri("/")
715 .method("POST")
716 .header("content-type", "application/json")
717 .header("authorization", &token)
718 .body(Body::from(serde_json::to_string(&rpc).unwrap()))
719 .unwrap();
720 let resp = app.oneshot(req).await.unwrap();
721 assert_eq!(resp.status(), StatusCode::OK);
722 assert_header_captured(&captured, handler_method, "authorization", &token);
723 }
724 }
725
726 #[tokio::test]
727 async fn test_jsonrpc_subscribe_to_task_propagates_headers_as_service_params() {
728 let (handler, captured) = CapturingHandler::new();
729 let app = jsonrpc_router(handler);
730 let rpc = JsonRpcRequest::new(
731 JsonRpcId::Number(1),
732 methods::SUBSCRIBE_TO_TASK,
733 Some(serde_json::json!({"id": "t1"})),
734 );
735 let req = Request::builder()
736 .uri("/")
737 .method("POST")
738 .header("content-type", "application/json")
739 .header("authorization", "Bearer subscribe-token")
740 .body(Body::from(serde_json::to_string(&rpc).unwrap()))
741 .unwrap();
742 let resp = app.oneshot(req).await.unwrap();
743 assert_eq!(resp.status(), StatusCode::OK);
744 let _ = resp.into_body().collect().await.unwrap().to_bytes();
745 assert_header_captured(
746 &captured,
747 "subscribe_to_task",
748 "authorization",
749 "Bearer subscribe-token",
750 );
751 }
752
753 #[tokio::test]
754 async fn test_streaming_invalid_method() {
755 let app = make_app();
756 let rpc = serde_json::json!({
757 "jsonrpc": "2.0",
758 "id": 1,
759 "method": "tasks.subscribe",
760 "params": {"bogus": true}
761 });
762 let req = Request::builder()
763 .uri("/")
764 .method("POST")
765 .header("content-type", "application/json")
766 .body(Body::from(serde_json::to_string(&rpc).unwrap()))
767 .unwrap();
768 let resp = app.oneshot(req).await.unwrap();
769 assert_eq!(resp.status(), StatusCode::OK);
770 }
771}