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