pub mod context;
pub mod http;
pub mod jsonrpc;
pub mod provider;
pub mod sse;
pub mod stdio;
pub use http::{HttpTransport, ResponseHandle, ResponseHandleAdapter};
pub use jsonrpc::{
JSONRPC_VERSION, JsonRpcError, JsonRpcMessage, JsonRpcNotification, JsonRpcRequest,
JsonRpcResponse,
};
pub use stdio::StdioTransport;
use crate::error::TransportError;
use bytes::Bytes;
use std::fmt;
use std::net::SocketAddr;
use tokio::sync::mpsc;
use tokio::time::Instant;
pub type Result<T> = std::result::Result<T, TransportError>;
pub const DEFAULT_MAX_MESSAGE_SIZE: usize = 10 * 1024 * 1024;
pub const DEFAULT_STDIO_BUFFER_SIZE: usize = 64 * 1024;
#[async_trait::async_trait]
pub trait Transport: Send + Sync {
async fn send_message(&self, message: &JsonRpcMessage) -> Result<()>;
async fn send_raw(&self, bytes: &[u8]) -> Result<()>;
async fn receive_message(&self) -> Result<Option<JsonRpcMessage>>;
fn transport_type(&self) -> TransportType;
async fn finalize_response(&self) -> Result<()>;
fn connection_context(&self) -> ConnectionContext;
async fn capture_raw_writer(&self) -> Result<Option<RawResponseWriter>> {
Ok(None)
}
fn as_any(&self) -> &dyn std::any::Any;
}
pub struct RawResponseWriter {
tx: mpsc::Sender<std::result::Result<Bytes, std::io::Error>>,
}
impl RawResponseWriter {
#[must_use]
pub const fn new(tx: mpsc::Sender<std::result::Result<Bytes, std::io::Error>>) -> Self {
Self { tx }
}
pub async fn send_raw(&self, bytes: &[u8]) -> Result<()> {
self.tx
.send(Ok(Bytes::copy_from_slice(bytes)))
.await
.map_err(|_| TransportError::ConnectionClosed("response channel closed".into()))
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum TransportType {
Stdio,
Http,
Context,
}
impl fmt::Display for TransportType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Stdio => write!(f, "stdio"),
Self::Http => write!(f, "http"),
Self::Context => write!(f, "context"),
}
}
}
#[derive(Debug, Clone)]
pub struct ConnectionContext {
pub connection_id: u64,
pub remote_addr: Option<SocketAddr>,
pub is_exclusive: bool,
pub connected_at: Instant,
}
impl ConnectionContext {
#[must_use]
pub fn stdio() -> Self {
Self {
connection_id: 0,
remote_addr: None,
is_exclusive: true,
connected_at: Instant::now(),
}
}
}
#[cfg(test)]
pub struct NullTransport;
#[cfg(test)]
#[async_trait::async_trait]
impl Transport for NullTransport {
async fn send_message(&self, _message: &JsonRpcMessage) -> Result<()> {
Ok(())
}
async fn send_raw(&self, _bytes: &[u8]) -> Result<()> {
Ok(())
}
async fn receive_message(&self) -> Result<Option<JsonRpcMessage>> {
std::future::pending().await
}
fn transport_type(&self) -> TransportType {
TransportType::Stdio
}
async fn finalize_response(&self) -> Result<()> {
Ok(())
}
fn connection_context(&self) -> ConnectionContext {
ConnectionContext::stdio()
}
fn as_any(&self) -> &dyn std::any::Any {
self
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_transport_type_display() {
assert_eq!(TransportType::Stdio.to_string(), "stdio");
assert_eq!(TransportType::Http.to_string(), "http");
assert_eq!(TransportType::Context.to_string(), "context");
}
#[test]
fn test_connection_context_stdio() {
let ctx = ConnectionContext::stdio();
assert_eq!(ctx.connection_id, 0);
assert!(ctx.remote_addr.is_none());
assert!(ctx.is_exclusive);
}
#[test]
fn test_constants() {
assert_eq!(DEFAULT_MAX_MESSAGE_SIZE, 10 * 1024 * 1024);
assert_eq!(DEFAULT_STDIO_BUFFER_SIZE, 64 * 1024);
}
}