@@ -108,8 +108,19 @@ private async Task FlushOutgoingBytesAsync()
108
108
{
109
109
Debug . Assert ( _outgoingBuffer . ActiveSpan . Length > 0 ) ;
110
110
111
- await _stream . WriteAsync ( _outgoingBuffer . ActiveMemory ) . ConfigureAwait ( false ) ;
112
- _outgoingBuffer . Discard ( _outgoingBuffer . ActiveMemory . Length ) ;
111
+ try
112
+ {
113
+ await _stream . WriteAsync ( _outgoingBuffer . ActiveMemory ) . ConfigureAwait ( false ) ;
114
+ }
115
+ catch ( Exception )
116
+ {
117
+ Abort ( ) ;
118
+ throw ;
119
+ }
120
+ finally
121
+ {
122
+ _outgoingBuffer . Discard ( _outgoingBuffer . ActiveMemory . Length ) ;
123
+ }
113
124
}
114
125
115
126
private async Task < FrameHeader > ReadFrameAsync ( )
@@ -181,7 +192,7 @@ private async void ProcessIncomingFrames()
181
192
}
182
193
catch ( Exception )
183
194
{
184
- AbortStreams ( 0 ) ;
195
+ Abort ( ) ;
185
196
}
186
197
}
187
198
@@ -216,7 +227,9 @@ private async Task ProcessHeadersFrame(FrameHeader frameHeader)
216
227
if ( http2Stream == null )
217
228
{
218
229
_incomingBuffer . Discard ( frameHeader . Length ) ;
219
- SendRstStream ( streamId , Http2ProtocolErrorCode . StreamClosed ) ;
230
+
231
+ // Don't wait for completion, which could happen asynchronously.
232
+ ValueTask ignored = SendRstStreamAsync ( streamId , Http2ProtocolErrorCode . StreamClosed ) ;
220
233
return ;
221
234
}
222
235
@@ -292,7 +305,9 @@ private void ProcessDataFrame(FrameHeader frameHeader)
292
305
if ( http2Stream == null )
293
306
{
294
307
_incomingBuffer . Discard ( frameHeader . Length ) ;
295
- SendRstStream ( frameHeader . StreamId , Http2ProtocolErrorCode . StreamClosed ) ;
308
+
309
+ // Don't wait for completion, which could happen asynchronously.
310
+ ValueTask ignored = SendRstStreamAsync ( frameHeader . StreamId , Http2ProtocolErrorCode . StreamClosed ) ;
296
311
return ;
297
312
}
298
313
@@ -349,7 +364,8 @@ private void ProcessSettingsFrame(FrameHeader frameHeader)
349
364
_incomingBuffer . Discard ( frameHeader . Length ) ;
350
365
351
366
// Send acknowledgement
352
- SendSettingsAck ( ) ;
367
+ // Don't wait for completion, which could happen asynchronously.
368
+ ValueTask ignored = SendSettingsAckAsync ( ) ;
353
369
}
354
370
}
355
371
@@ -388,7 +404,8 @@ private void ProcessPingFrame(FrameHeader frameHeader)
388
404
}
389
405
390
406
// Send PING ACK
391
- SendPingAck ( _incomingBuffer . ActiveMemory . Slice ( 0 , FrameHeader . PingLength ) ) ;
407
+ // Don't wait for completion, which could happen asynchronously.
408
+ ValueTask ignored = SendPingAckAsync ( _incomingBuffer . ActiveMemory . Slice ( 0 , FrameHeader . PingLength ) ) ;
392
409
393
410
_incomingBuffer . Discard ( frameHeader . Length ) ;
394
411
}
@@ -461,9 +478,29 @@ private void ProcessGoAwayFrame(FrameHeader frameHeader)
461
478
_incomingBuffer . Discard ( frameHeader . Length ) ;
462
479
}
463
480
464
- private async void SendSettingsAck ( )
481
+ private async ValueTask AcquireWriteLockAsync ( )
465
482
{
466
483
await _writerLock . WaitAsync ( ) . ConfigureAwait ( false ) ;
484
+
485
+ // If the connection has been aborted, then fail now instead of trying to send more data.
486
+ if ( IsAborted ( ) )
487
+ {
488
+ throw new IOException ( SR . net_http_invalid_response ) ;
489
+ }
490
+ }
491
+
492
+ private void ReleaseWriteLock ( )
493
+ {
494
+ // Currently, we always flush the write buffer before releasing the lock.
495
+ // If we change this in the future, we will need to revisit this assert.
496
+ Debug . Assert ( _outgoingBuffer . ActiveMemory . IsEmpty ) ;
497
+
498
+ _writerLock . Release ( ) ;
499
+ }
500
+
501
+ private async ValueTask SendSettingsAckAsync ( )
502
+ {
503
+ await AcquireWriteLockAsync ( ) . ConfigureAwait ( false ) ;
467
504
try
468
505
{
469
506
WriteFrameHeader ( new FrameHeader ( 0 , FrameType . Settings , FrameFlags . Ack , 0 ) ) ;
@@ -472,15 +509,15 @@ private async void SendSettingsAck()
472
509
}
473
510
finally
474
511
{
475
- _writerLock . Release ( ) ;
512
+ ReleaseWriteLock ( ) ;
476
513
}
477
514
}
478
515
479
- private async void SendPingAck ( ReadOnlyMemory < byte > pingContent )
516
+ private async ValueTask SendPingAckAsync ( ReadOnlyMemory < byte > pingContent )
480
517
{
481
518
Debug . Assert ( pingContent . Length == FrameHeader . PingLength ) ;
482
519
483
- await _writerLock . WaitAsync ( ) . ConfigureAwait ( false ) ;
520
+ await AcquireWriteLockAsync ( ) . ConfigureAwait ( false ) ;
484
521
try
485
522
{
486
523
WriteFrameHeader ( new FrameHeader ( FrameHeader . PingLength , FrameType . Ping , FrameFlags . Ack , 0 ) ) ;
@@ -492,13 +529,13 @@ private async void SendPingAck(ReadOnlyMemory<byte> pingContent)
492
529
}
493
530
finally
494
531
{
495
- _writerLock . Release ( ) ;
532
+ ReleaseWriteLock ( ) ;
496
533
}
497
534
}
498
535
499
- private async void SendRstStream ( int streamId , Http2ProtocolErrorCode errorCode )
536
+ private async ValueTask SendRstStreamAsync ( int streamId , Http2ProtocolErrorCode errorCode )
500
537
{
501
- await _writerLock . WaitAsync ( ) . ConfigureAwait ( false ) ;
538
+ await AcquireWriteLockAsync ( ) . ConfigureAwait ( false ) ;
502
539
try
503
540
{
504
541
WriteFrameHeader ( new FrameHeader ( FrameHeader . RstStreamLength , FrameType . RstStream , FrameFlags . None , streamId ) ) ;
@@ -514,7 +551,7 @@ private async void SendRstStream(int streamId, Http2ProtocolErrorCode errorCode)
514
551
}
515
552
finally
516
553
{
517
- _writerLock . Release ( ) ;
554
+ ReleaseWriteLock ( ) ;
518
555
}
519
556
}
520
557
@@ -532,7 +569,7 @@ private async ValueTask SendStreamDataAsync(int streamId, ReadOnlyMemory<byte> b
532
569
ReadOnlyMemory < byte > current ;
533
570
( current , remaining ) = SplitBufferForFraming ( remaining ) ;
534
571
535
- await _writerLock . WaitAsync ( ) . ConfigureAwait ( false ) ;
572
+ await AcquireWriteLockAsync ( ) . ConfigureAwait ( false ) ;
536
573
try
537
574
{
538
575
_outgoingBuffer . EnsureAvailableSpace ( FrameHeader . Size + current . Length ) ;
@@ -544,14 +581,14 @@ private async ValueTask SendStreamDataAsync(int streamId, ReadOnlyMemory<byte> b
544
581
}
545
582
finally
546
583
{
547
- _writerLock . Release ( ) ;
584
+ ReleaseWriteLock ( ) ;
548
585
}
549
586
}
550
587
}
551
588
552
- private async void SendEndStream ( int streamId )
589
+ private async ValueTask SendEndStreamAsync ( int streamId )
553
590
{
554
- await _writerLock . WaitAsync ( ) . ConfigureAwait ( false ) ;
591
+ await AcquireWriteLockAsync ( ) . ConfigureAwait ( false ) ;
555
592
try
556
593
{
557
594
_outgoingBuffer . EnsureAvailableSpace ( FrameHeader . Size ) ;
@@ -560,7 +597,7 @@ private async void SendEndStream(int streamId)
560
597
}
561
598
finally
562
599
{
563
- _writerLock . Release ( ) ;
600
+ ReleaseWriteLock ( ) ;
564
601
}
565
602
}
566
603
@@ -571,6 +608,18 @@ private void WriteFrameHeader(FrameHeader frameHeader)
571
608
_outgoingBuffer . Commit ( FrameHeader . Size ) ;
572
609
}
573
610
611
+ private void Abort ( )
612
+ {
613
+ // The connection has failed, e.g. failed IO or a connection-level frame error.
614
+ // Abort all streams and cause further processing to fail.
615
+ AbortStreams ( 0 ) ;
616
+ }
617
+
618
+ private bool IsAborted ( )
619
+ {
620
+ return _disposed ;
621
+ }
622
+
574
623
private void AbortStreams ( int lastValidStream )
575
624
{
576
625
lock ( _syncObject )
0 commit comments