1use crate::Error;
42use crate::buffer_pool::acquire_buffer_for_bytes;
43use bytes::{BufMut, Bytes, BytesMut};
44use http_body_util::BodyExt;
45use hyper::body::Incoming as IncomingBody;
46use serde::de::DeserializeOwned;
47use std::sync::atomic::{AtomicU64, Ordering};
48
49#[derive(Debug, Default)]
55pub struct BodyCollectorStats {
56 collections: AtomicU64,
58 bytes_collected: AtomicU64,
60 pool_hits: AtomicU64,
62 pool_misses: AtomicU64,
64 stream_reads: AtomicU64,
66 json_parses: AtomicU64,
68}
69
70impl BodyCollectorStats {
71 pub fn new() -> Self {
73 Self::default()
74 }
75
76 #[inline]
78 pub fn record_collection(&self, bytes: usize, from_pool: bool) {
79 self.collections.fetch_add(1, Ordering::Relaxed);
80 self.bytes_collected
81 .fetch_add(bytes as u64, Ordering::Relaxed);
82 if from_pool {
83 self.pool_hits.fetch_add(1, Ordering::Relaxed);
84 } else {
85 self.pool_misses.fetch_add(1, Ordering::Relaxed);
86 }
87 }
88
89 #[inline]
91 pub fn record_stream_read(&self) {
92 self.stream_reads.fetch_add(1, Ordering::Relaxed);
93 }
94
95 #[inline]
97 pub fn record_json_parse(&self) {
98 self.json_parses.fetch_add(1, Ordering::Relaxed);
99 }
100
101 pub fn collections(&self) -> u64 {
103 self.collections.load(Ordering::Relaxed)
104 }
105
106 pub fn bytes_collected(&self) -> u64 {
108 self.bytes_collected.load(Ordering::Relaxed)
109 }
110
111 pub fn pool_hit_rate(&self) -> f64 {
113 let hits = self.pool_hits.load(Ordering::Relaxed) as f64;
114 let total = hits + self.pool_misses.load(Ordering::Relaxed) as f64;
115 if total > 0.0 {
116 (hits / total) * 100.0
117 } else {
118 0.0
119 }
120 }
121}
122
123static BODY_STATS: BodyCollectorStats = BodyCollectorStats {
125 collections: AtomicU64::new(0),
126 bytes_collected: AtomicU64::new(0),
127 pool_hits: AtomicU64::new(0),
128 pool_misses: AtomicU64::new(0),
129 stream_reads: AtomicU64::new(0),
130 json_parses: AtomicU64::new(0),
131};
132
133pub fn body_stats() -> &'static BodyCollectorStats {
135 &BODY_STATS
136}
137
138pub struct BodyCollector;
144
145impl BodyCollector {
146 #[inline]
151 pub async fn collect(body: IncomingBody) -> Result<Bytes, Error> {
152 let collected = body
153 .collect()
154 .await
155 .map_err(|e| Error::Internal(format!("Failed to collect body: {}", e)))?;
156 let bytes = collected.to_bytes();
157 BODY_STATS.record_collection(bytes.len(), false);
158 Ok(bytes)
159 }
160
161 #[inline]
166 pub async fn collect_pooled(body: IncomingBody) -> Result<CollectedBody, Error> {
167 let collected = body
169 .collect()
170 .await
171 .map_err(|e| Error::Internal(format!("Failed to collect body: {}", e)))?;
172 let bytes = collected.to_bytes();
173
174 BODY_STATS.record_collection(bytes.len(), true);
175
176 Ok(CollectedBody { inner: bytes })
177 }
178
179 #[inline]
184 pub async fn collect_with_hint(
185 body: IncomingBody,
186 size_hint: usize,
187 ) -> Result<CollectedBody, Error> {
188 let _buf = acquire_buffer_for_bytes(size_hint);
190
191 let collected = body
193 .collect()
194 .await
195 .map_err(|e| Error::Internal(format!("Failed to collect body: {}", e)))?;
196 let bytes = collected.to_bytes();
197
198 BODY_STATS.record_collection(bytes.len(), true);
199
200 Ok(CollectedBody { inner: bytes })
201 }
202
203 #[inline]
208 pub async fn collect_into(body: IncomingBody, buf: &mut BytesMut) -> Result<usize, Error> {
209 let collected = body
210 .collect()
211 .await
212 .map_err(|e| Error::Internal(format!("Failed to collect body: {}", e)))?;
213
214 let bytes = collected.to_bytes();
215 let len = bytes.len();
216
217 buf.reserve(len);
218 buf.put_slice(&bytes);
219
220 BODY_STATS.record_collection(len, false);
221
222 Ok(len)
223 }
224}
225
226#[derive(Clone)]
235pub struct CollectedBody {
236 inner: Bytes,
237}
238
239impl CollectedBody {
240 #[inline]
242 pub fn from_bytes(bytes: Bytes) -> Self {
243 Self { inner: bytes }
244 }
245
246 #[inline]
248 pub fn from_slice(slice: &[u8]) -> Self {
249 Self {
250 inner: Bytes::copy_from_slice(slice),
251 }
252 }
253
254 #[inline]
256 pub fn empty() -> Self {
257 Self {
258 inner: Bytes::new(),
259 }
260 }
261
262 #[inline]
264 pub fn len(&self) -> usize {
265 self.inner.len()
266 }
267
268 #[inline]
270 pub fn is_empty(&self) -> bool {
271 self.inner.is_empty()
272 }
273
274 #[inline]
276 pub fn as_slice(&self) -> &[u8] {
277 &self.inner
278 }
279
280 #[inline]
282 pub fn as_bytes(&self) -> &Bytes {
283 &self.inner
284 }
285
286 #[inline]
288 pub fn into_bytes(self) -> Bytes {
289 self.inner
290 }
291
292 #[inline]
296 pub fn parse_json<T: DeserializeOwned>(&self) -> Result<T, Error> {
297 BODY_STATS.record_json_parse();
298 crate::json::from_slice(&self.inner).map_err(|e| Error::Deserialization(e.to_string()))
299 }
300
301 #[inline]
306 pub fn parse_json_mut<T: DeserializeOwned>(&self) -> Result<T, Error> {
307 BODY_STATS.record_json_parse();
308
309 #[cfg(feature = "simd-json")]
310 {
311 let mut data = self.inner.to_vec();
312 simd_json::from_slice(&mut data).map_err(|e| Error::Deserialization(e.to_string()))
313 }
314
315 #[cfg(not(feature = "simd-json"))]
316 {
317 self.parse_json()
318 }
319 }
320
321 #[inline]
323 pub fn parse_form<T: DeserializeOwned>(&self) -> Result<T, Error> {
324 crate::form::parse_form(&self.inner)
325 }
326
327 #[inline]
329 pub fn as_str(&self) -> Result<&str, std::str::Utf8Error> {
330 std::str::from_utf8(&self.inner)
331 }
332
333 #[inline]
335 pub fn to_string_lossy(&self) -> std::borrow::Cow<'_, str> {
336 String::from_utf8_lossy(&self.inner)
337 }
338
339 #[inline]
341 pub fn split_at(&self, mid: usize) -> (Self, Self) {
342 let left = self.inner.slice(..mid);
343 let right = self.inner.slice(mid..);
344 (Self { inner: left }, Self { inner: right })
345 }
346
347 #[inline]
349 pub fn slice(&self, range: std::ops::Range<usize>) -> Self {
350 Self {
351 inner: self.inner.slice(range),
352 }
353 }
354}
355
356impl std::ops::Deref for CollectedBody {
357 type Target = [u8];
358
359 #[inline]
360 fn deref(&self) -> &Self::Target {
361 &self.inner
362 }
363}
364
365impl AsRef<[u8]> for CollectedBody {
366 #[inline]
367 fn as_ref(&self) -> &[u8] {
368 &self.inner
369 }
370}
371
372impl From<Bytes> for CollectedBody {
373 #[inline]
374 fn from(bytes: Bytes) -> Self {
375 Self::from_bytes(bytes)
376 }
377}
378
379impl From<Vec<u8>> for CollectedBody {
380 #[inline]
381 fn from(vec: Vec<u8>) -> Self {
382 Self {
383 inner: Bytes::from(vec),
384 }
385 }
386}
387
388impl std::fmt::Debug for CollectedBody {
389 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
390 f.debug_struct("CollectedBody")
391 .field("len", &self.inner.len())
392 .finish()
393 }
394}
395
396#[derive(Debug, Clone)]
402pub struct StreamingConfig {
403 pub chunk_size: usize,
405 pub max_size: usize,
407 pub use_pool: bool,
409}
410
411impl Default for StreamingConfig {
412 fn default() -> Self {
413 Self {
414 chunk_size: 16384, max_size: 10485760, use_pool: true,
417 }
418 }
419}
420
421pub struct StreamingBody {
426 inner: IncomingBody,
427 config: StreamingConfig,
428 bytes_read: usize,
429 exhausted: bool,
430}
431
432impl StreamingBody {
433 pub fn new(body: IncomingBody) -> Self {
435 Self {
436 inner: body,
437 config: StreamingConfig::default(),
438 bytes_read: 0,
439 exhausted: false,
440 }
441 }
442
443 pub fn with_config(body: IncomingBody, config: StreamingConfig) -> Self {
445 Self {
446 inner: body,
447 config,
448 bytes_read: 0,
449 exhausted: false,
450 }
451 }
452
453 pub async fn next_chunk(&mut self) -> Result<Option<Bytes>, Error> {
455 if self.exhausted {
456 return Ok(None);
457 }
458
459 if self.bytes_read >= self.config.max_size {
461 return Err(Error::PayloadTooLarge(format!(
462 "Body exceeds maximum size of {} bytes",
463 self.config.max_size
464 )));
465 }
466
467 match self.inner.frame().await {
468 Some(Ok(frame)) => {
469 if let Ok(data) = frame.into_data() {
470 self.bytes_read += data.len();
471 BODY_STATS.record_stream_read();
472 Ok(Some(data))
473 } else {
474 Ok(None)
476 }
477 }
478 Some(Err(e)) => Err(Error::Internal(format!("Body read error: {}", e))),
479 None => {
480 self.exhausted = true;
481 Ok(None)
482 }
483 }
484 }
485
486 pub async fn collect_remaining(mut self) -> Result<CollectedBody, Error> {
488 let mut buf = BytesMut::with_capacity(self.config.chunk_size);
489
490 while let Some(chunk) = self.next_chunk().await? {
491 if buf.len() + chunk.len() > self.config.max_size {
492 return Err(Error::PayloadTooLarge(format!(
493 "Body exceeds maximum size of {} bytes",
494 self.config.max_size
495 )));
496 }
497 buf.extend_from_slice(&chunk);
498 }
499
500 Ok(CollectedBody::from_bytes(buf.freeze()))
501 }
502
503 pub fn bytes_read(&self) -> usize {
505 self.bytes_read
506 }
507
508 pub fn is_exhausted(&self) -> bool {
510 self.exhausted
511 }
512}
513
514pub struct LazyBody {
523 state: LazyBodyState,
524 #[allow(dead_code)] config: StreamingConfig,
526}
527
528enum LazyBodyState {
529 Pending(Option<IncomingBody>),
530 Collected(CollectedBody),
531 Error(String),
532}
533
534impl LazyBody {
535 pub fn new(body: IncomingBody) -> Self {
537 Self {
538 state: LazyBodyState::Pending(Some(body)),
539 config: StreamingConfig::default(),
540 }
541 }
542
543 pub fn with_config(body: IncomingBody, config: StreamingConfig) -> Self {
545 Self {
546 state: LazyBodyState::Pending(Some(body)),
547 config,
548 }
549 }
550
551 pub fn from_collected(body: CollectedBody) -> Self {
553 Self {
554 state: LazyBodyState::Collected(body),
555 config: StreamingConfig::default(),
556 }
557 }
558
559 pub fn is_collected(&self) -> bool {
561 matches!(self.state, LazyBodyState::Collected(_))
562 }
563
564 pub async fn ensure_collected(&mut self) -> Result<(), Error> {
568 match &self.state {
570 LazyBodyState::Collected(_) => return Ok(()),
571 LazyBodyState::Error(msg) => return Err(Error::Internal(msg.clone())),
572 LazyBodyState::Pending(_) => {}
573 }
574
575 if let LazyBodyState::Pending(body_opt) = &mut self.state {
577 let body = body_opt
578 .take()
579 .ok_or_else(|| Error::Internal("Body already consumed".to_string()))?;
580
581 match BodyCollector::collect_pooled(body).await {
582 Ok(collected) => {
583 self.state = LazyBodyState::Collected(collected);
584 Ok(())
585 }
586 Err(e) => {
587 let msg = e.to_string();
588 self.state = LazyBodyState::Error(msg.clone());
589 Err(Error::Internal(msg))
590 }
591 }
592 } else {
593 Ok(())
594 }
595 }
596
597 pub fn get_collected(&self) -> Option<&CollectedBody> {
602 match &self.state {
603 LazyBodyState::Collected(body) => Some(body),
604 _ => None,
605 }
606 }
607
608 pub async fn collect(mut self) -> Result<CollectedBody, Error> {
610 self.ensure_collected().await?;
611 match self.state {
612 LazyBodyState::Collected(body) => Ok(body),
613 _ => Err(Error::Internal("Body not collected".to_string())),
614 }
615 }
616
617 pub async fn json<T: DeserializeOwned>(&mut self) -> Result<T, Error> {
619 self.ensure_collected().await?;
620 self.get_collected()
621 .ok_or_else(|| Error::Internal("Body not collected".to_string()))?
622 .parse_json()
623 }
624
625 pub async fn form<T: DeserializeOwned>(&mut self) -> Result<T, Error> {
627 self.ensure_collected().await?;
628 self.get_collected()
629 .ok_or_else(|| Error::Internal("Body not collected".to_string()))?
630 .parse_form()
631 }
632
633 pub async fn text(&mut self) -> Result<String, Error> {
635 self.ensure_collected().await?;
636 self.get_collected()
637 .ok_or_else(|| Error::Internal("Body not collected".to_string()))?
638 .as_str()
639 .map(|s| s.to_string())
640 .map_err(|e| Error::Internal(format!("Invalid UTF-8: {}", e)))
641 }
642}
643
644#[inline]
650pub fn get_content_length(headers: &hyper::HeaderMap) -> Option<usize> {
651 headers
652 .get(hyper::header::CONTENT_LENGTH)?
653 .to_str()
654 .ok()?
655 .parse()
656 .ok()
657}
658
659#[inline]
661pub fn has_body(method: &hyper::Method, headers: &hyper::HeaderMap) -> bool {
662 if method == hyper::Method::GET || method == hyper::Method::HEAD {
664 return false;
665 }
666
667 if let Some(len) = get_content_length(headers) {
669 return len > 0;
670 }
671
672 headers
674 .get(hyper::header::TRANSFER_ENCODING)
675 .map(|v| {
676 v.to_str()
677 .ok()
678 .map(|s| s.contains("chunked"))
679 .unwrap_or(false)
680 })
681 .unwrap_or(false)
682}
683
684#[cfg(test)]
689mod tests {
690 use super::*;
691
692 #[test]
693 fn test_collected_body_from_bytes() {
694 let bytes = Bytes::from_static(b"hello world");
695 let body = CollectedBody::from_bytes(bytes);
696 assert_eq!(body.len(), 11);
697 assert_eq!(body.as_slice(), b"hello world");
698 }
699
700 #[test]
701 fn test_collected_body_json() {
702 let json = br#"{"name":"test","value":42}"#;
703 let body = CollectedBody::from_slice(json);
704
705 #[derive(serde::Deserialize)]
706 struct Data {
707 name: String,
708 value: u32,
709 }
710
711 let data: Data = body.parse_json().unwrap();
712 assert_eq!(data.name, "test");
713 assert_eq!(data.value, 42);
714 }
715
716 #[test]
717 fn test_collected_body_slice() {
718 let body = CollectedBody::from_slice(b"hello world");
719 let slice = body.slice(0..5);
720 assert_eq!(slice.as_slice(), b"hello");
721 }
722
723 #[test]
724 fn test_collected_body_split() {
725 let body = CollectedBody::from_slice(b"hello world");
726 let (left, right) = body.split_at(6);
727 assert_eq!(left.as_slice(), b"hello ");
728 assert_eq!(right.as_slice(), b"world");
729 }
730
731 #[test]
732 fn test_streaming_config() {
733 let config = StreamingConfig::default();
734 assert_eq!(config.chunk_size, 16384);
735 assert_eq!(config.max_size, 10485760);
736 }
737
738 #[test]
739 fn test_lazy_body_from_collected() {
740 let collected = CollectedBody::from_slice(b"test data");
741 let lazy = LazyBody::from_collected(collected);
742 assert!(lazy.is_collected());
743 }
744
745 #[test]
746 fn test_body_stats() {
747 let stats = body_stats();
748 let initial = stats.collections();
749
750 let _ = stats.bytes_collected();
752 let _ = stats.pool_hit_rate();
753
754 assert!(stats.collections() >= initial);
756 }
757}