1use 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#[cfg(feature = "chunked-encoding")]
68use bytes::{Bytes, BytesMut};
69#[cfg(feature = "chunked-encoding")]
70use tokio_stream::wrappers::ReceiverStream;
71
72#[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#[cfg(feature = "http2")]
82use http::HeaderMap;
83#[cfg(feature = "http2")]
84use std::collections::HashMap;
85#[cfg(feature = "http2")]
86use std::pin::Pin;
87
88#[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#[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#[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), }
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, }
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 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 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#[cfg(feature = "http2")]
200#[derive(Debug, Clone)]
201pub struct Http2Config {
202 pub max_concurrent_streams: usize,
204 pub initial_window_size: u32,
206 pub max_frame_size: u32,
208 pub enable_server_push: bool,
210 pub push_cache_size: usize,
212 pub connection_timeout: Duration,
214 pub keep_alive_interval: Duration,
216 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#[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#[derive(Debug, Clone)]
268pub struct StreamingConfig {
269 pub enable_chunked_transfer: bool,
271 pub chunk_threshold: usize,
273 pub chunk_size: usize,
275 pub enable_compression: bool,
277 pub compression_type: CompressionType,
279 pub enable_http2_server_push: bool,
281 pub streaming_timeout_ms: u64,
283 pub max_concurrent_chunks: usize,
285 pub backpressure_threshold: usize,
287 pub adaptive_chunk_sizing: bool,
289 #[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, chunk_size: 16384, enable_compression: true,
301 compression_type: CompressionType::Gzip,
302 enable_http2_server_push: false, streaming_timeout_ms: 60_000,
304 max_concurrent_chunks: 10,
305 backpressure_threshold: 1024 * 1024, adaptive_chunk_sizing: true,
307 #[cfg(feature = "http2")]
308 http2_config: Http2Config::default(),
309 }
310 }
311}
312
313impl StreamingConfig {
314 pub fn memory_improved() -> Self {
316 Self {
317 chunk_threshold: 4096, chunk_size: 8192, compression_type: CompressionType::Gzip,
320 max_concurrent_chunks: 5,
321 backpressure_threshold: 512 * 1024, ..Default::default()
323 }
324 }
325
326 pub fn performance_improved() -> Self {
328 Self {
329 chunk_threshold: 32768, chunk_size: 65536, enable_http2_server_push: true,
332 max_concurrent_chunks: 20,
333 backpressure_threshold: 4 * 1024 * 1024, #[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, max_frame_size: 32768, 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 pub fn with_chunk_size(mut self, chunk_size: usize) -> Self {
355 self.chunk_size = chunk_size;
356 self
357 }
358
359 pub fn with_compression_level(mut self, level: u8) -> Self {
361 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 pub fn with_max_concurrent_streams(mut self, max_streams: usize) -> Self {
391 self.max_concurrent_chunks = max_streams;
392 self
393 }
394
395 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 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#[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
436pub 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 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 {
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 #[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 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 pub fn should_stream(&self, content: &[u8]) -> bool {
627 let size = content.len();
628 let threshold = 8192; if size > threshold {
631 return true;
632 }
633
634 self.is_binary_content(content)
636 }
637
638 pub fn detect_optimal_compression_type(&self, content: &[u8]) -> CompressionType {
640 if content.is_empty() {
641 return CompressionType::None;
642 }
643
644 let entropy = self.calculate_entropy(content);
646
647 if entropy < 0.3 {
648 #[cfg(feature = "compression")]
650 return CompressionType::Brotli;
651 #[cfg(not(feature = "compression"))]
652 return CompressionType::Gzip;
653 } else if entropy < 0.7 {
654 CompressionType::Gzip
656 } else {
657 CompressionType::None
659 }
660 }
661
662 pub fn streaming_threshold(&self) -> usize {
664 8192 }
666
667 fn calculate_entropy(&self, content: &[u8]) -> f64 {
669 if content.is_empty() {
670 return 0.0;
671 }
672
673 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 }
695
696 fn is_binary_content(&self, content: &[u8]) -> bool {
698 if content.is_empty() {
699 return false;
700 }
701
702 if content.contains(&0) {
704 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; }
709 return true;
710 }
711
712 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#[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 pub async fn add_data(&mut self, data: &[u8]) -> McpResult<Vec<Bytes>> {
767 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 if chunks.len() >= self.flow_control.max_concurrent_chunks {
784 break;
785 }
786 }
787
788 if self.flow_control.adaptive_chunk_sizing {
790 self.adjust_chunk_size().await;
791 }
792
793 Ok(chunks)
794 }
795
796 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 async fn adjust_chunk_size(&mut self) {
807 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; }
823}
824
825#[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, }
839 }
840
841 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 pub fn estimate_compression_ratio(&self, data: &[u8]) -> f64 {
884 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 }
912}
913
914#[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 pub async fn new<S: AsRef<str>>(base_url: S) -> McpResult<Self> {
939 Self::with_config(base_url, StreamingConfig::default()).await
940 }
941
942 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 #[cfg(feature = "http2")]
954 let _http2_config = config.enable_http2_server_push; 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 #[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, #[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 async fn send_request_smart(&mut self, request: JsonRpcRequest) -> McpResult<JsonRpcResponse> {
994 let start_time = Instant::now();
995
996 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 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 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 }
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 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 let final_chunk = buffer.flush();
1090
1091 let (tx, rx) = tokio::sync::mpsc::channel::<Result<bytes::Bytes, McpError>>(
1093 self.config.max_concurrent_chunks,
1094 );
1095
1096 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 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 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 if self.h2_client.is_none() {
1168 self.init_h2_client().await?;
1169 }
1170
1171 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 if let Some(ref mut h2_client) = self.h2_client {
1188 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 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_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 let response = response_future
1209 .await
1210 .map_err(|e| McpError::Http(format!("HTTP/2 response error: {e}")))?;
1211
1212 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 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 let _ = body.flow_control().release_capacity(chunk.len());
1226 }
1227
1228 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 {
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 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 #[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 #[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 #[cfg(feature = "http2")]
1316 async fn init_h2_client(&mut self) -> McpResult<()> {
1317 use url::Url;
1318
1319 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); 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 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 self.h2_client = Some(h2_client);
1340 self.h2_connection = Some(connection);
1341
1342 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 #[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 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 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 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 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 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 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 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(¬ification)
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 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#[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
1540impl Default for ContentAnalyzer {
1542 fn default() -> Self {
1543 Self::new()
1544 }
1545}
1546
1547impl Clone for ContentAnalyzer {
1549 fn clone(&self) -> Self {
1550 Self {
1551 stats: Arc::clone(&self.stats),
1552 }
1553 }
1554}
1555
1556#[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); Self {
1573 buffer,
1574 optimal_size: 16384, }
1576 }
1577
1578 pub fn write(&mut self, data: &[u8]) {
1579 self.buffer.extend_from_slice(data);
1580 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, 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 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); assert_eq!(config.compression_level(), 6); assert_eq!(config.max_concurrent_streams(), 50);
1739 }
1740
1741 #[test]
1742 fn test_content_analyzer_creation() {
1743 let _analyzer = ContentAnalyzer::new();
1744 let _default_analyzer = ContentAnalyzer::default();
1746 }
1747
1748 #[test]
1749 fn test_content_analyzer_should_stream() {
1750 let analyzer = ContentAnalyzer::new();
1751
1752 let small_data = "small data".as_bytes();
1754 assert!(!analyzer.should_stream(small_data));
1755
1756 let large_data = vec![0u8; 10_000];
1758 assert!(analyzer.should_stream(&large_data));
1759
1760 let boundary_data = vec![0u8; analyzer.streaming_threshold()];
1762 assert!(!analyzer.should_stream(&boundary_data)); let over_boundary = vec![0u8; analyzer.streaming_threshold() + 1];
1765 assert!(analyzer.should_stream(&over_boundary)); }
1767
1768 #[test]
1769 fn test_content_analyzer_detect_compression_type() {
1770 let analyzer = ContentAnalyzer::new();
1771
1772 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 }
1779 #[cfg(feature = "compression")]
1780 CompressionType::Brotli => {
1781 }
1783 #[cfg(feature = "compression")]
1784 CompressionType::Zstd => {
1785 }
1787 CompressionType::None => {
1788 }
1790 }
1791
1792 let compressed_data = vec![0x1f, 0x8b, 0x08]; let compression = analyzer.detect_optimal_compression_type(&compressed_data);
1795 assert_eq!(compression, CompressionType::None);
1796
1797 let random_data = vec![42u8; 1000]; let compression = analyzer.detect_optimal_compression_type(&random_data);
1800 match compression {
1801 CompressionType::Gzip => {
1802 }
1804 #[cfg(feature = "compression")]
1805 CompressionType::Brotli => {
1806 }
1808 #[cfg(feature = "compression")]
1809 CompressionType::Zstd => {
1810 }
1812 CompressionType::None => {
1813 }
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); }
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 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 let large_data = vec![42u8; initial_capacity + 1000];
1849 buffer.write(&large_data);
1850
1851 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); }
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 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 metrics.record_request_start();
1910 assert!(!metrics.should_throttle());
1911
1912 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 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 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 let result = StreamingHttpClientTransport::new("http://localhost:3000").await;
1953 if let Ok(mut transport) = result {
1954 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 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 assert_eq!(none, CompressionType::None);
1984 assert_ne!(gzip, CompressionType::None);
1985
1986 #[cfg(feature = "compression")]
1988 {
1989 assert_ne!(brotli, gzip);
1990 assert_ne!(zstd, brotli);
1991 }
1992
1993 #[cfg(not(feature = "compression"))]
1994 {
1995 assert_eq!(brotli, gzip);
1997 assert_eq!(zstd, gzip);
1998 }
1999 }
2000
2001 #[test]
2002 fn test_performance_metrics() {
2003 let analyzer = ContentAnalyzer::new();
2005
2006 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 assert!(duration.as_millis() < 10);
2015 }
2016
2017 #[test]
2018 fn test_edge_cases() {
2019 let analyzer = ContentAnalyzer::new();
2020
2021 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 let single_byte = b"a";
2031 assert!(!analyzer.should_stream(single_byte));
2032
2033 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 assert!(config.chunk_size > 0);
2048 assert!(config.chunk_size <= 64 * 1024); assert!(config.compression_level() <= 9); assert!(config.max_concurrent_streams() > 0);
2051 assert!(config.max_concurrent_streams() <= 1000); }
2053
2054 #[tokio::test]
2055 async fn test_concurrent_operations() {
2056 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 for handle in handles {
2071 let result = timeout(Duration::from_millis(100), handle).await;
2072 assert!(result.is_ok());
2073 }
2074 }
2075}