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 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 ConnectionShouldCloseAndSignalDoneWhenReadingFromInputFails(bool isServer) { var inPipe = new BufferedPipe(1024); var outPipe = new BufferedPipe(1024); var failableInPipe = new FailingPipe(inPipe); var http2Con = await ConnectionUtils.BuildEstablishedConnection( isServer, failableInPipe, outPipe, loggerProvider); // Make the next write attempt fail failableInPipe.FailNextRead = true; // Send something which triggers no response but will start a new read call await inPipe.WriteWindowUpdate(0, 128); // Wait for the connection to close the outgoing part await outPipe.AssertStreamEnd(); // If the connection was successfully closed close the incoming data // stream, since this is expected from a bidirectional stream implementation await inPipe.CloseAsync(); // Expect the connection to close within timeout var closed = http2Con.Done; Assert.True( closed == await Task.WhenAny(closed, Task.Delay(1000)), "Expected connection to close"); }
public async Task ShouldGoAwayWhenStreamWindowIsOverflowedThroughSettingsUpdate() { var inPipe = new BufferedPipe(1024); var outPipe = new BufferedPipe(1024); var res = await ServerStreamTests.StreamCreator.CreateConnectionAndStream( StreamState.Open, loggerProvider, inPipe, outPipe); // Let the remote increase the flow control window of the stream await inPipe.WriteWindowUpdate( 1u, (int)(int.MaxValue - Settings.Default.InitialWindowSize)); // Should be still alive await inPipe.WritePing(new byte[8], false); await outPipe.ReadAndDiscardPong(); // And now write new settings which overflow the window var newSettings = Settings.Default; newSettings.InitialWindowSize++; await inPipe.WriteSettings(newSettings); // Dead through overflow await outPipe.AssertGoAwayReception(ErrorCode.FlowControlError, 1u); }
public async Task ReceivingWindowUpdatesOnIdleStreamsShouldTriggerGoAway( uint streamId) { var inPipe = new BufferedPipe(1024); var outPipe = new BufferedPipe(1024); var res = await ServerStreamTests.StreamCreator.CreateConnectionAndStream( StreamState.Open, loggerProvider, inPipe, outPipe); await inPipe.WriteWindowUpdate(streamId, 0); await outPipe.AssertGoAwayReception(ErrorCode.ProtocolError, 1); await outPipe.AssertStreamEnd(); }
public async Task ShouldGoAwayWhenConnectionFlowControlWindowIsOverloaded( int amount) { var inPipe = new BufferedPipe(1024); var outPipe = new BufferedPipe(1024); var http2Con = await ConnectionUtils.BuildEstablishedConnection( true, inPipe, outPipe, loggerProvider, null); await inPipe.WriteWindowUpdate(0, amount); await outPipe.AssertGoAwayReception(ErrorCode.FlowControlError, 0); await outPipe.AssertStreamEnd(); }
public async Task ShouldAllowToSetTheMaxPossibleConnectionFlowControlWindowSize() { var inPipe = new BufferedPipe(1024); var outPipe = new BufferedPipe(1024); var http2Con = await ConnectionUtils.BuildEstablishedConnection( true, inPipe, outPipe, loggerProvider, null); var amount = int.MaxValue - 65535; await inPipe.WriteWindowUpdate(0, amount); // Check aliveness with ping/pong await inPipe.WritePing(new byte[8], false); await outPipe.ReadAndDiscardPong(); }
public async Task ShouldResetStreamWhenStreamFlowControlWindowIsOverloaded( int amount) { var inPipe = new BufferedPipe(1024); var outPipe = new BufferedPipe(1024); Func <IStream, bool> listener = (s) => true; var http2Con = await ConnectionUtils.BuildEstablishedConnection( true, inPipe, outPipe, loggerProvider, listener); var hEncoder = new Encoder(); await inPipe.WriteHeaders(hEncoder, 1, false, DefaultGetHeaders); await inPipe.WriteWindowUpdate(1, amount); await outPipe.AssertResetStreamReception(1, ErrorCode.FlowControlError); }
public async Task ShouldAllowToSetTheMaxPossibleStreamFlowControlWindowSize() { var inPipe = new BufferedPipe(1024); var outPipe = new BufferedPipe(1024); Func <IStream, bool> listener = (s) => true; var http2Con = await ConnectionUtils.BuildEstablishedConnection( true, inPipe, outPipe, loggerProvider, listener); var hEncoder = new Encoder(); await inPipe.WriteHeaders(hEncoder, 1, false, DefaultGetHeaders); var amount = int.MaxValue - 65535; await inPipe.WriteWindowUpdate(1, amount); // Check aliveness with ping/pong await inPipe.WritePing(new byte[8], false); await outPipe.ReadAndDiscardPong(); }
public async Task ReceivingWindowUpdatesWith0AmountShouldTriggerGoAwayOrReset( uint streamId) { var inPipe = new BufferedPipe(1024); var outPipe = new BufferedPipe(1024); var res = await ServerStreamTests.StreamCreator.CreateConnectionAndStream( StreamState.Open, loggerProvider, inPipe, outPipe); await inPipe.WriteWindowUpdate(streamId, 0); if (streamId == 0) { await outPipe.AssertGoAwayReception(ErrorCode.ProtocolError, 1); await outPipe.AssertStreamEnd(); } else { await outPipe.AssertResetStreamReception(1u, ErrorCode.ProtocolError); } }