/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 | | } |