@@ -15,7 +15,7 @@ use crate::{
15
15
proto:: {
16
16
frame:: { Frame , PayloadLen , SettingId , Settings } ,
17
17
headers:: Header ,
18
- stream:: { StreamId , StreamType } ,
18
+ stream:: StreamType ,
19
19
varint:: VarInt ,
20
20
} ,
21
21
qpack,
@@ -29,10 +29,8 @@ pub struct SharedState {
29
29
pub peer_max_field_section_size : u64 ,
30
30
// connection-wide error, concerns all RequestStreams and drivers
31
31
pub error : Option < Error > ,
32
- // Has the connection received a GoAway frame? If so, this StreamId is the last
33
- // we're willing to accept. This lets us finish the requests or pushes that were
34
- // already in flight when the graceful shutdown was initiated.
35
- pub closing : Option < StreamId > ,
32
+ // Has a GOAWAY frame been sent or received?
33
+ pub closing : bool ,
36
34
}
37
35
38
36
#[ derive( Clone ) ]
@@ -54,7 +52,7 @@ impl Default for SharedStateRef {
54
52
Self ( Arc :: new ( RwLock :: new ( SharedState {
55
53
peer_max_field_section_size : VarInt :: MAX . 0 ,
56
54
error : None ,
57
- closing : None ,
55
+ closing : false ,
58
56
} ) ) )
59
57
}
60
58
}
83
81
decoder_recv : Option < AcceptedRecvStream < C :: RecvStream , B > > ,
84
82
encoder_recv : Option < AcceptedRecvStream < C :: RecvStream , B > > ,
85
83
pending_recv_streams : Vec < AcceptRecvStream < C :: RecvStream > > ,
86
- // The id of the last stream received by this connection:
87
- // request and push stream for server and clients respectively.
88
- last_accepted_stream : Option < StreamId > ,
89
84
got_peer_settings : bool ,
90
85
pub ( super ) send_grease_frame : bool ,
91
86
}
@@ -180,7 +175,6 @@ where
180
175
decoder_recv : None ,
181
176
encoder_recv : None ,
182
177
pending_recv_streams : Vec :: with_capacity ( 3 ) ,
183
- last_accepted_stream : None ,
184
178
got_peer_settings : false ,
185
179
send_grease_frame : grease,
186
180
} ;
@@ -198,22 +192,32 @@ where
198
192
Ok ( conn_inner)
199
193
}
200
194
201
- /// Initiate graceful shutdown, accepting `max_streams` potentially in-flight streams
202
- pub async fn shutdown ( & mut self , max_streams : usize ) -> Result < ( ) , Error > {
203
- let max_id = self
204
- . last_accepted_stream
205
- . map ( |id| id + max_streams)
206
- . unwrap_or_else ( StreamId :: first_request) ;
195
+ /// Send GOAWAY with specified max_id, iff max_id is smaller than the previous one.
196
+ pub async fn shutdown < T > (
197
+ & mut self ,
198
+ sent_closing : & mut Option < T > ,
199
+ max_id : T ,
200
+ ) -> Result < ( ) , Error >
201
+ where
202
+ T : From < VarInt > + PartialOrd < T > + Copy ,
203
+ VarInt : From < T > ,
204
+ {
205
+ if let Some ( sent_id) = sent_closing {
206
+ if * sent_id <= max_id {
207
+ return Ok ( ( ) ) ;
208
+ }
209
+ }
207
210
208
- self . shared . write ( "graceful shutdown" ) . closing = Some ( max_id) ;
211
+ * sent_closing = Some ( max_id) ;
212
+ self . shared . write ( "shutdown" ) . closing = true ;
209
213
210
214
//= https://www.rfc-editor.org/rfc/rfc9114#section-3.3
211
215
//# When either endpoint chooses to close the HTTP/3
212
216
//# connection, the terminating endpoint SHOULD first send a GOAWAY frame
213
217
//# (Section 5.2) so that both endpoints can reliably determine whether
214
218
//# previously sent frames have been processed and gracefully complete or
215
219
//# terminate any necessary remaining tasks.
216
- stream:: write ( & mut self . control_send , Frame :: Goaway ( max_id) ) . await
220
+ stream:: write ( & mut self . control_send , Frame :: Goaway ( max_id. into ( ) ) ) . await
217
221
}
218
222
219
223
pub fn poll_accept_request (
@@ -370,43 +374,7 @@ where
370
374
. unwrap_or ( VarInt :: MAX . 0 ) ;
371
375
Ok ( Frame :: Settings ( settings) )
372
376
}
373
- Frame :: Goaway ( id) => {
374
- let closing = self . shared . read ( "connection goaway read" ) . closing ;
375
- match closing {
376
- Some ( closing_id) if closing_id. initiator ( ) == id. initiator ( ) => {
377
- //= https://www.rfc-editor.org/rfc/rfc9114#section-5.2
378
- //# An endpoint MAY send multiple GOAWAY frames indicating different
379
- //# identifiers, but the identifier in each frame MUST NOT be greater
380
- //# than the identifier in any previous frame, since clients might
381
- //# already have retried unprocessed requests on another HTTP connection.
382
-
383
- //= https://www.rfc-editor.org/rfc/rfc9114#section-5.2
384
- //# Like the server,
385
- //# the client MAY send subsequent GOAWAY frames so long as the specified
386
- //# push ID is no greater than any previously sent value.
387
- if id <= closing_id {
388
- self . shared . write ( "connection goaway overwrite" ) . closing =
389
- Some ( id) ;
390
- Ok ( Frame :: Goaway ( id) )
391
- } else {
392
- //= https://www.rfc-editor.org/rfc/rfc9114#section-5.2
393
- //# Receiving a GOAWAY containing a larger identifier than previously
394
- //# received MUST be treated as a connection error of type H3_ID_ERROR.
395
- Err ( self . close (
396
- Code :: H3_ID_ERROR ,
397
- format ! ( "received a GoAway({}) greater than the former one ({})" , id, closing_id)
398
- ) )
399
- }
400
- }
401
- // When closing initiator is different, the current side has already started to close
402
- // and should not be initiating any new requests / pushes anyway. So we can ignore it.
403
- Some ( _) => Ok ( Frame :: Goaway ( id) ) ,
404
- None => {
405
- self . shared . write ( "connection goaway write" ) . closing = Some ( id) ;
406
- Ok ( Frame :: Goaway ( id) )
407
- }
408
- }
409
- }
377
+ f @ Frame :: Goaway ( _) => Ok ( f) ,
410
378
f @ Frame :: CancelPush ( _) | f @ Frame :: MaxPushId ( _) => {
411
379
if self . got_peer_settings {
412
380
//= https://www.rfc-editor.org/rfc/rfc9114#section-7.2.3
@@ -460,8 +428,46 @@ where
460
428
Poll :: Ready ( res)
461
429
}
462
430
463
- pub fn start_stream ( & mut self , id : StreamId ) {
464
- self . last_accepted_stream = Some ( id) ;
431
+ pub ( crate ) fn process_goaway < T > (
432
+ & mut self ,
433
+ recv_closing : & mut Option < T > ,
434
+ id : VarInt ,
435
+ ) -> Result < ( ) , Error >
436
+ where
437
+ T : From < VarInt > + Copy ,
438
+ VarInt : From < T > ,
439
+ {
440
+ {
441
+ //= https://www.rfc-editor.org/rfc/rfc9114#section-5.2
442
+ //# An endpoint MAY send multiple GOAWAY frames indicating different
443
+ //# identifiers, but the identifier in each frame MUST NOT be greater
444
+ //# than the identifier in any previous frame, since clients might
445
+ //# already have retried unprocessed requests on another HTTP connection.
446
+
447
+ //= https://www.rfc-editor.org/rfc/rfc9114#section-5.2
448
+ //# Like the server,
449
+ //# the client MAY send subsequent GOAWAY frames so long as the specified
450
+ //# push ID is no greater than any previously sent value.
451
+ if let Some ( prev_id) = recv_closing. map ( VarInt :: from) {
452
+ if prev_id < id {
453
+ //= https://www.rfc-editor.org/rfc/rfc9114#section-5.2
454
+ //# Receiving a GOAWAY containing a larger identifier than previously
455
+ //# received MUST be treated as a connection error of type H3_ID_ERROR.
456
+ return Err ( self . close (
457
+ Code :: H3_ID_ERROR ,
458
+ format ! (
459
+ "received a GoAway({}) greater than the former one ({})" ,
460
+ id, prev_id
461
+ ) ,
462
+ ) ) ;
463
+ }
464
+ }
465
+ * recv_closing = Some ( id. into ( ) ) ;
466
+ if !self . shared . read ( "connection goaway read" ) . closing {
467
+ self . shared . write ( "connection goaway overwrite" ) . closing = true ;
468
+ }
469
+ Ok ( ( ) )
470
+ }
465
471
}
466
472
467
473
/// Closes a Connection with code and reason.
0 commit comments