@@ -85,31 +85,8 @@ public Http2Connection(HttpConnectionContext context)
85
85
// Capture the ExecutionContext before dispatching HTTP/2 middleware. Will be restored by streams when processing request
86
86
_context . InitialExecutionContext = ExecutionContext . Capture ( ) ;
87
87
88
- var inputPipeOptions = new PipeOptions ( pool : context . MemoryPool ,
89
- readerScheduler : context . ServiceContext . Scheduler ,
90
- writerScheduler : PipeScheduler . Inline ,
91
- pauseWriterThreshold : 1 ,
92
- resumeWriterThreshold : 1 ,
93
- minimumSegmentSize : context . MemoryPool . GetMinimumSegmentSize ( ) ,
94
- useSynchronizationContext : false ) ;
95
-
96
- // Never write inline because we do not want to hold Http2FramerWriter._writeLock for potentially expensive TLS
97
- // write operations. This essentially doubles the MaxResponseBufferSize for HTTP/2 connections compared to
98
- // HTTP/1.x. This seems reasonable given HTTP/2's support for many concurrent streams per connection. We don't
99
- // want every write to return an incomplete ValueTasK now that we're dispatching TLS write operations which
100
- // would likely happen with a pauseWriterThreashold of 1, but we still need to respect connection backpressure.
101
- var maxWriteBufferSize = httpLimits . MaxResponseBufferSize ?? 0 ;
102
-
103
- var outputPipeOptions = new PipeOptions ( pool : context . MemoryPool ,
104
- readerScheduler : PipeScheduler . ThreadPool ,
105
- writerScheduler : PipeScheduler . Inline ,
106
- pauseWriterThreshold : maxWriteBufferSize ,
107
- resumeWriterThreshold : maxWriteBufferSize / 2 ,
108
- minimumSegmentSize : context . MemoryPool . GetMinimumSegmentSize ( ) ,
109
- useSynchronizationContext : false ) ;
110
-
111
- _input = new Pipe ( inputPipeOptions ) ;
112
- _output = new Pipe ( outputPipeOptions ) ;
88
+ _input = new Pipe ( GetInputPipeOptions ( ) ) ;
89
+ _output = new Pipe ( GetOutputPipeOptions ( ) ) ;
113
90
114
91
_frameWriter = new Http2FrameWriter (
115
92
_output . Writer ,
@@ -1677,6 +1654,46 @@ public void DecrementActiveClientStreamCount()
1677
1654
Interlocked . Decrement ( ref _clientActiveStreamCount ) ;
1678
1655
}
1679
1656
1657
+ private PipeOptions GetInputPipeOptions ( ) => new PipeOptions ( pool : _context . MemoryPool ,
1658
+ readerScheduler : _context . ServiceContext . Scheduler ,
1659
+ writerScheduler : PipeScheduler . Inline ,
1660
+ pauseWriterThreshold : 1 ,
1661
+ resumeWriterThreshold : 1 ,
1662
+ minimumSegmentSize : _context . MemoryPool . GetMinimumSegmentSize ( ) ,
1663
+ useSynchronizationContext : false ) ;
1664
+
1665
+ private PipeOptions GetOutputPipeOptions ( )
1666
+ {
1667
+ // Never write inline because we do not want to hold Http2FramerWriter._writeLock for potentially expensive TLS
1668
+ // write operations. This essentially doubles the MaxResponseBufferSize for HTTP/2 connections compared to
1669
+ // HTTP/1.x. This seems reasonable given HTTP/2's support for many concurrent streams per connection. We don't
1670
+ // want every write to return an incomplete ValueTask now that we're dispatching TLS write operations which
1671
+ // would likely happen with a pauseWriterThreashold of 1, but we still need to respect connection back pressure.
1672
+ var pauseWriterThreshold = _context . ServiceContext . ServerOptions . Limits . MaxResponseBufferSize switch
1673
+ {
1674
+ // null means that we have no back pressure
1675
+ null => 0 ,
1676
+ // 0 = no buffering so we need to configure the pipe so the writer waits on the reader directly
1677
+ 0 => 1 ,
1678
+ long limit => limit ,
1679
+ } ;
1680
+
1681
+ var resumeWriterThreshold = pauseWriterThreshold switch
1682
+ {
1683
+ // The resumeWriterThreashould must be at least 1 to ever resume after pausing.
1684
+ 1 => 1 ,
1685
+ long limit => limit / 2 ,
1686
+ } ;
1687
+
1688
+ return new PipeOptions ( pool : _context . MemoryPool ,
1689
+ readerScheduler : _context . ServiceContext . Scheduler ,
1690
+ writerScheduler : PipeScheduler . Inline ,
1691
+ pauseWriterThreshold : pauseWriterThreshold ,
1692
+ resumeWriterThreshold : resumeWriterThreshold ,
1693
+ minimumSegmentSize : _context . MemoryPool . GetMinimumSegmentSize ( ) ,
1694
+ useSynchronizationContext : false ) ;
1695
+ }
1696
+
1680
1697
private async Task CopyPipe ( PipeReader reader , PipeWriter writer )
1681
1698
{
1682
1699
Exception ? error = null ;
@@ -1699,11 +1716,12 @@ private async Task CopyPipe(PipeReader reader, PipeWriter writer)
1699
1716
1700
1717
bufferSlice . CopyTo ( outputBuffer . Span ) ;
1701
1718
1702
- reader . AdvanceTo ( bufferSlice . End ) ;
1703
1719
writer . Advance ( copyAmount ) ;
1704
1720
1705
1721
var result = await writer . FlushAsync ( ) ;
1706
1722
1723
+ reader . AdvanceTo ( bufferSlice . End ) ;
1724
+
1707
1725
if ( result . IsCompleted || result . IsCanceled )
1708
1726
{
1709
1727
// flushResult should not be canceled.
@@ -1718,7 +1736,7 @@ private async Task CopyPipe(PipeReader reader, PipeWriter writer)
1718
1736
}
1719
1737
finally
1720
1738
{
1721
- await reader . CompleteAsync ( ) ;
1739
+ await reader . CompleteAsync ( error ) ;
1722
1740
await writer . CompleteAsync ( error ) ;
1723
1741
}
1724
1742
}
0 commit comments