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
351impl From<String> for Body {
353 fn from(s: String) -> Self {
354 Body::Text(s)
355 }
356}
357
358impl From<&str> for Body {
359 fn from(s: &str) -> Self {
360 Body::Text(s.to_string())
361 }
362}
363
364impl From<Bytes> for Body {
365 fn from(b: Bytes) -> Self {
366 Body::Bytes(b)
367 }
368}
369
370impl From<Vec<u8>> for Body {
371 fn from(v: Vec<u8>) -> Self {
372 Body::Bytes(Bytes::from(v))
373 }
374}
375
376impl From<serde_json::Value> for Body {
377 fn from(v: serde_json::Value) -> Self {
378 Body::Json(v)
379 }
380}
381
382#[cfg(test)]
383mod tests {
384 use super::*;
385
386 #[test]
387 fn test_body_default_is_empty() {
388 let body = Body::default();
389 assert!(body.is_empty());
390 }
391
392 #[test]
393 fn test_body_from_string() {
394 let body = Body::from("hello".to_string());
395 assert_eq!(body.as_text(), Some("hello"));
396 }
397
398 #[test]
399 fn test_body_from_str() {
400 let body = Body::from("world");
401 assert_eq!(body.as_text(), Some("world"));
402 }
403
404 #[test]
405 fn test_body_from_bytes() {
406 let body = Body::from(Bytes::from_static(b"data"));
407 assert!(!body.is_empty());
408 assert!(matches!(body, Body::Bytes(_)));
409 }
410
411 #[test]
412 fn test_body_from_json() {
413 let val = serde_json::json!({"key": "value"});
414 let body = Body::from(val.clone());
415 assert!(matches!(body, Body::Json(_)));
416 }
417
418 #[tokio::test]
419 async fn test_into_bytes_from_stream() {
420 use futures::stream;
421 let chunks = vec![Ok(Bytes::from("hello ")), Ok(Bytes::from("world"))];
422 let stream = stream::iter(chunks);
423 let body = Body::Stream(StreamBody {
424 stream: Arc::new(Mutex::new(Some(Box::pin(stream)))),
425 metadata: StreamMetadata::default(),
426 });
427
428 let result = body.into_bytes(100).await.unwrap();
429 assert_eq!(result, Bytes::from("hello world"));
430 }
431
432 #[tokio::test]
433 async fn test_into_bytes_limit_exceeded() {
434 use futures::stream;
435 let chunks = vec![Ok(Bytes::from("this is too long"))];
436 let stream = stream::iter(chunks);
437 let body = Body::Stream(StreamBody {
438 stream: Arc::new(Mutex::new(Some(Box::pin(stream)))),
439 metadata: StreamMetadata::default(),
440 });
441
442 let result = body.into_bytes(5).await;
443 assert!(matches!(result, Err(CamelError::StreamLimitExceeded(5))));
444 }
445
446 #[tokio::test]
447 async fn test_into_bytes_already_consumed() {
448 use futures::stream;
449 let chunks = vec![Ok(Bytes::from("data"))];
450 let stream = stream::iter(chunks);
451 let body = Body::Stream(StreamBody {
452 stream: Arc::new(Mutex::new(Some(Box::pin(stream)))),
453 metadata: StreamMetadata::default(),
454 });
455
456 let cloned = body.clone();
457 let _ = body.into_bytes(100).await.unwrap();
458
459 let result = cloned.into_bytes(100).await;
460 assert!(matches!(result, Err(CamelError::AlreadyConsumed)));
461 }
462
463 #[tokio::test]
464 async fn test_materialize_with_default_limit() {
465 use futures::stream;
466
467 let chunks = vec![Ok(Bytes::from("test data"))];
469 let stream = stream::iter(chunks);
470 let body = Body::Stream(StreamBody {
471 stream: Arc::new(Mutex::new(Some(Box::pin(stream)))),
472 metadata: StreamMetadata::default(),
473 });
474
475 let result = body.materialize().await;
476 assert!(result.is_ok());
477 assert_eq!(result.unwrap(), Bytes::from("test data"));
478 }
479
480 #[tokio::test]
481 async fn test_materialize_non_stream_body_types() {
482 let body = Body::Empty;
486 let result = body.materialize().await.unwrap();
487 assert!(result.is_empty());
488
489 let body = Body::Bytes(Bytes::from("bytes data"));
491 let result = body.materialize().await.unwrap();
492 assert_eq!(result, Bytes::from("bytes data"));
493
494 let body = Body::Text("text data".to_string());
496 let result = body.materialize().await.unwrap();
497 assert_eq!(result, Bytes::from("text data"));
498
499 let body = Body::Json(serde_json::json!({"key": "value"}));
501 let result = body.materialize().await.unwrap();
502 assert_eq!(result, Bytes::from_static(br#"{"key":"value"}"#));
503
504 let xml = "<root><child>value</child></root>";
506 let body = Body::Xml(xml.to_string());
507 let result = body.materialize().await.unwrap();
508 assert_eq!(result, Bytes::from(xml));
509 }
510
511 #[tokio::test]
512 async fn test_materialize_exceeds_default_limit() {
513 use futures::stream;
514
515 let large_data = vec![0u8; 11 * 1024 * 1024];
517 let chunks = vec![Ok(Bytes::from(large_data))];
518 let stream = stream::iter(chunks);
519 let body = Body::Stream(StreamBody {
520 stream: Arc::new(Mutex::new(Some(Box::pin(stream)))),
521 metadata: StreamMetadata::default(),
522 });
523
524 let result = body.materialize().await;
525 assert!(matches!(
526 result,
527 Err(CamelError::StreamLimitExceeded(10_485_760))
528 ));
529 }
530
531 #[test]
532 fn stream_variants_are_never_equal() {
533 use futures::stream;
534
535 let make_stream = || {
536 let s = stream::iter(vec![Ok(Bytes::from_static(b"data"))]);
537 Body::Stream(StreamBody {
538 stream: Arc::new(Mutex::new(Some(Box::pin(s)))),
539 metadata: StreamMetadata::default(),
540 })
541 };
542 assert_ne!(make_stream(), make_stream());
543 }
544
545 #[test]
548 fn test_body_xml_as_xml() {
549 let xml = "<root><child>value</child></root>";
550 let body = Body::Xml(xml.to_string());
551 assert_eq!(body.as_xml(), Some(xml));
552 }
553
554 #[test]
555 fn test_body_non_xml_as_xml_returns_none() {
556 let body = Body::Text("<root/>".to_string());
558 assert_eq!(body.as_xml(), None);
559
560 let body = Body::Empty;
562 assert_eq!(body.as_xml(), None);
563
564 let body = Body::Bytes(Bytes::from("<root/>"));
566 assert_eq!(body.as_xml(), None);
567
568 let body = Body::Json(serde_json::json!({"key": "value"}));
570 assert_eq!(body.as_xml(), None);
571 }
572
573 #[test]
574 fn test_body_xml_partial_eq() {
575 let body1 = Body::Xml("a".to_string());
577 let body2 = Body::Xml("a".to_string());
578 assert_eq!(body1, body2);
579
580 let body1 = Body::Xml("a".to_string());
582 let body2 = Body::Xml("b".to_string());
583 assert_ne!(body1, body2);
584 }
585
586 #[test]
587 fn test_body_xml_not_equal_to_other_variants() {
588 let xml_body = Body::Xml("x".to_string());
590 let text_body = Body::Text("x".to_string());
591 assert_ne!(xml_body, text_body);
592 }
593
594 #[test]
595 fn test_try_into_xml_from_text() {
596 let body = Body::Text("<root/>".to_string());
597 let result = body.try_into_xml();
598 assert!(matches!(result, Ok(Body::Xml(ref s)) if s == "<root/>"));
599 }
600
601 #[test]
602 fn test_try_into_xml_invalid_text() {
603 let body = Body::Text("not xml".to_string());
604 let result = body.try_into_xml();
605 assert!(matches!(result, Err(CamelError::TypeConversionFailed(_))));
606 }
607
608 #[test]
609 fn test_body_xml_clone() {
610 let original = Body::Xml("hello".to_string());
611 let cloned = original.clone();
612 assert_eq!(original, cloned);
613 }
614
615 #[tokio::test]
618 async fn test_into_async_read_empty() {
619 use tokio::io::AsyncReadExt;
620 let body = Body::Empty;
621 let mut reader = body.into_async_read().unwrap();
622 let mut buf = Vec::new();
623 reader.read_to_end(&mut buf).await.unwrap();
624 assert!(buf.is_empty());
625 }
626
627 #[tokio::test]
628 async fn test_into_async_read_bytes() {
629 use tokio::io::AsyncReadExt;
630 let body = Body::Bytes(Bytes::from("hello"));
631 let mut reader = body.into_async_read().unwrap();
632 let mut buf = Vec::new();
633 reader.read_to_end(&mut buf).await.unwrap();
634 assert_eq!(buf, b"hello");
635 }
636
637 #[tokio::test]
638 async fn test_into_async_read_text() {
639 use tokio::io::AsyncReadExt;
640 let body = Body::Text("world".to_string());
641 let mut reader = body.into_async_read().unwrap();
642 let mut buf = Vec::new();
643 reader.read_to_end(&mut buf).await.unwrap();
644 assert_eq!(buf, b"world");
645 }
646
647 #[tokio::test]
648 async fn test_into_async_read_json() {
649 use tokio::io::AsyncReadExt;
650 let body = Body::Json(serde_json::json!({"key": "val"}));
651 let mut reader = body.into_async_read().unwrap();
652 let mut buf = Vec::new();
653 reader.read_to_end(&mut buf).await.unwrap();
654 let parsed: serde_json::Value = serde_json::from_slice(&buf).unwrap();
655 assert_eq!(parsed["key"], "val");
656 }
657
658 #[tokio::test]
659 async fn test_into_async_read_xml() {
660 use tokio::io::AsyncReadExt;
661 let body = Body::Xml("<root/>".to_string());
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"<root/>");
666 }
667
668 #[tokio::test]
669 async fn test_into_async_read_stream_multichunk() {
670 use tokio::io::AsyncReadExt;
671 let chunks: Vec<Result<Bytes, CamelError>> = vec![
672 Ok(Bytes::from("foo")),
673 Ok(Bytes::from("bar")),
674 Ok(Bytes::from("baz")),
675 ];
676 let stream = futures::stream::iter(chunks);
677 let body = Body::Stream(StreamBody {
678 stream: Arc::new(Mutex::new(Some(Box::pin(stream)))),
679 metadata: StreamMetadata {
680 size_hint: None,
681 content_type: None,
682 origin: None,
683 },
684 });
685 let mut reader = body.into_async_read().unwrap();
686 let mut buf = Vec::new();
687 reader.read_to_end(&mut buf).await.unwrap();
688 assert_eq!(buf, b"foobarbaz");
689 }
690
691 #[tokio::test]
692 async fn test_into_async_read_already_consumed() {
693 use tokio::io::AsyncReadExt;
694 type MaybeStream = Arc<Mutex<Option<BoxStream<'static, Result<Bytes, CamelError>>>>>;
696 let arc: MaybeStream = Arc::new(Mutex::new(None));
697 let body = Body::Stream(StreamBody {
698 stream: arc,
699 metadata: StreamMetadata {
700 size_hint: None,
701 content_type: None,
702 origin: None,
703 },
704 });
705 let mut reader = body.into_async_read().unwrap();
706 let mut buf = Vec::new();
707 let result = reader.read_to_end(&mut buf).await;
708 assert!(result.is_err());
709 }
710}