Skip to content

Commit f46fc9f

Browse files
committed
fix(server): start header read timeout immediately
The `http1_header_read_timeout` used to start once there was a single read of headers. This change makes it start the timer immediately, right when the connection is estabilished.
1 parent 47f614f commit f46fc9f

File tree

5 files changed

+68
-148
lines changed

5 files changed

+68
-148
lines changed

src/proto/h1/conn.rs

Lines changed: 48 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
use std::fmt;
2+
#[cfg(feature = "server")]
3+
use std::future::Future;
24
use std::io;
35
use std::marker::PhantomData;
46
#[cfg(feature = "server")]
5-
use std::time::Duration;
7+
use std::time::{Duration, Instant};
68

79
use bytes::{Buf, Bytes};
810
use http::header::{HeaderValue, CONNECTION};
@@ -192,32 +194,65 @@ where
192194
debug_assert!(self.can_read_head());
193195
trace!("Conn::read_head");
194196

195-
let msg = match ready!(self.io.parse::<T>(
197+
#[cfg(feature = "server")]
198+
if !self.state.h1_header_read_timeout_running {
199+
if let Some(h1_header_read_timeout) = self.state.h1_header_read_timeout {
200+
let deadline = Instant::now() + h1_header_read_timeout;
201+
self.state.h1_header_read_timeout_running = true;
202+
match self.state.h1_header_read_timeout_fut {
203+
Some(ref mut h1_header_read_timeout_fut) => {
204+
trace!("resetting h1 header read timeout timer");
205+
self.state.timer.reset(h1_header_read_timeout_fut, deadline);
206+
}
207+
None => {
208+
trace!("setting h1 header read timeout timer");
209+
self.state.h1_header_read_timeout_fut = Some(self.state.timer.sleep_until(deadline));
210+
}
211+
}
212+
}
213+
}
214+
215+
let msg = match self.io.parse::<T>(
196216
cx,
197217
ParseContext {
198218
cached_headers: &mut self.state.cached_headers,
199219
req_method: &mut self.state.method,
200220
h1_parser_config: self.state.h1_parser_config.clone(),
201-
#[cfg(feature = "server")]
202-
h1_header_read_timeout: self.state.h1_header_read_timeout,
203-
#[cfg(feature = "server")]
204-
h1_header_read_timeout_fut: &mut self.state.h1_header_read_timeout_fut,
205-
#[cfg(feature = "server")]
206-
h1_header_read_timeout_running: &mut self.state.h1_header_read_timeout_running,
207-
#[cfg(feature = "server")]
208-
timer: self.state.timer.clone(),
209221
preserve_header_case: self.state.preserve_header_case,
210222
#[cfg(feature = "ffi")]
211223
preserve_header_order: self.state.preserve_header_order,
212224
h09_responses: self.state.h09_responses,
213225
#[cfg(feature = "ffi")]
214226
on_informational: &mut self.state.on_informational,
215227
}
216-
)) {
217-
Ok(msg) => msg,
218-
Err(e) => return self.on_read_head_error(e),
228+
) {
229+
Poll::Ready(Ok(msg)) => msg,
230+
Poll::Ready(Err(e)) => return self.on_read_head_error(e),
231+
Poll::Pending => {
232+
#[cfg(feature = "server")]
233+
if self.state.h1_header_read_timeout_running {
234+
if let Some(ref mut h1_header_read_timeout_fut) =
235+
self.state.h1_header_read_timeout_fut
236+
{
237+
if Pin::new(h1_header_read_timeout_fut).poll(cx).is_ready() {
238+
self.state.h1_header_read_timeout_running = false;
239+
240+
tracing::warn!("read header from client timeout");
241+
return Poll::Ready(Some(Err(crate::Error::new_header_timeout())));
242+
}
243+
}
244+
}
245+
246+
return Poll::Pending;
247+
}
219248
};
220249

250+
#[cfg(feature = "server")]
251+
{
252+
self.state.h1_header_read_timeout_running = false;
253+
self.state.h1_header_read_timeout_fut = None;
254+
}
255+
221256
// Note: don't deconstruct `msg` into local variables, it appears
222257
// the optimizer doesn't remove the extra copies.
223258

src/proto/h1/io.rs

Lines changed: 0 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
use std::cmp;
22
use std::fmt;
3-
#[cfg(feature = "server")]
4-
use std::future::Future;
53
use std::io::{self, IoSlice};
64
use std::marker::Unpin;
75
use std::mem::MaybeUninit;
@@ -183,14 +181,6 @@ where
183181
cached_headers: parse_ctx.cached_headers,
184182
req_method: parse_ctx.req_method,
185183
h1_parser_config: parse_ctx.h1_parser_config.clone(),
186-
#[cfg(feature = "server")]
187-
h1_header_read_timeout: parse_ctx.h1_header_read_timeout,
188-
#[cfg(feature = "server")]
189-
h1_header_read_timeout_fut: parse_ctx.h1_header_read_timeout_fut,
190-
#[cfg(feature = "server")]
191-
h1_header_read_timeout_running: parse_ctx.h1_header_read_timeout_running,
192-
#[cfg(feature = "server")]
193-
timer: parse_ctx.timer.clone(),
194184
preserve_header_case: parse_ctx.preserve_header_case,
195185
#[cfg(feature = "ffi")]
196186
preserve_header_order: parse_ctx.preserve_header_order,
@@ -201,12 +191,6 @@ where
201191
)? {
202192
Some(msg) => {
203193
debug!("parsed {} headers", msg.head.headers.len());
204-
205-
#[cfg(feature = "server")]
206-
{
207-
*parse_ctx.h1_header_read_timeout_running = false;
208-
parse_ctx.h1_header_read_timeout_fut.take();
209-
}
210194
return Poll::Ready(Ok(msg));
211195
}
212196
None => {
@@ -215,20 +199,6 @@ where
215199
debug!("max_buf_size ({}) reached, closing", max);
216200
return Poll::Ready(Err(crate::Error::new_too_large()));
217201
}
218-
219-
#[cfg(feature = "server")]
220-
if *parse_ctx.h1_header_read_timeout_running {
221-
if let Some(h1_header_read_timeout_fut) =
222-
parse_ctx.h1_header_read_timeout_fut
223-
{
224-
if Pin::new(h1_header_read_timeout_fut).poll(cx).is_ready() {
225-
*parse_ctx.h1_header_read_timeout_running = false;
226-
227-
tracing::warn!("read header from client timeout");
228-
return Poll::Ready(Err(crate::Error::new_header_timeout()));
229-
}
230-
}
231-
}
232202
}
233203
}
234204
if ready!(self.poll_read_from_io(cx)).map_err(crate::Error::new_io)? == 0 {
@@ -726,10 +696,6 @@ mod tests {
726696
cached_headers: &mut None,
727697
req_method: &mut None,
728698
h1_parser_config: Default::default(),
729-
h1_header_read_timeout: None,
730-
h1_header_read_timeout_fut: &mut None,
731-
h1_header_read_timeout_running: &mut false,
732-
timer: Time::Empty,
733699
preserve_header_case: false,
734700
#[cfg(feature = "ffi")]
735701
preserve_header_order: false,

src/proto/h1/mod.rs

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,9 @@
1-
#[cfg(feature = "server")]
2-
use std::{pin::Pin, time::Duration};
3-
41
use bytes::BytesMut;
52
use http::{HeaderMap, Method};
63
use httparse::ParserConfig;
74

85
use crate::body::DecodedLength;
9-
#[cfg(feature = "server")]
10-
use crate::common::time::Time;
116
use crate::proto::{BodyLength, MessageHead};
12-
#[cfg(feature = "server")]
13-
use crate::rt::Sleep;
147

158
pub(crate) use self::conn::Conn;
169
pub(crate) use self::decode::Decoder;
@@ -78,14 +71,6 @@ pub(crate) struct ParseContext<'a> {
7871
cached_headers: &'a mut Option<HeaderMap>,
7972
req_method: &'a mut Option<Method>,
8073
h1_parser_config: ParserConfig,
81-
#[cfg(feature = "server")]
82-
h1_header_read_timeout: Option<Duration>,
83-
#[cfg(feature = "server")]
84-
h1_header_read_timeout_fut: &'a mut Option<Pin<Box<dyn Sleep>>>,
85-
#[cfg(feature = "server")]
86-
h1_header_read_timeout_running: &'a mut bool,
87-
#[cfg(feature = "server")]
88-
timer: Time,
8974
preserve_header_case: bool,
9075
#[cfg(feature = "ffi")]
9176
preserve_header_order: bool,

0 commit comments

Comments
 (0)