1use crate::error::CamelError;
2pub const DEFAULT_MATERIALIZE_LIMIT: usize = 10 * 1024 * 1024;
7
8use bytes::{Bytes, BytesMut};
9use futures::stream::BoxStream;
10use futures::{StreamExt, TryStreamExt};
11use std::io;
12use std::pin::Pin;
13use std::sync::Arc;
14use std::task::{Context as TaskContext, Poll};
15use tokio::io::{AsyncRead, ReadBuf};
16use tokio::sync::Mutex;
17use tokio_util::io::StreamReader;
18
19pub type BoxAsyncRead = Pin<Box<dyn AsyncRead + Send + Unpin>>;
23
24#[derive(Debug, Clone, Default)]
26pub struct StreamMetadata {
27 pub size_hint: Option<u64>,
29 pub content_type: Option<String>,
31 pub origin: Option<String>,
33}
34
35pub struct StreamBody {
74 #[allow(clippy::type_complexity)]
76 pub stream: Arc<Mutex<Option<BoxStream<'static, Result<Bytes, CamelError>>>>>,
77 pub metadata: StreamMetadata,
79}
80
81impl std::fmt::Debug for StreamBody {
82 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
83 f.debug_struct("StreamBody")
84 .field("metadata", &self.metadata)
85 .field("stream", &"<BoxStream>")
86 .finish()
87 }
88}
89
90impl Clone for StreamBody {
91 fn clone(&self) -> Self {
92 Self {
93 stream: Arc::clone(&self.stream),
94 metadata: self.metadata.clone(),
95 }
96 }
97}
98
99#[allow(clippy::type_complexity)]
113struct StreamAsyncRead {
114 arc: Arc<Mutex<Option<BoxStream<'static, Result<Bytes, CamelError>>>>>,
115 reader: Option<Box<dyn AsyncRead + Send + Unpin>>,
117 consumed: bool,
119}
120
121impl AsyncRead for StreamAsyncRead {
122 fn poll_read(
123 mut self: Pin<&mut Self>,
124 cx: &mut TaskContext<'_>,
125 buf: &mut ReadBuf<'_>,
126 ) -> Poll<io::Result<()>> {
127 if self.consumed {
128 return Poll::Ready(Err(io::Error::other("stream already consumed")));
129 }
130 if self.reader.is_none() {
132 let extracted = {
134 match self.arc.try_lock() {
135 Ok(mut guard) => guard.take(),
136 Err(_) => {
137 cx.waker().wake_by_ref();
139 return Poll::Pending;
140 }
141 }
142 };
143 match extracted {
145 Some(stream) => {
146 let mapped = stream.map_err(|e: CamelError| io::Error::other(e.to_string()));
147 self.reader = Some(Box::new(StreamReader::new(mapped)));
148 }
149 None => {
150 self.consumed = true;
151 return Poll::Ready(Err(io::Error::other("stream already consumed")));
152 }
153 }
154 }
155 Pin::new(self.reader.as_mut().unwrap()).poll_read(cx, buf) }
158}
159
160#[derive(Debug, Default)]
162#[non_exhaustive]
163pub enum Body {
164 #[default]
166 Empty,
167 Bytes(Bytes),
169 Text(String),
171 Json(serde_json::Value),
173 Xml(String),
175 Stream(StreamBody),
177}
178
179impl Clone for Body {
180 fn clone(&self) -> Self {
181 match self {
182 Body::Empty => Body::Empty,
183 Body::Bytes(b) => Body::Bytes(b.clone()),
184 Body::Text(s) => Body::Text(s.clone()),
185 Body::Json(v) => Body::Json(v.clone()),
186 Body::Xml(s) => Body::Xml(s.clone()),
187 Body::Stream(s) => Body::Stream(s.clone()),
188 }
189 }
190}
191
192impl PartialEq for Body {
193 fn eq(&self, other: &Self) -> bool {
194 match (self, other) {
195 (Body::Empty, Body::Empty) => true,
196 (Body::Text(a), Body::Text(b)) => a == b,
197 (Body::Json(a), Body::Json(b)) => a == b,
198 (Body::Bytes(a), Body::Bytes(b)) => a == b,
199 (Body::Xml(a), Body::Xml(b)) => a == b,
200 _ => false,
202 }
203 }
204}
205
206impl Body {
207 pub fn is_empty(&self) -> bool {
209 matches!(self, Body::Empty)
210 }
211
212 pub async fn into_bytes(self, max_size: usize) -> Result<Bytes, CamelError> {
216 match self {
217 Body::Empty => Ok(Bytes::new()),
218 Body::Bytes(b) => {
219 if b.len() > max_size {
220 return Err(CamelError::StreamLimitExceeded(max_size));
221 }
222 Ok(b)
223 }
224 Body::Text(s) => {
225 if s.len() > max_size {
226 return Err(CamelError::StreamLimitExceeded(max_size));
227 }
228 Ok(Bytes::from(s))
229 }
230 Body::Json(v) => {
231 let b = serde_json::to_vec(&v)
232 .map_err(|e| CamelError::TypeConversionFailed(e.to_string()))?;
233 if b.len() > max_size {
234 return Err(CamelError::StreamLimitExceeded(max_size));
235 }
236 Ok(Bytes::from(b))
237 }
238 Body::Xml(s) => {
239 if s.len() > max_size {
240 return Err(CamelError::StreamLimitExceeded(max_size));
241 }
242 Ok(Bytes::from(s))
243 }
244 Body::Stream(s) => {
245 let mut stream_lock = s.stream.lock().await;
246 let mut stream = stream_lock.take().ok_or(CamelError::AlreadyConsumed)?;
247
248 let mut buffer = BytesMut::new();
249 while let Some(chunk_res) = stream.next().await {
250 let chunk = chunk_res?;
251 if buffer.len() + chunk.len() > max_size {
252 return Err(CamelError::StreamLimitExceeded(max_size));
253 }
254 buffer.extend_from_slice(&chunk);
255 }
256 Ok(buffer.freeze())
257 }
258 }
259 }
260
261 pub async fn materialize(self) -> Result<Bytes, CamelError> {
274 self.into_bytes(DEFAULT_MATERIALIZE_LIMIT).await
275 }
276
277 pub fn into_async_read(self) -> Result<BoxAsyncRead, CamelError> {
288 match self {
289 Body::Empty => Ok(Box::pin(tokio::io::empty())),
290 Body::Bytes(b) => Ok(Box::pin(std::io::Cursor::new(b))),
291 Body::Text(s) => Ok(Box::pin(std::io::Cursor::new(s.into_bytes()))),
292 Body::Json(v) => {
293 let bytes = serde_json::to_vec(&v)
294 .map_err(|e| CamelError::TypeConversionFailed(e.to_string()))?;
295 Ok(Box::pin(std::io::Cursor::new(bytes)) as BoxAsyncRead)
296 }
297 Body::Xml(s) => Ok(Box::pin(std::io::Cursor::new(s.into_bytes()))),
298 Body::Stream(s) => Ok(Box::pin(StreamAsyncRead {
299 arc: s.stream,
300 reader: None,
301 consumed: false,
302 })),
303 }
304 }
305
306 pub fn as_text(&self) -> Option<&str> {
308 match self {
309 Body::Text(s) => Some(s.as_str()),
310 _ => None,
311 }
312 }
313
314 pub fn as_xml(&self) -> Option<&str> {
316 match self {
317 Body::Xml(s) => Some(s.as_str()),
318 _ => None,
319 }
320 }
321
322 pub fn try_into_text(self) -> Result<Body, CamelError> {
326 crate::body_converter::convert(self, crate::body_converter::BodyType::Text)
327 }
328
329 pub fn try_into_json(self) -> Result<Body, CamelError> {
333 crate::body_converter::convert(self, crate::body_converter::BodyType::Json)
334 }
335
336 pub fn try_into_bytes_body(self) -> Result<Body, CamelError> {
340 crate::body_converter::convert(self, crate::body_converter::BodyType::Bytes)
341 }
342
343 pub fn try_into_xml(self) -> Result<Body, CamelError> {
347 crate::body_converter::convert(self, crate::body_converter::BodyType::Xml)
348 }
349}
350
351#[allow(unreachable_patterns)]
356pub fn body_type_name(body: &Body) -> &'static str {
357 match body {
358 Body::Empty => "empty",
359 Body::Bytes(_) => "bytes",
360 Body::Text(_) => "text",
361 Body::Json(_) => "json",
362 Body::Xml(_) => "xml",
363 Body::Stream(_) => "stream",
364 _ => "unknown",
365 }
366}
367
368impl From<String> for Body {
370 fn from(s: String) -> Self {
371 Body::Text(s)
372 }
373}
374
375impl From<&str> for Body {
376 fn from(s: &str) -> Self {
377 Body::Text(s.to_string())
378 }
379}
380
381impl From<Bytes> for Body {
382 fn from(b: Bytes) -> Self {
383 Body::Bytes(b)
384 }
385}
386
387impl From<Vec<u8>> for Body {
388 fn from(v: Vec<u8>) -> Self {
389 Body::Bytes(Bytes::from(v))
390 }
391}
392
393impl From<serde_json::Value> for Body {
394 fn from(v: serde_json::Value) -> Self {
395 Body::Json(v)
396 }
397}
398
399#[cfg(test)]
400mod tests {
401 use super::*;
402
403 #[test]
404 fn test_body_type_name_variants() {
405 assert_eq!(body_type_name(&Body::Empty), "empty");
406 assert_eq!(body_type_name(&Body::Bytes(Bytes::new())), "bytes");
407 assert_eq!(body_type_name(&Body::Text(String::new())), "text");
408 assert_eq!(body_type_name(&Body::Json(serde_json::Value::Null)), "json");
409 assert_eq!(body_type_name(&Body::Xml(String::new())), "xml");
410 let stream_body = Body::Stream(StreamBody {
411 stream: Arc::new(Mutex::new(None)),
412 metadata: StreamMetadata::default(),
413 });
414 assert_eq!(body_type_name(&stream_body), "stream");
415 }
416
417 #[test]
418 fn test_body_default_is_empty() {
419 let body = Body::default();
420 assert!(body.is_empty());
421 }
422
423 #[test]
424 fn test_body_from_string() {
425 let body = Body::from("hello".to_string());
426 assert_eq!(body.as_text(), Some("hello"));
427 }
428
429 #[test]
430 fn test_body_from_str() {
431 let body = Body::from("world");
432 assert_eq!(body.as_text(), Some("world"));
433 }
434
435 #[test]
436 fn test_body_from_bytes() {
437 let body = Body::from(Bytes::from_static(b"data"));
438 assert!(!body.is_empty());
439 assert!(matches!(body, Body::Bytes(_)));
440 }
441
442 #[test]
443 fn test_body_from_json() {
444 let val = serde_json::json!({"key": "value"});
445 let body = Body::from(val.clone());
446 assert!(matches!(body, Body::Json(_)));
447 }
448
449 #[tokio::test]
450 async fn test_into_bytes_from_stream() {
451 use futures::stream;
452 let chunks = vec![Ok(Bytes::from("hello ")), Ok(Bytes::from("world"))];
453 let stream = stream::iter(chunks);
454 let body = Body::Stream(StreamBody {
455 stream: Arc::new(Mutex::new(Some(Box::pin(stream)))),
456 metadata: StreamMetadata::default(),
457 });
458
459 let result = body.into_bytes(100).await.unwrap();
460 assert_eq!(result, Bytes::from("hello world"));
461 }
462
463 #[tokio::test]
464 async fn test_into_bytes_limit_exceeded() {
465 use futures::stream;
466 let chunks = vec![Ok(Bytes::from("this is too long"))];
467 let stream = stream::iter(chunks);
468 let body = Body::Stream(StreamBody {
469 stream: Arc::new(Mutex::new(Some(Box::pin(stream)))),
470 metadata: StreamMetadata::default(),
471 });
472
473 let result = body.into_bytes(5).await;
474 assert!(matches!(result, Err(CamelError::StreamLimitExceeded(5))));
475 }
476
477 #[tokio::test]
478 async fn test_into_bytes_already_consumed() {
479 use futures::stream;
480 let chunks = vec![Ok(Bytes::from("data"))];
481 let stream = stream::iter(chunks);
482 let body = Body::Stream(StreamBody {
483 stream: Arc::new(Mutex::new(Some(Box::pin(stream)))),
484 metadata: StreamMetadata::default(),
485 });
486
487 let cloned = body.clone();
488 let _ = body.into_bytes(100).await.unwrap();
489
490 let result = cloned.into_bytes(100).await;
491 assert!(matches!(result, Err(CamelError::AlreadyConsumed)));
492 }
493
494 #[tokio::test]
495 async fn test_materialize_with_default_limit() {
496 use futures::stream;
497
498 let chunks = vec![Ok(Bytes::from("test data"))];
500 let stream = stream::iter(chunks);
501 let body = Body::Stream(StreamBody {
502 stream: Arc::new(Mutex::new(Some(Box::pin(stream)))),
503 metadata: StreamMetadata::default(),
504 });
505
506 let result = body.materialize().await;
507 assert!(result.is_ok());
508 assert_eq!(result.unwrap(), Bytes::from("test data"));
509 }
510
511 #[tokio::test]
512 async fn test_materialize_non_stream_body_types() {
513 let body = Body::Empty;
517 let result = body.materialize().await.unwrap();
518 assert!(result.is_empty());
519
520 let body = Body::Bytes(Bytes::from("bytes data"));
522 let result = body.materialize().await.unwrap();
523 assert_eq!(result, Bytes::from("bytes data"));
524
525 let body = Body::Text("text data".to_string());
527 let result = body.materialize().await.unwrap();
528 assert_eq!(result, Bytes::from("text data"));
529
530 let body = Body::Json(serde_json::json!({"key": "value"}));
532 let result = body.materialize().await.unwrap();
533 assert_eq!(result, Bytes::from_static(br#"{"key":"value"}"#));
534
535 let xml = "<root><child>value</child></root>";
537 let body = Body::Xml(xml.to_string());
538 let result = body.materialize().await.unwrap();
539 assert_eq!(result, Bytes::from(xml));
540 }
541
542 #[tokio::test]
543 async fn test_materialize_exceeds_default_limit() {
544 use futures::stream;
545
546 let large_data = vec![0u8; 11 * 1024 * 1024];
548 let chunks = vec![Ok(Bytes::from(large_data))];
549 let stream = stream::iter(chunks);
550 let body = Body::Stream(StreamBody {
551 stream: Arc::new(Mutex::new(Some(Box::pin(stream)))),
552 metadata: StreamMetadata::default(),
553 });
554
555 let result = body.materialize().await;
556 assert!(matches!(
557 result,
558 Err(CamelError::StreamLimitExceeded(10_485_760))
559 ));
560 }
561
562 #[test]
563 fn stream_variants_are_never_equal() {
564 use futures::stream;
565
566 let make_stream = || {
567 let s = stream::iter(vec![Ok(Bytes::from_static(b"data"))]);
568 Body::Stream(StreamBody {
569 stream: Arc::new(Mutex::new(Some(Box::pin(s)))),
570 metadata: StreamMetadata::default(),
571 })
572 };
573 assert_ne!(make_stream(), make_stream());
574 }
575
576 #[test]
579 fn test_body_xml_as_xml() {
580 let xml = "<root><child>value</child></root>";
581 let body = Body::Xml(xml.to_string());
582 assert_eq!(body.as_xml(), Some(xml));
583 }
584
585 #[test]
586 fn test_body_non_xml_as_xml_returns_none() {
587 let body = Body::Text("<root/>".to_string());
589 assert_eq!(body.as_xml(), None);
590
591 let body = Body::Empty;
593 assert_eq!(body.as_xml(), None);
594
595 let body = Body::Bytes(Bytes::from("<root/>"));
597 assert_eq!(body.as_xml(), None);
598
599 let body = Body::Json(serde_json::json!({"key": "value"}));
601 assert_eq!(body.as_xml(), None);
602 }
603
604 #[test]
605 fn test_body_xml_partial_eq() {
606 let body1 = Body::Xml("a".to_string());
608 let body2 = Body::Xml("a".to_string());
609 assert_eq!(body1, body2);
610
611 let body1 = Body::Xml("a".to_string());
613 let body2 = Body::Xml("b".to_string());
614 assert_ne!(body1, body2);
615 }
616
617 #[test]
618 fn test_body_xml_not_equal_to_other_variants() {
619 let xml_body = Body::Xml("x".to_string());
621 let text_body = Body::Text("x".to_string());
622 assert_ne!(xml_body, text_body);
623 }
624
625 #[test]
626 fn test_try_into_xml_from_text() {
627 let body = Body::Text("<root/>".to_string());
628 let result = body.try_into_xml();
629 assert!(matches!(result, Ok(Body::Xml(ref s)) if s == "<root/>"));
630 }
631
632 #[test]
633 fn test_try_into_xml_invalid_text() {
634 let body = Body::Text("not xml".to_string());
635 let result = body.try_into_xml();
636 assert!(matches!(result, Err(CamelError::TypeConversionFailed(_))));
637 }
638
639 #[test]
640 fn test_body_xml_clone() {
641 let original = Body::Xml("hello".to_string());
642 let cloned = original.clone();
643 assert_eq!(original, cloned);
644 }
645
646 #[tokio::test]
649 async fn test_into_async_read_empty() {
650 use tokio::io::AsyncReadExt;
651 let body = Body::Empty;
652 let mut reader = body.into_async_read().unwrap();
653 let mut buf = Vec::new();
654 reader.read_to_end(&mut buf).await.unwrap();
655 assert!(buf.is_empty());
656 }
657
658 #[tokio::test]
659 async fn test_into_async_read_bytes() {
660 use tokio::io::AsyncReadExt;
661 let body = Body::Bytes(Bytes::from("hello"));
662 let mut reader = body.into_async_read().unwrap();
663 let mut buf = Vec::new();
664 reader.read_to_end(&mut buf).await.unwrap();
665 assert_eq!(buf, b"hello");
666 }
667
668 #[tokio::test]
669 async fn test_into_async_read_text() {
670 use tokio::io::AsyncReadExt;
671 let body = Body::Text("world".to_string());
672 let mut reader = body.into_async_read().unwrap();
673 let mut buf = Vec::new();
674 reader.read_to_end(&mut buf).await.unwrap();
675 assert_eq!(buf, b"world");
676 }
677
678 #[tokio::test]
679 async fn test_into_async_read_json() {
680 use tokio::io::AsyncReadExt;
681 let body = Body::Json(serde_json::json!({"key": "val"}));
682 let mut reader = body.into_async_read().unwrap();
683 let mut buf = Vec::new();
684 reader.read_to_end(&mut buf).await.unwrap();
685 let parsed: serde_json::Value = serde_json::from_slice(&buf).unwrap();
686 assert_eq!(parsed["key"], "val");
687 }
688
689 #[tokio::test]
690 async fn test_into_async_read_xml() {
691 use tokio::io::AsyncReadExt;
692 let body = Body::Xml("<root/>".to_string());
693 let mut reader = body.into_async_read().unwrap();
694 let mut buf = Vec::new();
695 reader.read_to_end(&mut buf).await.unwrap();
696 assert_eq!(buf, b"<root/>");
697 }
698
699 #[tokio::test]
700 async fn test_into_async_read_stream_multichunk() {
701 use tokio::io::AsyncReadExt;
702 let chunks: Vec<Result<Bytes, CamelError>> = vec![
703 Ok(Bytes::from("foo")),
704 Ok(Bytes::from("bar")),
705 Ok(Bytes::from("baz")),
706 ];
707 let stream = futures::stream::iter(chunks);
708 let body = Body::Stream(StreamBody {
709 stream: Arc::new(Mutex::new(Some(Box::pin(stream)))),
710 metadata: StreamMetadata {
711 size_hint: None,
712 content_type: None,
713 origin: None,
714 },
715 });
716 let mut reader = body.into_async_read().unwrap();
717 let mut buf = Vec::new();
718 reader.read_to_end(&mut buf).await.unwrap();
719 assert_eq!(buf, b"foobarbaz");
720 }
721
722 #[tokio::test]
723 async fn test_into_async_read_already_consumed() {
724 use tokio::io::AsyncReadExt;
725 type MaybeStream = Arc<Mutex<Option<BoxStream<'static, Result<Bytes, CamelError>>>>>;
727 let arc: MaybeStream = Arc::new(Mutex::new(None));
728 let body = Body::Stream(StreamBody {
729 stream: arc,
730 metadata: StreamMetadata {
731 size_hint: None,
732 content_type: None,
733 origin: None,
734 },
735 });
736 let mut reader = body.into_async_read().unwrap();
737 let mut buf = Vec::new();
738 let result = reader.read_to_end(&mut buf).await;
739 assert!(result.is_err());
740 }
741}