1use crate::{Error, HttpResponse};
55use bytes::Bytes;
56use futures_util::Stream;
57use serde::Serialize;
58use std::collections::HashMap;
59use std::pin::Pin;
60use std::sync::Arc;
61use std::sync::atomic::{AtomicBool, AtomicU64, AtomicUsize, Ordering};
62use std::task::{Context, Poll};
63use std::time::Duration;
64use tokio::sync::mpsc;
65
66#[derive(Debug, Clone)]
72pub enum StreamChunk {
73 Bytes(Bytes),
75 End,
77 Error(String),
79}
80
81impl From<Vec<u8>> for StreamChunk {
82 fn from(v: Vec<u8>) -> Self {
83 StreamChunk::Bytes(Bytes::from(v))
84 }
85}
86
87impl From<Bytes> for StreamChunk {
88 fn from(b: Bytes) -> Self {
89 StreamChunk::Bytes(b)
90 }
91}
92
93impl From<String> for StreamChunk {
94 fn from(s: String) -> Self {
95 StreamChunk::Bytes(Bytes::from(s))
96 }
97}
98
99impl From<&str> for StreamChunk {
100 fn from(s: &str) -> Self {
101 StreamChunk::Bytes(Bytes::from(s.to_owned()))
102 }
103}
104
105pub struct ByteStream {
128 receiver: mpsc::Receiver<StreamChunk>,
129}
130
131pub struct ByteStreamSender {
133 sender: mpsc::Sender<StreamChunk>,
134 bytes_sent: Arc<AtomicU64>,
135}
136
137impl ByteStream {
138 pub fn new() -> (Self, ByteStreamSender) {
140 Self::with_buffer_size(64)
141 }
142
143 pub fn with_buffer_size(size: usize) -> (Self, ByteStreamSender) {
145 let (sender, receiver) = mpsc::channel(size);
146 let bytes_sent = Arc::new(AtomicU64::new(0));
147 (Self { receiver }, ByteStreamSender { sender, bytes_sent })
148 }
149}
150
151impl Default for ByteStream {
152 fn default() -> Self {
153 let (stream, _) = Self::new();
154 stream
155 }
156}
157
158impl Stream for ByteStream {
159 type Item = Result<Bytes, Error>;
160
161 fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
162 match Pin::new(&mut self.receiver).poll_recv(cx) {
163 Poll::Ready(Some(chunk)) => match chunk {
164 StreamChunk::Bytes(bytes) => Poll::Ready(Some(Ok(bytes))),
165 StreamChunk::End => Poll::Ready(None),
166 StreamChunk::Error(e) => Poll::Ready(Some(Err(Error::Internal(e)))),
167 },
168 Poll::Ready(None) => Poll::Ready(None),
169 Poll::Pending => Poll::Pending,
170 }
171 }
172}
173
174impl ByteStreamSender {
175 pub async fn send(&self, data: impl Into<Vec<u8>>) -> Result<(), Error> {
177 let bytes = data.into();
178 let len = bytes.len() as u64;
179 self.sender
180 .send(StreamChunk::Bytes(Bytes::from(bytes)))
181 .await
182 .map_err(|e| Error::Internal(format!("Failed to send to stream: {}", e)))?;
183 self.bytes_sent.fetch_add(len, Ordering::Relaxed);
184 Ok(())
185 }
186
187 pub async fn send_bytes(&self, bytes: Bytes) -> Result<(), Error> {
189 let len = bytes.len() as u64;
190 self.sender
191 .send(StreamChunk::Bytes(bytes))
192 .await
193 .map_err(|e| Error::Internal(format!("Failed to send to stream: {}", e)))?;
194 self.bytes_sent.fetch_add(len, Ordering::Relaxed);
195 Ok(())
196 }
197
198 pub async fn send_str(&self, s: &str) -> Result<(), Error> {
200 self.send(s.as_bytes().to_vec()).await
201 }
202
203 pub async fn send_error(&self, error: impl Into<String>) -> Result<(), Error> {
205 self.sender
206 .send(StreamChunk::Error(error.into()))
207 .await
208 .map_err(|e| Error::Internal(format!("Failed to send error: {}", e)))
209 }
210
211 pub async fn close(&self) {
213 let _ = self.sender.send(StreamChunk::End).await;
214 }
215
216 pub fn bytes_sent(&self) -> u64 {
218 self.bytes_sent.load(Ordering::Relaxed)
219 }
220
221 pub fn is_closed(&self) -> bool {
223 self.sender.is_closed()
224 }
225}
226
227pub struct JsonStream {
256 inner: ByteStream,
257}
258
259pub struct JsonStreamSender {
261 inner: ByteStreamSender,
262 items_sent: Arc<AtomicU64>,
263}
264
265impl JsonStream {
266 pub fn new() -> (Self, JsonStreamSender) {
268 Self::with_buffer_size(64)
269 }
270
271 pub fn with_buffer_size(size: usize) -> (Self, JsonStreamSender) {
273 let (stream, sender) = ByteStream::with_buffer_size(size);
274 let items_sent = Arc::new(AtomicU64::new(0));
275 (
276 Self { inner: stream },
277 JsonStreamSender {
278 inner: sender,
279 items_sent,
280 },
281 )
282 }
283
284 pub fn into_inner(self) -> ByteStream {
286 self.inner
287 }
288}
289
290impl Default for JsonStream {
291 fn default() -> Self {
292 let (stream, _) = Self::new();
293 stream
294 }
295}
296
297impl Stream for JsonStream {
298 type Item = Result<Bytes, Error>;
299
300 fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
301 Pin::new(&mut self.inner).poll_next(cx)
302 }
303}
304
305impl JsonStreamSender {
306 pub async fn send_json<T: Serialize>(&self, value: &T) -> Result<(), Error> {
308 let json = serde_json::to_string(value).map_err(|e| Error::Serialization(e.to_string()))?;
309 self.inner.send(format!("{}\n", json)).await?;
310 self.items_sent.fetch_add(1, Ordering::Relaxed);
311 Ok(())
312 }
313
314 pub async fn send_raw(&self, json: &str) -> Result<(), Error> {
316 self.inner.send(format!("{}\n", json.trim())).await?;
317 self.items_sent.fetch_add(1, Ordering::Relaxed);
318 Ok(())
319 }
320
321 pub async fn send_error(&self, error: impl Into<String>) -> Result<(), Error> {
323 let error_json = serde_json::json!({
324 "error": error.into()
325 });
326 self.send_json(&error_json).await
327 }
328
329 pub async fn close(&self) {
331 self.inner.close().await;
332 }
333
334 pub fn items_sent(&self) -> u64 {
336 self.items_sent.load(Ordering::Relaxed)
337 }
338
339 pub fn is_closed(&self) -> bool {
341 self.inner.is_closed()
342 }
343}
344
345pub struct TextStream {
353 inner: ByteStream,
354}
355
356pub struct TextStreamSender {
358 inner: ByteStreamSender,
359 lines_sent: Arc<AtomicU64>,
360}
361
362impl TextStream {
363 pub fn new() -> (Self, TextStreamSender) {
365 Self::with_buffer_size(64)
366 }
367
368 pub fn with_buffer_size(size: usize) -> (Self, TextStreamSender) {
370 let (stream, sender) = ByteStream::with_buffer_size(size);
371 let lines_sent = Arc::new(AtomicU64::new(0));
372 (
373 Self { inner: stream },
374 TextStreamSender {
375 inner: sender,
376 lines_sent,
377 },
378 )
379 }
380
381 pub fn into_inner(self) -> ByteStream {
383 self.inner
384 }
385}
386
387impl Default for TextStream {
388 fn default() -> Self {
389 let (stream, _) = Self::new();
390 stream
391 }
392}
393
394impl Stream for TextStream {
395 type Item = Result<Bytes, Error>;
396
397 fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
398 Pin::new(&mut self.inner).poll_next(cx)
399 }
400}
401
402impl TextStreamSender {
403 pub async fn send_line(&self, line: &str) -> Result<(), Error> {
405 self.inner.send(format!("{}\n", line)).await?;
406 self.lines_sent.fetch_add(1, Ordering::Relaxed);
407 Ok(())
408 }
409
410 pub async fn send(&self, text: &str) -> Result<(), Error> {
412 self.inner.send(text.as_bytes().to_vec()).await
413 }
414
415 pub async fn close(&self) {
417 self.inner.close().await;
418 }
419
420 pub fn lines_sent(&self) -> u64 {
422 self.lines_sent.load(Ordering::Relaxed)
423 }
424
425 pub fn is_closed(&self) -> bool {
427 self.inner.is_closed()
428 }
429}
430
431pub struct StreamingResponse {
463 pub status: u16,
465 pub headers: HashMap<String, String>,
467 body: StreamBody,
469}
470
471pub enum StreamBody {
473 Bytes(ByteStream),
475 Json(JsonStream),
477 Text(TextStream),
479 Empty,
481}
482
483impl StreamingResponse {
484 pub fn new(stream: ByteStream) -> Self {
486 Self {
487 status: 200,
488 headers: HashMap::new(),
489 body: StreamBody::Bytes(stream),
490 }
491 }
492
493 pub fn ndjson(stream: JsonStream) -> Self {
495 let mut response = Self {
496 status: 200,
497 headers: HashMap::new(),
498 body: StreamBody::Json(stream),
499 };
500 response.headers.insert(
501 "Content-Type".to_string(),
502 "application/x-ndjson".to_string(),
503 );
504 response
505 }
506
507 pub fn text(stream: TextStream) -> Self {
509 let mut response = Self {
510 status: 200,
511 headers: HashMap::new(),
512 body: StreamBody::Text(stream),
513 };
514 response.headers.insert(
515 "Content-Type".to_string(),
516 "text/plain; charset=utf-8".to_string(),
517 );
518 response
519 }
520
521 pub fn empty() -> Self {
523 Self {
524 status: 200,
525 headers: HashMap::new(),
526 body: StreamBody::Empty,
527 }
528 }
529
530 pub fn status(mut self, status: u16) -> Self {
532 self.status = status;
533 self
534 }
535
536 pub fn content_type(mut self, content_type: impl Into<String>) -> Self {
538 self.headers
539 .insert("Content-Type".to_string(), content_type.into());
540 self
541 }
542
543 pub fn header(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
545 self.headers.insert(key.into(), value.into());
546 self
547 }
548
549 pub fn no_cache(mut self) -> Self {
551 self.headers.insert(
552 "Cache-Control".to_string(),
553 "no-cache, no-store, must-revalidate".to_string(),
554 );
555 self
556 }
557
558 pub fn cors(mut self, origin: impl Into<String>) -> Self {
560 self.headers
561 .insert("Access-Control-Allow-Origin".to_string(), origin.into());
562 self
563 }
564
565 pub fn nosniff(mut self) -> Self {
567 self.headers
568 .insert("X-Content-Type-Options".to_string(), "nosniff".to_string());
569 self
570 }
571
572 pub fn into_body(self) -> StreamBody {
574 self.body
575 }
576
577 pub fn is_empty(&self) -> bool {
579 matches!(self.body, StreamBody::Empty)
580 }
581}
582
583impl Default for StreamingResponse {
584 fn default() -> Self {
585 Self::empty()
586 }
587}
588
589pub fn stream_iter<I, T, F>(iter: I, transform: F) -> (ByteStream, tokio::task::JoinHandle<()>)
604where
605 I: Iterator<Item = T> + Send + 'static,
606 T: Send + 'static,
607 F: Fn(T) -> Vec<u8> + Send + 'static,
608{
609 let (stream, sender) = ByteStream::new();
610 let items: Vec<T> = iter.collect(); let handle = tokio::spawn(async move {
612 for item in items {
613 if sender.send(transform(item)).await.is_err() {
614 break;
615 }
616 }
617 sender.close().await;
618 });
619 (stream, handle)
620}
621
622pub fn stream_iter_with_delay<I, T, F>(
624 iter: I,
625 transform: F,
626 delay: Duration,
627) -> (ByteStream, tokio::task::JoinHandle<()>)
628where
629 I: Iterator<Item = T> + Send + 'static,
630 T: Send + 'static,
631 F: Fn(T) -> Vec<u8> + Send + 'static,
632{
633 let (stream, sender) = ByteStream::new();
634 let items: Vec<T> = iter.collect(); let handle = tokio::spawn(async move {
636 for item in items {
637 if sender.send(transform(item)).await.is_err() {
638 break;
639 }
640 tokio::time::sleep(delay).await;
641 }
642 sender.close().await;
643 });
644 (stream, handle)
645}
646
647pub fn stream_json_iter<I, T>(iter: I) -> (JsonStream, tokio::task::JoinHandle<()>)
649where
650 I: Iterator<Item = T> + Send + 'static,
651 T: Serialize + Send + Sync + 'static,
652{
653 let (stream, sender) = JsonStream::new();
654 let items: Vec<T> = iter.collect(); let handle = tokio::spawn(async move {
656 for item in items {
657 if sender.send_json(&item).await.is_err() {
658 break;
659 }
660 }
661 sender.close().await;
662 });
663 (stream, handle)
664}
665
666pub fn stream_reader<R>(reader: R, chunk_size: usize) -> (ByteStream, tokio::task::JoinHandle<()>)
682where
683 R: tokio::io::AsyncRead + Unpin + Send + 'static,
684{
685 use tokio::io::AsyncReadExt;
686
687 let (stream, sender) = ByteStream::new();
688 let handle = tokio::spawn(async move {
689 let mut reader = reader;
690 let mut buffer = vec![0u8; chunk_size];
691
692 loop {
693 match reader.read(&mut buffer).await {
694 Ok(0) => break, Ok(n) => {
696 if sender.send(buffer[..n].to_vec()).await.is_err() {
697 break;
698 }
699 }
700 Err(e) => {
701 let _ = sender.send_error(e.to_string()).await;
702 break;
703 }
704 }
705 }
706 sender.close().await;
707 });
708 (stream, handle)
709}
710
711pub struct ProgressStream {
717 inner: ByteStream,
718 bytes_received: Arc<AtomicU64>,
719 callback: Option<Box<dyn Fn(u64) + Send + Sync>>,
720}
721
722impl ProgressStream {
723 pub fn new(inner: ByteStream) -> Self {
725 Self {
726 inner,
727 bytes_received: Arc::new(AtomicU64::new(0)),
728 callback: None,
729 }
730 }
731
732 pub fn on_progress<F>(mut self, callback: F) -> Self
734 where
735 F: Fn(u64) + Send + Sync + 'static,
736 {
737 self.callback = Some(Box::new(callback));
738 self
739 }
740
741 pub fn bytes_received(&self) -> u64 {
743 self.bytes_received.load(Ordering::Relaxed)
744 }
745}
746
747impl Stream for ProgressStream {
748 type Item = Result<Bytes, Error>;
749
750 fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
751 match Pin::new(&mut self.inner).poll_next(cx) {
752 Poll::Ready(Some(Ok(bytes))) => {
753 let len = bytes.len() as u64;
754 let total = self.bytes_received.fetch_add(len, Ordering::Relaxed) + len;
755 if let Some(ref callback) = self.callback {
756 callback(total);
757 }
758 Poll::Ready(Some(Ok(bytes)))
759 }
760 other => other,
761 }
762 }
763}
764
765impl StreamingResponse {
770 pub async fn into_buffered(mut self) -> Result<HttpResponse, Error> {
775 use futures_util::StreamExt;
776
777 let mut body = Vec::new();
778
779 match &mut self.body {
780 StreamBody::Bytes(stream) => {
781 while let Some(chunk) = stream.next().await {
782 body.extend_from_slice(&chunk?);
783 }
784 }
785 StreamBody::Json(stream) => {
786 while let Some(chunk) = stream.next().await {
787 body.extend_from_slice(&chunk?);
788 }
789 }
790 StreamBody::Text(stream) => {
791 while let Some(chunk) = stream.next().await {
792 body.extend_from_slice(&chunk?);
793 }
794 }
795 StreamBody::Empty => {}
796 }
797
798 let mut response = HttpResponse::new(self.status);
799 response.headers = self.headers.into();
800 response.body = Bytes::from(body);
801 Ok(response)
802 }
803}
804
805#[cfg(test)]
810mod tests {
811 use super::*;
812 use futures_util::StreamExt;
813
814 #[test]
815 fn test_backpressure_concurrent_acks_do_not_underflow() {
816 let controller = Arc::new(BackpressureController::new(BackpressureConfig::default()));
819 controller.record_send(100);
820
821 let handles: Vec<_> = (0..4)
822 .map(|_| {
823 let c = Arc::clone(&controller);
824 std::thread::spawn(move || {
825 for _ in 0..1000 {
826 c.record_ack(64);
827 }
828 })
829 })
830 .collect();
831 for h in handles {
832 h.join().unwrap();
833 }
834
835 assert_eq!(controller.buffer_level(), 0);
836 assert!(!controller.is_paused());
837 }
838
839 #[tokio::test]
840 async fn test_backpressure_wait_if_paused_resumes() {
841 let config = BackpressureConfig::new()
842 .high_watermark(10)
843 .low_watermark(2);
844 let controller = Arc::new(BackpressureController::new(config));
845
846 controller.record_send(20);
848 assert!(controller.is_paused());
849
850 let waiter = {
851 let c = Arc::clone(&controller);
852 tokio::spawn(async move {
853 c.wait_if_paused().await;
854 })
855 };
856
857 tokio::task::yield_now().await;
859 controller.record_ack(20);
860
861 tokio::time::timeout(std::time::Duration::from_secs(1), waiter)
862 .await
863 .expect("wait_if_paused should resume after ack")
864 .unwrap();
865 assert!(!controller.is_paused());
866 }
867
868 #[tokio::test]
869 async fn test_byte_stream() {
870 let (mut stream, sender) = ByteStream::new();
871
872 tokio::spawn(async move {
873 sender.send(b"hello".to_vec()).await.unwrap();
874 sender.send(b" world".to_vec()).await.unwrap();
875 sender.close().await;
876 });
877
878 let mut result = Vec::new();
879 while let Some(chunk) = stream.next().await {
880 result.extend_from_slice(&chunk.unwrap());
881 }
882
883 assert_eq!(result, b"hello world");
884 }
885
886 #[tokio::test]
887 async fn test_json_stream() {
888 let (mut stream, sender) = JsonStream::new();
889
890 #[derive(Serialize)]
891 struct Item {
892 id: u64,
893 }
894
895 tokio::spawn(async move {
896 sender.send_json(&Item { id: 1 }).await.unwrap();
897 sender.send_json(&Item { id: 2 }).await.unwrap();
898 sender.close().await;
899 });
900
901 let mut result = Vec::new();
902 while let Some(chunk) = stream.next().await {
903 result.extend_from_slice(&chunk.unwrap());
904 }
905
906 let result_str = String::from_utf8(result).unwrap();
907 assert!(result_str.contains("{\"id\":1}"));
908 assert!(result_str.contains("{\"id\":2}"));
909 }
910
911 #[tokio::test]
912 async fn test_text_stream() {
913 let (mut stream, sender) = TextStream::new();
914
915 tokio::spawn(async move {
916 sender.send_line("line 1").await.unwrap();
917 sender.send_line("line 2").await.unwrap();
918 sender.close().await;
919 });
920
921 let mut result = Vec::new();
922 while let Some(chunk) = stream.next().await {
923 result.extend_from_slice(&chunk.unwrap());
924 }
925
926 let result_str = String::from_utf8(result).unwrap();
927 assert_eq!(result_str, "line 1\nline 2\n");
928 }
929
930 #[tokio::test]
931 async fn test_streaming_response() {
932 let (stream, sender) = ByteStream::new();
933
934 tokio::spawn(async move {
935 sender.send(b"test data".to_vec()).await.unwrap();
936 sender.close().await;
937 });
938
939 let response = StreamingResponse::new(stream)
940 .status(200)
941 .content_type("text/plain")
942 .no_cache();
943
944 assert_eq!(response.status, 200);
945 assert_eq!(
946 response.headers.get("Content-Type"),
947 Some(&"text/plain".to_string())
948 );
949 }
950
951 #[tokio::test]
952 async fn test_stream_iter() {
953 let items = vec![1, 2, 3];
954 let (mut stream, _) = stream_iter(items.into_iter(), |i| format!("{}", i).into_bytes());
955
956 let mut result = Vec::new();
957 while let Some(chunk) = stream.next().await {
958 result.extend_from_slice(&chunk.unwrap());
959 }
960
961 assert_eq!(String::from_utf8(result).unwrap(), "123");
962 }
963
964 #[tokio::test]
965 async fn test_bytes_sent_tracking() {
966 let (stream, sender) = ByteStream::new();
967
968 sender.send(b"hello".to_vec()).await.unwrap();
969 assert_eq!(sender.bytes_sent(), 5);
970
971 sender.send(b" world".to_vec()).await.unwrap();
972 assert_eq!(sender.bytes_sent(), 11);
973
974 drop(stream);
976 }
977
978 #[tokio::test]
979 async fn test_json_items_sent_tracking() {
980 let (stream, sender) = JsonStream::new();
981
982 #[derive(Serialize)]
983 struct Item {
984 id: u64,
985 }
986
987 sender.send_json(&Item { id: 1 }).await.unwrap();
988 assert_eq!(sender.items_sent(), 1);
989
990 sender.send_json(&Item { id: 2 }).await.unwrap();
991 assert_eq!(sender.items_sent(), 2);
992
993 drop(stream);
995 }
996
997 #[tokio::test]
998 async fn test_streaming_response_into_buffered() {
999 let (stream, sender) = ByteStream::new();
1000
1001 tokio::spawn(async move {
1002 sender.send(b"buffered".to_vec()).await.unwrap();
1003 sender.close().await;
1004 });
1005
1006 let response = StreamingResponse::new(stream)
1007 .status(200)
1008 .content_type("text/plain");
1009
1010 let buffered = response.into_buffered().await.unwrap();
1011 assert_eq!(buffered.status, 200);
1012 assert_eq!(buffered.body, Bytes::from_static(b"buffered"));
1013 }
1014
1015 #[test]
1016 fn test_stream_chunk_from() {
1017 let from_vec: StreamChunk = vec![1, 2, 3].into();
1018 assert!(matches!(from_vec, StreamChunk::Bytes(_)));
1019
1020 let from_string: StreamChunk = "hello".to_string().into();
1021 assert!(matches!(from_string, StreamChunk::Bytes(_)));
1022
1023 let from_str: StreamChunk = "world".into();
1024 assert!(matches!(from_str, StreamChunk::Bytes(_)));
1025 }
1026
1027 #[test]
1030 fn test_backpressure_config() {
1031 let config = BackpressureConfig::new()
1032 .high_watermark(100)
1033 .low_watermark(20)
1034 .strategy(BackpressureStrategy::PauseResume);
1035
1036 assert_eq!(config.high_watermark, 100);
1037 assert_eq!(config.low_watermark, 20);
1038 }
1039
1040 #[test]
1041 fn test_chunk_optimizer_default() {
1042 let optimizer = ChunkOptimizer::default();
1043 assert_eq!(optimizer.min_chunk, DEFAULT_MIN_CHUNK);
1044 assert_eq!(optimizer.max_chunk, DEFAULT_MAX_CHUNK);
1045 }
1046
1047 #[test]
1048 fn test_chunk_optimizer_sizing() {
1049 let optimizer = ChunkOptimizer::new(512, 8192);
1050
1051 assert_eq!(optimizer.optimal_chunk_size(100), 512); assert_eq!(optimizer.optimal_chunk_size(1000), 1000); assert_eq!(optimizer.optimal_chunk_size(10000), 8192); }
1055
1056 #[test]
1057 fn test_streaming_stats() {
1058 let stats = streaming_stats();
1059 let _ = stats.streams_created();
1060 let _ = stats.chunks_sent();
1061 let _ = stats.bytes_sent();
1062 }
1063
1064 #[tokio::test]
1065 async fn test_streaming_body_builder() {
1066 let (body, handle) = StreamingBodyBuilder::new()
1067 .chunk_size(1024)
1068 .build_with_sender();
1069
1070 tokio::spawn(async move {
1071 handle.send(b"test data".to_vec()).await.ok();
1072 handle.close().await;
1073 });
1074
1075 let mut total = 0;
1076 let mut body = body;
1077 while let Some(chunk) = body.next().await {
1078 total += chunk.unwrap().len();
1079 }
1080 assert_eq!(total, 9);
1081 }
1082
1083 #[test]
1084 fn test_rate_limiter() {
1085 let limiter = StreamRateLimiter::new(1024); assert_eq!(limiter.bytes_per_sec, 1024);
1087 }
1088
1089 #[test]
1092 fn test_chunk_content_type_detection() {
1093 assert_eq!(
1094 ChunkContentType::from_mime("application/json"),
1095 ChunkContentType::Json
1096 );
1097 assert_eq!(
1098 ChunkContentType::from_mime("text/html"),
1099 ChunkContentType::Html
1100 );
1101 assert_eq!(
1102 ChunkContentType::from_mime("text/event-stream"),
1103 ChunkContentType::RealTime
1104 );
1105 assert_eq!(
1106 ChunkContentType::from_mime("video/mp4"),
1107 ChunkContentType::Media
1108 );
1109 assert_eq!(
1110 ChunkContentType::from_mime("application/octet-stream"),
1111 ChunkContentType::Binary
1112 );
1113 }
1114
1115 #[test]
1116 fn test_chunk_content_type_recommendations() {
1117 let realtime = ChunkContentType::RealTime;
1118 assert!(realtime.recommended_chunk_size() < CHUNK_SMALL);
1119
1120 let media = ChunkContentType::Media;
1121 assert!(media.recommended_chunk_size() >= CHUNK_TCP_OPTIMAL);
1122
1123 let binary = ChunkContentType::Binary;
1124 assert!(binary.recommended_chunk_size() >= CHUNK_LARGE);
1125 }
1126
1127 #[test]
1128 fn test_network_condition_from_rtt() {
1129 assert_eq!(
1130 NetworkCondition::from_rtt_ms(5),
1131 NetworkCondition::Excellent
1132 );
1133 assert_eq!(NetworkCondition::from_rtt_ms(30), NetworkCondition::Good);
1134 assert_eq!(NetworkCondition::from_rtt_ms(80), NetworkCondition::Fair);
1135 assert_eq!(NetworkCondition::from_rtt_ms(300), NetworkCondition::Poor);
1136 assert_eq!(
1137 NetworkCondition::from_rtt_ms(1000),
1138 NetworkCondition::Terrible
1139 );
1140 }
1141
1142 #[test]
1143 fn test_network_condition_multipliers() {
1144 assert!(NetworkCondition::Excellent.chunk_multiplier() > 1.0);
1145 assert!((NetworkCondition::Good.chunk_multiplier() - 1.0).abs() < 0.01);
1146 assert!(NetworkCondition::Poor.chunk_multiplier() < 1.0);
1147 }
1148
1149 #[test]
1150 fn test_adaptive_chunk_optimizer() {
1151 let optimizer = AdaptiveChunkOptimizer::new(ChunkContentType::Json);
1152
1153 let size = optimizer.optimal_size();
1155 assert!(size >= optimizer.min_chunk);
1156 assert!(size <= optimizer.max_chunk);
1157 }
1158
1159 #[test]
1160 fn test_adaptive_optimizer_rtt_adaptation() {
1161 let optimizer = AdaptiveChunkOptimizer::new(ChunkContentType::Binary);
1162
1163 for _ in 0..5 {
1165 optimizer.record_rtt(300);
1166 }
1167
1168 let poor_size = optimizer.optimal_size();
1169
1170 for _ in 0..10 {
1172 optimizer.record_rtt(5);
1173 }
1174
1175 let good_size = optimizer.optimal_size();
1176
1177 assert!(good_size >= poor_size);
1179 }
1180
1181 #[test]
1182 fn test_chunked_encoding_optimizer() {
1183 let optimizer = ChunkedEncodingOptimizer::new();
1184
1185 let plan = optimizer.optimal_for_data(500);
1187 assert_eq!(plan.num_chunks, 1);
1188 assert_eq!(plan.chunk_size, 500);
1189
1190 let large_data = optimizer.max_chunk * 3;
1192 let plan = optimizer.optimal_for_data(large_data);
1193 assert!(plan.num_chunks > 1);
1194 assert!(plan.efficiency > 0.99);
1195 }
1196
1197 #[test]
1198 fn test_chunked_encoding_efficiency() {
1199 let small_eff = ChunkedEncodingOptimizer::chunk_efficiency(100);
1201 let large_eff = ChunkedEncodingOptimizer::chunk_efficiency(16384);
1202
1203 assert!(large_eff > small_eff);
1204 assert!(large_eff > 0.99); }
1206
1207 #[test]
1208 fn test_chunked_encoding_create_chunks() {
1209 let optimizer = ChunkedEncodingOptimizer::new().target_chunk(100);
1210
1211 let data = vec![0u8; 350];
1212 let chunks = optimizer.create_chunks(&data);
1213
1214 assert!(chunks.len() >= 3);
1216
1217 let total: usize = chunks.iter().map(|c| c.len()).sum();
1219 assert_eq!(total, 350);
1220 }
1221
1222 #[test]
1223 fn test_chunk_stats() {
1224 let stats = chunk_stats();
1225 let _ = stats.chunks_created();
1226 let _ = stats.bytes_chunked();
1227 let _ = stats.average_chunk_size();
1228 let _ = stats.average_rtt();
1229 }
1230}
1231
1232pub const DEFAULT_MIN_CHUNK: usize = 4 * 1024;
1239pub const DEFAULT_CHUNK_SIZE: usize = 16 * 1024;
1241pub const DEFAULT_MAX_CHUNK: usize = 64 * 1024;
1243
1244#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
1250pub enum BackpressureStrategy {
1251 #[default]
1253 PauseResume,
1254 DropOldest,
1256 DropNewest,
1258 Block,
1260 Error,
1262}
1263
1264#[derive(Debug, Clone)]
1266pub struct BackpressureConfig {
1267 pub high_watermark: usize,
1269 pub low_watermark: usize,
1271 pub strategy: BackpressureStrategy,
1273 pub max_buffer: usize,
1275}
1276
1277impl Default for BackpressureConfig {
1278 fn default() -> Self {
1279 Self {
1280 high_watermark: 64,
1281 low_watermark: 16,
1282 strategy: BackpressureStrategy::PauseResume,
1283 max_buffer: 256,
1284 }
1285 }
1286}
1287
1288impl BackpressureConfig {
1289 pub fn new() -> Self {
1291 Self::default()
1292 }
1293
1294 pub fn high_watermark(mut self, watermark: usize) -> Self {
1296 self.high_watermark = watermark;
1297 self
1298 }
1299
1300 pub fn low_watermark(mut self, watermark: usize) -> Self {
1302 self.low_watermark = watermark;
1303 self
1304 }
1305
1306 pub fn strategy(mut self, strategy: BackpressureStrategy) -> Self {
1308 self.strategy = strategy;
1309 self
1310 }
1311
1312 pub fn max_buffer(mut self, size: usize) -> Self {
1314 self.max_buffer = size;
1315 self
1316 }
1317}
1318
1319#[derive(Debug)]
1351pub struct BackpressureController {
1352 config: BackpressureConfig,
1353 buffer_level: AtomicUsize,
1355 is_paused: AtomicBool,
1357 resume_notify: Arc<tokio::sync::Notify>,
1359 stats: BackpressureStats,
1361}
1362
1363#[derive(Debug, Default)]
1365pub struct BackpressureStats {
1366 pub bytes_sent: AtomicU64,
1368 pub bytes_acked: AtomicU64,
1370 pub pause_count: AtomicU64,
1372 pub resume_count: AtomicU64,
1374 pub dropped_chunks: AtomicU64,
1376 pub dropped_bytes: AtomicU64,
1378}
1379
1380impl BackpressureController {
1381 pub fn new(config: BackpressureConfig) -> Self {
1383 Self {
1384 config,
1385 buffer_level: AtomicUsize::new(0),
1386 is_paused: AtomicBool::new(false),
1387 resume_notify: Arc::new(tokio::sync::Notify::new()),
1388 stats: BackpressureStats::default(),
1389 }
1390 }
1391
1392 pub fn default_controller() -> Self {
1394 Self::new(BackpressureConfig::default())
1395 }
1396
1397 #[inline]
1399 pub fn can_send(&self) -> bool {
1400 !self.is_paused.load(Ordering::Acquire)
1401 }
1402
1403 #[inline]
1405 pub fn is_paused(&self) -> bool {
1406 self.is_paused.load(Ordering::Acquire)
1407 }
1408
1409 #[inline]
1411 pub fn buffer_level(&self) -> usize {
1412 self.buffer_level.load(Ordering::Acquire)
1413 }
1414
1415 pub fn buffer_utilization(&self) -> f64 {
1417 let level = self.buffer_level() as f64;
1418 let max = self.config.max_buffer as f64;
1419 level / max
1420 }
1421
1422 pub fn record_send(&self, bytes: usize) {
1424 let new_level = self.buffer_level.fetch_add(bytes, Ordering::AcqRel) + bytes;
1425 self.stats
1426 .bytes_sent
1427 .fetch_add(bytes as u64, Ordering::Relaxed);
1428
1429 if new_level >= self.config.high_watermark && !self.is_paused.swap(true, Ordering::AcqRel) {
1431 self.stats.pause_count.fetch_add(1, Ordering::Relaxed);
1432 }
1433 }
1434
1435 pub fn record_ack(&self, bytes: usize) {
1437 let old_level = self
1441 .buffer_level
1442 .fetch_update(Ordering::AcqRel, Ordering::Acquire, |level| {
1443 Some(level.saturating_sub(bytes))
1444 })
1445 .expect("fetch_update closure never returns None");
1446 let new_level = old_level.saturating_sub(bytes);
1447 self.stats
1448 .bytes_acked
1449 .fetch_add(bytes as u64, Ordering::Relaxed);
1450
1451 if new_level <= self.config.low_watermark && self.is_paused.swap(false, Ordering::AcqRel) {
1453 self.stats.resume_count.fetch_add(1, Ordering::Relaxed);
1454 self.resume_notify.notify_waiters();
1455 }
1456 }
1457
1458 pub async fn wait_if_paused(&self) {
1460 loop {
1461 let notified = self.resume_notify.notified();
1465 if !self.is_paused() {
1466 return;
1467 }
1468 notified.await;
1469 }
1470 }
1471
1472 pub fn try_send(&self, bytes: usize) -> Result<bool, BackpressureError> {
1479 let current = self.buffer_level();
1480
1481 match self.config.strategy {
1482 BackpressureStrategy::PauseResume => {
1483 if current < self.config.max_buffer {
1484 self.record_send(bytes);
1485 Ok(true)
1486 } else {
1487 Ok(false)
1489 }
1490 }
1491 BackpressureStrategy::Block => {
1492 self.record_send(bytes);
1494 Ok(true)
1495 }
1496 BackpressureStrategy::DropOldest | BackpressureStrategy::DropNewest => {
1497 if current + bytes > self.config.max_buffer {
1498 self.stats.dropped_chunks.fetch_add(1, Ordering::Relaxed);
1499 self.stats
1500 .dropped_bytes
1501 .fetch_add(bytes as u64, Ordering::Relaxed);
1502 Ok(false)
1503 } else {
1504 self.record_send(bytes);
1505 Ok(true)
1506 }
1507 }
1508 BackpressureStrategy::Error => {
1509 if current + bytes > self.config.max_buffer {
1510 Err(BackpressureError::BufferFull {
1511 current,
1512 max: self.config.max_buffer,
1513 })
1514 } else {
1515 self.record_send(bytes);
1516 Ok(true)
1517 }
1518 }
1519 }
1520 }
1521
1522 pub fn reset(&self) {
1524 self.buffer_level.store(0, Ordering::Release);
1525 self.is_paused.store(false, Ordering::Release);
1526 self.resume_notify.notify_waiters();
1527 }
1528
1529 pub fn stats(&self) -> &BackpressureStats {
1531 &self.stats
1532 }
1533
1534 pub fn snapshot(&self) -> BackpressureSnapshot {
1536 BackpressureSnapshot {
1537 buffer_level: self.buffer_level(),
1538 is_paused: self.is_paused(),
1539 utilization: self.buffer_utilization(),
1540 bytes_sent: self.stats.bytes_sent.load(Ordering::Relaxed),
1541 bytes_acked: self.stats.bytes_acked.load(Ordering::Relaxed),
1542 pause_count: self.stats.pause_count.load(Ordering::Relaxed),
1543 dropped_chunks: self.stats.dropped_chunks.load(Ordering::Relaxed),
1544 }
1545 }
1546}
1547
1548#[derive(Debug, Clone)]
1550pub struct BackpressureSnapshot {
1551 pub buffer_level: usize,
1552 pub is_paused: bool,
1553 pub utilization: f64,
1554 pub bytes_sent: u64,
1555 pub bytes_acked: u64,
1556 pub pause_count: u64,
1557 pub dropped_chunks: u64,
1558}
1559
1560#[derive(Debug, Clone)]
1562pub enum BackpressureError {
1563 BufferFull { current: usize, max: usize },
1564}
1565
1566impl std::fmt::Display for BackpressureError {
1567 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1568 match self {
1569 Self::BufferFull { current, max } => {
1570 write!(f, "Backpressure buffer full: {} / {} bytes", current, max)
1571 }
1572 }
1573 }
1574}
1575
1576impl std::error::Error for BackpressureError {}
1577
1578#[derive(Debug, Clone)]
1584pub struct ChunkOptimizer {
1585 pub min_chunk: usize,
1587 pub max_chunk: usize,
1589 pub target_latency_ms: u64,
1591 throughput: Arc<AtomicU64>,
1593 chunk_count: Arc<AtomicU64>,
1595}
1596
1597impl ChunkOptimizer {
1598 pub fn new(min_chunk: usize, max_chunk: usize) -> Self {
1600 Self {
1601 min_chunk,
1602 max_chunk,
1603 target_latency_ms: 50, throughput: Arc::new(AtomicU64::new(0)),
1605 chunk_count: Arc::new(AtomicU64::new(0)),
1606 }
1607 }
1608
1609 pub fn with_target_latency(mut self, ms: u64) -> Self {
1611 self.target_latency_ms = ms;
1612 self
1613 }
1614
1615 #[inline]
1617 pub fn optimal_chunk_size(&self, available: usize) -> usize {
1618 available.clamp(self.min_chunk, self.max_chunk)
1619 }
1620
1621 pub fn record_chunk(&self, size: usize) {
1623 self.throughput.fetch_add(size as u64, Ordering::Relaxed);
1624 self.chunk_count.fetch_add(1, Ordering::Relaxed);
1625 }
1626
1627 pub fn total_bytes(&self) -> u64 {
1629 self.throughput.load(Ordering::Relaxed)
1630 }
1631
1632 pub fn total_chunks(&self) -> u64 {
1634 self.chunk_count.load(Ordering::Relaxed)
1635 }
1636
1637 pub fn average_chunk_size(&self) -> usize {
1639 self.total_bytes()
1640 .checked_div(self.total_chunks())
1641 .map(|v| v as usize)
1642 .unwrap_or(self.min_chunk)
1643 }
1644}
1645
1646impl Default for ChunkOptimizer {
1647 fn default() -> Self {
1648 Self::new(DEFAULT_MIN_CHUNK, DEFAULT_MAX_CHUNK)
1649 }
1650}
1651
1652pub const CHUNK_TINY: usize = 512;
1658pub const CHUNK_SMALL: usize = 1024;
1660pub const CHUNK_MEDIUM: usize = 8 * 1024;
1662pub const CHUNK_LARGE: usize = 32 * 1024;
1664pub const CHUNK_XLARGE: usize = 128 * 1024;
1666pub const CHUNK_TCP_OPTIMAL: usize = 64 * 1024;
1668
1669#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1671pub enum ChunkContentType {
1672 RealTime,
1674 Json,
1676 Html,
1678 Text,
1680 Binary,
1682 Media,
1684 Unknown,
1686}
1687
1688impl ChunkContentType {
1689 pub fn from_mime(mime: &str) -> Self {
1691 let mime_lower = mime.to_lowercase();
1692 if mime_lower.contains("text/event-stream") || mime_lower.contains("x-ndjson") {
1693 Self::RealTime
1694 } else if mime_lower.contains("json") {
1695 Self::Json
1696 } else if mime_lower.contains("html") {
1697 Self::Html
1698 } else if mime_lower.contains("text/") {
1699 Self::Text
1700 } else if mime_lower.contains("application/octet-stream")
1701 || mime_lower.contains("image/")
1702 || mime_lower.contains("font/")
1703 {
1704 Self::Binary
1705 } else if mime_lower.contains("video/") || mime_lower.contains("audio/") {
1706 Self::Media
1707 } else {
1708 Self::Unknown
1709 }
1710 }
1711
1712 pub fn recommended_chunk_size(&self) -> usize {
1714 match self {
1715 Self::RealTime => CHUNK_TINY, Self::Json => CHUNK_MEDIUM, Self::Html => CHUNK_MEDIUM, Self::Text => CHUNK_SMALL, Self::Binary => CHUNK_LARGE, Self::Media => CHUNK_TCP_OPTIMAL, Self::Unknown => DEFAULT_CHUNK_SIZE, }
1723 }
1724
1725 pub fn min_chunk_size(&self) -> usize {
1727 match self {
1728 Self::RealTime => 64, Self::Json => CHUNK_SMALL, Self::Html => CHUNK_SMALL, Self::Text => 128, Self::Binary => CHUNK_MEDIUM, Self::Media => CHUNK_MEDIUM, Self::Unknown => CHUNK_SMALL, }
1736 }
1737
1738 pub fn max_chunk_size(&self) -> usize {
1740 match self {
1741 Self::RealTime => CHUNK_SMALL, Self::Json => CHUNK_LARGE, Self::Html => CHUNK_LARGE, Self::Text => CHUNK_MEDIUM, Self::Binary => CHUNK_XLARGE, Self::Media => CHUNK_XLARGE, Self::Unknown => CHUNK_LARGE, }
1749 }
1750}
1751
1752#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1754pub enum NetworkCondition {
1755 Excellent,
1757 Good,
1759 Fair,
1761 Poor,
1763 Terrible,
1765 Unknown,
1767}
1768
1769impl NetworkCondition {
1770 pub fn from_rtt_ms(rtt_ms: u64) -> Self {
1772 match rtt_ms {
1773 0..=10 => Self::Excellent,
1774 11..=50 => Self::Good,
1775 51..=100 => Self::Fair,
1776 101..=500 => Self::Poor,
1777 _ => Self::Terrible,
1778 }
1779 }
1780
1781 pub fn from_throughput(bytes_per_sec: u64) -> Self {
1783 match bytes_per_sec {
1784 x if x > 12_500_000 => Self::Excellent, x if x > 1_250_000 => Self::Good, x if x > 125_000 => Self::Fair, x if x > 12_500 => Self::Poor, _ => Self::Terrible,
1789 }
1790 }
1791
1792 pub fn chunk_multiplier(&self) -> f32 {
1794 match self {
1795 Self::Excellent => 2.0, Self::Good => 1.0, Self::Fair => 0.75, Self::Poor => 0.5, Self::Terrible => 0.25, Self::Unknown => 1.0, }
1802 }
1803}
1804
1805#[derive(Debug)]
1807pub struct AdaptiveChunkOptimizer {
1808 content_type: ChunkContentType,
1810 network_condition: std::sync::atomic::AtomicU8,
1812 base_chunk: usize,
1814 min_chunk: usize,
1816 max_chunk: usize,
1818 rtt_samples: std::sync::Mutex<RttTracker>,
1820 throughput_tracker: ThroughputTracker,
1822 bytes_sent: AtomicU64,
1824 chunks_sent: AtomicU64,
1826}
1827
1828impl AdaptiveChunkOptimizer {
1829 pub fn new(content_type: ChunkContentType) -> Self {
1831 Self {
1832 min_chunk: content_type.min_chunk_size(),
1833 max_chunk: content_type.max_chunk_size(),
1834 base_chunk: content_type.recommended_chunk_size(),
1835 content_type,
1836 network_condition: std::sync::atomic::AtomicU8::new(NetworkCondition::Unknown as u8),
1837 rtt_samples: std::sync::Mutex::new(RttTracker::new()),
1838 throughput_tracker: ThroughputTracker::new(),
1839 bytes_sent: AtomicU64::new(0),
1840 chunks_sent: AtomicU64::new(0),
1841 }
1842 }
1843
1844 pub fn from_mime(mime: &str) -> Self {
1846 Self::new(ChunkContentType::from_mime(mime))
1847 }
1848
1849 pub fn with_bounds(mut self, min: usize, max: usize) -> Self {
1851 self.min_chunk = min;
1852 self.max_chunk = max;
1853 self
1854 }
1855
1856 pub fn with_base_chunk(mut self, base: usize) -> Self {
1858 self.base_chunk = base;
1859 self
1860 }
1861
1862 #[inline]
1864 pub fn optimal_size(&self) -> usize {
1865 let condition = self.current_condition();
1866 let multiplier = condition.chunk_multiplier();
1867 let optimal = (self.base_chunk as f32 * multiplier) as usize;
1868 optimal.clamp(self.min_chunk, self.max_chunk)
1869 }
1870
1871 #[inline]
1873 pub fn optimal_for_data(&self, data_len: usize) -> usize {
1874 let optimal = self.optimal_size();
1875 if data_len <= optimal * 3 / 2 {
1877 data_len } else {
1879 optimal
1880 }
1881 }
1882
1883 pub fn record_rtt(&self, rtt_ms: u64) {
1885 let mut tracker = self.rtt_samples.lock().unwrap();
1886 tracker.add_sample(rtt_ms);
1887 let avg_rtt = tracker.average();
1888 let condition = NetworkCondition::from_rtt_ms(avg_rtt);
1889 self.network_condition
1890 .store(condition as u8, Ordering::Relaxed);
1891 CHUNK_STATS.record_rtt_sample(rtt_ms);
1892 }
1893
1894 pub fn record_throughput(&self, bytes: usize, duration_ms: u64) {
1896 let Some(bytes_per_sec) = (bytes as u64 * 1000).checked_div(duration_ms) else {
1897 return;
1898 };
1899 self.throughput_tracker.record(bytes_per_sec);
1900 let throughput_condition = NetworkCondition::from_throughput(bytes_per_sec);
1902 let rtt_condition = self.current_condition();
1903 let combined = if (throughput_condition as u8) > (rtt_condition as u8) {
1905 throughput_condition
1906 } else {
1907 rtt_condition
1908 };
1909 self.network_condition
1910 .store(combined as u8, Ordering::Relaxed);
1911 }
1912
1913 pub fn record_chunk(&self, size: usize) {
1915 self.bytes_sent.fetch_add(size as u64, Ordering::Relaxed);
1916 self.chunks_sent.fetch_add(1, Ordering::Relaxed);
1917 CHUNK_STATS.record_chunk(size);
1918 }
1919
1920 pub fn current_condition(&self) -> NetworkCondition {
1922 let val = self.network_condition.load(Ordering::Relaxed);
1923 match val {
1924 0 => NetworkCondition::Excellent,
1925 1 => NetworkCondition::Good,
1926 2 => NetworkCondition::Fair,
1927 3 => NetworkCondition::Poor,
1928 4 => NetworkCondition::Terrible,
1929 _ => NetworkCondition::Unknown,
1930 }
1931 }
1932
1933 pub fn average_rtt(&self) -> u64 {
1935 self.rtt_samples.lock().unwrap().average()
1936 }
1937
1938 pub fn estimated_throughput(&self) -> u64 {
1940 self.throughput_tracker.average()
1941 }
1942
1943 pub fn bytes_sent(&self) -> u64 {
1945 self.bytes_sent.load(Ordering::Relaxed)
1946 }
1947
1948 pub fn chunks_sent(&self) -> u64 {
1950 self.chunks_sent.load(Ordering::Relaxed)
1951 }
1952
1953 pub fn average_chunk_size(&self) -> usize {
1955 self.bytes_sent()
1956 .checked_div(self.chunks_sent())
1957 .map(|v| v as usize)
1958 .unwrap_or(self.base_chunk)
1959 }
1960
1961 pub fn content_type(&self) -> ChunkContentType {
1963 self.content_type
1964 }
1965}
1966
1967#[derive(Debug)]
1969struct RttTracker {
1970 samples: [u64; 16],
1971 index: usize,
1972 count: usize,
1973}
1974
1975impl RttTracker {
1976 fn new() -> Self {
1977 Self {
1978 samples: [0; 16],
1979 index: 0,
1980 count: 0,
1981 }
1982 }
1983
1984 fn add_sample(&mut self, rtt_ms: u64) {
1985 self.samples[self.index] = rtt_ms;
1986 self.index = (self.index + 1) % 16;
1987 if self.count < 16 {
1988 self.count += 1;
1989 }
1990 }
1991
1992 fn average(&self) -> u64 {
1993 if self.count == 0 {
1994 return 50; }
1996 let sum: u64 = self.samples[..self.count].iter().sum();
1997 sum / self.count as u64
1998 }
1999}
2000
2001#[derive(Debug)]
2003struct ThroughputTracker {
2004 samples: std::sync::Mutex<Vec<u64>>,
2005 max_samples: usize,
2006}
2007
2008impl ThroughputTracker {
2009 fn new() -> Self {
2010 Self {
2011 samples: std::sync::Mutex::new(Vec::with_capacity(16)),
2012 max_samples: 16,
2013 }
2014 }
2015
2016 fn record(&self, bytes_per_sec: u64) {
2017 let mut samples = self.samples.lock().unwrap();
2018 if samples.len() >= self.max_samples {
2019 samples.remove(0);
2020 }
2021 samples.push(bytes_per_sec);
2022 }
2023
2024 fn average(&self) -> u64 {
2025 let samples = self.samples.lock().unwrap();
2026 if samples.is_empty() {
2027 return 0;
2028 }
2029 let sum: u64 = samples.iter().sum();
2030 sum / samples.len() as u64
2031 }
2032}
2033
2034#[derive(Debug, Clone)]
2047pub struct ChunkedEncodingOptimizer {
2048 pub min_chunk: usize,
2050 pub target_chunk: usize,
2052 pub max_chunk: usize,
2054 pub min_efficiency: f32,
2056}
2057
2058impl ChunkedEncodingOptimizer {
2059 pub fn new() -> Self {
2061 Self::default()
2062 }
2063
2064 pub fn target_chunk(mut self, size: usize) -> Self {
2066 self.target_chunk = size;
2067 self
2068 }
2069
2070 pub fn min_efficiency(mut self, efficiency: f32) -> Self {
2072 self.min_efficiency = efficiency.clamp(0.5, 1.0);
2073 self
2074 }
2075
2076 #[inline]
2078 pub fn chunk_overhead(chunk_size: usize) -> usize {
2079 let hex_digits = if chunk_size == 0 {
2081 1
2082 } else {
2083 (chunk_size as f64).log(16.0).floor() as usize + 1
2084 };
2085 hex_digits + 4 }
2087
2088 #[inline]
2090 pub fn chunk_efficiency(chunk_size: usize) -> f32 {
2091 if chunk_size == 0 {
2092 return 0.0;
2093 }
2094 let overhead = Self::chunk_overhead(chunk_size);
2095 chunk_size as f32 / (chunk_size + overhead) as f32
2096 }
2097
2098 pub fn optimal_for_data(&self, data_len: usize) -> ChunkingPlan {
2100 if data_len == 0 {
2101 return ChunkingPlan {
2102 chunk_size: 0,
2103 num_chunks: 0,
2104 final_chunk: 0,
2105 efficiency: 1.0,
2106 };
2107 }
2108
2109 if data_len <= self.max_chunk {
2111 let eff = Self::chunk_efficiency(data_len);
2112 if eff >= self.min_efficiency {
2113 return ChunkingPlan {
2114 chunk_size: data_len,
2115 num_chunks: 1,
2116 final_chunk: 0,
2117 efficiency: eff,
2118 };
2119 }
2120 }
2121
2122 let target = self.target_chunk.min(data_len);
2124 let num_chunks = data_len / target;
2125 let remainder = data_len % target;
2126
2127 let (chunk_size, final_chunk) = if remainder > 0 && remainder < self.min_chunk {
2129 let adjusted_chunks = num_chunks;
2131 let adjusted_size = data_len / adjusted_chunks;
2132 let adjusted_remainder = data_len % adjusted_chunks;
2133 (adjusted_size, adjusted_remainder)
2134 } else {
2135 (target, remainder)
2136 };
2137
2138 let total_chunks = if final_chunk > 0 {
2139 num_chunks + 1
2140 } else {
2141 num_chunks
2142 };
2143 let total_overhead = total_chunks * Self::chunk_overhead(chunk_size);
2144 let efficiency = data_len as f32 / (data_len + total_overhead) as f32;
2145
2146 ChunkingPlan {
2147 chunk_size,
2148 num_chunks: total_chunks,
2149 final_chunk,
2150 efficiency,
2151 }
2152 }
2153
2154 pub fn create_chunks(&self, data: &[u8]) -> Vec<Bytes> {
2156 let plan = self.optimal_for_data(data.len());
2157 if plan.num_chunks == 0 {
2158 return vec![];
2159 }
2160
2161 let mut chunks = Vec::with_capacity(plan.num_chunks);
2162 let mut offset = 0;
2163
2164 for i in 0..plan.num_chunks {
2165 let size = if i == plan.num_chunks - 1 && plan.final_chunk > 0 {
2166 plan.final_chunk
2167 } else {
2168 plan.chunk_size
2169 };
2170 chunks.push(Bytes::copy_from_slice(&data[offset..offset + size]));
2171 offset += size;
2172 }
2173
2174 chunks
2175 }
2176}
2177
2178impl Default for ChunkedEncodingOptimizer {
2179 fn default() -> Self {
2180 Self {
2181 min_chunk: CHUNK_SMALL, target_chunk: DEFAULT_CHUNK_SIZE, max_chunk: CHUNK_XLARGE, min_efficiency: 0.99, }
2186 }
2187}
2188
2189#[derive(Debug, Clone, Copy)]
2191pub struct ChunkingPlan {
2192 pub chunk_size: usize,
2194 pub num_chunks: usize,
2196 pub final_chunk: usize,
2198 pub efficiency: f32,
2200}
2201
2202impl ChunkingPlan {
2203 pub fn total_overhead(&self) -> usize {
2205 self.num_chunks * ChunkedEncodingOptimizer::chunk_overhead(self.chunk_size)
2206 }
2207}
2208
2209#[derive(Debug, Default)]
2215pub struct ChunkStats {
2216 chunks_created: AtomicU64,
2218 bytes_chunked: AtomicU64,
2220 rtt_samples: AtomicU64,
2222 rtt_sum: AtomicU64,
2224}
2225
2226impl ChunkStats {
2227 fn record_chunk(&self, size: usize) {
2228 self.chunks_created.fetch_add(1, Ordering::Relaxed);
2229 self.bytes_chunked.fetch_add(size as u64, Ordering::Relaxed);
2230 }
2231
2232 fn record_rtt_sample(&self, rtt_ms: u64) {
2233 self.rtt_samples.fetch_add(1, Ordering::Relaxed);
2234 self.rtt_sum.fetch_add(rtt_ms, Ordering::Relaxed);
2235 }
2236
2237 pub fn chunks_created(&self) -> u64 {
2239 self.chunks_created.load(Ordering::Relaxed)
2240 }
2241
2242 pub fn bytes_chunked(&self) -> u64 {
2244 self.bytes_chunked.load(Ordering::Relaxed)
2245 }
2246
2247 pub fn average_chunk_size(&self) -> usize {
2249 self.bytes_chunked()
2250 .checked_div(self.chunks_created())
2251 .map(|v| v as usize)
2252 .unwrap_or(0)
2253 }
2254
2255 pub fn average_rtt(&self) -> u64 {
2257 self.rtt_sum
2258 .load(Ordering::Relaxed)
2259 .checked_div(self.rtt_samples.load(Ordering::Relaxed))
2260 .unwrap_or(0)
2261 }
2262}
2263
2264static CHUNK_STATS: ChunkStats = ChunkStats {
2266 chunks_created: AtomicU64::new(0),
2267 bytes_chunked: AtomicU64::new(0),
2268 rtt_samples: AtomicU64::new(0),
2269 rtt_sum: AtomicU64::new(0),
2270};
2271
2272pub fn chunk_stats() -> &'static ChunkStats {
2274 &CHUNK_STATS
2275}
2276
2277pub struct StreamingBodyBuilder {
2296 chunk_size: usize,
2297 buffer_size: usize,
2298 backpressure: BackpressureConfig,
2299 content_type: Option<String>,
2300 rate_limit: Option<u64>,
2301}
2302
2303impl StreamingBodyBuilder {
2304 pub fn new() -> Self {
2306 Self {
2307 chunk_size: DEFAULT_CHUNK_SIZE,
2308 buffer_size: 64,
2309 backpressure: BackpressureConfig::default(),
2310 content_type: None,
2311 rate_limit: None,
2312 }
2313 }
2314
2315 pub fn chunk_size(mut self, size: usize) -> Self {
2317 self.chunk_size = size;
2318 self
2319 }
2320
2321 pub fn buffer_size(mut self, size: usize) -> Self {
2323 self.buffer_size = size;
2324 self
2325 }
2326
2327 pub fn backpressure(mut self, config: BackpressureConfig) -> Self {
2329 self.backpressure = config;
2330 self
2331 }
2332
2333 pub fn content_type(mut self, content_type: impl Into<String>) -> Self {
2335 self.content_type = Some(content_type.into());
2336 self
2337 }
2338
2339 pub fn rate_limit(mut self, bytes_per_sec: u64) -> Self {
2341 self.rate_limit = Some(bytes_per_sec);
2342 self
2343 }
2344
2345 pub fn build_with_sender(self) -> (ByteStream, StreamingHandle) {
2347 let (stream, sender) = ByteStream::with_buffer_size(self.buffer_size);
2348 let handle = StreamingHandle {
2349 sender,
2350 chunk_size: self.chunk_size,
2351 rate_limiter: self.rate_limit.map(StreamRateLimiter::new),
2352 stats: Arc::new(StreamingHandleStats::default()),
2353 };
2354 STREAMING_STATS.record_stream_created();
2355 (stream, handle)
2356 }
2357
2358 pub fn build_response(self) -> (StreamingResponse, StreamingHandle) {
2360 let content_type = self.content_type.clone();
2361 let (stream, handle) = self.build_with_sender();
2362 let mut response = StreamingResponse::new(stream);
2363 if let Some(ct) = content_type {
2364 response = response.content_type(ct);
2365 }
2366 (response, handle)
2367 }
2368}
2369
2370impl Default for StreamingBodyBuilder {
2371 fn default() -> Self {
2372 Self::new()
2373 }
2374}
2375
2376pub struct StreamingHandle {
2378 sender: ByteStreamSender,
2379 chunk_size: usize,
2380 rate_limiter: Option<StreamRateLimiter>,
2381 stats: Arc<StreamingHandleStats>,
2382}
2383
2384impl StreamingHandle {
2385 pub async fn send(&self, data: impl Into<Vec<u8>>) -> Result<(), Error> {
2387 let data = data.into();
2388 let len = data.len();
2389
2390 if let Some(ref limiter) = self.rate_limiter {
2392 limiter.wait_for_capacity(len).await;
2393 }
2394
2395 self.sender.send(data).await?;
2396 self.stats.record_send(len);
2397 STREAMING_STATS.record_chunk_sent(len);
2398 Ok(())
2399 }
2400
2401 pub async fn send_bytes(&self, bytes: Bytes) -> Result<(), Error> {
2403 let len = bytes.len();
2404
2405 if let Some(ref limiter) = self.rate_limiter {
2406 limiter.wait_for_capacity(len).await;
2407 }
2408
2409 self.sender.send_bytes(bytes).await?;
2410 self.stats.record_send(len);
2411 STREAMING_STATS.record_chunk_sent(len);
2412 Ok(())
2413 }
2414
2415 pub async fn send_chunked(&self, data: &[u8]) -> Result<(), Error> {
2417 for chunk in data.chunks(self.chunk_size) {
2418 self.send(chunk.to_vec()).await?;
2419 }
2420 Ok(())
2421 }
2422
2423 pub async fn send_error(&self, error: impl Into<String>) -> Result<(), Error> {
2425 self.sender.send_error(error).await
2426 }
2427
2428 pub async fn close(&self) {
2430 self.sender.close().await;
2431 }
2432
2433 pub fn is_closed(&self) -> bool {
2435 self.sender.is_closed()
2436 }
2437
2438 pub fn bytes_sent(&self) -> u64 {
2440 self.stats.bytes_sent.load(Ordering::Relaxed)
2441 }
2442
2443 pub fn chunks_sent(&self) -> u64 {
2445 self.stats.chunks_sent.load(Ordering::Relaxed)
2446 }
2447}
2448
2449#[derive(Debug, Default)]
2451struct StreamingHandleStats {
2452 bytes_sent: AtomicU64,
2453 chunks_sent: AtomicU64,
2454}
2455
2456impl StreamingHandleStats {
2457 fn record_send(&self, len: usize) {
2458 self.bytes_sent.fetch_add(len as u64, Ordering::Relaxed);
2459 self.chunks_sent.fetch_add(1, Ordering::Relaxed);
2460 }
2461}
2462
2463pub struct StreamRateLimiter {
2469 pub bytes_per_sec: u64,
2471 bytes_in_window: AtomicU64,
2473 window_start: std::sync::Mutex<std::time::Instant>,
2475}
2476
2477impl StreamRateLimiter {
2478 pub fn new(bytes_per_sec: u64) -> Self {
2480 Self {
2481 bytes_per_sec,
2482 bytes_in_window: AtomicU64::new(0),
2483 window_start: std::sync::Mutex::new(std::time::Instant::now()),
2484 }
2485 }
2486
2487 pub async fn wait_for_capacity(&self, bytes: usize) {
2489 loop {
2490 let now = std::time::Instant::now();
2492 let elapsed = {
2493 let start = self.window_start.lock().unwrap();
2494 now.duration_since(*start)
2495 };
2496
2497 if elapsed.as_secs() >= 1 {
2499 self.bytes_in_window.store(0, Ordering::Relaxed);
2500 *self.window_start.lock().unwrap() = now;
2501 }
2502
2503 let current = self.bytes_in_window.load(Ordering::Relaxed);
2504 if current + bytes as u64 <= self.bytes_per_sec {
2505 self.bytes_in_window
2506 .fetch_add(bytes as u64, Ordering::Relaxed);
2507 return;
2508 }
2509
2510 let remaining = Duration::from_secs(1).saturating_sub(elapsed);
2512 if !remaining.is_zero() {
2513 tokio::time::sleep(remaining.min(Duration::from_millis(10))).await;
2514 }
2515 }
2516 }
2517}
2518
2519#[derive(Debug, Default)]
2525pub struct StreamingStats {
2526 streams_created: AtomicU64,
2528 chunks_sent: AtomicU64,
2530 bytes_sent: AtomicU64,
2532}
2533
2534impl StreamingStats {
2535 pub fn new() -> Self {
2537 Self::default()
2538 }
2539
2540 fn record_stream_created(&self) {
2541 self.streams_created.fetch_add(1, Ordering::Relaxed);
2542 }
2543
2544 fn record_chunk_sent(&self, len: usize) {
2545 self.chunks_sent.fetch_add(1, Ordering::Relaxed);
2546 self.bytes_sent.fetch_add(len as u64, Ordering::Relaxed);
2547 }
2548
2549 pub fn streams_created(&self) -> u64 {
2551 self.streams_created.load(Ordering::Relaxed)
2552 }
2553
2554 pub fn chunks_sent(&self) -> u64 {
2556 self.chunks_sent.load(Ordering::Relaxed)
2557 }
2558
2559 pub fn bytes_sent(&self) -> u64 {
2561 self.bytes_sent.load(Ordering::Relaxed)
2562 }
2563
2564 pub fn average_chunk_size(&self) -> usize {
2566 self.bytes_sent()
2567 .checked_div(self.chunks_sent())
2568 .map(|v| v as usize)
2569 .unwrap_or(0)
2570 }
2571}
2572
2573static STREAMING_STATS: StreamingStats = StreamingStats {
2575 streams_created: AtomicU64::new(0),
2576 chunks_sent: AtomicU64::new(0),
2577 bytes_sent: AtomicU64::new(0),
2578};
2579
2580pub fn streaming_stats() -> &'static StreamingStats {
2582 &STREAMING_STATS
2583}