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