Skip to content

Commit 9115986

Browse files
committed
feat(h1): allow to set keep alive configuration from response header
1 parent 28df1ef commit 9115986

2 files changed

Lines changed: 77 additions & 1 deletion

File tree

client/src/pool/exclusive.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,19 @@ where
206206
self.destroy_on_drop = true;
207207
}
208208

209+
#[cfg(feature = "http1")]
210+
pub(crate) fn keep_alive_hint(&mut self, timeout: Option<Duration>, max_requests: Option<usize>) {
211+
if let Some(conn) = self.conn.as_mut() {
212+
if let Some(timeout) = timeout {
213+
conn.state.keep_alive_idle = timeout;
214+
}
215+
216+
if let Some(max_requests) = max_requests {
217+
conn.state.max_requests = max_requests;
218+
}
219+
}
220+
}
221+
209222
#[cfg(feature = "http1")]
210223
pub(crate) fn is_destroy_on_drop(&self) -> bool {
211224
self.destroy_on_drop

client/src/service.rs

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use crate::{
55
client::Client,
66
connect::Connect,
77
error::Error,
8-
http::{Request, Version},
8+
http::{HeaderName, Request, Version},
99
pool::{exclusive, shared},
1010
response::Response,
1111
uri::Uri,
@@ -227,6 +227,9 @@ pub(crate) fn base_service() -> HttpService {
227227
Ok(Ok((res, buf, decoder, is_close))) => {
228228
if is_close {
229229
_conn.destroy_on_drop();
230+
} else {
231+
let (timeout, max) = parse_keep_alive(&res);
232+
_conn.keep_alive_hint(timeout, max);
230233
}
231234
let body = crate::h1::body::ResponseBody::new(_conn, buf, decoder);
232235
let res = res.map(|_| crate::body::ResponseBody::H1(body));
@@ -263,11 +266,71 @@ pub(crate) fn base_service() -> HttpService {
263266
Box::new(HttpService)
264267
}
265268

269+
const KEEP_ALIVE: HeaderName = HeaderName::from_static("keep-alive");
270+
271+
fn parse_keep_alive<B>(res: &crate::http::Response<B>) -> (Option<Duration>, Option<usize>) {
272+
let header = match res.headers().get(KEEP_ALIVE).map(|h| h.to_str()) {
273+
Some(Ok(header)) => header,
274+
_ => return (None, None),
275+
};
276+
277+
let mut timeout = None;
278+
let mut max = None;
279+
280+
for (key, value) in header.split(',').map(|item| {
281+
let mut kv = item.splitn(2, '=');
282+
283+
(
284+
kv.next().map(|s| s.trim()).unwrap_or_default(),
285+
kv.next().map(|s| s.trim()).unwrap_or_default(),
286+
)
287+
}) {
288+
match key.to_lowercase().as_str() {
289+
"timeout" => {
290+
timeout = value.parse::<u64>().ok().map(Duration::from_secs);
291+
}
292+
"max" => {
293+
max = value.parse().ok();
294+
}
295+
_ => {}
296+
}
297+
}
298+
299+
(timeout, max)
300+
}
301+
266302
#[cfg(test)]
267303
pub(crate) use test::mock_service;
268304

269305
#[cfg(test)]
270306
mod test {
307+
#[test]
308+
fn test_parse_keep_alive() {
309+
let mut res = http::Response::new(());
310+
res.headers_mut().insert("keep-alive", "timeout=5, max=100".try_into().unwrap());
311+
312+
let (timeout, max) = super::parse_keep_alive(&res);
313+
314+
assert_eq!(timeout, Some(Duration::from_secs(5)));
315+
assert_eq!(max, Some(100));
316+
317+
let mut res = http::Response::new(());
318+
res.headers_mut().insert("keep-alive", "timeout=5".try_into().unwrap());
319+
320+
let (timeout, max) = super::parse_keep_alive(&res);
321+
322+
assert_eq!(timeout, Some(Duration::from_secs(5)));
323+
assert_eq!(max, None);
324+
325+
let mut res = http::Response::new(());
326+
res.headers_mut().insert("keep-alive", "max=5".try_into().unwrap());
327+
328+
let (timeout, max) = super::parse_keep_alive(&res);
329+
330+
assert_eq!(timeout, None);
331+
assert_eq!(max, Some(5));
332+
}
333+
271334
use core::time::Duration;
272335

273336
use std::sync::Arc;

0 commit comments

Comments
 (0)