1959 lines
58 KiB
Markdown
1959 lines
58 KiB
Markdown
# QUIC Multi-Stream Crate Architecture
|
|
|
|
**Crate Name**: `quic-multistream`
|
|
**Version**: 0.1.0
|
|
**Status**: Design Phase
|
|
**Authors**: rUv Development Team
|
|
**Last Updated**: 2025-10-26
|
|
|
|
---
|
|
|
|
## Table of Contents
|
|
|
|
1. [Executive Summary](#executive-summary)
|
|
2. [Architecture Overview](#architecture-overview)
|
|
3. [Crate Structure](#crate-structure)
|
|
4. [Core Abstractions](#core-abstractions)
|
|
5. [Platform-Specific Implementations](#platform-specific-implementations)
|
|
6. [Integration Points](#integration-points)
|
|
7. [Dependencies](#dependencies)
|
|
8. [API Design](#api-design)
|
|
9. [Performance Targets](#performance-targets)
|
|
10. [Testing Strategy](#testing-strategy)
|
|
11. [Security Considerations](#security-considerations)
|
|
12. [Implementation Roadmap](#implementation-roadmap)
|
|
|
|
---
|
|
|
|
## Executive Summary
|
|
|
|
The `quic-multistream` crate provides a unified, cross-platform abstraction for QUIC multiplexed streaming that works seamlessly across native Rust (using `quinn`) and WebAssembly (using `WebTransport` API). This crate is a critical component of the MidStream Lean Agentic Learning System, enabling ultra-low-latency, multiplexed communication for distributed agent coordination.
|
|
|
|
### Key Features
|
|
|
|
- **Unified API**: Single API surface across native and WASM targets
|
|
- **Zero-Copy Streaming**: Efficient data transfer with minimal overhead
|
|
- **Multi-Stream Multiplexing**: 1000+ concurrent streams per connection
|
|
- **0-RTT Connection**: Sub-millisecond reconnection latency
|
|
- **Stream Prioritization**: Fine-grained control over stream urgency
|
|
- **Type-Safe**: Leverages Rust's type system for correctness
|
|
- **Production-Ready**: Comprehensive error handling and observability
|
|
|
|
### Design Principles
|
|
|
|
1. **Platform Abstraction**: Hide platform differences behind clean traits
|
|
2. **Performance First**: Zero-cost abstractions, minimal allocations
|
|
3. **Type Safety**: Compile-time guarantees for protocol correctness
|
|
4. **Observability**: Built-in metrics and tracing support
|
|
5. **Testability**: Mockable interfaces for comprehensive testing
|
|
|
|
---
|
|
|
|
## Architecture Overview
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────────────────────┐
|
|
│ quic-multistream Crate │
|
|
├─────────────────────────────────────────────────────────────────┤
|
|
│ │
|
|
│ ┌──────────────────────────────────────────────────────────┐ │
|
|
│ │ Public API (Unified Traits) │ │
|
|
│ │ QuicConnection, QuicStream, QuicServer, QuicClient │ │
|
|
│ └────────────────────┬─────────────────────────────────────┘ │
|
|
│ │ │
|
|
│ ┌───────────────┴──────────────────┐ │
|
|
│ │ │ │
|
|
│ ┌────▼─────────────┐ ┌─────────▼──────────┐ │
|
|
│ │ Native Backend │ │ WASM Backend │ │
|
|
│ │ (quinn-based) │ │ (WebTransport) │ │
|
|
│ ├──────────────────┤ ├────────────────────┤ │
|
|
│ │ - quinn 0.11 │ │ - web-sys │ │
|
|
│ │ - rustls 0.23 │ │ - wasm-bindgen │ │
|
|
│ │ - tokio 1.42 │ │ - js-sys │ │
|
|
│ │ - rcgen 0.13 │ │ - wasm-bindgen- │ │
|
|
│ │ │ │ futures 0.4 │ │
|
|
│ └──────────────────┘ └────────────────────┘ │
|
|
│ │
|
|
│ ┌──────────────────────────────────────────────────────────┐ │
|
|
│ │ Common Infrastructure │ │
|
|
│ │ - Error Types │ │
|
|
│ │ - Configuration │ │
|
|
│ │ - Metrics & Tracing │ │
|
|
│ │ - Buffer Management │ │
|
|
│ │ - Stream Prioritization │ │
|
|
│ └──────────────────────────────────────────────────────────┘ │
|
|
│ │
|
|
└─────────────────────────────────────────────────────────────────┘
|
|
│
|
|
▼
|
|
┌──────────────────────┐
|
|
│ Lean Agentic System │
|
|
│ Integration Layer │
|
|
└──────────────────────┘
|
|
```
|
|
|
|
---
|
|
|
|
## Crate Structure
|
|
|
|
### Directory Layout
|
|
|
|
```
|
|
crates/quic-multistream/
|
|
├── Cargo.toml # Crate manifest with feature flags
|
|
├── README.md # Crate documentation
|
|
├── LICENSE # MIT license
|
|
├── src/
|
|
│ ├── lib.rs # Crate root, re-exports
|
|
│ ├── error.rs # Error types
|
|
│ ├── config.rs # Configuration types
|
|
│ ├── connection.rs # QuicConnection trait and types
|
|
│ ├── stream.rs # QuicStream trait and types
|
|
│ ├── server.rs # QuicServer trait and types
|
|
│ ├── client.rs # QuicClient trait and types
|
|
│ ├── priority.rs # Stream priority management
|
|
│ ├── metrics.rs # Metrics and observability
|
|
│ ├── buffer.rs # Buffer pool and management
|
|
│ │
|
|
│ ├── native/ # Native (quinn) implementation
|
|
│ │ ├── mod.rs # Native module root
|
|
│ │ ├── connection.rs # quinn::Connection wrapper
|
|
│ │ ├── stream.rs # quinn::Stream wrapper
|
|
│ │ ├── server.rs # quinn::Endpoint server
|
|
│ │ ├── client.rs # quinn::Endpoint client
|
|
│ │ ├── tls.rs # TLS/rustls configuration
|
|
│ │ └── transport.rs # Transport configuration
|
|
│ │
|
|
│ ├── wasm/ # WASM (WebTransport) implementation
|
|
│ │ ├── mod.rs # WASM module root
|
|
│ │ ├── connection.rs # WebTransport session wrapper
|
|
│ │ ├── stream.rs # WebTransport stream wrapper
|
|
│ │ ├── client.rs # WebTransport client
|
|
│ │ └── bindings.rs # JavaScript bindings
|
|
│ │
|
|
│ └── util/ # Shared utilities
|
|
│ ├── mod.rs
|
|
│ ├── async_utils.rs # Async helpers
|
|
│ └── frame.rs # Frame encoding/decoding
|
|
│
|
|
├── tests/
|
|
│ ├── integration_tests.rs # Cross-platform integration tests
|
|
│ ├── native_tests.rs # Native-specific tests
|
|
│ └── wasm_tests.rs # WASM-specific tests
|
|
│
|
|
├── benches/
|
|
│ ├── throughput.rs # Throughput benchmarks
|
|
│ ├── latency.rs # Latency benchmarks
|
|
│ └── concurrent_streams.rs # Multi-stream benchmarks
|
|
│
|
|
└── examples/
|
|
├── echo_server.rs # Simple echo server
|
|
├── echo_client.rs # Simple echo client
|
|
├── multistream_demo.rs # Multiple streams demo
|
|
├── prioritized_streams.rs # Stream priority demo
|
|
└── wasm_client.rs # WASM client example
|
|
```
|
|
|
|
### File Responsibilities
|
|
|
|
| File | Lines of Code | Responsibility |
|
|
|------|---------------|----------------|
|
|
| `lib.rs` | ~150 | Public API re-exports, feature flags |
|
|
| `error.rs` | ~100 | Error types with thiserror |
|
|
| `config.rs` | ~200 | Configuration structs |
|
|
| `connection.rs` | ~300 | Connection trait and common types |
|
|
| `stream.rs` | ~250 | Stream traits and abstractions |
|
|
| `server.rs` | ~200 | Server trait and builder |
|
|
| `client.rs` | ~200 | Client trait and builder |
|
|
| `priority.rs` | ~150 | Priority queue and scheduling |
|
|
| `metrics.rs` | ~180 | Metrics collection and reporting |
|
|
| `buffer.rs` | ~220 | Buffer pool implementation |
|
|
| `native/connection.rs` | ~400 | quinn connection implementation |
|
|
| `native/stream.rs` | ~350 | quinn stream implementation |
|
|
| `native/server.rs` | ~300 | quinn server implementation |
|
|
| `native/client.rs` | ~280 | quinn client implementation |
|
|
| `native/tls.rs` | ~250 | TLS configuration |
|
|
| `wasm/connection.rs` | ~380 | WebTransport session implementation |
|
|
| `wasm/stream.rs` | ~320 | WebTransport stream implementation |
|
|
| `wasm/client.rs` | ~250 | WebTransport client implementation |
|
|
| **Total Production** | **~4,280 LOC** | **Core implementation** |
|
|
|
|
---
|
|
|
|
## Core Abstractions
|
|
|
|
### 1. QuicConnection Trait
|
|
|
|
The central abstraction representing a QUIC connection.
|
|
|
|
```rust
|
|
/// A QUIC connection that can open multiple streams
|
|
#[async_trait]
|
|
pub trait QuicConnection: Send + Sync {
|
|
/// Open a bidirectional stream
|
|
async fn open_bi_stream(&self) -> Result<Box<dyn QuicBiStream>, QuicError>;
|
|
|
|
/// Open a unidirectional stream
|
|
async fn open_uni_stream(&self) -> Result<Box<dyn QuicSendStream>, QuicError>;
|
|
|
|
/// Accept an incoming bidirectional stream
|
|
async fn accept_bi_stream(&self) -> Result<Box<dyn QuicBiStream>, QuicError>;
|
|
|
|
/// Accept an incoming unidirectional stream
|
|
async fn accept_uni_stream(&self) -> Result<Box<dyn QuicRecvStream>, QuicError>;
|
|
|
|
/// Open a bidirectional stream with priority
|
|
async fn open_bi_stream_with_priority(
|
|
&self,
|
|
priority: StreamPriority,
|
|
) -> Result<Box<dyn QuicBiStream>, QuicError>;
|
|
|
|
/// Send an unreliable datagram
|
|
async fn send_datagram(&self, data: Bytes) -> Result<(), QuicError>;
|
|
|
|
/// Receive an unreliable datagram
|
|
async fn recv_datagram(&self) -> Result<Bytes, QuicError>;
|
|
|
|
/// Get connection statistics
|
|
fn stats(&self) -> ConnectionStats;
|
|
|
|
/// Get connection ID
|
|
fn id(&self) -> ConnectionId;
|
|
|
|
/// Check if connection is closed
|
|
fn is_closed(&self) -> bool;
|
|
|
|
/// Close the connection gracefully
|
|
async fn close(&self, error_code: u64, reason: &[u8]) -> Result<(), QuicError>;
|
|
|
|
/// Get remote address
|
|
fn remote_address(&self) -> Result<SocketAddr, QuicError>;
|
|
|
|
/// Get local address
|
|
fn local_address(&self) -> Result<SocketAddr, QuicError>;
|
|
}
|
|
```
|
|
|
|
### 2. QuicStream Traits
|
|
|
|
Stream abstractions for different stream types.
|
|
|
|
```rust
|
|
/// A bidirectional QUIC stream
|
|
#[async_trait]
|
|
pub trait QuicBiStream: Send + Sync {
|
|
/// Write data to the stream
|
|
async fn write(&mut self, data: &[u8]) -> Result<usize, QuicError>;
|
|
|
|
/// Write all data to the stream
|
|
async fn write_all(&mut self, data: &[u8]) -> Result<(), QuicError>;
|
|
|
|
/// Read data from the stream
|
|
async fn read(&mut self, buf: &mut [u8]) -> Result<usize, QuicError>;
|
|
|
|
/// Read exact amount of data
|
|
async fn read_exact(&mut self, buf: &mut [u8]) -> Result<(), QuicError>;
|
|
|
|
/// Finish writing (send FIN)
|
|
async fn finish(&mut self) -> Result<(), QuicError>;
|
|
|
|
/// Set stream priority
|
|
fn set_priority(&mut self, priority: StreamPriority);
|
|
|
|
/// Get stream ID
|
|
fn id(&self) -> StreamId;
|
|
|
|
/// Get stream statistics
|
|
fn stats(&self) -> StreamStats;
|
|
|
|
/// Split into send and receive halves
|
|
fn split(self: Box<Self>) -> (Box<dyn QuicSendStream>, Box<dyn QuicRecvStream>);
|
|
}
|
|
|
|
/// A send-only QUIC stream
|
|
#[async_trait]
|
|
pub trait QuicSendStream: Send + Sync {
|
|
async fn write(&mut self, data: &[u8]) -> Result<usize, QuicError>;
|
|
async fn write_all(&mut self, data: &[u8]) -> Result<(), QuicError>;
|
|
async fn finish(&mut self) -> Result<(), QuicError>;
|
|
fn set_priority(&mut self, priority: StreamPriority);
|
|
fn id(&self) -> StreamId;
|
|
fn stats(&self) -> StreamStats;
|
|
}
|
|
|
|
/// A receive-only QUIC stream
|
|
#[async_trait]
|
|
pub trait QuicRecvStream: Send + Sync {
|
|
async fn read(&mut self, buf: &mut [u8]) -> Result<usize, QuicError>;
|
|
async fn read_exact(&mut self, buf: &mut [u8]) -> Result<(), QuicError>;
|
|
fn id(&self) -> StreamId;
|
|
fn stats(&self) -> StreamStats;
|
|
}
|
|
```
|
|
|
|
### 3. QuicServer Trait
|
|
|
|
Server-side abstraction for accepting connections.
|
|
|
|
```rust
|
|
/// A QUIC server that accepts incoming connections
|
|
#[async_trait]
|
|
pub trait QuicServer: Send + Sync {
|
|
/// Accept an incoming connection
|
|
async fn accept(&self) -> Result<Box<dyn QuicConnection>, QuicError>;
|
|
|
|
/// Get server statistics
|
|
fn stats(&self) -> ServerStats;
|
|
|
|
/// Get listening address
|
|
fn local_address(&self) -> Result<SocketAddr, QuicError>;
|
|
|
|
/// Shutdown the server
|
|
async fn shutdown(&self) -> Result<(), QuicError>;
|
|
}
|
|
|
|
/// Builder for creating a QuicServer
|
|
pub struct QuicServerBuilder {
|
|
config: ServerConfig,
|
|
}
|
|
|
|
impl QuicServerBuilder {
|
|
pub fn new() -> Self;
|
|
pub fn bind<A: ToSocketAddrs>(self, addr: A) -> Self;
|
|
pub fn with_tls_config(self, config: TlsConfig) -> Self;
|
|
pub fn with_max_connections(self, max: usize) -> Self;
|
|
pub fn with_max_streams_per_connection(self, max: u64) -> Self;
|
|
pub fn with_max_idle_timeout(self, timeout: Duration) -> Self;
|
|
pub fn build(self) -> Result<Box<dyn QuicServer>, QuicError>;
|
|
}
|
|
```
|
|
|
|
### 4. QuicClient Trait
|
|
|
|
Client-side abstraction for creating connections.
|
|
|
|
```rust
|
|
/// A QUIC client that can create connections
|
|
#[async_trait]
|
|
pub trait QuicClient: Send + Sync {
|
|
/// Connect to a remote server
|
|
async fn connect(&self, addr: &str) -> Result<Box<dyn QuicConnection>, QuicError>;
|
|
|
|
/// Connect with custom configuration
|
|
async fn connect_with_config(
|
|
&self,
|
|
addr: &str,
|
|
config: ClientConnectionConfig,
|
|
) -> Result<Box<dyn QuicConnection>, QuicError>;
|
|
|
|
/// Get client statistics
|
|
fn stats(&self) -> ClientStats;
|
|
}
|
|
|
|
/// Builder for creating a QuicClient
|
|
pub struct QuicClientBuilder {
|
|
config: ClientConfig,
|
|
}
|
|
|
|
impl QuicClientBuilder {
|
|
pub fn new() -> Self;
|
|
pub fn with_tls_config(self, config: TlsConfig) -> Self;
|
|
pub fn with_max_streams(self, max: u64) -> Self;
|
|
pub fn with_keep_alive(self, interval: Duration) -> Self;
|
|
pub fn with_0rtt(self, enabled: bool) -> Self;
|
|
pub fn build(self) -> Result<Box<dyn QuicClient>, QuicError>;
|
|
}
|
|
```
|
|
|
|
### 5. Supporting Types
|
|
|
|
```rust
|
|
/// Stream priority (0-255, higher is more important)
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
|
pub struct StreamPriority(u8);
|
|
|
|
impl StreamPriority {
|
|
pub const CRITICAL: Self = Self(255);
|
|
pub const HIGH: Self = Self(192);
|
|
pub const NORMAL: Self = Self(128);
|
|
pub const LOW: Self = Self(64);
|
|
pub const BACKGROUND: Self = Self(0);
|
|
|
|
pub fn new(value: u8) -> Self {
|
|
Self(value)
|
|
}
|
|
}
|
|
|
|
/// Unique connection identifier
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
|
pub struct ConnectionId([u8; 16]);
|
|
|
|
/// Unique stream identifier
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
|
pub struct StreamId(u64);
|
|
|
|
/// Connection statistics
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct ConnectionStats {
|
|
pub bytes_sent: u64,
|
|
pub bytes_received: u64,
|
|
pub packets_sent: u64,
|
|
pub packets_received: u64,
|
|
pub rtt: Duration,
|
|
pub cwnd: u64,
|
|
pub streams_open: u64,
|
|
pub streams_closed: u64,
|
|
pub datagrams_sent: u64,
|
|
pub datagrams_received: u64,
|
|
}
|
|
|
|
/// Stream statistics
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct StreamStats {
|
|
pub bytes_sent: u64,
|
|
pub bytes_received: u64,
|
|
pub write_buffer_size: usize,
|
|
pub read_buffer_size: usize,
|
|
}
|
|
|
|
/// Server statistics
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct ServerStats {
|
|
pub connections_accepted: u64,
|
|
pub connections_active: u64,
|
|
pub connections_rejected: u64,
|
|
pub total_bytes_sent: u64,
|
|
pub total_bytes_received: u64,
|
|
}
|
|
|
|
/// Client statistics
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct ClientStats {
|
|
pub connections_attempted: u64,
|
|
pub connections_successful: u64,
|
|
pub connections_failed: u64,
|
|
pub total_bytes_sent: u64,
|
|
pub total_bytes_received: u64,
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Platform-Specific Implementations
|
|
|
|
### Native Implementation (quinn)
|
|
|
|
The native implementation uses the production-ready `quinn` QUIC library.
|
|
|
|
#### Key Components
|
|
|
|
**1. NativeQuicConnection** (`native/connection.rs`)
|
|
|
|
```rust
|
|
pub struct NativeQuicConnection {
|
|
inner: Arc<quinn::Connection>,
|
|
metrics: Arc<RwLock<ConnectionMetrics>>,
|
|
stream_priority_manager: Arc<PriorityManager>,
|
|
}
|
|
|
|
impl NativeQuicConnection {
|
|
pub fn new(connection: quinn::Connection) -> Self {
|
|
Self {
|
|
inner: Arc::new(connection),
|
|
metrics: Arc::new(RwLock::new(ConnectionMetrics::default())),
|
|
stream_priority_manager: Arc::new(PriorityManager::new()),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[async_trait]
|
|
impl QuicConnection for NativeQuicConnection {
|
|
async fn open_bi_stream(&self) -> Result<Box<dyn QuicBiStream>, QuicError> {
|
|
let (send, recv) = self.inner
|
|
.open_bi()
|
|
.await
|
|
.map_err(QuicError::from)?;
|
|
|
|
Ok(Box::new(NativeQuicBiStream::new(send, recv, self.metrics.clone())))
|
|
}
|
|
|
|
// ... other trait implementations
|
|
}
|
|
```
|
|
|
|
**2. NativeQuicBiStream** (`native/stream.rs`)
|
|
|
|
```rust
|
|
pub struct NativeQuicBiStream {
|
|
send: quinn::SendStream,
|
|
recv: quinn::RecvStream,
|
|
metrics: Arc<RwLock<StreamMetrics>>,
|
|
id: StreamId,
|
|
}
|
|
|
|
#[async_trait]
|
|
impl QuicBiStream for NativeQuicBiStream {
|
|
async fn write(&mut self, data: &[u8]) -> Result<usize, QuicError> {
|
|
let written = self.send
|
|
.write(data)
|
|
.await
|
|
.map_err(QuicError::from)?;
|
|
|
|
self.metrics.write().unwrap().bytes_sent += written as u64;
|
|
Ok(written)
|
|
}
|
|
|
|
async fn read(&mut self, buf: &mut [u8]) -> Result<usize, QuicError> {
|
|
let read = self.recv
|
|
.read(buf)
|
|
.await
|
|
.map_err(QuicError::from)?
|
|
.unwrap_or(0);
|
|
|
|
self.metrics.write().unwrap().bytes_received += read as u64;
|
|
Ok(read)
|
|
}
|
|
|
|
// ... other implementations
|
|
}
|
|
```
|
|
|
|
**3. NativeQuicServer** (`native/server.rs`)
|
|
|
|
```rust
|
|
pub struct NativeQuicServer {
|
|
endpoint: Arc<quinn::Endpoint>,
|
|
config: ServerConfig,
|
|
stats: Arc<RwLock<ServerStats>>,
|
|
}
|
|
|
|
impl NativeQuicServer {
|
|
pub async fn bind(config: ServerConfig) -> Result<Self, QuicError> {
|
|
let server_config = build_quinn_server_config(&config)?;
|
|
let endpoint = quinn::Endpoint::server(
|
|
server_config,
|
|
config.bind_address,
|
|
)?;
|
|
|
|
Ok(Self {
|
|
endpoint: Arc::new(endpoint),
|
|
config,
|
|
stats: Arc::new(RwLock::new(ServerStats::default())),
|
|
})
|
|
}
|
|
}
|
|
|
|
#[async_trait]
|
|
impl QuicServer for NativeQuicServer {
|
|
async fn accept(&self) -> Result<Box<dyn QuicConnection>, QuicError> {
|
|
let connecting = self.endpoint
|
|
.accept()
|
|
.await
|
|
.ok_or(QuicError::ServerClosed)?;
|
|
|
|
let connection = connecting
|
|
.await
|
|
.map_err(QuicError::from)?;
|
|
|
|
self.stats.write().unwrap().connections_accepted += 1;
|
|
Ok(Box::new(NativeQuicConnection::new(connection)))
|
|
}
|
|
}
|
|
```
|
|
|
|
**4. TLS Configuration** (`native/tls.rs`)
|
|
|
|
```rust
|
|
pub fn build_server_tls_config(config: &TlsConfig) -> Result<rustls::ServerConfig, QuicError> {
|
|
let mut server_config = rustls::ServerConfig::builder()
|
|
.with_safe_default_cipher_suites()
|
|
.with_safe_default_kx_groups()
|
|
.with_protocol_versions(&[&rustls::version::TLS13])
|
|
.map_err(|e| QuicError::TlsError(e.to_string()))?
|
|
.with_no_client_auth();
|
|
|
|
match config {
|
|
TlsConfig::SelfSigned => {
|
|
// Generate self-signed certificate using rcgen
|
|
let cert = rcgen::generate_simple_self_signed(vec!["localhost".into()])
|
|
.map_err(|e| QuicError::TlsError(e.to_string()))?;
|
|
|
|
let cert_der = cert.serialize_der()
|
|
.map_err(|e| QuicError::TlsError(e.to_string()))?;
|
|
let key_der = cert.serialize_private_key_der();
|
|
|
|
server_config.cert_resolver = Arc::new(/* ... */);
|
|
}
|
|
TlsConfig::Certificate { cert_path, key_path } => {
|
|
// Load certificate from files
|
|
let certs = load_certs(cert_path)?;
|
|
let key = load_private_key(key_path)?;
|
|
server_config.cert_resolver = Arc::new(/* ... */);
|
|
}
|
|
}
|
|
|
|
Ok(server_config)
|
|
}
|
|
```
|
|
|
|
### WASM Implementation (WebTransport)
|
|
|
|
The WASM implementation uses browser WebTransport API via `web-sys`.
|
|
|
|
#### Key Components
|
|
|
|
**1. WasmQuicConnection** (`wasm/connection.rs`)
|
|
|
|
```rust
|
|
use wasm_bindgen::prelude::*;
|
|
use wasm_bindgen_futures::JsFuture;
|
|
use web_sys::{WebTransport, WebTransportBidirectionalStream, WebTransportDatagramDuplexStream};
|
|
|
|
pub struct WasmQuicConnection {
|
|
transport: WebTransport,
|
|
metrics: Arc<RwLock<ConnectionMetrics>>,
|
|
stream_counter: Arc<AtomicU64>,
|
|
}
|
|
|
|
impl WasmQuicConnection {
|
|
pub async fn connect(url: &str) -> Result<Self, QuicError> {
|
|
let transport = WebTransport::new(url)
|
|
.map_err(|e| QuicError::ConnectionFailed(format!("{:?}", e)))?;
|
|
|
|
// Wait for connection to be ready
|
|
let ready_promise = transport.ready();
|
|
JsFuture::from(ready_promise)
|
|
.await
|
|
.map_err(|e| QuicError::ConnectionFailed(format!("{:?}", e)))?;
|
|
|
|
Ok(Self {
|
|
transport,
|
|
metrics: Arc::new(RwLock::new(ConnectionMetrics::default())),
|
|
stream_counter: Arc::new(AtomicU64::new(0)),
|
|
})
|
|
}
|
|
}
|
|
|
|
#[async_trait(?Send)] // WASM is single-threaded
|
|
impl QuicConnection for WasmQuicConnection {
|
|
async fn open_bi_stream(&self) -> Result<Box<dyn QuicBiStream>, QuicError> {
|
|
let stream_promise = self.transport.create_bidirectional_stream();
|
|
let stream = JsFuture::from(stream_promise)
|
|
.await
|
|
.map_err(|e| QuicError::StreamOpenFailed(format!("{:?}", e)))?;
|
|
|
|
let bi_stream: WebTransportBidirectionalStream = stream
|
|
.dyn_into()
|
|
.map_err(|_| QuicError::InvalidStream)?;
|
|
|
|
let id = StreamId(self.stream_counter.fetch_add(1, Ordering::SeqCst));
|
|
Ok(Box::new(WasmQuicBiStream::new(bi_stream, id, self.metrics.clone())))
|
|
}
|
|
|
|
// ... other implementations
|
|
}
|
|
```
|
|
|
|
**2. WasmQuicBiStream** (`wasm/stream.rs`)
|
|
|
|
```rust
|
|
pub struct WasmQuicBiStream {
|
|
stream: WebTransportBidirectionalStream,
|
|
reader: web_sys::ReadableStreamDefaultReader,
|
|
writer: web_sys::WritableStreamDefaultWriter,
|
|
id: StreamId,
|
|
metrics: Arc<RwLock<StreamMetrics>>,
|
|
}
|
|
|
|
impl WasmQuicBiStream {
|
|
pub fn new(
|
|
stream: WebTransportBidirectionalStream,
|
|
id: StreamId,
|
|
metrics: Arc<RwLock<ConnectionMetrics>>,
|
|
) -> Self {
|
|
let reader = stream
|
|
.readable()
|
|
.get_reader()
|
|
.dyn_into()
|
|
.expect("Failed to get reader");
|
|
|
|
let writer = stream
|
|
.writable()
|
|
.get_writer()
|
|
.dyn_into()
|
|
.expect("Failed to get writer");
|
|
|
|
Self {
|
|
stream,
|
|
reader,
|
|
writer,
|
|
id,
|
|
metrics: Arc::new(RwLock::new(StreamMetrics::default())),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[async_trait(?Send)]
|
|
impl QuicBiStream for WasmQuicBiStream {
|
|
async fn write(&mut self, data: &[u8]) -> Result<usize, QuicError> {
|
|
let array = js_sys::Uint8Array::from(data);
|
|
let promise = self.writer.write_with_chunk(&array);
|
|
|
|
JsFuture::from(promise)
|
|
.await
|
|
.map_err(|e| QuicError::WriteError(format!("{:?}", e)))?;
|
|
|
|
self.metrics.write().unwrap().bytes_sent += data.len() as u64;
|
|
Ok(data.len())
|
|
}
|
|
|
|
async fn read(&mut self, buf: &mut [u8]) -> Result<usize, QuicError> {
|
|
let read_promise = self.reader.read();
|
|
let result = JsFuture::from(read_promise)
|
|
.await
|
|
.map_err(|e| QuicError::ReadError(format!("{:?}", e)))?;
|
|
|
|
let result_obj = js_sys::Object::from(result);
|
|
let done = js_sys::Reflect::get(&result_obj, &JsValue::from_str("done"))
|
|
.unwrap()
|
|
.as_bool()
|
|
.unwrap_or(false);
|
|
|
|
if done {
|
|
return Ok(0); // Stream closed
|
|
}
|
|
|
|
let value = js_sys::Reflect::get(&result_obj, &JsValue::from_str("value"))
|
|
.unwrap();
|
|
let array = js_sys::Uint8Array::from(value);
|
|
|
|
let length = array.length() as usize;
|
|
let copy_len = length.min(buf.len());
|
|
array.copy_to(&mut buf[..copy_len]);
|
|
|
|
self.metrics.write().unwrap().bytes_received += copy_len as u64;
|
|
Ok(copy_len)
|
|
}
|
|
|
|
// ... other implementations
|
|
}
|
|
```
|
|
|
|
**3. JavaScript Bindings** (`wasm/bindings.rs`)
|
|
|
|
```rust
|
|
#[wasm_bindgen]
|
|
pub struct WasmQuicClient {
|
|
inner: WasmQuicConnection,
|
|
}
|
|
|
|
#[wasm_bindgen]
|
|
impl WasmQuicClient {
|
|
#[wasm_bindgen(constructor)]
|
|
pub async fn connect(url: String) -> Result<WasmQuicClient, JsValue> {
|
|
let connection = WasmQuicConnection::connect(&url)
|
|
.await
|
|
.map_err(|e| JsValue::from_str(&e.to_string()))?;
|
|
|
|
Ok(WasmQuicClient { inner: connection })
|
|
}
|
|
|
|
#[wasm_bindgen]
|
|
pub async fn open_stream(&mut self) -> Result<JsValue, JsValue> {
|
|
let stream = self.inner
|
|
.open_bi_stream()
|
|
.await
|
|
.map_err(|e| JsValue::from_str(&e.to_string()))?;
|
|
|
|
// Wrap stream for JavaScript access
|
|
Ok(JsValue::from(/* stream wrapper */))
|
|
}
|
|
|
|
#[wasm_bindgen]
|
|
pub fn get_stats(&self) -> JsValue {
|
|
let stats = self.inner.stats();
|
|
serde_wasm_bindgen::to_value(&stats).unwrap()
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Integration Points
|
|
|
|
### 1. Lean Agentic Learning System Integration
|
|
|
|
**File**: `src/lean_agentic/quic_transport.rs` (new)
|
|
|
|
```rust
|
|
use quic_multistream::{QuicClient, QuicConnection, StreamPriority};
|
|
use crate::lean_agentic::{AgentMessage, AgentId};
|
|
|
|
/// QUIC-based transport for agent communication
|
|
pub struct QuicAgentTransport {
|
|
client: Box<dyn QuicClient>,
|
|
connections: DashMap<AgentId, Box<dyn QuicConnection>>,
|
|
}
|
|
|
|
impl QuicAgentTransport {
|
|
pub async fn new(config: QuicClientConfig) -> Result<Self, Error> {
|
|
let client = QuicClientBuilder::new()
|
|
.with_0rtt(true)
|
|
.with_max_streams(1000)
|
|
.build()?;
|
|
|
|
Ok(Self {
|
|
client,
|
|
connections: DashMap::new(),
|
|
})
|
|
}
|
|
|
|
/// Send high-priority message to agent
|
|
pub async fn send_critical(&self, agent_id: AgentId, message: AgentMessage) -> Result<(), Error> {
|
|
let connection = self.get_or_create_connection(agent_id).await?;
|
|
let mut stream = connection
|
|
.open_bi_stream_with_priority(StreamPriority::CRITICAL)
|
|
.await?;
|
|
|
|
let data = bincode::serialize(&message)?;
|
|
stream.write_all(&data).await?;
|
|
stream.finish().await?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// Receive messages from all agents
|
|
pub async fn receive_stream(&self, agent_id: AgentId) -> impl Stream<Item = AgentMessage> {
|
|
let connection = self.connections.get(&agent_id).unwrap();
|
|
|
|
async_stream::stream! {
|
|
loop {
|
|
let mut stream = connection.accept_bi_stream().await?;
|
|
let mut buffer = Vec::new();
|
|
|
|
loop {
|
|
let mut chunk = vec![0u8; 4096];
|
|
let n = stream.read(&mut chunk).await?;
|
|
if n == 0 { break; }
|
|
buffer.extend_from_slice(&chunk[..n]);
|
|
}
|
|
|
|
let message: AgentMessage = bincode::deserialize(&buffer)?;
|
|
yield message;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
### 2. Temporal-Compare Integration
|
|
|
|
**Enhancement**: Add QUIC streaming support for temporal sequences.
|
|
|
|
```rust
|
|
// In temporal-compare crate
|
|
use quic_multistream::{QuicStream, QuicConnection};
|
|
|
|
impl<T> TemporalComparator<T> {
|
|
/// Stream temporal sequences over QUIC for comparison
|
|
pub async fn compare_over_quic(
|
|
&self,
|
|
connection: &dyn QuicConnection,
|
|
seq1: &Sequence<T>,
|
|
seq2: &Sequence<T>,
|
|
algorithm: ComparisonAlgorithm,
|
|
) -> Result<ComparisonResult, TemporalError> {
|
|
// Open dedicated stream for comparison
|
|
let mut stream = connection.open_bi_stream().await?;
|
|
|
|
// Send sequences
|
|
let data = bincode::serialize(&(seq1, seq2, algorithm))?;
|
|
stream.write_all(&data).await?;
|
|
|
|
// Receive result
|
|
let mut result_buf = Vec::new();
|
|
stream.read_to_end(&mut result_buf).await?;
|
|
|
|
let result = bincode::deserialize(&result_buf)?;
|
|
Ok(result)
|
|
}
|
|
}
|
|
```
|
|
|
|
### 3. Dashboard Integration
|
|
|
|
**Enhancement**: Real-time QUIC metrics in dashboard.
|
|
|
|
```typescript
|
|
// In npm/src/dashboard.ts
|
|
import { QuicConnectionStats, QuicStreamStats } from 'quic-multistream-wasm';
|
|
|
|
interface QuicMetrics {
|
|
connections: Map<string, QuicConnectionStats>;
|
|
streams: Map<string, QuicStreamStats>;
|
|
totalThroughput: number;
|
|
averageRtt: number;
|
|
}
|
|
|
|
class MidStreamDashboard {
|
|
private quicMetrics: QuicMetrics;
|
|
|
|
updateQuicMetrics(connectionId: string, stats: QuicConnectionStats) {
|
|
this.quicMetrics.connections.set(connectionId, stats);
|
|
this.renderQuicSection();
|
|
}
|
|
|
|
private renderQuicSection() {
|
|
// Display QUIC connection stats, RTT graphs, stream counts, etc.
|
|
}
|
|
}
|
|
```
|
|
|
|
### 4. WASM Bindings Integration
|
|
|
|
**File**: `wasm-bindings/src/quic.rs` (new)
|
|
|
|
```rust
|
|
use wasm_bindgen::prelude::*;
|
|
use quic_multistream::wasm::{WasmQuicClient, WasmQuicConnection};
|
|
|
|
#[wasm_bindgen]
|
|
pub struct QuicClientHandle {
|
|
client: WasmQuicClient,
|
|
}
|
|
|
|
#[wasm_bindgen]
|
|
impl QuicClientHandle {
|
|
#[wasm_bindgen(constructor)]
|
|
pub async fn new() -> Result<QuicClientHandle, JsValue> {
|
|
let client = WasmQuicClient::new().await?;
|
|
Ok(QuicClientHandle { client })
|
|
}
|
|
|
|
#[wasm_bindgen]
|
|
pub async fn connect(&self, url: String) -> Result<ConnectionHandle, JsValue> {
|
|
let connection = self.client.connect(&url).await?;
|
|
Ok(ConnectionHandle { connection })
|
|
}
|
|
}
|
|
|
|
#[wasm_bindgen]
|
|
pub struct ConnectionHandle {
|
|
connection: WasmQuicConnection,
|
|
}
|
|
|
|
#[wasm_bindgen]
|
|
impl ConnectionHandle {
|
|
#[wasm_bindgen]
|
|
pub async fn open_stream(&self, priority: u8) -> Result<StreamHandle, JsValue> {
|
|
let stream = self.connection
|
|
.open_bi_stream_with_priority(StreamPriority::new(priority))
|
|
.await?;
|
|
Ok(StreamHandle { stream })
|
|
}
|
|
|
|
#[wasm_bindgen]
|
|
pub fn stats(&self) -> JsValue {
|
|
let stats = self.connection.stats();
|
|
serde_wasm_bindgen::to_value(&stats).unwrap()
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Dependencies
|
|
|
|
### Native Dependencies
|
|
|
|
**Cargo.toml**:
|
|
|
|
```toml
|
|
[package]
|
|
name = "quic-multistream"
|
|
version = "0.1.0"
|
|
edition = "2021"
|
|
description = "Cross-platform QUIC multiplexed streaming (native & WASM)"
|
|
license = "MIT"
|
|
|
|
[dependencies]
|
|
# Core async runtime
|
|
tokio = { version = "1.42", features = ["full"], optional = true }
|
|
async-trait = "0.1"
|
|
futures = "0.3"
|
|
|
|
# Error handling
|
|
thiserror = "2.0"
|
|
anyhow = "1.0"
|
|
|
|
# Serialization
|
|
serde = { version = "1.0", features = ["derive"] }
|
|
serde_json = "1.0"
|
|
bincode = "1.3"
|
|
|
|
# Bytes and buffers
|
|
bytes = "1.5"
|
|
parking_lot = "0.12"
|
|
|
|
# Metrics and tracing
|
|
tracing = "0.1"
|
|
tracing-subscriber = { version = "0.3", optional = true }
|
|
|
|
# Concurrency
|
|
dashmap = "6.1"
|
|
|
|
# Native QUIC dependencies (feature-gated)
|
|
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
|
quinn = { version = "0.11", optional = true }
|
|
rustls = { version = "0.23", optional = true }
|
|
rcgen = { version = "0.13", optional = true }
|
|
ring = "0.17"
|
|
|
|
# WASM dependencies (feature-gated)
|
|
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
|
wasm-bindgen = { version = "0.2", optional = true }
|
|
wasm-bindgen-futures = { version = "0.4", optional = true }
|
|
js-sys = { version = "0.3", optional = true }
|
|
web-sys = { version = "0.3", features = [
|
|
"WebTransport",
|
|
"WebTransportBidirectionalStream",
|
|
"WebTransportDatagramDuplexStream",
|
|
"WebTransportSendStream",
|
|
"WebTransportReceiveStream",
|
|
"ReadableStream",
|
|
"ReadableStreamDefaultReader",
|
|
"WritableStream",
|
|
"WritableStreamDefaultWriter",
|
|
], optional = true }
|
|
serde-wasm-bindgen = { version = "0.6", optional = true }
|
|
|
|
[dev-dependencies]
|
|
tokio-test = "0.4"
|
|
criterion = { version = "0.5", features = ["html_reports", "async_tokio"] }
|
|
tempfile = "3.8"
|
|
|
|
[features]
|
|
default = ["native"]
|
|
native = ["quinn", "rustls", "rcgen", "tokio", "tracing-subscriber"]
|
|
wasm = ["wasm-bindgen", "wasm-bindgen-futures", "js-sys", "web-sys", "serde-wasm-bindgen"]
|
|
full = ["native", "wasm"]
|
|
|
|
[[bench]]
|
|
name = "throughput"
|
|
harness = false
|
|
|
|
[[bench]]
|
|
name = "latency"
|
|
harness = false
|
|
|
|
[[bench]]
|
|
name = "concurrent_streams"
|
|
harness = false
|
|
```
|
|
|
|
### Version Requirements Rationale
|
|
|
|
| Dependency | Version | Rationale |
|
|
|------------|---------|-----------|
|
|
| `quinn` | 0.11 | Latest stable, HTTP/3 support, excellent performance |
|
|
| `rustls` | 0.23 | TLS 1.3, no OpenSSL dependency, WASM-compatible |
|
|
| `tokio` | 1.42 | Latest stable async runtime |
|
|
| `web-sys` | 0.3 | WebTransport API bindings |
|
|
| `wasm-bindgen` | 0.2 | Stable WASM interop |
|
|
| `bytes` | 1.5 | Zero-copy buffer management |
|
|
| `dashmap` | 6.1 | Lock-free concurrent hash map |
|
|
| `serde` | 1.0 | De facto serialization standard |
|
|
|
|
---
|
|
|
|
## API Design
|
|
|
|
### High-Level Usage Examples
|
|
|
|
#### 1. Simple Echo Server (Native)
|
|
|
|
```rust
|
|
use quic_multistream::{QuicServerBuilder, QuicServer};
|
|
|
|
#[tokio::main]
|
|
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|
let server = QuicServerBuilder::new()
|
|
.bind("0.0.0.0:4433")?
|
|
.with_max_connections(1000)
|
|
.with_max_streams_per_connection(100)
|
|
.build()?;
|
|
|
|
println!("QUIC server listening on 0.0.0.0:4433");
|
|
|
|
loop {
|
|
let connection = server.accept().await?;
|
|
tokio::spawn(async move {
|
|
while let Ok(mut stream) = connection.accept_bi_stream().await {
|
|
tokio::spawn(async move {
|
|
let mut buffer = vec![0u8; 4096];
|
|
while let Ok(n) = stream.read(&mut buffer).await {
|
|
if n == 0 { break; }
|
|
stream.write_all(&buffer[..n]).await.unwrap();
|
|
}
|
|
stream.finish().await.unwrap();
|
|
});
|
|
}
|
|
});
|
|
}
|
|
}
|
|
```
|
|
|
|
#### 2. Client with Prioritized Streams (Native)
|
|
|
|
```rust
|
|
use quic_multistream::{QuicClientBuilder, StreamPriority};
|
|
|
|
#[tokio::main]
|
|
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|
let client = QuicClientBuilder::new()
|
|
.with_0rtt(true)
|
|
.build()?;
|
|
|
|
let connection = client.connect("localhost:4433").await?;
|
|
|
|
// High-priority control stream
|
|
let mut control_stream = connection
|
|
.open_bi_stream_with_priority(StreamPriority::CRITICAL)
|
|
.await?;
|
|
control_stream.write_all(b"CONTROL MESSAGE").await?;
|
|
|
|
// Normal priority data stream
|
|
let mut data_stream = connection
|
|
.open_bi_stream_with_priority(StreamPriority::NORMAL)
|
|
.await?;
|
|
data_stream.write_all(b"DATA PAYLOAD").await?;
|
|
|
|
// Low priority logs stream
|
|
let mut log_stream = connection
|
|
.open_bi_stream_with_priority(StreamPriority::LOW)
|
|
.await?;
|
|
log_stream.write_all(b"LOG: operation completed").await?;
|
|
|
|
// Read responses
|
|
let mut response = vec![0u8; 1024];
|
|
let n = control_stream.read(&mut response).await?;
|
|
println!("Control response: {}", String::from_utf8_lossy(&response[..n]));
|
|
|
|
Ok(())
|
|
}
|
|
```
|
|
|
|
#### 3. WASM Client (Browser)
|
|
|
|
```rust
|
|
use wasm_bindgen::prelude::*;
|
|
use quic_multistream::wasm::WasmQuicClient;
|
|
|
|
#[wasm_bindgen]
|
|
pub async fn connect_and_send() -> Result<(), JsValue> {
|
|
let client = WasmQuicClient::new().await?;
|
|
let connection = client.connect("https://example.com:4433").await?;
|
|
|
|
let mut stream = connection.open_bi_stream().await?;
|
|
stream.write_all(b"Hello from WASM!").await?;
|
|
|
|
let mut response = vec![0u8; 1024];
|
|
let n = stream.read(&mut response).await?;
|
|
|
|
web_sys::console::log_1(&format!("Received: {}", String::from_utf8_lossy(&response[..n])).into());
|
|
|
|
Ok(())
|
|
}
|
|
```
|
|
|
|
#### 4. Multi-Agent Coordination
|
|
|
|
```rust
|
|
use quic_multistream::{QuicClientBuilder, StreamPriority};
|
|
use tokio::sync::mpsc;
|
|
|
|
async fn coordinate_agents(agent_urls: Vec<String>) -> Result<(), Box<dyn std::error::Error>> {
|
|
let client = QuicClientBuilder::new().build()?;
|
|
|
|
let (tx, mut rx) = mpsc::channel(100);
|
|
|
|
// Connect to all agents
|
|
for url in agent_urls {
|
|
let client = client.clone();
|
|
let tx = tx.clone();
|
|
|
|
tokio::spawn(async move {
|
|
let connection = client.connect(&url).await.unwrap();
|
|
|
|
// Open coordination stream
|
|
let mut stream = connection
|
|
.open_bi_stream_with_priority(StreamPriority::HIGH)
|
|
.await
|
|
.unwrap();
|
|
|
|
// Send coordination message
|
|
stream.write_all(b"COORDINATE").await.unwrap();
|
|
|
|
// Receive agent response
|
|
let mut response = vec![0u8; 1024];
|
|
let n = stream.read(&mut response).await.unwrap();
|
|
|
|
tx.send(response[..n].to_vec()).await.unwrap();
|
|
});
|
|
}
|
|
|
|
// Collect responses from all agents
|
|
drop(tx);
|
|
while let Some(response) = rx.recv().await {
|
|
println!("Agent response: {:?}", response);
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Performance Targets
|
|
|
|
### Latency Targets
|
|
|
|
| Metric | Target | Measurement Method |
|
|
|--------|--------|-------------------|
|
|
| 0-RTT Connection | < 1ms | Time from `connect()` to first stream open |
|
|
| Stream Open | < 100μs | Time to open new stream on existing connection |
|
|
| First Byte Latency | < 5ms | Time to send and receive first byte |
|
|
| Round-Trip Time (RTT) | < 10ms (LAN)<br>< 50ms (WAN) | Measured via connection stats |
|
|
|
|
### Throughput Targets
|
|
|
|
| Metric | Target | Measurement Method |
|
|
|--------|--------|-------------------|
|
|
| Single Stream | > 100 MB/s | Sequential write benchmark |
|
|
| Aggregate (100 streams) | > 1 GB/s | Parallel writes across streams |
|
|
| Small Messages | > 50,000 msg/s | 1KB message throughput |
|
|
| Large Messages | > 10,000 msg/s | 64KB message throughput |
|
|
|
|
### Resource Targets
|
|
|
|
| Metric | Target | Measurement Method |
|
|
|--------|--------|-------------------|
|
|
| Memory per Connection | < 256 KB | RSS measurement |
|
|
| Memory per Stream | < 16 KB | Heap profiling |
|
|
| CPU per Connection | < 1% (idle)<br>< 10% (active) | CPU profiling |
|
|
| Max Concurrent Streams | > 1,000 | Stress testing |
|
|
| Max Concurrent Connections | > 10,000 | Server stress testing |
|
|
|
|
### Benchmarking Infrastructure
|
|
|
|
**Criterion Benchmark Suite**:
|
|
|
|
```rust
|
|
// benches/throughput.rs
|
|
use criterion::{criterion_group, criterion_main, Criterion, BenchmarkId, Throughput};
|
|
use quic_multistream::{QuicClientBuilder, QuicServerBuilder};
|
|
|
|
async fn setup_connection() -> (QuicServer, QuicConnection) {
|
|
let server = QuicServerBuilder::new()
|
|
.bind("127.0.0.1:0")
|
|
.unwrap()
|
|
.build()
|
|
.unwrap();
|
|
|
|
let addr = server.local_address().unwrap();
|
|
let client = QuicClientBuilder::new().build().unwrap();
|
|
let connection = client.connect(&addr.to_string()).await.unwrap();
|
|
|
|
(server, connection)
|
|
}
|
|
|
|
fn throughput_benchmark(c: &mut Criterion) {
|
|
let rt = tokio::runtime::Runtime::new().unwrap();
|
|
|
|
let mut group = c.benchmark_group("throughput");
|
|
|
|
for size in [1024, 4096, 16384, 65536].iter() {
|
|
group.throughput(Throughput::Bytes(*size as u64));
|
|
|
|
group.bench_with_input(BenchmarkId::from_parameter(size), size, |b, &size| {
|
|
b.to_async(&rt).iter(|| async {
|
|
let (_, connection) = setup_connection().await;
|
|
let mut stream = connection.open_bi_stream().await.unwrap();
|
|
|
|
let data = vec![0u8; size];
|
|
stream.write_all(&data).await.unwrap();
|
|
stream.finish().await.unwrap();
|
|
});
|
|
});
|
|
}
|
|
|
|
group.finish();
|
|
}
|
|
|
|
criterion_group!(benches, throughput_benchmark);
|
|
criterion_main!(benches);
|
|
```
|
|
|
|
---
|
|
|
|
## Testing Strategy
|
|
|
|
### Test Pyramid
|
|
|
|
```
|
|
┌─────────────┐
|
|
│ E2E Tests │ (10%)
|
|
│ Cross-plat │
|
|
└──────┬──────┘
|
|
┌─────────┴─────────┐
|
|
│ Integration Tests │ (30%)
|
|
│ Native + WASM │
|
|
└─────────┬─────────┘
|
|
┌──────────────┴──────────────┐
|
|
│ Unit Tests │ (60%)
|
|
│ Per-module, Per-function │
|
|
└──────────────────────────────┘
|
|
```
|
|
|
|
### Unit Tests (60% of tests)
|
|
|
|
**Coverage**: Each module and function
|
|
|
|
```rust
|
|
// tests in src/connection.rs
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[tokio::test]
|
|
async fn test_connection_open_bi_stream() {
|
|
let connection = setup_test_connection().await;
|
|
let stream = connection.open_bi_stream().await;
|
|
assert!(stream.is_ok());
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_connection_stats() {
|
|
let connection = setup_test_connection().await;
|
|
let stats = connection.stats();
|
|
assert_eq!(stats.streams_open, 0);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_stream_write_read() {
|
|
let mut stream = setup_test_stream().await;
|
|
|
|
let data = b"test data";
|
|
stream.write_all(data).await.unwrap();
|
|
stream.finish().await.unwrap();
|
|
|
|
let mut buf = vec![0u8; 1024];
|
|
let n = stream.read(&mut buf).await.unwrap();
|
|
assert_eq!(&buf[..n], data);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_stream_priority() {
|
|
let mut stream = setup_test_stream().await;
|
|
stream.set_priority(StreamPriority::HIGH);
|
|
// Verify priority is set correctly
|
|
}
|
|
}
|
|
```
|
|
|
|
### Integration Tests (30% of tests)
|
|
|
|
**Coverage**: Cross-module interactions, end-to-end flows
|
|
|
|
```rust
|
|
// tests/integration_tests.rs
|
|
|
|
#[tokio::test]
|
|
async fn test_server_client_communication() {
|
|
let server = QuicServerBuilder::new()
|
|
.bind("127.0.0.1:0")
|
|
.unwrap()
|
|
.build()
|
|
.unwrap();
|
|
|
|
let addr = server.local_address().unwrap();
|
|
|
|
// Spawn server task
|
|
tokio::spawn(async move {
|
|
let connection = server.accept().await.unwrap();
|
|
let mut stream = connection.accept_bi_stream().await.unwrap();
|
|
|
|
let mut buf = vec![0u8; 1024];
|
|
let n = stream.read(&mut buf).await.unwrap();
|
|
stream.write_all(&buf[..n]).await.unwrap();
|
|
stream.finish().await.unwrap();
|
|
});
|
|
|
|
// Client connects and sends data
|
|
let client = QuicClientBuilder::new().build().unwrap();
|
|
let connection = client.connect(&addr.to_string()).await.unwrap();
|
|
let mut stream = connection.open_bi_stream().await.unwrap();
|
|
|
|
let message = b"Hello, QUIC!";
|
|
stream.write_all(message).await.unwrap();
|
|
stream.finish().await.unwrap();
|
|
|
|
let mut response = vec![0u8; 1024];
|
|
let n = stream.read(&mut response).await.unwrap();
|
|
|
|
assert_eq!(&response[..n], message);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_multiple_concurrent_streams() {
|
|
let (server, connection) = setup_connection().await;
|
|
|
|
let mut streams = vec![];
|
|
for i in 0..100 {
|
|
let stream = connection.open_bi_stream().await.unwrap();
|
|
streams.push(stream);
|
|
}
|
|
|
|
assert_eq!(streams.len(), 100);
|
|
assert_eq!(connection.stats().streams_open, 100);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_stream_prioritization() {
|
|
let connection = setup_connection().await;
|
|
|
|
let mut critical = connection
|
|
.open_bi_stream_with_priority(StreamPriority::CRITICAL)
|
|
.await
|
|
.unwrap();
|
|
|
|
let mut low = connection
|
|
.open_bi_stream_with_priority(StreamPriority::LOW)
|
|
.await
|
|
.unwrap();
|
|
|
|
// Verify critical stream gets bandwidth preference
|
|
// (requires custom test infrastructure)
|
|
}
|
|
```
|
|
|
|
### WASM-Specific Tests
|
|
|
|
```rust
|
|
// tests/wasm_tests.rs
|
|
#![cfg(target_arch = "wasm32")]
|
|
|
|
use wasm_bindgen_test::*;
|
|
use quic_multistream::wasm::WasmQuicClient;
|
|
|
|
wasm_bindgen_test_configure!(run_in_browser);
|
|
|
|
#[wasm_bindgen_test]
|
|
async fn test_wasm_connection() {
|
|
let client = WasmQuicClient::new().await.unwrap();
|
|
let connection = client
|
|
.connect("https://localhost:4433")
|
|
.await
|
|
.unwrap();
|
|
|
|
assert!(!connection.is_closed());
|
|
}
|
|
|
|
#[wasm_bindgen_test]
|
|
async fn test_wasm_stream_write() {
|
|
let client = WasmQuicClient::new().await.unwrap();
|
|
let connection = client.connect("https://localhost:4433").await.unwrap();
|
|
let mut stream = connection.open_bi_stream().await.unwrap();
|
|
|
|
let data = b"test from wasm";
|
|
let written = stream.write(data).await.unwrap();
|
|
assert_eq!(written, data.len());
|
|
}
|
|
```
|
|
|
|
### End-to-End Tests (10% of tests)
|
|
|
|
**Coverage**: Real-world scenarios, cross-platform
|
|
|
|
```rust
|
|
// tests/e2e_tests.rs
|
|
|
|
#[tokio::test]
|
|
async fn test_native_server_wasm_client() {
|
|
// Start native server
|
|
let server = start_native_server().await;
|
|
|
|
// Connect with WASM client (via headless browser)
|
|
let wasm_client = spawn_wasm_client().await;
|
|
|
|
// Send data from WASM to native
|
|
wasm_client.send(b"Hello from WASM").await;
|
|
|
|
// Verify server receives data
|
|
let received = server.receive().await;
|
|
assert_eq!(received, b"Hello from WASM");
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_multi_agent_coordination() {
|
|
// Spawn 10 agents
|
|
let agents = spawn_agents(10).await;
|
|
|
|
// Each agent opens 10 streams
|
|
for agent in &agents {
|
|
for _ in 0..10 {
|
|
agent.open_stream().await.unwrap();
|
|
}
|
|
}
|
|
|
|
// Verify all connections stable
|
|
for agent in &agents {
|
|
assert!(agent.is_connected());
|
|
assert_eq!(agent.stream_count(), 10);
|
|
}
|
|
}
|
|
```
|
|
|
|
### Test Coverage Goals
|
|
|
|
| Component | Target Coverage |
|
|
|-----------|-----------------|
|
|
| Core traits | 100% |
|
|
| Native implementation | 95% |
|
|
| WASM implementation | 90% |
|
|
| Error handling | 100% |
|
|
| Configuration | 90% |
|
|
| Overall | 95% |
|
|
|
|
---
|
|
|
|
## Security Considerations
|
|
|
|
### TLS Configuration
|
|
|
|
**Default Security Posture**:
|
|
|
|
```rust
|
|
pub fn default_tls_config() -> TlsConfig {
|
|
TlsConfig {
|
|
// TLS 1.3 only
|
|
min_version: TlsVersion::TLS13,
|
|
max_version: TlsVersion::TLS13,
|
|
|
|
// Strong cipher suites only
|
|
cipher_suites: vec![
|
|
CipherSuite::TLS13_AES_256_GCM_SHA384,
|
|
CipherSuite::TLS13_CHACHA20_POLY1305_SHA256,
|
|
],
|
|
|
|
// Require certificate verification
|
|
verify_certificates: true,
|
|
|
|
// No session resumption by default (can enable for 0-RTT)
|
|
enable_session_resumption: false,
|
|
|
|
// Certificate pinning for production
|
|
certificate_pins: None,
|
|
}
|
|
}
|
|
```
|
|
|
|
### Authentication
|
|
|
|
```rust
|
|
pub trait QuicAuthenticator: Send + Sync {
|
|
/// Authenticate incoming connection
|
|
async fn authenticate_connection(
|
|
&self,
|
|
peer_cert: &Certificate,
|
|
remote_addr: SocketAddr,
|
|
) -> Result<AuthResult, AuthError>;
|
|
|
|
/// Authenticate stream within connection
|
|
async fn authenticate_stream(
|
|
&self,
|
|
connection_id: ConnectionId,
|
|
stream_id: StreamId,
|
|
first_bytes: &[u8],
|
|
) -> Result<StreamAuthResult, AuthError>;
|
|
}
|
|
|
|
pub struct TokenAuthenticator {
|
|
valid_tokens: DashMap<String, TokenInfo>,
|
|
}
|
|
|
|
impl QuicAuthenticator for TokenAuthenticator {
|
|
async fn authenticate_stream(
|
|
&self,
|
|
_connection_id: ConnectionId,
|
|
_stream_id: StreamId,
|
|
first_bytes: &[u8],
|
|
) -> Result<StreamAuthResult, AuthError> {
|
|
// Parse token from first bytes
|
|
let token = parse_token(first_bytes)?;
|
|
|
|
// Validate token
|
|
if let Some(info) = self.valid_tokens.get(&token) {
|
|
if info.is_expired() {
|
|
return Err(AuthError::TokenExpired);
|
|
}
|
|
Ok(StreamAuthResult::Authenticated(info.user_id))
|
|
} else {
|
|
Err(AuthError::InvalidToken)
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
### Rate Limiting
|
|
|
|
```rust
|
|
pub struct RateLimiter {
|
|
connections_per_ip: DashMap<IpAddr, RateLimitState>,
|
|
global_connection_limit: AtomicU64,
|
|
max_connections_per_ip: u64,
|
|
max_global_connections: u64,
|
|
}
|
|
|
|
impl RateLimiter {
|
|
pub fn check_connection(&self, remote_addr: SocketAddr) -> Result<(), RateLimitError> {
|
|
// Check global limit
|
|
let global = self.global_connection_limit.load(Ordering::Relaxed);
|
|
if global >= self.max_global_connections {
|
|
return Err(RateLimitError::GlobalLimitExceeded);
|
|
}
|
|
|
|
// Check per-IP limit
|
|
let ip = remote_addr.ip();
|
|
let mut state = self.connections_per_ip
|
|
.entry(ip)
|
|
.or_insert_with(RateLimitState::new);
|
|
|
|
if state.connection_count >= self.max_connections_per_ip {
|
|
return Err(RateLimitError::IpLimitExceeded(ip));
|
|
}
|
|
|
|
state.connection_count += 1;
|
|
self.global_connection_limit.fetch_add(1, Ordering::Relaxed);
|
|
|
|
Ok(())
|
|
}
|
|
}
|
|
```
|
|
|
|
### Input Validation
|
|
|
|
```rust
|
|
pub struct StreamValidator;
|
|
|
|
impl StreamValidator {
|
|
/// Validate stream data before processing
|
|
pub fn validate_data(data: &[u8], max_size: usize) -> Result<(), ValidationError> {
|
|
// Check size limits
|
|
if data.len() > max_size {
|
|
return Err(ValidationError::DataTooLarge {
|
|
size: data.len(),
|
|
max: max_size,
|
|
});
|
|
}
|
|
|
|
// Check for null bytes in text data
|
|
if data.contains(&0) {
|
|
return Err(ValidationError::InvalidData("null bytes detected"));
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// Validate stream metadata
|
|
pub fn validate_stream_id(id: StreamId, max_streams: u64) -> Result<(), ValidationError> {
|
|
if id.0 > max_streams {
|
|
return Err(ValidationError::InvalidStreamId(id));
|
|
}
|
|
Ok(())
|
|
}
|
|
}
|
|
```
|
|
|
|
### Security Checklist
|
|
|
|
- [x] TLS 1.3 enforcement
|
|
- [x] Certificate verification
|
|
- [x] Rate limiting (per-IP and global)
|
|
- [x] Input validation
|
|
- [x] Authentication framework
|
|
- [x] No hardcoded secrets
|
|
- [x] Secure random number generation
|
|
- [x] Memory-safe (Rust guarantees)
|
|
- [x] DoS protection (connection limits)
|
|
- [x] Logging (no sensitive data)
|
|
|
|
---
|
|
|
|
## Implementation Roadmap
|
|
|
|
### Phase 1: Core Infrastructure (Week 1)
|
|
|
|
**Goals**: Establish foundation and build system
|
|
|
|
- [x] Create crate structure
|
|
- [ ] Define core traits (`QuicConnection`, `QuicStream`, etc.)
|
|
- [ ] Implement error types
|
|
- [ ] Set up configuration types
|
|
- [ ] Add metrics infrastructure
|
|
- [ ] Configure build system with feature flags
|
|
- [ ] Set up CI/CD pipeline
|
|
|
|
**Deliverables**:
|
|
- Compilable crate skeleton
|
|
- Core trait definitions
|
|
- Build infrastructure
|
|
- Basic tests (unit tests for types)
|
|
|
|
### Phase 2: Native Implementation (Week 2)
|
|
|
|
**Goals**: Complete quinn-based native implementation
|
|
|
|
- [ ] Implement `NativeQuicConnection`
|
|
- [ ] Implement `NativeQuicBiStream`, `NativeQuicSendStream`, `NativeQuicRecvStream`
|
|
- [ ] Implement `NativeQuicServer`
|
|
- [ ] Implement `NativeQuicClient`
|
|
- [ ] Add TLS configuration
|
|
- [ ] Add transport configuration
|
|
- [ ] Write unit tests (60% coverage)
|
|
- [ ] Write integration tests
|
|
|
|
**Deliverables**:
|
|
- Fully functional native QUIC implementation
|
|
- Echo server/client examples
|
|
- Test suite with 95% coverage
|
|
- Performance benchmarks
|
|
|
|
### Phase 3: WASM Implementation (Week 3)
|
|
|
|
**Goals**: Complete WebTransport-based WASM implementation
|
|
|
|
- [ ] Implement `WasmQuicConnection`
|
|
- [ ] Implement `WasmQuicBiStream`
|
|
- [ ] Implement `WasmQuicClient`
|
|
- [ ] Create JavaScript bindings
|
|
- [ ] Add wasm-bindgen exports
|
|
- [ ] Write WASM-specific tests
|
|
- [ ] Create browser demo
|
|
|
|
**Deliverables**:
|
|
- Fully functional WASM QUIC implementation
|
|
- Browser-based examples
|
|
- WASM test suite
|
|
- Documentation for browser usage
|
|
|
|
### Phase 4: Advanced Features (Week 4)
|
|
|
|
**Goals**: Add production-ready features
|
|
|
|
- [ ] Stream prioritization
|
|
- [ ] Datagram support (unreliable messages)
|
|
- [ ] Connection migration
|
|
- [ ] 0-RTT support
|
|
- [ ] Buffer pooling optimization
|
|
- [ ] Congestion control tuning
|
|
- [ ] Performance benchmarking
|
|
- [ ] Cross-platform integration tests
|
|
|
|
**Deliverables**:
|
|
- Advanced feature set
|
|
- Optimized performance
|
|
- Comprehensive benchmarks
|
|
- Cross-platform tests
|
|
|
|
### Phase 5: Integration & Documentation (Week 5)
|
|
|
|
**Goals**: Integrate with MidStream ecosystem
|
|
|
|
- [ ] Integrate with Lean Agentic system
|
|
- [ ] Integrate with temporal-compare
|
|
- [ ] Integrate with dashboard
|
|
- [ ] Create WASM bindings package
|
|
- [ ] Write comprehensive documentation
|
|
- [ ] Create tutorial and examples
|
|
- [ ] Performance tuning
|
|
- [ ] Security audit
|
|
|
|
**Deliverables**:
|
|
- Full ecosystem integration
|
|
- Complete documentation
|
|
- Example applications
|
|
- Security audit report
|
|
- Production-ready crate
|
|
|
|
### Phase 6: Production Hardening (Week 6)
|
|
|
|
**Goals**: Make production-ready
|
|
|
|
- [ ] Load testing (10,000+ connections)
|
|
- [ ] Stress testing (max streams)
|
|
- [ ] Failure recovery testing
|
|
- [ ] Memory leak testing
|
|
- [ ] Performance regression testing
|
|
- [ ] Security penetration testing
|
|
- [ ] Cross-browser testing (WASM)
|
|
- [ ] Final optimizations
|
|
|
|
**Deliverables**:
|
|
- Production-hardened crate
|
|
- Performance report
|
|
- Security report
|
|
- Published to crates.io
|
|
- Published npm package (WASM)
|
|
|
|
---
|
|
|
|
## Success Metrics
|
|
|
|
### Performance Metrics
|
|
|
|
| Metric | Baseline | Target | Measured |
|
|
|--------|----------|--------|----------|
|
|
| 0-RTT Connection | 5ms | < 1ms | TBD |
|
|
| Stream Open Latency | 500μs | < 100μs | TBD |
|
|
| Throughput (single) | 50 MB/s | > 100 MB/s | TBD |
|
|
| Throughput (100 streams) | 200 MB/s | > 1 GB/s | TBD |
|
|
| Memory per Connection | 512 KB | < 256 KB | TBD |
|
|
| Max Concurrent Streams | 100 | > 1,000 | TBD |
|
|
|
|
### Quality Metrics
|
|
|
|
| Metric | Target | Status |
|
|
|--------|--------|--------|
|
|
| Test Coverage | 95% | TBD |
|
|
| Documentation Coverage | 100% (public API) | TBD |
|
|
| Security Audit Score | A+ | TBD |
|
|
| Cross-platform Tests | Pass 100% | TBD |
|
|
| Example Coverage | 5+ examples | TBD |
|
|
|
|
### Ecosystem Integration
|
|
|
|
| Integration | Status | Notes |
|
|
|-------------|--------|-------|
|
|
| Lean Agentic System | Planned | Agent communication transport |
|
|
| Temporal Compare | Planned | Streaming sequence comparison |
|
|
| Dashboard | Planned | Real-time metrics display |
|
|
| WASM Bindings | Planned | Browser-based agents |
|
|
|
|
---
|
|
|
|
## Appendix A: Example Configurations
|
|
|
|
### Production Server Configuration
|
|
|
|
```rust
|
|
let server = QuicServerBuilder::new()
|
|
.bind("0.0.0.0:443")?
|
|
.with_tls_config(TlsConfig::Certificate {
|
|
cert_path: "/etc/ssl/certs/server.crt",
|
|
key_path: "/etc/ssl/private/server.key",
|
|
})
|
|
.with_max_connections(10_000)
|
|
.with_max_streams_per_connection(1_000)
|
|
.with_max_idle_timeout(Duration::from_secs(30))
|
|
.with_keep_alive(Duration::from_secs(10))
|
|
.with_congestion_control(CongestionControl::Bbr)
|
|
.with_rate_limiter(RateLimiter::new(100, 1000))
|
|
.with_authenticator(TokenAuthenticator::new())
|
|
.build()?;
|
|
```
|
|
|
|
### Development Client Configuration
|
|
|
|
```rust
|
|
let client = QuicClientBuilder::new()
|
|
.with_tls_config(TlsConfig::SelfSigned) // Accept self-signed certs
|
|
.with_0rtt(true)
|
|
.with_max_streams(100)
|
|
.with_keep_alive(Duration::from_secs(5))
|
|
.build()?;
|
|
```
|
|
|
|
---
|
|
|
|
## Appendix B: Error Handling
|
|
|
|
### Error Type Hierarchy
|
|
|
|
```rust
|
|
#[derive(Debug, Error)]
|
|
pub enum QuicError {
|
|
#[error("Connection failed: {0}")]
|
|
ConnectionFailed(String),
|
|
|
|
#[error("Stream open failed: {0}")]
|
|
StreamOpenFailed(String),
|
|
|
|
#[error("Write error: {0}")]
|
|
WriteError(String),
|
|
|
|
#[error("Read error: {0}")]
|
|
ReadError(String),
|
|
|
|
#[error("TLS error: {0}")]
|
|
TlsError(String),
|
|
|
|
#[error("Invalid configuration: {0}")]
|
|
InvalidConfig(String),
|
|
|
|
#[error("Server closed")]
|
|
ServerClosed,
|
|
|
|
#[error("Connection closed: {0}")]
|
|
ConnectionClosed(String),
|
|
|
|
#[error("Stream reset: {0}")]
|
|
StreamReset(u64),
|
|
|
|
#[error("Rate limit exceeded")]
|
|
RateLimitExceeded,
|
|
|
|
#[error("Authentication failed: {0}")]
|
|
AuthenticationFailed(String),
|
|
|
|
#[error("Invalid stream")]
|
|
InvalidStream,
|
|
|
|
#[error("Timeout after {0:?}")]
|
|
Timeout(Duration),
|
|
|
|
#[error("IO error: {0}")]
|
|
Io(#[from] std::io::Error),
|
|
|
|
#[error("Native error: {0}")]
|
|
Native(String),
|
|
|
|
#[error("WASM error: {0}")]
|
|
Wasm(String),
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Appendix C: References
|
|
|
|
1. **QUIC Specification**: RFC 9000 - QUIC: A UDP-Based Multiplexed and Secure Transport
|
|
2. **HTTP/3 Specification**: RFC 9114 - HTTP/3
|
|
3. **TLS for QUIC**: RFC 9001 - Using TLS to Secure QUIC
|
|
4. **WebTransport**: W3C WebTransport Specification
|
|
5. **Quinn Library**: https://github.com/quinn-rs/quinn
|
|
6. **Rustls**: https://github.com/rustls/rustls
|
|
7. **web-sys WebTransport**: https://rustwasm.github.io/wasm-bindgen/api/web_sys/struct.WebTransport.html
|
|
|
|
---
|
|
|
|
## Document History
|
|
|
|
| Version | Date | Author | Changes |
|
|
|---------|------|--------|---------|
|
|
| 0.1.0 | 2025-10-26 | rUv Team | Initial architecture design |
|
|
|
|
---
|
|
|
|
**End of Architecture Document**
|