public async Task StreamWindowUpdatesShouldRespectBufferState() { var inPipe = new BufferedPipe(1024); var outPipe = new BufferedPipe(1024); // Lower the initial window size so that stream window updates are // sent earlier than connection window updates var settings = Settings.Default; settings.InitialWindowSize = 16000; settings.MaxFrameSize = 1000000; var res = await ServerStreamTests.StreamCreator.CreateConnectionAndStream( StreamState.Open, loggerProvider, inPipe, outPipe, localSettings : settings); // Write 12k of data. Buffer amount: 12k. Remaining window: 4k await inPipe.WriteData(1u, 12000); // Read 5k of data. Buffer amount: 7k await res.stream.ReadAllWithTimeout(new ArraySegment <byte>(new byte[5000])); // This should not trigger a window update await inPipe.WritePing(new byte[8], false); await outPipe.ReadAndDiscardPong(); // Read 5k of data. Buffer amount: 2k await res.stream.ReadAllWithTimeout(new ArraySegment <byte>(new byte[5000])); // Expect a window update of 10k. Remaining window: 14k await outPipe.AssertWindowUpdate(1u, 10000); // Read 2k of data - buffer is now drained await res.stream.ReadAllWithTimeout(new ArraySegment <byte>(new byte[2000])); // This should not trigger a window update await inPipe.WritePing(new byte[8], false); await outPipe.ReadAndDiscardPong(); // Write 4k of data. Buffer amount: 4k. Remaining window: 10k await inPipe.WriteData(1u, 4000); // Read 4k of data. Buffer amount: 0k await res.stream.ReadAllWithTimeout(new ArraySegment <byte>(new byte[4000])); // This should not trigger a window update await inPipe.WritePing(new byte[8], false); await outPipe.ReadAndDiscardPong(); // Write 8k of data. Buffer amount: 8k. Remaining window: 2k await inPipe.WriteData(1u, 8000); // Read 5k of data. Buffer amount: 3k await res.stream.ReadAllWithTimeout(new ArraySegment <byte>(new byte[5000])); // Expect a window update of 11k. Remaining window: 13k await outPipe.AssertWindowUpdate(1u, 11000); }
public async Task ReceivingDataDirectlyAfterInformationalHeadersShouldBeAnError() { var inPipe = new BufferedPipe(1024); var outPipe = new BufferedPipe(1024); var res = await StreamCreator.CreateConnectionAndStream( StreamState.Open, loggerProvider, inPipe, outPipe); // Send informational headers to the client var infoHeaders = new HeaderField[] { new HeaderField { Name = ":status", Value = "100" }, new HeaderField { Name = "extension-field", Value = "bar" }, }; await inPipe.WriteHeaders(res.hEncoder, 1u, false, infoHeaders); var recvdHeaders = await res.stream.ReadHeadersAsync(); Assert.True(infoHeaders.SequenceEqual(recvdHeaders)); // Try to send data await inPipe.WriteData(1u, 100, null, true); // Expect to receive an error await outPipe.AssertResetStreamReception(1u, ErrorCode.ProtocolError); Assert.Equal(StreamState.Reset, res.stream.State); await Assert.ThrowsAsync <StreamResetException>( () => res.stream.ReadHeadersAsync()); }
public async Task ADataFrameOnAnUnknownStreamIdShouldTriggerAStreamReset( bool isServer, uint streamId) { // TODO: Add test cases for clients var inPipe = new BufferedPipe(1024); var outPipe = new BufferedPipe(1024); Func <IStream, bool> listener = (s) => true; var http2Con = await ConnectionUtils.BuildEstablishedConnection( isServer, inPipe, outPipe, loggerProvider, listener); // Establish a high stream ID, which means all below are invalid var hEncoder = new Encoder(); var createdStreamId = 111u; if (!isServer) { throw new Exception("For clients the stream must be created from connection"); } await inPipe.WriteHeaders( hEncoder, createdStreamId, false, DefaultGetHeaders); await inPipe.WriteData(streamId, 0); await outPipe.AssertResetStreamReception(streamId, ErrorCode.StreamClosed); }
public async Task ReceivingHeadersOrDataOnAClosedStreamShouldTriggerAStreamReset( StreamState streamState, bool sendHeaders, bool sendData, bool sendTrailers) { var inPipe = new BufferedPipe(1024); var outPipe = new BufferedPipe(1024); var localCloseDone = new SemaphoreSlim(0); var res = await StreamCreator.CreateConnectionAndStream( streamState, loggerProvider, inPipe, outPipe); if (sendHeaders) { await inPipe.WriteHeaders(res.hEncoder, 1, false, DefaultGetHeaders); } if (sendData) { await inPipe.WriteData(1u, 0); } if (sendTrailers) { await inPipe.WriteHeaders(res.hEncoder, 1, true, DefaultGetHeaders); } await outPipe.AssertResetStreamReception(1u, ErrorCode.StreamClosed); var expectedState = streamState == StreamState.Closed ? StreamState.Closed : StreamState.Reset; Assert.Equal(expectedState, res.stream.State); }
public async Task ClientUpgradeRequestShouldYieldStream1() { 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); Assert.Equal(1, conn.ActiveStreamCount); Assert.Equal(StreamState.HalfClosedLocal, stream.State); var readHeadersTask = stream.ReadHeadersAsync(); Assert.False(readHeadersTask.IsCompleted); var hEncoder = new Http2.Hpack.Encoder(); await inPipe.WriteHeaders(hEncoder, 1u, false, DefaultStatusHeaders); Assert.True( await Task.WhenAny( readHeadersTask, Task.Delay(ReadableStreamTestExtensions.ReadTimeout)) == readHeadersTask, "Expected to read headers, got timeout"); var headers = await readHeadersTask; Assert.True(headers.SequenceEqual(DefaultStatusHeaders)); Assert.Equal(StreamState.HalfClosedLocal, stream.State); await inPipe.WriteData(1u, 100, 5, true); var data = await stream.ReadAllToArrayWithTimeout(); Assert.True(data.Length == 100); Assert.Equal(StreamState.Closed, stream.State); Assert.Equal(0, conn.ActiveStreamCount); }
public async Task ADataFrameOnAnIdleStreamIdShouldTriggerAGoAway( bool isServer, uint streamId) { var inPipe = new BufferedPipe(1024); var outPipe = new BufferedPipe(1024); Func <IStream, bool> listener = (s) => true; var http2Con = await ConnectionUtils.BuildEstablishedConnection( isServer, inPipe, outPipe, loggerProvider, listener); await inPipe.WriteData(streamId, 0); await outPipe.AssertGoAwayReception(ErrorCode.StreamClosed, 0u); }
public async Task ReceivingEmptyDataFramesShouldBePossibleIfStreamWindowIsDrained( bool emptyFrameIsEndOfStream) { var inPipe = new BufferedPipe(1024); var outPipe = new BufferedPipe(1024); var localSettings = Settings.Default; localSettings.InitialWindowSize = 10; var res = await ServerStreamTests.StreamCreator.CreateConnectionAndStream( StreamState.Open, loggerProvider, inPipe, outPipe, localSettings : localSettings); // Consume the complete stream flow control window await inPipe.WriteData(1u, 10); // Send a 0byte data frame await inPipe.WriteData(1u, 0, endOfStream : emptyFrameIsEndOfStream); // Check if we are still alive await inPipe.WritePing(new byte[8], false); await outPipe.ReadAndDiscardPong(); }
public async Task SendingLargeAmountOfDataShouldTriggerWindowUpdates( int[] dataLength, int[] expectedStreamWindowUpdates, int[] expectedConnWindowUpdates) { var inPipe = new BufferedPipe(1024); var outPipe = new BufferedPipe(1024); // Lower the initial window size so that stream window updates are // sent earlier than connection window updates var settings = Settings.Default; settings.InitialWindowSize = 16000; var res = await ServerStreamTests.StreamCreator.CreateConnectionAndStream( StreamState.Open, loggerProvider, inPipe, outPipe, localSettings : settings); // Consume all data on reader side var readTask = Task.Run(async() => { await res.stream.ReadHeadersAsync(); var data = await res.stream.ReadAllToArrayWithTimeout(); }); for (var i = 0; i < dataLength.Length; i++) { var toSend = dataLength[i]; var isEndOfStream = i == (dataLength.Length - 1); await inPipe.WriteData(1u, toSend, endOfStream : isEndOfStream); // Wait for a short amount of time between DATA frames if (!isEndOfStream) { await Task.Delay(5); } // Check if window updates were received if required if (expectedConnWindowUpdates[i] != 0) { await outPipe.AssertWindowUpdate(0, expectedConnWindowUpdates[i]); } if (expectedStreamWindowUpdates[i] != 0) { await outPipe.AssertWindowUpdate(1, expectedStreamWindowUpdates[i]); } } }
public async Task ReceivingDataBeforeHeadersShouldYieldAResetException() { var inPipe = new BufferedPipe(1024); var outPipe = new BufferedPipe(1024); var res = await StreamCreator.CreateConnectionAndStream( StreamState.Open, loggerProvider, inPipe, outPipe); await inPipe.WriteData(1u, 1); await outPipe.AssertResetStreamReception(1u, ErrorCode.ProtocolError); var ex = await Assert.ThrowsAsync <AggregateException>( () => res.stream.ReadWithTimeout(new ArraySegment <byte>( new byte[1]))); Assert.IsType <StreamResetException>(ex.InnerException); Assert.Equal(StreamState.Reset, res.stream.State); }
public async Task EmptyDataFramesShouldBeValidSeperatorsBeforeTrailers( bool isServer) { var inPipe = new BufferedPipe(1024); var outPipe = new BufferedPipe(1024); var res = await StreamCreator.CreateConnectionAndStream( isServer, loggerProvider, inPipe, outPipe); // Send a 0byte data frame await inPipe.WriteData(1u, 0); // Send trailers await inPipe.WriteHeaders(res.hEncoder, 1u, true, DefaultTrailingHeaders); // Check for no stream error await inPipe.WritePing(new byte[8], false); await outPipe.ReadAndDiscardPong(); }
public async Task HeadersWithContentLengthShouldForceDataLengthValidation( int contentLen, int[] dataLength, bool useTrailers, bool shouldError) { 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 headers = TestHeaders.DefaultGetHeaders.Append( new HeaderField { Name = "content-length", Value = contentLen.ToString() }); var hEncoder = new Encoder(); await inPipe.WriteHeaders(hEncoder, 1, false, headers); for (var i = 0; i < dataLength.Length; i++) { var isEos = i == (dataLength.Length - 1) && !useTrailers; await inPipe.WriteData(1u, dataLength[i], endOfStream : isEos); } if (useTrailers) { await inPipe.WriteHeaders(hEncoder, 1, true, new HeaderField[0]); } if (shouldError) { await outPipe.AssertResetStreamReception(1u, ErrorCode.ProtocolError); } else { await inPipe.WritePing(new byte[8], false); await outPipe.ReadAndDiscardPong(); } }
public async Task EmptyDataFramesShouldNotWakeupPendingReads( bool isServer) { var inPipe = new BufferedPipe(1024); var outPipe = new BufferedPipe(1024); var res = await StreamCreator.CreateConnectionAndStream( isServer, loggerProvider, inPipe, outPipe); // Send a 0byte data frame await inPipe.WriteData(1u, 0); // Try to read - this should not unblock var readTask = res.stream.ReadAsync( new ArraySegment <byte>(new byte[1])).AsTask(); var doneTask = await Task.WhenAny(readTask, Task.Delay(100)); Assert.True( doneTask != readTask, "Expected read timeout but read finished"); }
public async Task ReceivingHeadersOrDataOnAResetStreamShouldProduceAClosedStreamError( bool sendHeaders, bool sendData, bool sendTrailers) { var inPipe = new BufferedPipe(1024); var outPipe = new BufferedPipe(1024); var r = await StreamCreator.CreateConnectionAndStream( StreamState.Reset, loggerProvider, inPipe, outPipe); if (sendHeaders) { await inPipe.WriteHeaders(r.hEncoder, 1, false, DefaultGetHeaders); } if (sendData) { await inPipe.WriteData(1u, 0); } if (sendTrailers) { await inPipe.WriteHeaders(r.hEncoder, 1, true, DefaultGetHeaders); } await outPipe.AssertResetStreamReception(1, ErrorCode.StreamClosed); }
public async Task ViolationsOfTheStreamFlowControlWindowShouldBeDetected( int?padLen, int streamWindowSize, int dataAmount, bool isError) { var inPipe = new BufferedPipe(1024); var outPipe = new BufferedPipe(1024); // Use a smaller flow control window for streams so that the stream // window errors. var settings = Settings.Default; settings.InitialWindowSize = (uint)streamWindowSize; settings.MaxFrameSize = 1024 * 1024; // The test values for the amount of data to be sent on the stream // are tuned small enough that no connection window // updates will be sent, which would fail the expected output. var res = await ServerStreamTests.StreamCreator.CreateConnectionAndStream( StreamState.Open, loggerProvider, inPipe, outPipe, localSettings : settings); // Write more data than the stream allows await inPipe.WriteData(1u, dataAmount, padLen : padLen); if (isError) { // Expect that the stream got reset await outPipe.AssertResetStreamReception(1u, ErrorCode.FlowControlError); Assert.Equal(StreamState.Reset, res.stream.State); } // Send a ping afterwards, which should be processed in all cases await inPipe.WritePing(new byte[8], false); await outPipe.ReadAndDiscardPong(); }
public async Task ViolationsOfTheConnectionFlowControlWindowShouldBeDetected( int?padLen, uint streamId, int dataAmount, bool isError) { var inPipe = new BufferedPipe(1024); var outPipe = new BufferedPipe(1024); // Use a bigger flow control window for streams so that the connection // window errors and streams dont send window updates. // Also update maxFrameSize, otherwise the connection will send // window updates faster than we can violate the contract var settings = Settings.Default; settings.InitialWindowSize = 256 * 1024; settings.MaxFrameSize = 1024 * 1024; var res = await ServerStreamTests.StreamCreator.CreateConnectionAndStream( StreamState.Open, loggerProvider, inPipe, outPipe, localSettings : settings); // Open an additional stream, so that streamId 3 is not in the IDLE // range, which causes a connection error await inPipe.WriteHeaders( res.hEncoder, 555u, false, DefaultGetHeaders); // Try to send the data // This might fail, if the connection goes away before // everything is read try { await inPipe.WriteData(streamId, dataAmount, padLen : padLen); } catch (Exception e) { if (!isError || !(e is TimeoutException)) { throw; } } if (isError) { await outPipe.AssertGoAwayReception(ErrorCode.FlowControlError, 555u); await outPipe.AssertStreamEnd(); await res.conn.Done; Assert.Equal(StreamState.Reset, res.stream.State); } else { // Expect the connection to be alive await outPipe.AssertWindowUpdate(0u, 65535); if (streamId != 1) { // We expect a reset for the unknown stream on which data // was transmitted await outPipe.AssertResetStreamReception(streamId, ErrorCode.StreamClosed); } await inPipe.WritePing(new byte[8], false); await outPipe.ReadAndDiscardPong(); Assert.Equal(StreamState.Open, res.stream.State); } }
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); }