Skip to main content

prism_mcp_rs/transport/
streaming_http.rs

1//! Proprietary Prism HTTP transport extensions
2//!
3//! This module implements historical Prism chunked/compressed endpoints. It is
4//! not the MCP 2026 Streamable HTTP conformance surface. Prefer
5//! `HttpClientTransport` and `HttpServerTransport` for interoperable MCP; these
6//! helpers require explicit `ProtocolMode::LegacyOnly` at the high-level client.
7//!
8//! ## Feature Requirements
9//!
10//! ```toml
11//! # Cargo.toml - HTTP enhancements
12//! [dependencies]
13//! prism-mcp-rs = { version = "3", features = ["http", "chunked-encoding", "compression", "http2"] }
14//! ```
15//!
16//! Individual features:
17//!
18//! ### Chunked Encoding Feature
19//! ```toml
20//! [dependencies]
21//! prism-mcp-rs = { version = "3", features = ["chunked-encoding"] }
22//! ```
23//! Enables Prism-specific chunked request endpoints for matching legacy peers.
24//!
25//! ### Compression Feature  
26//! ```toml
27//! [dependencies]
28//! prism-mcp-rs = { version = "3", features = ["compression"] }
29//! ```
30//! Enables response compression with Gzip, Brotli, and Zstd algorithms.
31//!
32//! ### HTTP/2 Feature
33//! ```toml
34//! [dependencies]
35//! prism-mcp-rs = { version = "3", features = ["http2"] }
36//! ```
37//! Enables HTTP/2 protocol support with multiplexing and server push.
38//!
39//! ## Capabilities
40//! - Chunked transfer encoding for large payloads
41//! - Response compression: Gzip, Brotli, Zstd
42//! - HTTP/2 protocol primitives
43//! - Configurable compression strategies
44//! - Backpressure and flow control
45//!
46//! Use only when both peers explicitly implement these endpoints and deployment
47//! measurements justify the additional protocol surface.
48
49use async_trait::async_trait;
50use serde_json::Value;
51use std::sync::Arc;
52use std::time::Duration;
53#[cfg(any(feature = "chunked-encoding", feature = "http2"))]
54use std::time::Instant;
55use tokio::sync::RwLock;
56
57#[cfg(feature = "chunked-encoding")]
58use tracing::{debug, warn};
59
60use crate::core::error::{McpError, McpResult};
61use crate::protocol::types::{JsonRpcNotification, JsonRpcRequest, JsonRpcResponse};
62use crate::transport::traits::Transport;
63#[cfg(feature = "chunked-encoding")]
64use crate::transport::traits::{ConnectionState, TransportStats};
65
66// Chunked encoding support
67#[cfg(feature = "chunked-encoding")]
68use bytes::{Bytes, BytesMut};
69#[cfg(feature = "chunked-encoding")]
70use tokio_stream::wrappers::ReceiverStream;
71
72// HTTP/2 protocol support
73#[cfg(feature = "http2")]
74use h2::client::{Connection, SendRequest};
75#[cfg(feature = "http2")]
76use http::{Method, Request};
77#[cfg(feature = "http2")]
78use tokio::net::TcpStream;
79
80// HTTP/2 specific types
81#[cfg(feature = "http2")]
82use http::HeaderMap;
83#[cfg(feature = "http2")]
84use std::collections::HashMap;
85#[cfg(feature = "http2")]
86use std::pin::Pin;
87
88/// HTTP/2 Server Push handler for processing pushed streams
89#[cfg(feature = "http2")]
90pub type ServerPushHandler = Box<
91    dyn Fn(PushPromise) -> Pin<Box<dyn std::future::Future<Output = McpResult<()>> + Send>>
92        + Send
93        + Sync,
94>;
95
96/// HTTP/2 Push Promise metadata
97#[cfg(feature = "http2")]
98#[derive(Debug, Clone)]
99pub struct PushPromise {
100    pub method: String,
101    pub path: String,
102    pub headers: HeaderMap,
103    pub stream_id: u32,
104    pub promised_stream_id: u32,
105}
106
107/// HTTP/2 stream management for bidirectional communication
108#[cfg(feature = "http2")]
109#[derive(Debug)]
110pub struct Http2StreamManager {
111    pub active_streams: HashMap<u32, StreamInfo>,
112    pub push_promises: HashMap<u32, PushPromise>,
113    pub max_concurrent_streams: usize,
114    pub connection_window_size: u32,
115}
116
117#[cfg(feature = "http2")]
118#[derive(Debug, Clone)]
119pub struct StreamInfo {
120    pub stream_id: u32,
121    pub method: String,
122    pub path: String,
123    pub state: StreamState,
124    pub created_at: Instant,
125    pub bytes_sent: u64,
126    pub bytes_received: u64,
127}
128
129#[cfg(feature = "http2")]
130#[derive(Debug, Clone, PartialEq)]
131pub enum StreamState {
132    Open,
133    HalfClosedLocal,
134    HalfClosedRemote,
135    Closed,
136    Reserved(u32), // For push promised streams
137}
138
139#[cfg(feature = "http2")]
140impl Http2StreamManager {
141    pub fn new(max_concurrent_streams: usize) -> Self {
142        Self {
143            active_streams: HashMap::new(),
144            push_promises: HashMap::new(),
145            max_concurrent_streams,
146            connection_window_size: 65535, // Default HTTP/2 window size
147        }
148    }
149
150    pub fn can_create_stream(&self) -> bool {
151        self.active_streams.len() < self.max_concurrent_streams
152    }
153
154    pub fn add_stream(&mut self, stream_id: u32, method: String, path: String) {
155        let stream_info = StreamInfo {
156            stream_id,
157            method,
158            path,
159            state: StreamState::Open,
160            created_at: Instant::now(),
161            bytes_sent: 0,
162            bytes_received: 0,
163        };
164        self.active_streams.insert(stream_id, stream_info);
165    }
166
167    pub fn add_push_promise(&mut self, stream_id: u32, promise: PushPromise) {
168        let promised_stream_id = promise.promised_stream_id;
169        self.push_promises.insert(stream_id, promise);
170        // Mark stream as reserved for the push promise
171        if let Some(stream_info) = self.active_streams.get_mut(&stream_id) {
172            stream_info.state = StreamState::Reserved(promised_stream_id);
173        }
174    }
175
176    pub fn close_stream(&mut self, stream_id: u32) {
177        if let Some(mut stream_info) = self.active_streams.remove(&stream_id) {
178            stream_info.state = StreamState::Closed;
179            // Clean up any associated push promises
180            self.push_promises.remove(&stream_id);
181        }
182    }
183
184    pub fn update_stream_bytes(&mut self, stream_id: u32, sent: u64, received: u64) {
185        if let Some(stream_info) = self.active_streams.get_mut(&stream_id) {
186            stream_info.bytes_sent += sent;
187            stream_info.bytes_received += received;
188        }
189    }
190
191    pub fn get_stream_stats(&self) -> (usize, u64, u64) {
192        let total_sent = self.active_streams.values().map(|s| s.bytes_sent).sum();
193        let total_received = self.active_streams.values().map(|s| s.bytes_received).sum();
194        (self.active_streams.len(), total_sent, total_received)
195    }
196}
197
198/// HTTP/2 specific configuration
199#[cfg(feature = "http2")]
200#[derive(Debug, Clone)]
201pub struct Http2Config {
202    /// Maximum concurrent streams per connection
203    pub max_concurrent_streams: usize,
204    /// Initial connection window size
205    pub initial_window_size: u32,
206    /// Maximum frame size
207    pub max_frame_size: u32,
208    /// Enable server push
209    pub enable_server_push: bool,
210    /// Server push cache size
211    pub push_cache_size: usize,
212    /// Connection timeout
213    pub connection_timeout: Duration,
214    /// Keep-alive interval
215    pub keep_alive_interval: Duration,
216    /// Enable push promise validation
217    pub validate_push_promises: bool,
218}
219
220#[cfg(feature = "http2")]
221impl Default for Http2Config {
222    fn default() -> Self {
223        Self {
224            max_concurrent_streams: 100,
225            initial_window_size: 65535,
226            max_frame_size: 16384,
227            enable_server_push: true,
228            push_cache_size: 1000,
229            connection_timeout: Duration::from_secs(30),
230            keep_alive_interval: Duration::from_secs(10),
231            validate_push_promises: true,
232        }
233    }
234}
235
236// Content analysis and streaming decision types
237#[derive(Debug, Clone, PartialEq)]
238pub struct StreamingAnalysis {
239    pub should_stream: bool,
240    pub estimated_size: usize,
241    pub content_type: ContentType,
242    pub recommended_strategy: StreamingStrategy,
243    pub estimated_chunks: usize,
244}
245
246#[derive(Debug, Clone, PartialEq)]
247pub enum ContentType {
248    Standard,
249    LargeText,
250    Binary,
251    Json,
252}
253
254#[derive(Debug, Clone, PartialEq)]
255pub enum StreamingStrategy {
256    Traditional,
257    ChunkedStreaming,
258    #[cfg(feature = "http2")]
259    Http2ServerPush,
260    #[cfg(feature = "http2")]
261    Http2Multiplexed,
262    #[cfg(feature = "compression")]
263    CompressedStreaming,
264}
265
266/// Configuration for streaming HTTP transport
267#[derive(Debug, Clone)]
268pub struct StreamingConfig {
269    /// Enable chunked transfer encoding for large requests
270    pub enable_chunked_transfer: bool,
271    /// Threshold in bytes to trigger chunked streaming
272    pub chunk_threshold: usize,
273    /// Chunk size for streaming operations
274    pub chunk_size: usize,
275    /// Enable compression support
276    pub enable_compression: bool,
277    /// Compression algorithm to use
278    pub compression_type: CompressionType,
279    /// Enable HTTP/2 Server Push if available
280    pub enable_http2_server_push: bool,
281    /// Timeout for streaming operations
282    pub streaming_timeout_ms: u64,
283    /// Maximum concurrent chunks
284    pub max_concurrent_chunks: usize,
285    /// Backpressure threshold
286    pub backpressure_threshold: usize,
287    /// Enable adaptive chunk sizing
288    pub adaptive_chunk_sizing: bool,
289    /// HTTP/2 specific configuration
290    #[cfg(feature = "http2")]
291    pub http2_config: Http2Config,
292}
293
294impl Default for StreamingConfig {
295    fn default() -> Self {
296        Self {
297            enable_chunked_transfer: true,
298            chunk_threshold: 8192, // 8KB threshold
299            chunk_size: 16384,     // 16KB chunks
300            enable_compression: true,
301            compression_type: CompressionType::Gzip,
302            enable_http2_server_push: false, // Opt-in
303            streaming_timeout_ms: 60_000,
304            max_concurrent_chunks: 10,
305            backpressure_threshold: 1024 * 1024, // 1MB
306            adaptive_chunk_sizing: true,
307            #[cfg(feature = "http2")]
308            http2_config: Http2Config::default(),
309        }
310    }
311}
312
313impl StreamingConfig {
314    /// Configuration for memory-constrained environments
315    pub fn memory_improved() -> Self {
316        Self {
317            chunk_threshold: 4096, // 4KB threshold
318            chunk_size: 8192,      // 8KB chunks
319            compression_type: CompressionType::Gzip,
320            max_concurrent_chunks: 5,
321            backpressure_threshold: 512 * 1024, // 512KB
322            ..Default::default()
323        }
324    }
325
326    /// Configuration for high-performance scenarios
327    pub fn performance_improved() -> Self {
328        Self {
329            chunk_threshold: 32768, // 32KB threshold
330            chunk_size: 65536,      // 64KB chunks
331            enable_http2_server_push: true,
332            max_concurrent_chunks: 20,
333            backpressure_threshold: 4 * 1024 * 1024, // 4MB
334            #[cfg(feature = "compression")]
335            compression_type: CompressionType::Brotli,
336            #[cfg(not(feature = "compression"))]
337            compression_type: CompressionType::Gzip,
338            #[cfg(feature = "http2")]
339            http2_config: Http2Config {
340                max_concurrent_streams: 200,
341                initial_window_size: 131072, // 128KB
342                max_frame_size: 32768,       // 32KB
343                enable_server_push: true,
344                push_cache_size: 2000,
345                connection_timeout: Duration::from_secs(60),
346                keep_alive_interval: Duration::from_secs(5),
347                validate_push_promises: true,
348            },
349            ..Default::default()
350        }
351    }
352
353    /// Builder method to set chunk size
354    pub fn with_chunk_size(mut self, chunk_size: usize) -> Self {
355        self.chunk_size = chunk_size;
356        self
357    }
358
359    /// Builder method to set compression level
360    pub fn with_compression_level(mut self, level: u8) -> Self {
361        // Map compression level to compression type
362        match level {
363            0 => {
364                self.compression_type = CompressionType::None;
365                self.enable_compression = false;
366            }
367            1..=6 => {
368                self.compression_type = CompressionType::Gzip;
369                self.enable_compression = true;
370            }
371            #[cfg(feature = "compression")]
372            7..=9 => {
373                self.compression_type = CompressionType::Brotli;
374                self.enable_compression = true;
375            }
376            #[cfg(not(feature = "compression"))]
377            7..=9 => {
378                self.compression_type = CompressionType::Gzip;
379                self.enable_compression = true;
380            }
381            _ => {
382                self.compression_type = CompressionType::Gzip;
383                self.enable_compression = true;
384            }
385        }
386        self
387    }
388
389    /// Builder method to set max concurrent streams
390    pub fn with_max_concurrent_streams(mut self, max_streams: usize) -> Self {
391        self.max_concurrent_chunks = max_streams;
392        self
393    }
394
395    /// Get compression level as u8 for tests
396    pub fn compression_level(&self) -> u8 {
397        match self.compression_type {
398            CompressionType::None => 0,
399            CompressionType::Gzip => 6,
400            #[cfg(feature = "compression")]
401            CompressionType::Brotli => 8,
402            #[cfg(feature = "compression")]
403            CompressionType::Zstd => 7,
404        }
405    }
406
407    /// Get max concurrent streams (alias for max_concurrent_chunks)
408    pub fn max_concurrent_streams(&self) -> usize {
409        self.max_concurrent_chunks
410    }
411}
412
413#[derive(Debug, Clone, PartialEq)]
414pub enum CompressionType {
415    None,
416    Gzip,
417    #[cfg(feature = "compression")]
418    Brotli,
419    #[cfg(feature = "compression")]
420    Zstd,
421}
422
423// For tests without streaming-compression feature
424#[cfg(all(test, not(feature = "compression")))]
425impl CompressionType {
426    #[allow(dead_code)]
427    pub fn test_brotli() -> Self {
428        Self::Gzip
429    }
430    #[allow(dead_code)]
431    pub fn test_zstd() -> Self {
432        Self::Gzip
433    }
434}
435
436/// smart content analyzer for streaming decisions
437pub struct ContentAnalyzer {
438    stats: Arc<RwLock<AnalysisStats>>,
439}
440
441#[derive(Debug, Default, Clone)]
442pub struct AnalysisStats {
443    pub total_requests: u64,
444    pub large_requests: u64,
445    pub binary_requests: u64,
446    pub avg_size: f64,
447}
448
449impl ContentAnalyzer {
450    pub fn new() -> Self {
451        Self {
452            stats: Arc::new(RwLock::new(AnalysisStats::default())),
453        }
454    }
455
456    /// Analyze request to determine if streaming is beneficial
457    pub async fn analyze_request(&self, request: &JsonRpcRequest) -> StreamingAnalysis {
458        let content_size = serde_json::to_string(request).unwrap_or_default().len();
459        let has_large_strings = self.has_large_string_content(request);
460        let has_binary_data = self.has_binary_content(request);
461
462        // Update statistics
463        {
464            let mut stats = self.stats.write().await;
465            stats.total_requests += 1;
466            if content_size > 8192 {
467                stats.large_requests += 1;
468            }
469            if has_binary_data {
470                stats.binary_requests += 1;
471            }
472            stats.avg_size = (stats.avg_size * (stats.total_requests - 1) as f64
473                + content_size as f64)
474                / stats.total_requests as f64;
475        }
476
477        let content_type = if has_binary_data {
478            ContentType::Binary
479        } else if has_large_strings {
480            ContentType::LargeText
481        } else if self.is_complex_json(request) {
482            ContentType::Json
483        } else {
484            ContentType::Standard
485        };
486
487        let should_stream = content_size > 8192 || has_large_strings || has_binary_data;
488
489        let recommended_strategy = if has_binary_data {
490            #[cfg(feature = "compression")]
491            {
492                StreamingStrategy::CompressedStreaming
493            }
494            #[cfg(not(feature = "compression"))]
495            {
496                StreamingStrategy::ChunkedStreaming
497            }
498        } else if content_size > 100_000 {
499            // Very large payloads benefit from multiplexing
500            #[cfg(feature = "http2")]
501            {
502                StreamingStrategy::Http2Multiplexed
503            }
504            #[cfg(not(feature = "http2"))]
505            {
506                StreamingStrategy::ChunkedStreaming
507            }
508        } else if content_size > 32768 {
509            #[cfg(feature = "http2")]
510            {
511                StreamingStrategy::Http2ServerPush
512            }
513            #[cfg(not(feature = "http2"))]
514            {
515                StreamingStrategy::ChunkedStreaming
516            }
517        } else if should_stream {
518            StreamingStrategy::ChunkedStreaming
519        } else {
520            StreamingStrategy::Traditional
521        };
522
523        StreamingAnalysis {
524            should_stream,
525            estimated_size: content_size,
526            content_type,
527            recommended_strategy,
528            estimated_chunks: (content_size / 16384).max(1),
529        }
530    }
531
532    fn has_large_string_content(&self, request: &JsonRpcRequest) -> bool {
533        if let Some(params) = &request.params {
534            self.find_large_strings(params, 4096)
535        } else {
536            false
537        }
538    }
539
540    fn has_binary_content(&self, request: &JsonRpcRequest) -> bool {
541        if let Some(params) = &request.params {
542            self.find_binary_indicators(params)
543        } else {
544            false
545        }
546    }
547
548    fn is_complex_json(&self, request: &JsonRpcRequest) -> bool {
549        if let Some(params) = &request.params {
550            self.count_json_depth(params) > 5
551        } else {
552            false
553        }
554    }
555
556    fn find_large_strings(&self, value: &Value, threshold: usize) -> bool {
557        Self::find_large_strings_recursive(value, threshold)
558    }
559
560    fn find_large_strings_recursive(value: &Value, threshold: usize) -> bool {
561        match value {
562            Value::String(s) => s.len() > threshold,
563            Value::Array(arr) => arr
564                .iter()
565                .any(|v| Self::find_large_strings_recursive(v, threshold)),
566            Value::Object(obj) => obj
567                .values()
568                .any(|v| Self::find_large_strings_recursive(v, threshold)),
569            _ => false,
570        }
571    }
572
573    fn find_binary_indicators(&self, value: &Value) -> bool {
574        Self::find_binary_indicators_recursive(value)
575    }
576
577    fn find_binary_indicators_recursive(value: &Value) -> bool {
578        match value {
579            Value::String(s) => {
580                // Look for base64 patterns or binary indicators
581                s.len() > 1000
582                    && (s
583                        .chars()
584                        .all(|c| c.is_ascii_alphanumeric() || c == '+' || c == '/' || c == '='))
585            }
586            Value::Array(arr) => arr.iter().any(Self::find_binary_indicators_recursive),
587            Value::Object(obj) => obj.iter().any(|(k, v)| {
588                k.contains("data")
589                    || k.contains("blob")
590                    || k.contains("binary")
591                    || Self::find_binary_indicators_recursive(v)
592            }),
593            _ => false,
594        }
595    }
596
597    fn count_json_depth(&self, value: &Value) -> usize {
598        Self::count_json_depth_recursive(value)
599    }
600
601    fn count_json_depth_recursive(value: &Value) -> usize {
602        match value {
603            Value::Array(arr) => {
604                1 + arr
605                    .iter()
606                    .map(Self::count_json_depth_recursive)
607                    .max()
608                    .unwrap_or(0)
609            }
610            Value::Object(obj) => {
611                1 + obj
612                    .values()
613                    .map(Self::count_json_depth_recursive)
614                    .max()
615                    .unwrap_or(0)
616            }
617            _ => 0,
618        }
619    }
620
621    pub async fn get_stats(&self) -> AnalysisStats {
622        self.stats.read().await.clone()
623    }
624
625    /// Check if content should be streamed based on size and type
626    pub fn should_stream(&self, content: &[u8]) -> bool {
627        let size = content.len();
628        let threshold = 8192; // 8KB default threshold
629
630        if size > threshold {
631            return true;
632        }
633
634        // Check for binary content that benefits from streaming
635        self.is_binary_content(content)
636    }
637
638    /// Detect optimal compression type for content
639    pub fn detect_optimal_compression_type(&self, content: &[u8]) -> CompressionType {
640        if content.is_empty() {
641            return CompressionType::None;
642        }
643
644        // Analyze content entropy to determine best compression
645        let entropy = self.calculate_entropy(content);
646
647        if entropy < 0.3 {
648            // Low entropy - highly repetitive content, good for compression
649            #[cfg(feature = "compression")]
650            return CompressionType::Brotli;
651            #[cfg(not(feature = "compression"))]
652            return CompressionType::Gzip;
653        } else if entropy < 0.7 {
654            // Medium entropy - moderate compression benefit
655            CompressionType::Gzip
656        } else {
657            // High entropy - minimal compression benefit
658            CompressionType::None
659        }
660    }
661
662    /// Get streaming threshold
663    pub fn streaming_threshold(&self) -> usize {
664        8192 // 8KB default
665    }
666
667    /// Calculate content entropy for compression analysis
668    fn calculate_entropy(&self, content: &[u8]) -> f64 {
669        if content.is_empty() {
670            return 0.0;
671        }
672
673        // For very small content, return high entropy (low compression benefit)
674        if content.len() < 50 {
675            return 0.8;
676        }
677
678        let mut freq = [0u32; 256];
679        for &byte in content {
680            freq[byte as usize] += 1;
681        }
682
683        let len = content.len() as f64;
684        let mut entropy = 0.0;
685
686        for &count in &freq {
687            if count > 0 {
688                let p = count as f64 / len;
689                entropy -= p * p.log2();
690            }
691        }
692
693        entropy / 8.0 // Normalize to 0-1 range
694    }
695
696    /// Check if content is binary
697    fn is_binary_content(&self, content: &[u8]) -> bool {
698        if content.is_empty() {
699            return false;
700        }
701
702        // Null bytes are a strong indicator of binary content
703        if content.contains(&0) {
704            // But if it's mostly nulls, it's probably test data
705            let null_count = content.iter().filter(|&&b| b == 0).count();
706            if (null_count as f64 / content.len() as f64) > 0.9 {
707                return false; // Mostly null = test data, not binary
708            }
709            return true;
710        }
711
712        // Simple heuristic: if more than 30% of bytes are non-printable, consider binary
713        let non_printable = content
714            .iter()
715            .filter(|&&b| b < 32 && b != b'\t' && b != b'\n' && b != b'\r')
716            .count();
717
718        (non_printable as f64 / content.len() as f64) > 0.3
719    }
720}
721
722/// Streaming buffer with flow control
723#[cfg(feature = "chunked-encoding")]
724pub struct StreamingBuffer {
725    buffer: BytesMut,
726    chunk_size: usize,
727    max_buffer_size: usize,
728    flow_control: FlowControl,
729    network_metrics: NetworkMetrics,
730}
731
732#[cfg(feature = "chunked-encoding")]
733#[derive(Debug, Clone)]
734pub struct FlowControl {
735    pub max_concurrent_chunks: usize,
736    pub backpressure_threshold: usize,
737    pub adaptive_chunk_sizing: bool,
738}
739
740#[cfg(feature = "chunked-encoding")]
741#[derive(Debug, Clone, Default)]
742pub struct NetworkMetrics {
743    pub high_bandwidth: bool,
744    pub low_latency: bool,
745    pub avg_latency_ms: f64,
746    pub throughput_bps: f64,
747}
748
749#[cfg(feature = "chunked-encoding")]
750impl StreamingBuffer {
751    pub fn new(chunk_size: usize, max_buffer_size: usize) -> Self {
752        Self {
753            buffer: BytesMut::with_capacity(chunk_size),
754            chunk_size,
755            max_buffer_size,
756            flow_control: FlowControl {
757                max_concurrent_chunks: 10,
758                backpressure_threshold: max_buffer_size / 2,
759                adaptive_chunk_sizing: true,
760            },
761            network_metrics: NetworkMetrics::default(),
762        }
763    }
764
765    /// Add data to buffer and yield chunks when ready
766    pub async fn add_data(&mut self, data: &[u8]) -> McpResult<Vec<Bytes>> {
767        // Check buffer size limit
768        if self.buffer.len() + data.len() > self.max_buffer_size {
769            return Err(McpError::transport(format!(
770                "Buffer size limit exceeded: {} bytes",
771                self.max_buffer_size
772            )));
773        }
774
775        self.buffer.extend_from_slice(data);
776
777        let mut chunks = Vec::new();
778        while self.buffer.len() >= self.chunk_size {
779            let chunk = self.buffer.split_to(self.chunk_size);
780            chunks.push(chunk.freeze());
781
782            // Apply flow control
783            if chunks.len() >= self.flow_control.max_concurrent_chunks {
784                break;
785            }
786        }
787
788        // Adaptive chunk sizing based on network conditions
789        if self.flow_control.adaptive_chunk_sizing {
790            self.adjust_chunk_size().await;
791        }
792
793        Ok(chunks)
794    }
795
796    /// Flush remaining data
797    pub fn flush(&mut self) -> Option<Bytes> {
798        if !self.buffer.is_empty() {
799            Some(self.buffer.split().freeze())
800        } else {
801            None
802        }
803    }
804
805    /// Dynamically adjust chunk size based on performance metrics
806    async fn adjust_chunk_size(&mut self) {
807        // Monitor network latency, throughput, etc.
808        let metrics = &self.network_metrics;
809
810        if metrics.high_bandwidth && metrics.low_latency {
811            self.chunk_size = (self.chunk_size * 2).min(64 * 1024);
812        } else if !metrics.low_latency {
813            self.chunk_size = (self.chunk_size / 2).max(1024);
814        }
815    }
816
817    pub fn update_network_metrics(&mut self, latency_ms: f64, throughput_bps: f64) {
818        self.network_metrics.avg_latency_ms = latency_ms;
819        self.network_metrics.throughput_bps = throughput_bps;
820        self.network_metrics.low_latency = latency_ms < 50.0;
821        self.network_metrics.high_bandwidth = throughput_bps > 10_000_000.0; // 10 Mbps
822    }
823}
824
825/// Streaming compressor for multiple compression algorithms
826#[cfg(feature = "compression")]
827pub struct StreamingCompressor {
828    compression_type: CompressionType,
829    threshold: usize,
830}
831
832#[cfg(feature = "compression")]
833impl StreamingCompressor {
834    pub fn new(compression_type: CompressionType) -> Self {
835        Self {
836            compression_type,
837            threshold: 1024, // Compress if > 1KB
838        }
839    }
840
841    /// Compress payload if above threshold and beneficial
842    pub async fn compress_if_beneficial(&self, data: &[u8]) -> McpResult<Vec<u8>> {
843        if data.len() < self.threshold {
844            return Ok(data.to_vec());
845        }
846
847        match self.compression_type {
848            CompressionType::Gzip => self.compress_gzip(data).await,
849            CompressionType::Brotli => self.compress_brotli(data).await,
850            CompressionType::Zstd => self.compress_zstd(data).await,
851            CompressionType::None => Ok(data.to_vec()),
852        }
853    }
854
855    async fn compress_gzip(&self, data: &[u8]) -> McpResult<Vec<u8>> {
856        use flate2::write::GzEncoder;
857        use flate2::Compression;
858        use std::io::Write;
859
860        let mut encoder = GzEncoder::new(Vec::new(), Compression::default());
861        encoder.write_all(data).map_err(McpError::io)?;
862        encoder.finish().map_err(McpError::io)
863    }
864
865    async fn compress_brotli(&self, data: &[u8]) -> McpResult<Vec<u8>> {
866        let mut output = Vec::new();
867        let mut reader = std::io::Cursor::new(data);
868        brotli::BrotliCompress(
869            &mut reader,
870            &mut output,
871            &brotli::enc::BrotliEncoderParams::default(),
872        )
873        .map_err(|e| McpError::internal(format!("Brotli compression failed: {e}")))?;
874        Ok(output)
875    }
876
877    async fn compress_zstd(&self, data: &[u8]) -> McpResult<Vec<u8>> {
878        zstd::bulk::compress(data, 3)
879            .map_err(|e| McpError::internal(format!("Zstd compression failed: {e}")))
880    }
881
882    /// Estimate compression ratio for decision making
883    pub fn estimate_compression_ratio(&self, data: &[u8]) -> f64 {
884        // Simple heuristic based on data characteristics
885        let entropy = self.calculate_entropy(data);
886        match self.compression_type {
887            CompressionType::None => 1.0,
888            CompressionType::Gzip => (1.0 - entropy * 0.6).max(0.3),
889            CompressionType::Brotli => (1.0 - entropy * 0.7).max(0.25),
890            CompressionType::Zstd => (1.0 - entropy * 0.65).max(0.28),
891        }
892    }
893
894    fn calculate_entropy(&self, data: &[u8]) -> f64 {
895        let mut counts = [0u32; 256];
896        for &byte in data {
897            counts[byte as usize] += 1;
898        }
899
900        let len = data.len() as f64;
901        let mut entropy = 0.0;
902
903        for &count in &counts {
904            if count > 0 {
905                let p = count as f64 / len;
906                entropy -= p * p.log2();
907            }
908        }
909
910        entropy / 8.0 // Normalize to 0-1
911    }
912}
913
914/// Streaming HTTP client transport with advanced features
915#[cfg(feature = "chunked-encoding")]
916pub struct StreamingHttpClientTransport {
917    client: reqwest::Client,
918    base_url: String,
919    config: StreamingConfig,
920    content_analyzer: ContentAnalyzer,
921    #[cfg(feature = "compression")]
922    compressor: Option<StreamingCompressor>,
923    #[cfg(feature = "http2")]
924    h2_client: Option<SendRequest<bytes::Bytes>>,
925    #[cfg(feature = "http2")]
926    h2_connection: Option<Connection<TcpStream, bytes::Bytes>>,
927    #[cfg(feature = "http2")]
928    server_push_handlers: Arc<RwLock<HashMap<String, ServerPushHandler>>>,
929    #[cfg(feature = "http2")]
930    stream_manager: Arc<RwLock<Http2StreamManager>>,
931    stats: Arc<RwLock<TransportStats>>,
932    connection_state: Arc<RwLock<ConnectionState>>,
933}
934
935#[cfg(feature = "chunked-encoding")]
936impl StreamingHttpClientTransport {
937    /// Create streaming HTTP transport with default configuration
938    pub async fn new<S: AsRef<str>>(base_url: S) -> McpResult<Self> {
939        Self::with_config(base_url, StreamingConfig::default()).await
940    }
941
942    /// Create streaming HTTP transport with custom configuration
943    pub async fn with_config<S: AsRef<str>>(
944        base_url: S,
945        config: StreamingConfig,
946    ) -> McpResult<Self> {
947        let client_builder =
948            reqwest::Client::builder().timeout(Duration::from_millis(config.streaming_timeout_ms));
949
950        // Note: For full HTTP/2 Server Push support, we would use h2 crate directly
951        // reqwest 0.12.x doesn't expose all HTTP/2 features we need
952        // The current implementation provides chunked streaming which works over HTTP/2
953        #[cfg(feature = "http2")]
954        let _http2_config = config.enable_http2_server_push; // Prepared for future h2 integration
955
956        let client = client_builder
957            .build()
958            .map_err(|e| McpError::Http(format!("Failed to create streaming client: {e}")))?;
959
960        #[cfg(feature = "compression")]
961        let compressor =
962            if config.enable_compression && config.compression_type != CompressionType::None {
963                Some(StreamingCompressor::new(config.compression_type.clone()))
964            } else {
965                None
966            };
967
968        // Extract HTTP/2 config before moving config
969        #[cfg(feature = "http2")]
970        let max_streams = config.http2_config.max_concurrent_streams;
971
972        Ok(Self {
973            client,
974            base_url: base_url.as_ref().to_string(),
975            config,
976            content_analyzer: ContentAnalyzer::new(),
977            #[cfg(feature = "compression")]
978            compressor,
979            #[cfg(feature = "http2")]
980            h2_client: None, // Will be initialized on first HTTP/2 request
981            #[cfg(feature = "http2")]
982            h2_connection: None,
983            #[cfg(feature = "http2")]
984            server_push_handlers: Arc::new(RwLock::new(HashMap::new())),
985            #[cfg(feature = "http2")]
986            stream_manager: Arc::new(RwLock::new(Http2StreamManager::new(max_streams))),
987            stats: Arc::new(RwLock::new(TransportStats::default())),
988            connection_state: Arc::new(RwLock::new(ConnectionState::Disconnected)),
989        })
990    }
991
992    /// Send request with smart streaming decision
993    async fn send_request_smart(&mut self, request: JsonRpcRequest) -> McpResult<JsonRpcResponse> {
994        let start_time = Instant::now();
995
996        // Analyze request to determine streaming strategy
997        let analysis = self.content_analyzer.analyze_request(&request).await;
998
999        debug!(
1000            "Streaming analysis: should_stream={}, size={}, strategy={:?}",
1001            analysis.should_stream, analysis.estimated_size, analysis.recommended_strategy
1002        );
1003
1004        let result = if analysis.should_stream && self.config.enable_chunked_transfer {
1005            match analysis.recommended_strategy {
1006                StreamingStrategy::ChunkedStreaming => {
1007                    match self.send_chunked_request(request.clone()).await {
1008                        Ok(response) => Ok(response),
1009                        Err(e) => {
1010                            warn!("Chunked request failed, falling back to traditional: {}", e);
1011                            self.send_traditional_request(request).await
1012                        }
1013                    }
1014                }
1015                #[cfg(feature = "compression")]
1016                StreamingStrategy::CompressedStreaming => {
1017                    match self.send_compressed_request(request.clone()).await {
1018                        Ok(response) => Ok(response),
1019                        Err(e) => {
1020                            warn!(
1021                                "Compressed request failed, falling back to traditional: {}",
1022                                e
1023                            );
1024                            self.send_traditional_request(request).await
1025                        }
1026                    }
1027                }
1028                #[cfg(feature = "http2")]
1029                StreamingStrategy::Http2ServerPush => {
1030                    match self.send_http2_request(request.clone()).await {
1031                        Ok(response) => Ok(response),
1032                        Err(e) => {
1033                            warn!("HTTP/2 request failed, falling back to chunked: {}", e);
1034                            match self.send_chunked_request(request.clone()).await {
1035                                Ok(response) => Ok(response),
1036                                Err(_) => self.send_traditional_request(request).await,
1037                            }
1038                        }
1039                    }
1040                }
1041                #[cfg(feature = "http2")]
1042                StreamingStrategy::Http2Multiplexed => {
1043                    // For multiplexed strategy, we use the same HTTP/2 implementation
1044                    // but could optimize for concurrent requests in the future
1045                    match self.send_http2_request(request.clone()).await {
1046                        Ok(response) => Ok(response),
1047                        Err(e) => {
1048                            warn!(
1049                                "HTTP/2 multiplexed request failed, falling back to chunked: {}",
1050                                e
1051                            );
1052                            match self.send_chunked_request(request.clone()).await {
1053                                Ok(response) => Ok(response),
1054                                Err(_) => self.send_traditional_request(request).await,
1055                            }
1056                        }
1057                    }
1058                }
1059                _ => self.send_traditional_request(request).await,
1060            }
1061        } else {
1062            self.send_traditional_request(request).await
1063        };
1064
1065        // Update statistics
1066        let _duration = start_time.elapsed();
1067        {
1068            let mut stats = self.stats.write().await;
1069            stats.requests_sent += 1;
1070            stats.bytes_sent += analysis.estimated_size as u64;
1071            // Note: More detailed latency tracking could be added to TransportStats in future
1072        }
1073
1074        result
1075    }
1076
1077    async fn send_chunked_request(
1078        &mut self,
1079        request: JsonRpcRequest,
1080    ) -> McpResult<JsonRpcResponse> {
1081        let request_json = serde_json::to_string(&request)?.into_bytes();
1082
1083        // Create streaming body
1084        let mut buffer =
1085            StreamingBuffer::new(self.config.chunk_size, self.config.backpressure_threshold);
1086        let chunks = buffer.add_data(&request_json).await?;
1087
1088        // Add final chunk if there's remaining data
1089        let final_chunk = buffer.flush();
1090
1091        // Create chunk stream
1092        let (tx, rx) = tokio::sync::mpsc::channel::<Result<bytes::Bytes, McpError>>(
1093            self.config.max_concurrent_chunks,
1094        );
1095
1096        // Send chunks
1097        tokio::spawn(async move {
1098            for chunk in chunks {
1099                if tx.send(Ok(chunk)).await.is_err() {
1100                    break;
1101                }
1102            }
1103            if let Some(chunk) = final_chunk {
1104                let _ = tx.send(Ok(chunk)).await;
1105            }
1106        });
1107
1108        let chunk_stream = ReceiverStream::new(rx);
1109
1110        let url = format!("{}/mcp/stream", self.base_url);
1111        let response = self
1112            .client
1113            .post(&url)
1114            .header("Transfer-Encoding", "chunked")
1115            .header("Content-Type", "application/json")
1116            .body(reqwest::Body::wrap_stream(chunk_stream))
1117            .send()
1118            .await
1119            .map_err(|e| McpError::Http(format!("Chunked request failed: {e}")))?;
1120
1121        self.handle_response(response).await
1122    }
1123
1124    #[cfg(feature = "compression")]
1125    async fn send_compressed_request(
1126        &mut self,
1127        request: JsonRpcRequest,
1128    ) -> McpResult<JsonRpcResponse> {
1129        let request_json = serde_json::to_string(&request)?.into_bytes();
1130
1131        // Compress if beneficial
1132        let request_bytes = if let Some(ref compressor) = self.compressor {
1133            compressor.compress_if_beneficial(&request_json).await?
1134        } else {
1135            request_json
1136        };
1137
1138        let url = format!("{}/mcp/compressed", self.base_url);
1139        let mut request_builder = self
1140            .client
1141            .post(&url)
1142            .header("Content-Type", "application/json")
1143            .body(request_bytes);
1144
1145        // Add compression headers
1146        if let Some(ref compressor) = self.compressor {
1147            let encoding = match compressor.compression_type {
1148                CompressionType::Gzip => "gzip",
1149                CompressionType::Brotli => "br",
1150                CompressionType::Zstd => "zstd",
1151                CompressionType::None => "identity",
1152            };
1153            request_builder = request_builder.header("Content-Encoding", encoding);
1154        }
1155
1156        let response = request_builder
1157            .send()
1158            .await
1159            .map_err(|e| McpError::Http(format!("Compressed request failed: {e}")))?;
1160
1161        self.handle_response(response).await
1162    }
1163
1164    #[cfg(feature = "http2")]
1165    async fn send_http2_request(&mut self, request: JsonRpcRequest) -> McpResult<JsonRpcResponse> {
1166        // Initialize H2 client if not already done
1167        if self.h2_client.is_none() {
1168            self.init_h2_client().await?;
1169        }
1170
1171        // Check if we can create a new stream
1172        let can_create_stream = {
1173            let stream_manager = self.stream_manager.read().await;
1174            stream_manager.can_create_stream()
1175        };
1176
1177        if !can_create_stream {
1178            debug!("Max streams reached, falling back to chunked streaming");
1179            return self.send_chunked_request(request).await;
1180        }
1181
1182        let request_json = serde_json::to_string(&request)?;
1183        let request_json_len = request_json.len();
1184        let request_bytes = request_json.into_bytes();
1185
1186        // Use direct h2 client for full control
1187        if let Some(ref mut h2_client) = self.h2_client {
1188            // Create HTTP request
1189            let http_request = Request::builder()
1190                .method(Method::POST)
1191                .uri("/mcp/http2")
1192                .header("content-type", "application/json")
1193                .header("content-length", request_bytes.len())
1194                .body(())
1195                .map_err(|e| McpError::Http(format!("Failed to build HTTP/2 request: {e}")))?;
1196
1197            // Send request and get stream
1198            let (response_future, mut send_stream) = h2_client
1199                .send_request(http_request, false)
1200                .map_err(|e| McpError::Http(format!("Failed to send HTTP/2 request: {e}")))?;
1201
1202            // Send body data
1203            send_stream
1204                .send_data(Bytes::from(request_bytes), true)
1205                .map_err(|e| McpError::Http(format!("Failed to send HTTP/2 body: {e}")))?;
1206
1207            // Wait for response
1208            let response = response_future
1209                .await
1210                .map_err(|e| McpError::Http(format!("HTTP/2 response error: {e}")))?;
1211
1212            // Process response
1213            let (parts, mut body) = response.into_parts();
1214
1215            if !parts.status.is_success() {
1216                return Err(McpError::Http(format!("HTTP/2 error: {}", parts.status)));
1217            }
1218
1219            // Collect response body
1220            let mut response_bytes = Vec::new();
1221            while let Some(chunk) = body.data().await {
1222                let chunk = chunk.map_err(|e| McpError::Http(format!("HTTP/2 body error: {e}")))?;
1223                response_bytes.extend_from_slice(&chunk);
1224                // Send flow control update
1225                let _ = body.flow_control().release_capacity(chunk.len());
1226            }
1227
1228            // Parse JSON response
1229            let response_text = String::from_utf8(response_bytes)
1230                .map_err(|e| McpError::Http(format!("Invalid UTF-8 in HTTP/2 response: {e}")))?;
1231
1232            let json_response: JsonRpcResponse =
1233                serde_json::from_str(&response_text).map_err(|e| {
1234                    McpError::Http(format!("Failed to parse HTTP/2 JSON response: {e}"))
1235                })?;
1236
1237            // Update stream manager
1238            {
1239                let mut stream_manager = self.stream_manager.write().await;
1240                stream_manager.update_stream_bytes(
1241                    0,
1242                    request_json_len as u64,
1243                    response_text.len() as u64,
1244                );
1245            }
1246
1247            Ok(json_response)
1248        } else {
1249            // Fallback to chunked streaming if h2 client not available
1250            debug!("H2 client not available, falling back to chunked streaming");
1251            self.send_chunked_request(request).await
1252        }
1253    }
1254
1255    async fn send_traditional_request(
1256        &mut self,
1257        request: JsonRpcRequest,
1258    ) -> McpResult<JsonRpcResponse> {
1259        let url = format!("{}/mcp", self.base_url);
1260
1261        let response = self
1262            .client
1263            .post(&url)
1264            .json(&request)
1265            .send()
1266            .await
1267            .map_err(|e| McpError::Http(format!("Traditional request failed: {e}")))?;
1268
1269        self.handle_response(response).await
1270    }
1271
1272    async fn handle_response(&self, response: reqwest::Response) -> McpResult<JsonRpcResponse> {
1273        if !response.status().is_success() {
1274            return Err(McpError::Http(format!("HTTP error: {}", response.status())));
1275        }
1276
1277        let json_response: JsonRpcResponse = response
1278            .json()
1279            .await
1280            .map_err(|e| McpError::Http(format!("Failed to parse response: {e}")))?;
1281
1282        Ok(json_response)
1283    }
1284
1285    pub async fn get_stats(&self) -> TransportStats {
1286        self.stats.read().await.clone()
1287    }
1288
1289    pub async fn get_analysis_stats(&self) -> AnalysisStats {
1290        self.content_analyzer.get_stats().await
1291    }
1292
1293    /// Register a server push handler for specific paths
1294    #[cfg(feature = "http2")]
1295    pub async fn register_push_handler<F>(&mut self, path: String, handler: F)
1296    where
1297        F: Fn(PushPromise) -> Pin<Box<dyn std::future::Future<Output = McpResult<()>> + Send>>
1298            + Send
1299            + Sync
1300            + 'static,
1301    {
1302        let boxed_handler: ServerPushHandler = Box::new(handler);
1303        let mut handlers = self.server_push_handlers.write().await;
1304        handlers.insert(path, boxed_handler);
1305    }
1306
1307    /// Get HTTP/2 stream statistics
1308    #[cfg(feature = "http2")]
1309    pub async fn get_http2_stats(&self) -> (usize, u64, u64) {
1310        let stream_manager = self.stream_manager.read().await;
1311        stream_manager.get_stream_stats()
1312    }
1313
1314    /// Initialize HTTP/2 client with direct h2 connection
1315    #[cfg(feature = "http2")]
1316    async fn init_h2_client(&mut self) -> McpResult<()> {
1317        use url::Url;
1318
1319        // Parse the base URL to extract host and port
1320        let url = Url::parse(&self.base_url)
1321            .map_err(|e| McpError::Http(format!("Invalid base URL: {e}")))?;
1322
1323        let host = url
1324            .host_str()
1325            .ok_or_else(|| McpError::Http("No host in base URL".to_string()))?;
1326        let port = url.port().unwrap_or(443); // Default to HTTPS port
1327
1328        // Establish TCP connection
1329        let tcp_stream = TcpStream::connect(format!("{host}:{port}"))
1330            .await
1331            .map_err(|e| McpError::Http(format!("Failed to connect to {host}:{port}: {e}")))?;
1332
1333        // Perform HTTP/2 handshake
1334        let (h2_client, connection) = h2::client::handshake(tcp_stream)
1335            .await
1336            .map_err(|e| McpError::Http(format!("HTTP/2 handshake failed: {e}")))?;
1337
1338        // Store the client and connection
1339        self.h2_client = Some(h2_client);
1340        self.h2_connection = Some(connection);
1341
1342        // Spawn connection driver task
1343        let connection = self.h2_connection.take().unwrap();
1344        tokio::spawn(async move {
1345            if let Err(e) = connection.await {
1346                tracing::error!("HTTP/2 connection error: {}", e);
1347            }
1348        });
1349
1350        debug!("HTTP/2 client initialized successfully");
1351        Ok(())
1352    }
1353
1354    /// Send multiplexed HTTP/2 requests for improved performance
1355    #[cfg(feature = "http2")]
1356    pub async fn send_multiplexed_requests(
1357        &mut self,
1358        requests: Vec<JsonRpcRequest>,
1359    ) -> McpResult<Vec<JsonRpcResponse>> {
1360        if self.h2_client.is_none() {
1361            self.init_h2_client().await?;
1362        }
1363
1364        let mut response_futures = Vec::new();
1365
1366        for request in requests {
1367            let request_json = serde_json::to_string(&request)?;
1368            let request_bytes = request_json.into_bytes();
1369
1370            if let Some(ref mut h2_client) = self.h2_client {
1371                // Create HTTP request
1372                let http_request = Request::builder()
1373                    .method(Method::POST)
1374                    .uri("/mcp/http2")
1375                    .header("content-type", "application/json")
1376                    .header("content-length", request_bytes.len())
1377                    .body(())
1378                    .map_err(|e| McpError::Http(format!("Failed to build HTTP/2 request: {e}")))?;
1379
1380                // Send request and get stream
1381                let (response_future, mut send_stream) = h2_client
1382                    .send_request(http_request, false)
1383                    .map_err(|e| McpError::Http(format!("Failed to send HTTP/2 request: {e}")))?;
1384
1385                // Send body data asynchronously
1386                tokio::spawn(async move {
1387                    if let Err(e) = send_stream.send_data(Bytes::from(request_bytes), true) {
1388                        tracing::error!("Failed to send HTTP/2 body: {}", e);
1389                    }
1390                });
1391
1392                response_futures.push(response_future);
1393            }
1394        }
1395
1396        // Wait for all responses concurrently
1397        let mut responses = Vec::new();
1398        for response_future in response_futures {
1399            match response_future.await {
1400                Ok(response) => {
1401                    let (parts, mut body) = response.into_parts();
1402
1403                    if !parts.status.is_success() {
1404                        return Err(McpError::Http(format!("HTTP/2 error: {}", parts.status)));
1405                    }
1406
1407                    // Collect response body
1408                    let mut response_bytes = Vec::new();
1409                    while let Some(chunk) = body.data().await {
1410                        let chunk =
1411                            chunk.map_err(|e| McpError::Http(format!("HTTP/2 body error: {e}")))?;
1412                        response_bytes.extend_from_slice(&chunk);
1413                        let _ = body.flow_control().release_capacity(chunk.len());
1414                    }
1415
1416                    // Parse JSON response
1417                    let response_text = String::from_utf8(response_bytes).map_err(|e| {
1418                        McpError::Http(format!("Invalid UTF-8 in HTTP/2 response: {e}"))
1419                    })?;
1420
1421                    let json_response: JsonRpcResponse = serde_json::from_str(&response_text)
1422                        .map_err(|e| {
1423                            McpError::Http(format!("Failed to parse HTTP/2 JSON response: {e}"))
1424                        })?;
1425
1426                    responses.push(json_response);
1427                }
1428                Err(e) => {
1429                    return Err(McpError::Http(format!("HTTP/2 response error: {e}")));
1430                }
1431            }
1432        }
1433
1434        Ok(responses)
1435    }
1436}
1437
1438#[cfg(feature = "chunked-encoding")]
1439#[async_trait]
1440impl Transport for StreamingHttpClientTransport {
1441    async fn send_request(&mut self, request: JsonRpcRequest) -> McpResult<JsonRpcResponse> {
1442        {
1443            let mut state = self.connection_state.write().await;
1444            *state = ConnectionState::Connected;
1445        }
1446
1447        let result = self.send_request_smart(request).await;
1448
1449        // Update connection state based on result
1450        if result.is_err() {
1451            let mut state = self.connection_state.write().await;
1452            *state = ConnectionState::Error("Request failed".to_string());
1453        }
1454
1455        result
1456    }
1457
1458    async fn send_notification(&mut self, notification: JsonRpcNotification) -> McpResult<()> {
1459        let url = format!("{}/mcp/notification", self.base_url);
1460
1461        let response = self
1462            .client
1463            .post(&url)
1464            .json(&notification)
1465            .send()
1466            .await
1467            .map_err(|e| McpError::Http(format!("Notification failed: {e}")))?;
1468
1469        if !response.status().is_success() {
1470            return Err(McpError::Http(format!(
1471                "Notification error: {}",
1472                response.status()
1473            )));
1474        }
1475
1476        Ok(())
1477    }
1478
1479    async fn receive_notification(&mut self) -> McpResult<Option<JsonRpcNotification>> {
1480        // Streaming HTTP doesn't support server-initiated notifications in the same way
1481        // This would require Server-Sent Events or WebSocket upgrade
1482        Ok(None)
1483    }
1484
1485    async fn close(&mut self) -> McpResult<()> {
1486        let mut state = self.connection_state.write().await;
1487        *state = ConnectionState::Disconnected;
1488        Ok(())
1489    }
1490}
1491
1492// Stub implementations for when streaming features are not enabled
1493#[cfg(not(feature = "chunked-encoding"))]
1494pub struct StreamingHttpClientTransport;
1495
1496#[cfg(not(feature = "chunked-encoding"))]
1497impl StreamingHttpClientTransport {
1498    pub async fn new<S: AsRef<str>>(_base_url: S) -> McpResult<Self> {
1499        Err(McpError::Transport(
1500            "Streaming HTTP feature not enabled".to_string(),
1501        ))
1502    }
1503
1504    pub async fn with_config<S: AsRef<str>>(
1505        _base_url: S,
1506        _config: StreamingConfig,
1507    ) -> McpResult<Self> {
1508        Err(McpError::Transport(
1509            "Streaming HTTP feature not enabled".to_string(),
1510        ))
1511    }
1512}
1513
1514#[cfg(not(feature = "chunked-encoding"))]
1515#[async_trait]
1516impl Transport for StreamingHttpClientTransport {
1517    async fn send_request(&mut self, _request: JsonRpcRequest) -> McpResult<JsonRpcResponse> {
1518        Err(McpError::Transport(
1519            "Streaming HTTP feature not enabled".to_string(),
1520        ))
1521    }
1522
1523    async fn send_notification(&mut self, _notification: JsonRpcNotification) -> McpResult<()> {
1524        Err(McpError::Transport(
1525            "Streaming HTTP feature not enabled".to_string(),
1526        ))
1527    }
1528
1529    async fn receive_notification(&mut self) -> McpResult<Option<JsonRpcNotification>> {
1530        Err(McpError::Transport(
1531            "Streaming HTTP feature not enabled".to_string(),
1532        ))
1533    }
1534
1535    async fn close(&mut self) -> McpResult<()> {
1536        Ok(())
1537    }
1538}
1539
1540// Default implementations and re-exports
1541impl Default for ContentAnalyzer {
1542    fn default() -> Self {
1543        Self::new()
1544    }
1545}
1546
1547// Make ContentAnalyzer cloneable
1548impl Clone for ContentAnalyzer {
1549    fn clone(&self) -> Self {
1550        Self {
1551            stats: Arc::clone(&self.stats),
1552        }
1553    }
1554}
1555
1556// Add missing helper types for tests
1557#[derive(Debug, Clone)]
1558pub struct AdaptiveBuffer {
1559    buffer: Vec<u8>,
1560    optimal_size: usize,
1561}
1562
1563impl Default for AdaptiveBuffer {
1564    fn default() -> Self {
1565        Self::new()
1566    }
1567}
1568
1569impl AdaptiveBuffer {
1570    pub fn new() -> Self {
1571        let buffer = Vec::with_capacity(16384); // Pre-allocate 16KB
1572        Self {
1573            buffer,
1574            optimal_size: 16384, // 16KB default
1575        }
1576    }
1577
1578    pub fn write(&mut self, data: &[u8]) {
1579        self.buffer.extend_from_slice(data);
1580        // Adaptive sizing logic
1581        if self.buffer.len() > self.optimal_size * 2 {
1582            self.optimal_size = (self.optimal_size * 3) / 2;
1583        }
1584    }
1585
1586    pub fn capacity(&self) -> usize {
1587        self.buffer.capacity()
1588    }
1589
1590    pub fn optimal_size(&self) -> usize {
1591        self.optimal_size
1592    }
1593
1594    pub fn len(&self) -> usize {
1595        self.buffer.len()
1596    }
1597
1598    pub fn is_empty(&self) -> bool {
1599        self.buffer.is_empty()
1600    }
1601
1602    pub fn read(&mut self, size: usize) -> Vec<u8> {
1603        let read_size = size.min(self.buffer.len());
1604
1605        self.buffer.drain(..read_size).collect()
1606    }
1607
1608    pub fn clear(&mut self) {
1609        self.buffer.clear();
1610    }
1611}
1612
1613#[derive(Debug, Clone)]
1614pub struct FlowControlMetrics {
1615    window_size: usize,
1616    bytes_sent: usize,
1617    bytes_acked: usize,
1618    bytes_received: usize,
1619    requests_pending: usize,
1620    last_activity: std::time::Instant,
1621    rtt_estimate: Duration,
1622}
1623
1624impl Default for FlowControlMetrics {
1625    fn default() -> Self {
1626        Self::new()
1627    }
1628}
1629
1630impl FlowControlMetrics {
1631    pub fn new() -> Self {
1632        Self {
1633            window_size: 65536, // 64KB default
1634            bytes_sent: 0,
1635            bytes_acked: 0,
1636            bytes_received: 0,
1637            requests_pending: 0,
1638            last_activity: std::time::Instant::now(),
1639            rtt_estimate: Duration::from_millis(100),
1640        }
1641    }
1642
1643    pub fn update_window_size(&mut self, new_size: usize) {
1644        self.window_size = new_size;
1645    }
1646
1647    pub fn record_bytes_sent(&mut self, bytes: usize) {
1648        self.bytes_sent += bytes;
1649    }
1650
1651    pub fn record_bytes_acked(&mut self, bytes: usize) {
1652        self.bytes_acked += bytes;
1653    }
1654
1655    pub fn window_size(&self) -> usize {
1656        self.window_size
1657    }
1658
1659    pub fn bytes_in_flight(&self) -> usize {
1660        self.bytes_sent.saturating_sub(self.bytes_acked)
1661    }
1662
1663    pub fn available_window(&self) -> usize {
1664        self.window_size.saturating_sub(self.bytes_in_flight())
1665    }
1666
1667    pub fn should_send(&self) -> bool {
1668        self.available_window() > 0
1669    }
1670
1671    pub fn update_rtt(&mut self, rtt: Duration) {
1672        // Simple exponential moving average
1673        self.rtt_estimate = Duration::from_millis(
1674            (self.rtt_estimate.as_millis() as f64 * 0.875 + rtt.as_millis() as f64 * 0.125) as u64,
1675        );
1676    }
1677
1678    pub fn estimated_rtt(&self) -> Duration {
1679        self.rtt_estimate
1680    }
1681
1682    pub fn record_bytes_received(&mut self, bytes: usize) {
1683        self.bytes_received += bytes;
1684        self.last_activity = std::time::Instant::now();
1685    }
1686
1687    pub fn record_request_start(&mut self) {
1688        self.requests_pending += 1;
1689        self.last_activity = std::time::Instant::now();
1690    }
1691
1692    pub fn record_request_complete(&mut self) {
1693        if self.requests_pending > 0 {
1694            self.requests_pending -= 1;
1695        }
1696        self.last_activity = std::time::Instant::now();
1697    }
1698
1699    pub fn calculate_throughput(&self) -> f64 {
1700        if self.rtt_estimate.as_millis() == 0 {
1701            return 0.0;
1702        }
1703        (self.bytes_received as f64 * 1000.0) / self.rtt_estimate.as_millis() as f64
1704    }
1705
1706    pub fn should_throttle(&self) -> bool {
1707        self.requests_pending > 10 || self.available_window() < 1024
1708    }
1709}
1710
1711#[cfg(test)]
1712mod tests {
1713    use super::*;
1714    use serde_json::json;
1715    use std::time::Duration;
1716    use tokio::time::timeout;
1717
1718    #[test]
1719    fn test_streaming_config_default() {
1720        let config = StreamingConfig::default();
1721        assert_eq!(config.chunk_size, 16384);
1722        assert!(config.enable_compression);
1723        assert_eq!(config.compression_level(), 6);
1724        assert_eq!(config.max_concurrent_streams(), 10);
1725    }
1726
1727    #[test]
1728    fn test_streaming_config_builder() {
1729        let config = StreamingConfig::default()
1730            .with_chunk_size(4096)
1731            .with_compression_level(0)
1732            .with_compression_level(3)
1733            .with_max_concurrent_streams(50);
1734
1735        assert_eq!(config.chunk_size, 4096);
1736        assert!(config.enable_compression); // Level 3 enables compression
1737        assert_eq!(config.compression_level(), 6); // Level 3 maps to Gzip which returns 6
1738        assert_eq!(config.max_concurrent_streams(), 50);
1739    }
1740
1741    #[test]
1742    fn test_content_analyzer_creation() {
1743        let _analyzer = ContentAnalyzer::new();
1744        // Basic creation test - if this compiles, the constructor works
1745        let _default_analyzer = ContentAnalyzer::default();
1746    }
1747
1748    #[test]
1749    fn test_content_analyzer_should_stream() {
1750        let analyzer = ContentAnalyzer::new();
1751
1752        // Test small content (should not stream)
1753        let small_data = "small data".as_bytes();
1754        assert!(!analyzer.should_stream(small_data));
1755
1756        // Test large content (should stream)
1757        let large_data = vec![0u8; 10_000];
1758        assert!(analyzer.should_stream(&large_data));
1759
1760        // Test boundary case
1761        let boundary_data = vec![0u8; analyzer.streaming_threshold()];
1762        assert!(!analyzer.should_stream(&boundary_data)); // Equal to threshold
1763
1764        let over_boundary = vec![0u8; analyzer.streaming_threshold() + 1];
1765        assert!(analyzer.should_stream(&over_boundary)); // Over threshold
1766    }
1767
1768    #[test]
1769    fn test_content_analyzer_detect_compression_type() {
1770        let analyzer = ContentAnalyzer::new();
1771
1772        // Test JSON content
1773        let json_data = r#"{"key": "value", "number": 123}"#.as_bytes();
1774        let compression = analyzer.detect_optimal_compression_type(json_data);
1775        match compression {
1776            CompressionType::Gzip => {
1777                // JSON should benefit from compression
1778            }
1779            #[cfg(feature = "compression")]
1780            CompressionType::Brotli => {
1781                // JSON should benefit from compression
1782            }
1783            #[cfg(feature = "compression")]
1784            CompressionType::Zstd => {
1785                // JSON should benefit from compression
1786            }
1787            CompressionType::None => {
1788                // Also acceptable for small JSON
1789            }
1790        }
1791
1792        // Test already compressed data (should not compress further)
1793        let compressed_data = vec![0x1f, 0x8b, 0x08]; // Gzip header
1794        let compression = analyzer.detect_optimal_compression_type(&compressed_data);
1795        assert_eq!(compression, CompressionType::None);
1796
1797        // Test random data (should use light compression)
1798        let random_data = vec![42u8; 1000]; // Repetitive data
1799        let compression = analyzer.detect_optimal_compression_type(&random_data);
1800        match compression {
1801            CompressionType::Gzip => {
1802                // Repetitive data should compress well
1803            }
1804            #[cfg(feature = "compression")]
1805            CompressionType::Brotli => {
1806                // Repetitive data should compress well
1807            }
1808            #[cfg(feature = "compression")]
1809            CompressionType::Zstd => {
1810                // Repetitive data should compress well
1811            }
1812            CompressionType::None => {
1813                // Also acceptable
1814            }
1815        }
1816    }
1817
1818    #[test]
1819    fn test_adaptive_buffer_creation() {
1820        let buffer = AdaptiveBuffer::new();
1821        assert_eq!(buffer.len(), 0);
1822        assert!(buffer.capacity() >= 8192); // Should have initial capacity
1823    }
1824
1825    #[test]
1826    fn test_adaptive_buffer_write_read() {
1827        let mut buffer = AdaptiveBuffer::new();
1828
1829        let test_data = b"Hello, streaming world!";
1830        buffer.write(test_data);
1831
1832        assert_eq!(buffer.len(), test_data.len());
1833
1834        let read_data = buffer.read(10);
1835        assert_eq!(read_data.len(), 10);
1836        assert_eq!(&read_data[..], &test_data[..10]);
1837
1838        // Buffer should have remaining data
1839        assert_eq!(buffer.len(), test_data.len() - 10);
1840    }
1841
1842    #[test]
1843    fn test_adaptive_buffer_resize() {
1844        let mut buffer = AdaptiveBuffer::new();
1845        let initial_capacity = buffer.capacity();
1846
1847        // Write data larger than initial capacity
1848        let large_data = vec![42u8; initial_capacity + 1000];
1849        buffer.write(&large_data);
1850
1851        // Buffer should have resized
1852        assert!(buffer.capacity() > initial_capacity);
1853        assert_eq!(buffer.len(), large_data.len());
1854    }
1855
1856    #[test]
1857    fn test_adaptive_buffer_clear() {
1858        let mut buffer = AdaptiveBuffer::new();
1859        buffer.write(b"test data");
1860
1861        assert!(!buffer.is_empty());
1862        buffer.clear();
1863        assert_eq!(buffer.len(), 0);
1864    }
1865
1866    #[test]
1867    fn test_flow_control_metrics_creation() {
1868        let metrics = FlowControlMetrics::new();
1869        assert_eq!(metrics.bytes_sent, 0);
1870        assert_eq!(metrics.bytes_received, 0);
1871        assert_eq!(metrics.requests_pending, 0);
1872        assert!(metrics.last_activity.elapsed().as_secs() < 1); // Should be recent
1873    }
1874
1875    #[test]
1876    fn test_flow_control_metrics_update() {
1877        let mut metrics = FlowControlMetrics::new();
1878
1879        metrics.record_bytes_sent(1024);
1880        assert_eq!(metrics.bytes_sent, 1024);
1881
1882        metrics.record_bytes_received(512);
1883        assert_eq!(metrics.bytes_received, 512);
1884
1885        metrics.record_request_start();
1886        assert_eq!(metrics.requests_pending, 1);
1887
1888        metrics.record_request_complete();
1889        assert_eq!(metrics.requests_pending, 0);
1890    }
1891
1892    #[test]
1893    fn test_flow_control_metrics_throughput() {
1894        let mut metrics = FlowControlMetrics::new();
1895
1896        // Simulate some activity
1897        metrics.record_bytes_sent(1000);
1898        metrics.record_bytes_received(500);
1899
1900        let throughput = metrics.calculate_throughput();
1901        assert!(throughput > 0.0);
1902    }
1903
1904    #[test]
1905    fn test_flow_control_metrics_should_throttle() {
1906        let mut metrics = FlowControlMetrics::new();
1907
1908        // Normal load - should not throttle
1909        metrics.record_request_start();
1910        assert!(!metrics.should_throttle());
1911
1912        // High load - add many pending requests
1913        for _ in 0..150 {
1914            metrics.record_request_start();
1915        }
1916        assert!(metrics.should_throttle());
1917    }
1918
1919    #[tokio::test]
1920    #[cfg(not(feature = "chunked-encoding"))]
1921    async fn test_streaming_client_creation_without_feature() {
1922        // When streaming-http feature is not enabled, should return error
1923        let result = StreamingHttpClientTransport::new("http://localhost:3000").await;
1924        assert!(result.is_err());
1925
1926        if let Err(McpError::Transport(msg)) = result {
1927            assert!(msg.contains("Streaming HTTP feature not enabled"));
1928        } else {
1929            panic!("Expected Transport error");
1930        }
1931    }
1932
1933    #[tokio::test]
1934    #[cfg(not(feature = "chunked-encoding"))]
1935    async fn test_streaming_client_with_config_without_feature() {
1936        // When streaming-http feature is not enabled, should return error
1937        let config = StreamingConfig::default();
1938        let result =
1939            StreamingHttpClientTransport::with_config("http://localhost:3000", config).await;
1940        assert!(result.is_err());
1941
1942        if let Err(McpError::Transport(msg)) = result {
1943            assert!(msg.contains("Streaming HTTP feature not enabled"));
1944        } else {
1945            panic!("Expected Transport error");
1946        }
1947    }
1948
1949    #[tokio::test]
1950    async fn test_transport_interface_without_feature() {
1951        // Test Transport trait implementation when feature is disabled
1952        let result = StreamingHttpClientTransport::new("http://localhost:3000").await;
1953        if let Ok(mut transport) = result {
1954            // This branch won't be reached when feature is disabled
1955            let request = JsonRpcRequest {
1956                jsonrpc: "2.0".to_string(),
1957                id: json!(1),
1958                method: "test".to_string(),
1959                params: None,
1960            };
1961
1962            let response = transport.send_request(request).await;
1963            assert!(response.is_err());
1964        }
1965    }
1966
1967    #[test]
1968    fn test_compression_type_enum() {
1969        // Test that CompressionType enum values are as expected
1970        let none = CompressionType::None;
1971        let gzip = CompressionType::Gzip;
1972        #[cfg(feature = "compression")]
1973        let brotli = CompressionType::Brotli;
1974        #[cfg(not(feature = "compression"))]
1975        let brotli = CompressionType::test_brotli();
1976
1977        #[cfg(feature = "compression")]
1978        let zstd = CompressionType::Zstd;
1979        #[cfg(not(feature = "compression"))]
1980        let zstd = CompressionType::test_zstd();
1981
1982        // Test that they can be compared
1983        assert_eq!(none, CompressionType::None);
1984        assert_ne!(gzip, CompressionType::None);
1985
1986        // Only test inequality when compression features are enabled
1987        #[cfg(feature = "compression")]
1988        {
1989            assert_ne!(brotli, gzip);
1990            assert_ne!(zstd, brotli);
1991        }
1992
1993        #[cfg(not(feature = "compression"))]
1994        {
1995            // When feature is disabled, test methods return Gzip
1996            assert_eq!(brotli, gzip);
1997            assert_eq!(zstd, gzip);
1998        }
1999    }
2000
2001    #[test]
2002    fn test_performance_metrics() {
2003        // Test performance-related functionality
2004        let analyzer = ContentAnalyzer::new();
2005
2006        // Test that analysis is fast for small data
2007        let small_data = b"small";
2008        let start = std::time::Instant::now();
2009        let _should_stream = analyzer.should_stream(small_data);
2010        let _compression = analyzer.detect_optimal_compression_type(small_data);
2011        let duration = start.elapsed();
2012
2013        // Should be fast (less than 1ms for small data)
2014        assert!(duration.as_millis() < 10);
2015    }
2016
2017    #[test]
2018    fn test_edge_cases() {
2019        let analyzer = ContentAnalyzer::new();
2020
2021        // Test empty data
2022        let empty_data = b"";
2023        assert!(!analyzer.should_stream(empty_data));
2024        assert_eq!(
2025            analyzer.detect_optimal_compression_type(empty_data),
2026            CompressionType::None
2027        );
2028
2029        // Test single byte
2030        let single_byte = b"a";
2031        assert!(!analyzer.should_stream(single_byte));
2032
2033        // Test adaptive buffer with empty data
2034        let mut buffer = AdaptiveBuffer::new();
2035        buffer.write(b"");
2036        assert_eq!(buffer.len(), 0);
2037
2038        let empty_read = buffer.read(10);
2039        assert_eq!(empty_read.len(), 0);
2040    }
2041
2042    #[test]
2043    fn test_streaming_config_validation() {
2044        let config = StreamingConfig::default();
2045
2046        // Test that default values are reasonable
2047        assert!(config.chunk_size > 0);
2048        assert!(config.chunk_size <= 64 * 1024); // Not too large
2049        assert!(config.compression_level() <= 9); // Valid compression level
2050        assert!(config.max_concurrent_streams() > 0);
2051        assert!(config.max_concurrent_streams() <= 1000); // Reasonable limit
2052    }
2053
2054    #[tokio::test]
2055    async fn test_concurrent_operations() {
2056        // Test that multiple operations can be performed concurrently
2057        let analyzer = ContentAnalyzer::new();
2058        let mut handles = vec![];
2059
2060        for i in 0..10 {
2061            let analyzer_clone = analyzer.clone();
2062            let handle = tokio::spawn(async move {
2063                let data = format!("test data {i}");
2064                analyzer_clone.should_stream(data.as_bytes())
2065            });
2066            handles.push(handle);
2067        }
2068
2069        // All operations should complete successfully
2070        for handle in handles {
2071            let result = timeout(Duration::from_millis(100), handle).await;
2072            assert!(result.is_ok());
2073        }
2074    }
2075}