public ConsumedBytesConverter(DelegatingDecompressorFrameListener frameListener, IHttp2LocalFlowController flowController) { if (flowController is null) { ThrowHelper.ThrowArgumentNullException(ExceptionArgument.flowController); } _flowController = flowController; _frameListener = frameListener; }
public void WindowUpdateDoesNotOverflowConnectionWindow() { IHttp2Connection connection = _frameCodec.Connection; IHttp2LocalFlowController localFlow = connection.Local.FlowController; int initialWindowSizeBefore = localFlow.InitialWindowSize; _channel.WriteAsync(new DefaultHttp2WindowUpdateFrame(int.MaxValue)); // The initial window size is only changed by Http2Settings, so it shouldn't change. Assert.Equal(initialWindowSizeBefore, localFlow.InitialWindowSize); // The connection window should be increased by the delta amount. Assert.Equal(int.MaxValue, localFlow.GetWindowSize(connection.ConnectionStream)); }
public void StreamZeroWindowUpdateIncrementsConnectionWindow() { IHttp2Connection connection = _frameCodec.Connection; IHttp2LocalFlowController localFlow = connection.Local.FlowController; int initialWindowSizeBefore = localFlow.InitialWindowSize; IHttp2Stream connectionStream = connection.ConnectionStream; int connectionWindowSizeBefore = localFlow.GetWindowSize(connectionStream); // We only replenish the flow control window after the amount consumed drops below the following threshold. // We make the threshold very "high" so that window updates will be sent when the delta is relatively small. ((DefaultHttp2LocalFlowController)localFlow).WindowUpdateRatio(connectionStream, .999f); int windowUpdate = 1024; _channel.WriteAsync(new DefaultHttp2WindowUpdateFrame(windowUpdate)); // The initial window size is only changed by Http2Settings, so it shouldn't change. Assert.Equal(initialWindowSizeBefore, localFlow.InitialWindowSize); // The connection window should be increased by the delta amount. Assert.Equal(connectionWindowSizeBefore + windowUpdate, localFlow.GetWindowSize(connectionStream)); }
public override int OnDataRead(IChannelHandlerContext ctx, int streamId, IByteBuffer data, int padding, bool endOfStream) { IHttp2Stream stream = _connection.Stream(streamId); Http2Decompressor decompressor = Decompressor(stream); if (decompressor is null) { // The decompressor may be null if no compatible encoding type was found in this stream's headers return(_listener.OnDataRead(ctx, streamId, data, padding, endOfStream)); } EmbeddedChannel channel = decompressor.Decompressor; int compressedBytes = data.ReadableBytes + padding; decompressor.IncrementCompressedBytes(compressedBytes); try { // call retain here as it will call release after its written to the channel _ = channel.WriteInbound(data.Retain()); var buf = NextReadableBuf(channel); if (buf is null && endOfStream && channel.Finish()) { buf = NextReadableBuf(channel); } if (buf is null) { if (endOfStream) { _ = _listener.OnDataRead(ctx, streamId, Unpooled.Empty, padding, true); } // No new decompressed data was extracted from the compressed data. This means the application could // not be provided with data and thus could not return how many bytes were processed. We will assume // there is more data coming which will complete the decompression block. To allow for more data we // return all bytes to the flow control window (so the peer can send more data). decompressor.IncrementDecompressedBytes(compressedBytes); return(compressedBytes); } try { IHttp2LocalFlowController flowController = _connection.Local.FlowController; decompressor.IncrementDecompressedBytes(padding); while (true) { var nextBuf = NextReadableBuf(channel); var decompressedEndOfStream = nextBuf is null && endOfStream; if (decompressedEndOfStream && channel.Finish()) { nextBuf = NextReadableBuf(channel); decompressedEndOfStream = nextBuf is null; } decompressor.IncrementDecompressedBytes(buf.ReadableBytes); // Immediately return the bytes back to the flow controller. ConsumedBytesConverter will convert // from the decompressed amount which the user knows about to the compressed amount which flow // control knows about. _ = flowController.ConsumeBytes(stream, _listener.OnDataRead(ctx, streamId, buf, padding, decompressedEndOfStream)); if (nextBuf is null) { break; } padding = 0; // Padding is only communicated once on the first iteration. _ = buf.Release(); buf = nextBuf; } // We consume bytes each time we call the listener to ensure if multiple frames are decompressed // that the bytes are accounted for immediately. Otherwise the user may see an inconsistent state of // flow control. return(0); } finally { _ = buf.Release(); } } catch (Http2Exception) { throw; } catch (Exception t) { return(ThrowHelper.ThrowStreamError_DecompressorErrorDetectedWhileDelegatingDataReadOnStream(stream.Id, t)); } }