Coverage Report

Created: 2026-03-14 06:47

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/ureq-proto-0.5.3/src/client/sendbody.rs
Line
Count
Source
1
use crate::body::calculate_max_input;
2
use crate::util::Writer;
3
use crate::Error;
4
5
use super::state::{RecvResponse, SendBody};
6
use super::Call;
7
8
impl Call<SendBody> {
9
    /// Write request body from `input` to `output`.
10
    ///
11
    /// This is called repeatedly until the entire body has been sent. The output buffer is filled
12
    /// as much as possible for each call.
13
    ///
14
    /// Depending on request headers, the output might be `transfer-encoding: chunked`. Chunking means
15
    /// the output is slightly larger than the input due to the extra length headers per chunk.
16
    /// When not doing chunked, the input/output will be the same per call.
17
    ///
18
    /// The result `(usize, usize)` is `(input consumed, output used)`.
19
    ///
20
    /// **Important**
21
    ///
22
    /// To indicate that the body is fully sent, you call write with an `input` parameter set to `&[]`.
23
    /// This ends the `transfer-encoding: chunked` and ensures the state is correct to proceed.
24
0
    pub fn write(&mut self, input: &[u8], output: &mut [u8]) -> Result<(usize, usize), Error> {
25
0
        let mut w = Writer::new(output);
26
27
0
        if !input.is_empty() && self.inner.state.writer.is_ended() {
28
0
            return Err(Error::BodyContentAfterFinish);
29
0
        }
30
31
0
        if let Some(left) = self.inner.state.writer.left_to_send() {
32
0
            if input.len() as u64 > left {
33
0
                return Err(Error::BodyLargerThanContentLength);
34
0
            }
35
0
        }
36
37
0
        let input_used = self.inner.state.writer.write(input, &mut w);
38
0
        let output_used = w.len();
39
40
0
        Ok((input_used, output_used))
41
0
    }
42
43
    /// Helper to avoid copying memory.
44
    ///
45
    /// When the transfer is _NOT_ chunked, `write()` just copies the `input` to the `output`.
46
    /// This memcopy might be possible to avoid if the user can use the `input` buffer directly
47
    /// against the transport.
48
    ///
49
    /// This function is used to "report" how much of the input that has been used. It's effectively
50
    /// the same as the first `usize` in the pair returned by `write()`.
51
0
    pub fn consume_direct_write(&mut self, amount: usize) -> Result<(), Error> {
52
0
        if let Some(left) = self.inner.state.writer.left_to_send() {
53
0
            if amount as u64 > left {
54
0
                return Err(Error::BodyLargerThanContentLength);
55
0
            }
56
        } else {
57
0
            return Err(Error::BodyIsChunked);
58
        }
59
60
0
        self.inner.state.writer.consume_direct_write(amount);
61
62
0
        Ok(())
63
0
    }
64
65
    /// Calculate the max amount of input we can transfer to fill the `output_len`.
66
    ///
67
    /// For chunked transfer, the input is less than the output.
68
0
    pub fn calculate_max_input(&self, output_len: usize) -> usize {
69
        // For non-chunked, the entire output can be used.
70
0
        if !self.is_chunked() {
71
0
            return output_len;
72
0
        }
73
74
0
        calculate_max_input(output_len)
75
0
    }
76
77
    /// Test if call is chunked.
78
    ///
79
    /// This might need some processing, hence the &mut and
80
0
    pub fn is_chunked(&self) -> bool {
81
0
        self.inner.state.writer.is_chunked()
82
0
    }
83
84
    /// Check whether the request body is fully sent.
85
    ///
86
    /// For requests with a `content-length` header set, this will only become `true` once the
87
    /// number of bytes communicated have been sent. For chunked transfer, this becomes `true`
88
    /// after calling `write()` with an input of `&[]`.
89
0
    pub fn can_proceed(&self) -> bool {
90
0
        self.inner.state.writer.is_ended()
91
0
    }
92
93
    /// Proceed to the next state.
94
    ///
95
    /// Returns `None` if it's not possible to proceed. It's guaranteed that if `can_proceed()` returns
96
    /// `true`, this will result in `Some`.
97
0
    pub fn proceed(self) -> Option<Call<RecvResponse>> {
98
0
        if !self.can_proceed() {
99
0
            return None;
100
0
        }
101
102
0
        Some(Call::wrap(self.inner))
103
0
    }
104
}