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}