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}