@@ -61,10 +61,14 @@ where
61
61
#[ cfg( feature = "server" ) ]
62
62
h1_header_read_timeout : None ,
63
63
#[ cfg( feature = "server" ) ]
64
- h1_header_read_timeout_fut : None ,
65
- #[ cfg( feature = "server" ) ]
66
64
h1_header_read_timeout_running : false ,
67
65
#[ cfg( feature = "server" ) ]
66
+ h1_idle_timeout : None ,
67
+ #[ cfg( feature = "server" ) ]
68
+ h1_idle_timeout_running : false ,
69
+ #[ cfg( feature = "server" ) ]
70
+ h1_timeout_fut : None ,
71
+ #[ cfg( feature = "server" ) ]
68
72
date_header : true ,
69
73
#[ cfg( feature = "server" ) ]
70
74
timer : Time :: Empty ,
@@ -147,6 +151,11 @@ where
147
151
self . state . h1_header_read_timeout = Some ( val) ;
148
152
}
149
153
154
+ #[ cfg( feature = "server" ) ]
155
+ pub ( crate ) fn set_http1_idle_timeout ( & mut self , val : Duration ) {
156
+ self . state . h1_idle_timeout = Some ( val) ;
157
+ }
158
+
150
159
#[ cfg( feature = "server" ) ]
151
160
pub ( crate ) fn set_allow_half_close ( & mut self ) {
152
161
self . state . allow_half_close = true ;
@@ -217,24 +226,10 @@ where
217
226
trace ! ( "Conn::read_head" ) ;
218
227
219
228
#[ cfg( feature = "server" ) ]
220
- if !self . state . h1_header_read_timeout_running {
221
- if let Some ( h1_header_read_timeout) = self . state . h1_header_read_timeout {
222
- let deadline = Instant :: now ( ) + h1_header_read_timeout;
223
- self . state . h1_header_read_timeout_running = true ;
224
- match self . state . h1_header_read_timeout_fut {
225
- Some ( ref mut h1_header_read_timeout_fut) => {
226
- trace ! ( "resetting h1 header read timeout timer" ) ;
227
- self . state . timer . reset ( h1_header_read_timeout_fut, deadline) ;
228
- }
229
- None => {
230
- trace ! ( "setting h1 header read timeout timer" ) ;
231
- self . state . h1_header_read_timeout_fut =
232
- Some ( self . state . timer . sleep_until ( deadline) ) ;
233
- }
234
- }
235
- }
236
- }
229
+ let first_head = !self . io . is_read_blocked ( ) ;
237
230
231
+ #[ cfg_attr( not( feature = "server" ) , allow( unused) ) ]
232
+ let mut progress = false ;
238
233
let msg = match self . io . parse :: < T > (
239
234
cx,
240
235
ParseContext {
@@ -249,20 +244,69 @@ where
249
244
#[ cfg( feature = "ffi" ) ]
250
245
on_informational : & mut self . state . on_informational ,
251
246
} ,
247
+ & mut progress,
252
248
) {
253
249
Poll :: Ready ( Ok ( msg) ) => msg,
254
250
Poll :: Ready ( Err ( e) ) => return self . on_read_head_error ( e) ,
255
251
Poll :: Pending => {
252
+ // - Use the read timeout on the first head to avoid common DoS.
253
+ // - If made progress in reading header, must no longer be idle.
256
254
#[ cfg( feature = "server" ) ]
257
- if self . state . h1_header_read_timeout_running {
258
- if let Some ( ref mut h1_header_read_timeout_fut) =
259
- self . state . h1_header_read_timeout_fut
260
- {
261
- if Pin :: new ( h1_header_read_timeout_fut) . poll ( cx) . is_ready ( ) {
262
- self . state . h1_header_read_timeout_running = false ;
263
-
264
- warn ! ( "read header from client timeout" ) ;
265
- return Poll :: Ready ( Some ( Err ( crate :: Error :: new_header_timeout ( ) ) ) ) ;
255
+ if first_head || progress {
256
+ if !self . state . h1_header_read_timeout_running {
257
+ if let Some ( h1_header_read_timeout) = self . state . h1_header_read_timeout {
258
+ let deadline = Instant :: now ( ) + h1_header_read_timeout;
259
+ self . state . h1_idle_timeout_running = false ;
260
+ self . state . h1_header_read_timeout_running = true ;
261
+ match self . state . h1_timeout_fut {
262
+ Some ( ref mut ht_timeout_fut) => {
263
+ trace ! ( "resetting h1 timeout timer for header read" ) ;
264
+ self . state . timer . reset ( ht_timeout_fut, deadline) ;
265
+ }
266
+ None => {
267
+ trace ! ( "setting h1 timeout timer for header read" ) ;
268
+ self . state . h1_timeout_fut =
269
+ Some ( self . state . timer . sleep_until ( deadline) ) ;
270
+ }
271
+ }
272
+ } else if std:: mem:: take ( & mut self . state . h1_idle_timeout_running ) {
273
+ trace ! ( "unsetting h1 timeout timer for idle" ) ;
274
+ self . state . h1_timeout_fut = None ;
275
+ }
276
+ }
277
+ } else if !self . state . h1_header_read_timeout_running
278
+ && !self . state . h1_idle_timeout_running
279
+ {
280
+ if let Some ( h1_idle_timeout) = self . state . h1_idle_timeout {
281
+ let deadline = Instant :: now ( ) + h1_idle_timeout;
282
+ self . state . h1_idle_timeout_running = true ;
283
+ match self . state . h1_timeout_fut {
284
+ Some ( ref mut h1_timeout_fut) => {
285
+ trace ! ( "resetting h1 timeout timer for idle" ) ;
286
+ self . state . timer . reset ( h1_timeout_fut, deadline) ;
287
+ }
288
+ None => {
289
+ trace ! ( "setting h1 timeout timer for idle" ) ;
290
+ self . state . h1_timeout_fut =
291
+ Some ( self . state . timer . sleep_until ( deadline) ) ;
292
+ }
293
+ }
294
+ }
295
+ }
296
+
297
+ #[ cfg( feature = "server" ) ]
298
+ if self . state . h1_header_read_timeout_running || self . state . h1_idle_timeout_running {
299
+ if let Some ( ref mut h1_timeout_fut) = self . state . h1_timeout_fut {
300
+ if Pin :: new ( h1_timeout_fut) . poll ( cx) . is_ready ( ) {
301
+ return Poll :: Ready ( Some ( Err (
302
+ if self . state . h1_header_read_timeout_running {
303
+ warn ! ( "read header from client timeout" ) ;
304
+ crate :: Error :: new_header_timeout ( )
305
+ } else {
306
+ warn ! ( "idle client timeout" ) ;
307
+ crate :: Error :: new_idle_timeout ( )
308
+ } ,
309
+ ) ) ) ;
266
310
}
267
311
}
268
312
}
@@ -274,7 +318,8 @@ where
274
318
#[ cfg( feature = "server" ) ]
275
319
{
276
320
self . state . h1_header_read_timeout_running = false ;
277
- self . state . h1_header_read_timeout_fut = None ;
321
+ self . state . h1_idle_timeout_running = false ;
322
+ self . state . h1_timeout_fut = None ;
278
323
}
279
324
280
325
// Note: don't deconstruct `msg` into local variables, it appears
@@ -919,10 +964,14 @@ struct State {
919
964
#[ cfg( feature = "server" ) ]
920
965
h1_header_read_timeout : Option < Duration > ,
921
966
#[ cfg( feature = "server" ) ]
922
- h1_header_read_timeout_fut : Option < Pin < Box < dyn Sleep > > > ,
923
- #[ cfg( feature = "server" ) ]
924
967
h1_header_read_timeout_running : bool ,
925
968
#[ cfg( feature = "server" ) ]
969
+ h1_idle_timeout : Option < Duration > ,
970
+ #[ cfg( feature = "server" ) ]
971
+ h1_idle_timeout_running : bool ,
972
+ #[ cfg( feature = "server" ) ]
973
+ h1_timeout_fut : Option < Pin < Box < dyn Sleep > > > ,
974
+ #[ cfg( feature = "server" ) ]
926
975
date_header : bool ,
927
976
#[ cfg( feature = "server" ) ]
928
977
timer : Time ,
@@ -1106,6 +1155,12 @@ impl State {
1106
1155
self . reading = Reading :: Init ;
1107
1156
self . writing = Writing :: Init ;
1108
1157
1158
+ #[ cfg( feature = "server" ) ]
1159
+ if self . h1_idle_timeout . is_some ( ) {
1160
+ // Next read will start and poll the idle timeout.
1161
+ self . notify_read = true ;
1162
+ }
1163
+
1109
1164
// !T::should_read_first() means Client.
1110
1165
//
1111
1166
// If Client connection has just gone idle, the Dispatcher
0 commit comments