public async Task IfSettingsDecreaseHeaderTableNextOutgoingHeadersShouldContainAnUpdate() { var inPipe = new BufferedPipe(1024); var outPipe = new BufferedPipe(1024); Func <IStream, bool> listener = (s) => { Task.Run(() => { var res = new HeaderField[] { new HeaderField { Name = ":status", Value = "200" }, }; s.WriteHeadersAsync(res, false); }); return(true); }; var http2Con = await ConnectionUtils.BuildEstablishedConnection( true, inPipe, outPipe, loggerProvider, listener); // Change remote settings 2 times var settings = Settings.Default; settings.HeaderTableSize = 8; await inPipe.WriteSettings(settings); await outPipe.AssertSettingsAck(); settings.HeaderTableSize = 30; await inPipe.WriteSettings(settings); await outPipe.AssertSettingsAck(); // Establish a stream // When we send a response through it we should observe the size udpate var hEncoder = new Encoder(); await inPipe.WriteHeaders(hEncoder, 1, false, DefaultGetHeaders); // Wait for the incoming status headers with header update var fh = await outPipe.ReadFrameHeaderWithTimeout(); Assert.Equal(FrameType.Headers, fh.Type); Assert.Equal((byte)(HeadersFrameFlags.EndOfHeaders), fh.Flags); Assert.Equal(1u, fh.StreamId); Assert.Equal(3, fh.Length); var data = new byte[fh.Length]; await outPipe.ReadAllWithTimeout(new ArraySegment <byte>(data)); Assert.Equal(0x28, data[0]); // Size Update to 8 Assert.Equal(0x3e, data[1]); // Size Update to 30 Assert.Equal(0x88, data[2]); // :status 200 }
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 ConnectionShouldGoAwayOnSettingsAckWithNonZeroLength( int frameLength) { var inPipe = new BufferedPipe(1024); var outPipe = new BufferedPipe(1024); var http2Con = BuildConnection(true, Settings.Default, inPipe, outPipe); await ClientPreface.WriteAsync(inPipe); await inPipe.WriteSettings(Settings.Default); // Wait for remote settings await outPipe.ReadAndDiscardSettings(); // Wait for ack to our settings await outPipe.AssertSettingsAck(); var fh = new FrameHeader { Type = FrameType.Settings, Flags = (byte)SettingsFrameFlags.Ack, StreamId = 0, Length = frameLength, }; await inPipe.WriteFrameHeader(fh); // Wait for GoAway due to wrong stream ID await outPipe.AssertGoAwayReception(ErrorCode.ProtocolError, 0); await outPipe.AssertStreamEnd(); }
public async Task ConnectionShouldGoAwayOnUnsolicitedSettingsAck() { var inPipe = new BufferedPipe(1024); var outPipe = new BufferedPipe(1024); var http2Con = BuildConnection(true, Settings.Default, inPipe, outPipe); await ClientPreface.WriteAsync(inPipe); await inPipe.WriteSettings(Settings.Default); // Wait for remote settings await outPipe.ReadAndDiscardSettings(); // Wait for ack to our settings await outPipe.AssertSettingsAck(); // Acknowledge remote settings 2 times await inPipe.WriteSettingsAck(); await inPipe.WriteSettingsAck(); // Wait for GoAway due to multiple ACKs await outPipe.AssertGoAwayReception(ErrorCode.ProtocolError, 0); await outPipe.AssertStreamEnd(); }
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 ConnectionShouldAcknowledgeValidSettings() { var inPipe = new BufferedPipe(1024); var outPipe = new BufferedPipe(1024); var http2Con = BuildConnection(true, Settings.Default, inPipe, outPipe); await ClientPreface.WriteAsync(inPipe); await inPipe.WriteSettings(Settings.Default); await outPipe.ReadAndDiscardSettings(); await outPipe.AssertSettingsAck(); }
public async Task ConnectionShouldAcceptSettingsAckAndNotGoAway() { var inPipe = new BufferedPipe(1024); var outPipe = new BufferedPipe(1024); var http2Con = BuildConnection(true, Settings.Default, inPipe, outPipe); await ClientPreface.WriteAsync(inPipe); await inPipe.WriteSettings(Settings.Default); // Wait for remote settings await outPipe.ReadAndDiscardSettings(); // Wait for ack to our settings await outPipe.AssertSettingsAck(); // Acknowledge remote settings await inPipe.WriteSettingsAck(); // And expect that no GoAway follows - which means a timeout happens on read await outPipe.AssertReadTimeout(); }
public async Task TheRemoteHeaderTableSizeShouldOnlyBeUsedUpToConfiguredLimit() { var inPipe = new BufferedPipe(1024); var outPipe = new BufferedPipe(1024); var handlerDone = new SemaphoreSlim(0); Func <IStream, bool> listener = (s) => { Task.Run(() => { var res = new HeaderField[] { new HeaderField { Name = ":status", Value = "200" }, }; s.WriteHeadersAsync(res, false); }); handlerDone.Release(); return(true); }; // Lower the initial window size so that stream window updates are // sent earlier than connection window updates var localSettings = Settings.Default; localSettings.HeaderTableSize = 20000; var http2Con = await ConnectionUtils.BuildEstablishedConnection( true, inPipe, outPipe, loggerProvider, listener, localSettings : localSettings); // Change remote settings, which grants us a giant header table var settings = Settings.Default; settings.HeaderTableSize = 50 * 1024 * 1024; await inPipe.WriteSettings(settings); await outPipe.AssertSettingsAck(); // Establish a stream // When we send a response through it we should observe the size udpate var hEncoder = new Encoder(); await inPipe.WriteHeaders(hEncoder, 1, false, DefaultGetHeaders); var ok = await handlerDone.WaitAsync( ReadableStreamTestExtensions.ReadTimeout); if (!ok) { throw new Exception("Stream was not created"); } // Wait for the incoming status headers with header update var fh = await outPipe.ReadFrameHeaderWithTimeout(); Assert.Equal(FrameType.Headers, fh.Type); Assert.Equal((byte)(HeadersFrameFlags.EndOfHeaders), fh.Flags); Assert.Equal(1u, fh.StreamId); // Observe headers with status update // We should update the header table to 20000 Assert.Equal(5, fh.Length); var data = new byte[fh.Length]; await outPipe.ReadAllWithTimeout(new ArraySegment <byte>(data)); Assert.Equal(0x3f, data[0]); // Size Update to 20000 => 20000 - 31 = 19969 Assert.Equal(0x81, data[1]); // 19969 % 128 + 128 = 0x81, 19969 / 128 = 156 Assert.Equal(0x9c, data[2]); // 156 % 128 + 128 = 0x9c, 156 / 128 = 1 Assert.Equal(0x01, data[3]); // 1 Assert.Equal(0x88, data[4]); // :status 200 }