ureq_proto/client/sendbody.rs
1use crate::body::calculate_max_input;
2use crate::util::Writer;
3use crate::Error;
4
5use super::state::{RecvResponse, SendBody};
6use super::Call;
7
8impl 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 pub fn write(&mut self, input: &[u8], output: &mut [u8]) -> Result<(usize, usize), Error> {
25 let mut w = Writer::new(output);
26
27 if !input.is_empty() && self.inner.state.writer.is_ended() {
28 return Err(Error::BodyContentAfterFinish);
29 }
30
31 if let Some(left) = self.inner.state.writer.left_to_send() {
32 if input.len() as u64 > left {
33 return Err(Error::BodyLargerThanContentLength);
34 }
35 }
36
37 let input_used = self.inner.state.writer.write(input, &mut w);
38 let output_used = w.len();
39
40 Ok((input_used, output_used))
41 }
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 pub fn consume_direct_write(&mut self, amount: usize) -> Result<(), Error> {
52 if let Some(left) = self.inner.state.writer.left_to_send() {
53 if amount as u64 > left {
54 return Err(Error::BodyLargerThanContentLength);
55 }
56 } else {
57 return Err(Error::BodyIsChunked);
58 }
59
60 self.inner.state.writer.consume_direct_write(amount);
61
62 Ok(())
63 }
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 pub fn calculate_max_input(&self, output_len: usize) -> usize {
69 // For non-chunked, the entire output can be used.
70 if !self.is_chunked() {
71 return output_len;
72 }
73
74 calculate_max_input(output_len)
75 }
76
77 /// Test if call is chunked.
78 ///
79 /// This might need some processing, hence the &mut and
80 pub fn is_chunked(&self) -> bool {
81 self.inner.state.writer.is_chunked()
82 }
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 pub fn can_proceed(&self) -> bool {
90 self.inner.state.writer.is_ended()
91 }
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 pub fn proceed(self) -> Option<Call<RecvResponse>> {
98 if !self.can_proceed() {
99 return None;
100 }
101
102 Some(Call::wrap(self.inner))
103 }
104}