public async Task WritesShouldRespectConnectionFlowControlWindow() { var inPipe = new BufferedPipe(1024); var outPipe = new BufferedPipe(1024); // Create a stream. It will have a flow control window of 64kb // for stream and connection var res = await ServerStreamTests.StreamCreator.CreateConnectionAndStream( StreamState.Open, loggerProvider, inPipe, outPipe); // Initiate response by sending and consuming headers await res.stream.WriteHeadersAsync( DefaultStatusHeaders, false); await outPipe.ReadAndDiscardHeaders(1u, false); // Increase flow control window for stream so that write is blocked // on connection window await inPipe.WriteWindowUpdate(1, 1024 *1024); var writeTask = Task.Run(async() => { await res.stream.WriteAsync(new ArraySegment <byte>(new byte[32000])); await res.stream.WriteAsync(new ArraySegment <byte>(new byte[33535 + 1024])); await res.stream.WriteAsync(new ArraySegment <byte>(new byte[1024])); }); // Expect to read the stream flow control window amount of data await outPipe.ReadAndDiscardData(1u, false, 16384); await outPipe.ReadAndDiscardData(1u, false, 15616); await outPipe.ReadAndDiscardData(1u, false, 16384); await outPipe.ReadAndDiscardData(1u, false, 16384); await outPipe.ReadAndDiscardData(1u, false, 767); // Give a bigger flow control window for connection and expect more data await inPipe.WriteWindowUpdate(0u, 512); await outPipe.ReadAndDiscardData(1u, false, 512); await inPipe.WriteWindowUpdate(0u, 1024); await outPipe.ReadAndDiscardData(1u, false, 512); await outPipe.ReadAndDiscardData(1u, false, 512); await inPipe.WriteWindowUpdate(0u, 512); await outPipe.ReadAndDiscardData(1u, false, 512); // Expect the writer to finish var doneTask = await Task.WhenAny(writeTask, Task.Delay(250)); Assert.True(writeTask == doneTask, "Expected write task to finish"); }
public async Task ItShouldBePossibleToSendInformationalHeaders() { var inPipe = new BufferedPipe(1024); var outPipe = new BufferedPipe(1024); var r = await StreamCreator.CreateConnectionAndStream( StreamState.Open, loggerProvider, inPipe, outPipe); var infoHeaders = new HeaderField[] { new HeaderField { Name = ":status", Value = "100" }, new HeaderField { Name = "extension-field", Value = "bar" }, }; await r.stream.WriteHeadersAsync(infoHeaders, false); await r.stream.WriteHeadersAsync(DefaultStatusHeaders, false); await r.stream.WriteAsync(new ArraySegment<byte>(new byte[0]), true); await outPipe.ReadAndDiscardHeaders(1, false); await outPipe.ReadAndDiscardHeaders(1, false); await outPipe.ReadAndDiscardData(1, true, 0); }
public async Task NegativeFlowControlWindowsThroughSettingsShouldBeSupported() { var inPipe = new BufferedPipe(1024); var outPipe = new BufferedPipe(1024); // Start with a stream window of 10 var settings = Settings.Default; settings.InitialWindowSize = 10; var res = await ServerStreamTests.StreamCreator.CreateConnectionAndStream( StreamState.Open, loggerProvider, inPipe, outPipe, remoteSettings : settings); // Initiate response by sending and consuming headers await res.stream.WriteHeadersAsync( DefaultStatusHeaders, false); await outPipe.ReadAndDiscardHeaders(1u, false); // Start to write 20 bytes of data var writeTask = Task.Run(async() => { var data = new byte[20]; await res.stream.WriteAsync(new ArraySegment <byte>(data), true); }); // Expect to receive the first 10 bytes await outPipe.ReadAndDiscardData(1u, false, 10); // Stream has now a window of 0 // Decrease that to -10 by decreasing the initial window settings.InitialWindowSize = 0; await inPipe.WriteSettings(settings); await outPipe.AssertSettingsAck(); // Increase to 0 with window update await inPipe.WriteWindowUpdate(1u, 10); // Check that we get no data so far by ping/pong // If the negative window is not applied we would get new data here await inPipe.WritePing(new byte[8], false); await outPipe.ReadAndDiscardPong(); // Increase to 10 with window update await inPipe.WriteWindowUpdate(1u, 10); // Expect the remaining 10 bytes of data await outPipe.ReadAndDiscardData(1u, true, 10); Assert.Equal(StreamState.HalfClosedLocal, res.stream.State); }
public async Task ReceivingResetShouldYieldAResetException() { var inPipe = new BufferedPipe(1024); var outPipe = new BufferedPipe(1024); var conn = await ConnectionUtils.BuildEstablishedConnection( false, inPipe, outPipe, loggerProvider); IStream stream = await conn.CreateStreamAsync(DefaultGetHeaders); await outPipe.ReadAndDiscardHeaders(1u, false); var readTask = stream.ReadWithTimeout(new ArraySegment <byte>(new byte[1])); await inPipe.WriteResetStream(1u, ErrorCode.Cancel); var ex = await Assert.ThrowsAsync <AggregateException>( () => readTask); Assert.IsType <StreamResetException>(ex.InnerException); Assert.Equal(StreamState.Reset, stream.State); }
public async Task SendingEmptyDataFramesShouldBePossibleWithoutFlowWindow( bool emptyFrameIsEndOfStream) { var inPipe = new BufferedPipe(1024); var outPipe = new BufferedPipe(1024); // Create a stream. It will have a flow control window of 64kb // for stream and connection var res = await ServerStreamTests.StreamCreator.CreateConnectionAndStream( StreamState.Open, loggerProvider, inPipe, outPipe); // Initiate response by sending and consuming headers await res.stream.WriteHeadersAsync( DefaultStatusHeaders, false); await outPipe.ReadAndDiscardHeaders(1u, false); var writeTask = Task.Run(async() => { // Consume the complete flow control window await res.stream.WriteAsync(new ArraySegment <byte>(new byte[65535])); // And try to send an empty data frame await res.stream.WriteAsync( new ArraySegment <byte>(new byte[0]), emptyFrameIsEndOfStream); }); // Expect to read the sent frames await outPipe.ReadAndDiscardData(1u, false, 16384); await outPipe.ReadAndDiscardData(1u, false, 16384); await outPipe.ReadAndDiscardData(1u, false, 16384); await outPipe.ReadAndDiscardData(1u, false, 16383); await outPipe.ReadAndDiscardData(1u, emptyFrameIsEndOfStream, 0); // Expect the writer to finish var doneTask = await Task.WhenAny(writeTask, Task.Delay(250)); Assert.True(writeTask == doneTask, "Expected write task to finish"); }
public async Task BlockedWritesShouldUnblockOnStreamCancel() { var inPipe = new BufferedPipe(1024); var outPipe = new BufferedPipe(1024); // Create a stream. It will have a flow control window of 64kb // for stream and connection var res = await ServerStreamTests.StreamCreator.CreateConnectionAndStream( StreamState.Open, loggerProvider, inPipe, outPipe); // Initiate response by sending and consuming headers await res.stream.WriteHeadersAsync( DefaultStatusHeaders, false); await outPipe.ReadAndDiscardHeaders(1u, false); // Start discarding all ougoing data -> Not needed for this test // and pipe may not be blocked var readTask = Task.Run(async() => { await outPipe.ReadAllToArrayWithTimeout(); }); // Write flow control window amount of data await res.stream.WriteWithTimeout(new ArraySegment <byte>(new byte[65535])); var resetTask = Task.Run(async() => { await Task.Delay(20); res.stream.Cancel(); }); // Write additional bytes. This should block and cause a streamreset // exception when the cancel arrives await Assert.ThrowsAsync <StreamResetException>(async() => { await res.stream.WriteAsync(new ArraySegment <byte>(new byte[1024])); }); }
public async Task TheNextOutgoingStreamAfterUpgradeShouldUseId3() { var inPipe = new BufferedPipe(1024); var outPipe = new BufferedPipe(1024); var upgrade = new ClientUpgradeRequestBuilder().Build(); var config = new ConnectionConfigurationBuilder(false) .Build(); var conn = new Connection( config, inPipe, outPipe, new Connection.Options { Logger = loggerProvider.CreateLogger("http2Con"), ClientUpgradeRequest = upgrade, }); await conn.PerformHandshakes(inPipe, outPipe); var stream = await upgrade.UpgradeRequestStream; Assert.Equal(1u, stream.Id); var readHeadersTask = stream.ReadHeadersAsync(); Assert.False(readHeadersTask.IsCompleted); var nextStream = await conn.CreateStreamAsync(DefaultGetHeaders); await outPipe.ReadAndDiscardHeaders(3u, false); Assert.Equal(3u, nextStream.Id); Assert.True(stream != nextStream); Assert.Equal(StreamState.HalfClosedLocal, stream.State); Assert.Equal(StreamState.Open, nextStream.State); var hEncoder = new Http2.Hpack.Encoder(); await inPipe.WriteHeaders(hEncoder, 3u, true, DefaultStatusHeaders); var nextStreamHeaders = await nextStream.ReadHeadersAsync(); Assert.True(nextStreamHeaders.SequenceEqual(DefaultStatusHeaders)); Assert.False(readHeadersTask.IsCompleted); Assert.Equal(StreamState.HalfClosedRemote, nextStream.State); Assert.Equal(StreamState.HalfClosedLocal, stream.State); Assert.Equal(2, conn.ActiveStreamCount); await nextStream.WriteAsync(new ArraySegment <byte>(new byte[0]), true); await outPipe.ReadAndDiscardData(3u, true, 0); Assert.Equal(StreamState.Closed, nextStream.State); Assert.Equal(1, conn.ActiveStreamCount); var headers2 = DefaultStatusHeaders.Append( new HeaderField() { Name = "hh", Value = "vv" }); await inPipe.WriteHeaders(hEncoder, 1u, false, headers2); var streamHeaders = await readHeadersTask; Assert.True(streamHeaders.SequenceEqual(headers2)); await inPipe.WriteData(1u, 10, 0, true); var data = await stream.ReadAllToArrayWithTimeout(); Assert.True(data.Length == 10); Assert.Equal(StreamState.Closed, stream.State); Assert.Equal(0, conn.ActiveStreamCount); }