#![allow(dead_code)]
use std::net::SocketAddr;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio::net::{TcpListener, TcpStream};
pub const SSE_HEAD: &str = "HTTP/1.1 200 OK\r\ncontent-type: text/event-stream\r\n\
transfer-encoding: chunked\r\n\r\n";
pub fn chunk(s: &str) -> String {
format!("{:x}\r\n{s}\r\n", s.len())
}
pub const LAST_CHUNK: &str = "0\r\n\r\n";
pub fn jsonrpc_status_frame(state: &str) -> String {
format!(
"data: {{\"jsonrpc\":\"2.0\",\"id\":1,\"result\":{{\"statusUpdate\":\
{{\"taskId\":\"t\",\"contextId\":\"c\",\"status\":{{\"state\":\"{state}\"}}}}}}}}\n\n"
)
}
pub fn rest_status_frame(state: &str) -> String {
format!(
"data: {{\"statusUpdate\":{{\"taskId\":\"t\",\"contextId\":\"c\",\
\"status\":{{\"state\":\"{state}\"}}}}}}\n\n"
)
}
pub async fn read_request(stream: &mut TcpStream) -> String {
let mut buf = [0u8; 8192];
let mut acc = Vec::new();
while let Ok(n) = stream.read(&mut buf).await {
if n == 0 {
break;
}
acc.extend_from_slice(&buf[..n]);
let text = String::from_utf8_lossy(&acc);
if let Some(head_end) = text.find("\r\n\r\n") {
let body_len = text[..head_end]
.lines()
.find_map(|l| {
let (name, value) = l.split_once(':')?;
name.eq_ignore_ascii_case("content-length")
.then(|| value.trim().parse::<usize>().ok())
.flatten()
})
.unwrap_or(0);
if acc.len() >= head_end + 4 + body_len {
break;
}
}
}
String::from_utf8_lossy(&acc).into_owned()
}
pub async fn serve<F, Fut>(handler: F) -> String
where
F: Fn(TcpStream, String) -> Fut + Clone + Send + Sync + 'static,
Fut: std::future::Future<Output = ()> + Send + 'static,
{
let listener = TcpListener::bind("127.0.0.1:0").await.expect("bind");
let addr: SocketAddr = listener.local_addr().expect("addr");
tokio::spawn(async move {
while let Ok((mut stream, _)) = listener.accept().await {
let handler = handler.clone();
tokio::spawn(async move {
let request = read_request(&mut stream).await;
handler(stream, request).await;
});
}
});
format!("http://{addr}")
}
pub async fn write(stream: &mut TcpStream, bytes: &str) {
let _ = stream.write_all(bytes.as_bytes()).await;
let _ = stream.flush().await;
}
pub fn params() -> a2a_protocol_types::MessageSendParams {
use a2a_protocol_types::{Message, MessageId, MessageRole, MessageSendParams, Part};
MessageSendParams {
tenant: None,
message: Message {
id: MessageId::new("m1"),
role: MessageRole::User,
parts: vec![Part::text("hi")],
task_id: None,
context_id: None,
reference_task_ids: None,
extensions: None,
metadata: None,
},
configuration: None,
metadata: None,
}
}