ureq_proto/server/
sendbody.rs

1use crate::body::calculate_max_input;
2use crate::util::Writer;
3use crate::Error;
4
5use super::state::{Cleanup, SendBody};
6use super::Reply;
7
8impl Reply<SendBody> {
9    /// Write response 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 response 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    pub fn write(&mut self, input: &[u8], output: &mut [u8]) -> Result<(usize, usize), Error> {
25        let mut w = Writer::new(output);
26
27        // unwrap is ok because we must have called analyze in above write()
28        // to use consume_direct_write()
29        let writer = self.inner.state.writer.as_mut().unwrap();
30
31        if !input.is_empty() && writer.is_ended() {
32            return Err(Error::BodyContentAfterFinish);
33        }
34
35        if let Some(left) = writer.left_to_send() {
36            if input.len() as u64 > left {
37                return Err(Error::BodyLargerThanContentLength);
38            }
39        }
40
41        let input_used = writer.write(input, &mut w);
42        let output_used = w.len();
43
44        Ok((input_used, output_used))
45    }
46
47    /// Helper to avoid copying memory.
48    ///
49    /// When the transfer is _NOT_ chunked, `write()` just copies the `input` to the `output`.
50    /// This memcopy might be possible to avoid if the user can use the `input` buffer directly
51    /// against the transport.
52    ///
53    /// This function is used to "report" how much of the input that has been used. It's effectively
54    /// the same as the first `usize` in the pair returned by `write()`.
55    pub fn consume_direct_write(&mut self, amount: usize) -> Result<(), Error> {
56        // unwrap is ok because we must have called analyze in above write()
57        // to use consume_direct_write()
58        let writer = self.inner.state.writer.as_mut().unwrap();
59
60        if let Some(left) = writer.left_to_send() {
61            if amount as u64 > left {
62                return Err(Error::BodyLargerThanContentLength);
63            }
64        } else {
65            return Err(Error::BodyIsChunked);
66        }
67
68        writer.consume_direct_write(amount);
69
70        Ok(())
71    }
72
73    /// Calculate the max amount of input we can transfer to fill the `output_len`.
74    ///
75    /// For chunked transfer, the input is less than the output.
76    pub fn calculate_max_input(&self, output_len: usize) -> usize {
77        // For non-chunked, the entire output can be used.
78        if !self.is_chunked() {
79            return output_len;
80        }
81
82        calculate_max_input(output_len)
83    }
84
85    /// Test if the response is using chunked transfer encoding.
86    pub fn is_chunked(&self) -> bool {
87        self.inner.state.writer.as_ref().unwrap().is_chunked()
88    }
89
90    /// Check whether the response body is fully sent.
91    ///
92    /// For responses with a `content-length` header set, this will only become `true` once the
93    /// number of bytes communicated have been sent. For chunked transfer, this becomes `true`
94    /// after calling `write()` with an input of `&[]`.
95    pub fn is_finished(&self) -> bool {
96        self.inner.state.writer.as_ref().unwrap().is_ended()
97    }
98
99    /// Proceed to the Cleanup state.
100    ///
101    /// Transitions to the Cleanup state after the response body has been fully sent.
102    /// This is only possible when the response body is fully sent.
103    ///
104    /// Panics if the response body has not been fully sent.
105    pub fn proceed(self) -> Reply<Cleanup> {
106        assert!(self.is_finished());
107
108        Reply::wrap(self.inner)
109    }
110}