Skip to main content

qubit_http/request/
http_request_streaming_body.rs

1/*******************************************************************************
2 *
3 *    Copyright (c) 2025 - 2026 Haixing Hu.
4 *
5 *    SPDX-License-Identifier: Apache-2.0
6 *
7 *    Licensed under the Apache License, Version 2.0.
8 *
9 ******************************************************************************/
10//! Deferred streaming request body wrapper.
11
12use std::future::Future;
13use std::pin::Pin;
14use std::sync::Arc;
15
16use super::http_request_body_byte_stream::HttpRequestBodyByteStream;
17
18type HttpRequestBodyStreamFactoryFuture = Pin<Box<dyn Future<Output = HttpRequestBodyByteStream> + Send + 'static>>;
19
20type HttpRequestBodyStreamFactoryFn = dyn Fn() -> HttpRequestBodyStreamFactoryFuture + Send + Sync + 'static;
21
22/// Deferred streaming upload body source.
23///
24/// Each send attempt obtains a fresh byte stream from the stored async factory,
25/// so retries can rebuild the outbound body stream deterministically.
26#[derive(Clone)]
27pub struct HttpRequestStreamingBody {
28    /// Async factory producing one stream for one request attempt.
29    factory: Arc<HttpRequestBodyStreamFactoryFn>,
30}
31
32impl std::fmt::Debug for HttpRequestStreamingBody {
33    /// Formats this type without exposing closure internals.
34    ///
35    /// # Parameters
36    /// - `f`: Formatter destination.
37    ///
38    /// # Returns
39    /// Formatting result.
40    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
41        f.debug_struct("HttpRequestStreamingBody").finish_non_exhaustive()
42    }
43}
44
45impl HttpRequestStreamingBody {
46    /// Creates a deferred streaming body from an async stream factory.
47    ///
48    /// # Parameters
49    /// - `factory`: Async factory that returns a fresh byte stream for one send
50    ///   attempt.
51    ///
52    /// # Returns
53    /// New deferred streaming body wrapper.
54    pub fn new<F>(factory: F) -> Self
55    where
56        F: Fn() -> HttpRequestBodyStreamFactoryFuture + Send + Sync + 'static,
57    {
58        Self {
59            factory: Arc::new(factory),
60        }
61    }
62
63    /// Builds reqwest body from one factory-produced byte stream.
64    ///
65    /// # Returns
66    /// New [`reqwest::Body`] stream instance for one send attempt.
67    pub(crate) async fn to_reqwest_body(&self) -> reqwest::Body {
68        let stream = (self.factory)().await;
69        reqwest::Body::wrap_stream(stream)
70    }
71}