ureq_proto/client/recvresp.rs
1use http::{header, HeaderName, HeaderValue, Response, StatusCode, Version};
2
3use crate::body::BodyReader;
4use crate::ext::HeaderIterExt;
5use crate::parser::{try_parse_partial_response, try_parse_response};
6use crate::util::log_data;
7use crate::Error;
8
9use super::state::RecvResponse;
10use super::MAX_RESPONSE_HEADERS;
11use super::{Call, CloseReason, RecvResponseResult};
12
13impl Call<RecvResponse> {
14 /// Try reading a response from the input.
15 ///
16 /// * `allow_partial_redirect` - if `true`, we can accept to find the `Location` header
17 /// and proceed without reading the entire header. This is useful for broken servers that
18 /// don't send an entire \r\n at the end of the preamble.
19 ///
20 /// The `(usize, Option<Response()>)` is `(input amount consumed, response`).
21 ///
22 /// Notice that it's possible that we get an `input amount consumed` despite not returning
23 /// a `Some(Response)`. This can happen if the server returned a 100-continue, and due to
24 /// timing reasons we did not receive it while we were in the `Await100` call state. This
25 /// "spurios" 100 will be discarded before we parse the actual response.
26 pub fn try_response(
27 &mut self,
28 input: &[u8],
29 allow_partial_redirect: bool,
30 ) -> Result<(usize, Option<Response<()>>), Error> {
31 let maybe_response = self.do_try_response(input, allow_partial_redirect)?;
32
33 let (input_used, response) = match maybe_response {
34 Some(v) => v,
35 // Not enough input for a full response yet
36 None => return Ok((0, None)),
37 };
38
39 if response.status() == StatusCode::CONTINUE {
40 // We have received a 100-continue response. This can happen in two scenarios:
41 // 1. A "delayed" 100-continue when we used Expect: 100-continue but the server
42 // did not produce the response in time while we were in the Await100 state.
43 // 2. An unsolicited 100-continue from a server that sends it regardless of
44 // whether the client requested it (e.g., on a regular GET request).
45 //
46 // According to RFC 7231, 100-continue is an informational (1xx) response and
47 // should always be consumed by clients, regardless of whether it was solicited.
48 self.inner.await_100_continue = false;
49
50 // We consume the response and wait for the actual final response.
51 return Ok((input_used, None));
52 }
53
54 self.inner.status = Some(response.status());
55 // We want the last Location header.
56 self.inner.location = response
57 .headers()
58 .get_all(header::LOCATION)
59 .into_iter()
60 .next_back()
61 .cloned();
62
63 if response.headers().iter().has(header::CONNECTION, "close") {
64 self.inner
65 .close_reason
66 .push(CloseReason::ServerConnectionClose);
67 }
68
69 Ok((input_used, Some(response)))
70 }
71
72 /// Try reading response headers
73 ///
74 /// A response is only possible once the `input` holds all the HTTP response
75 /// headers. Before that this returns `None`. When the response is succesfully read,
76 /// the return value `(usize, Response<()>)` contains how many bytes were consumed
77 /// of the `input`.
78 fn do_try_response(
79 &mut self,
80 input: &[u8],
81 allow_partial_redirect: bool,
82 ) -> Result<Option<(usize, Response<()>)>, Error> {
83 // ~3k for 100 headers
84 let (input_used, response) = match try_parse_response::<MAX_RESPONSE_HEADERS>(input)? {
85 Some(v) => v,
86 None => {
87 // The caller decides whether to allow a partial parse.
88 if !allow_partial_redirect {
89 return Ok(None);
90 }
91
92 // TODO(martin): I don't like this code. The mission is to be correct HTTP/1.1
93 // and this is a hack to allow for broken servers.
94 //
95 // As a special case, to handle broken servers that does a redirect without
96 // the final trailing \r\n, we try parsing the response as partial, and
97 // if it is a redirect, we can allow the request to continue.
98 let Some(mut r) = try_parse_partial_response::<MAX_RESPONSE_HEADERS>(input)? else {
99 return Ok(None);
100 };
101
102 // A redirection must have a location header.
103 let is_complete_redirection =
104 r.status().is_redirection() && r.headers().contains_key(header::LOCATION);
105
106 if !is_complete_redirection {
107 return Ok(None);
108 }
109
110 // Insert a synthetic connection: close, since the connection is
111 // not valid after using a partial request.
112 debug!("Partial redirection response, insert fake connection: close");
113 r.headers_mut()
114 .insert(header::CONNECTION, HeaderValue::from_static("close"));
115
116 (input.len(), r)
117 }
118 };
119
120 log_data(&input[..input_used]);
121
122 let http10 = response.version() == Version::HTTP_10;
123 let status = response.status().as_u16();
124
125 if status == StatusCode::CONTINUE {
126 // There should be no headers for this response.
127 if !response.headers().is_empty() {
128 return Err(Error::HeadersWith100);
129 }
130
131 return Ok(Some((input_used, response)));
132 }
133
134 let header_lookup = |name: HeaderName| {
135 if let Some(header) = response.headers().get(name) {
136 return header.to_str().ok();
137 }
138 None
139 };
140
141 let force_recv = self.inner.force_recv_body;
142 let recv_body_mode = BodyReader::for_response(
143 http10,
144 self.inner.request.method(),
145 status,
146 force_recv,
147 &header_lookup,
148 )?;
149
150 self.inner.state.reader = Some(recv_body_mode);
151
152 Ok(Some((input_used, response)))
153 }
154
155 /// Tell if we have finished receiving the response.
156 pub fn can_proceed(&self) -> bool {
157 self.inner.state.reader.is_some()
158 }
159
160 /// Tell if response body is closed delimited
161 ///
162 /// HTTP/1.0 does not have `content-length` to serialize many requests over the same
163 /// socket. Instead it uses socket close to determine the body is finished.
164 fn is_close_delimited(&self) -> bool {
165 let rbm = self.inner.state.reader.as_ref().unwrap();
166 matches!(rbm, BodyReader::CloseDelimited)
167 }
168
169 /// Proceed to the next state.
170 ///
171 /// This returns `None` if we have not finished receiving the response. It is guaranteed that if
172 /// `can_proceed()` returns true, this will return `Some`.
173 pub fn proceed(mut self) -> Option<RecvResponseResult> {
174 if !self.can_proceed() {
175 return None;
176 }
177
178 let has_response_body = self.inner.state.need_response_body();
179
180 if has_response_body {
181 if self.is_close_delimited() {
182 self.inner
183 .close_reason
184 .push(CloseReason::CloseDelimitedBody);
185 }
186
187 Some(RecvResponseResult::RecvBody(Call::wrap(self.inner)))
188 } else {
189 Some(if self.inner.is_redirect() {
190 RecvResponseResult::Redirect(Call::wrap(self.inner))
191 } else {
192 RecvResponseResult::Cleanup(Call::wrap(self.inner))
193 })
194 }
195 }
196
197 /// Convert the state to receive a body despite method.
198 ///
199 /// Methods like HEAD and CONNECT should not have attached bodies.
200 /// Some broken APIs use bodies anyway and this is an escape hatch to
201 /// interoperate with such services.
202 pub fn force_recv_body(&mut self) {
203 self.inner.force_recv_body = true;
204 }
205}