public async Task TrailersShouldBeCorrectlySent(bool isServer) { var inPipe = new BufferedPipe(1024); var outPipe = new BufferedPipe(1024); var r = await StreamCreator.CreateConnectionAndStream( isServer, loggerProvider, inPipe, outPipe); await r.stream.WriteAsync(new ArraySegment <byte>(new byte[0])); await outPipe.ReadAndDiscardData(1u, false, 0); // Send trailers await r.stream.WriteTrailersAsync(DefaultTrailingHeaders); // Check the received trailers var fh = await outPipe.ReadFrameHeaderWithTimeout(); Assert.Equal(FrameType.Headers, fh.Type); Assert.Equal(1u, fh.StreamId); var expectedFlags = HeadersFrameFlags.EndOfHeaders | HeadersFrameFlags.EndOfStream; Assert.Equal((byte)expectedFlags, fh.Flags); Assert.InRange(fh.Length, 1, 1024); var headerData = new byte[fh.Length]; await outPipe.ReadAllWithTimeout(new ArraySegment <byte>(headerData)); Assert.Equal(EncodedDefaultTrailingHeaders, headerData); }
public async Task ConnectionShouldRespondToPingWithPong(bool isServer) { var inPipe = new BufferedPipe(1024); var outPipe = new BufferedPipe(1024); var http2Con = await ConnectionUtils.BuildEstablishedConnection( isServer, inPipe, outPipe, loggerProvider); var pingData = new byte[8]; for (var i = 0; i < pingData.Length; i++) { pingData[i] = (byte)i; } await inPipe.WritePing(pingData, false); var res = await outPipe.ReadFrameHeaderWithTimeout(); Assert.Equal(FrameType.Ping, res.Type); Assert.Equal(0u, res.StreamId); Assert.Equal(8, res.Length); Assert.Equal((byte)PingFrameFlags.Ack, res.Flags); var pongData = new byte[8]; await outPipe.ReadAllWithTimeout(new ArraySegment <byte>(pongData)); for (var i = 0; i < pingData.Length; i++) { Assert.Equal((byte)i, pongData[i]); } }
public async Task PingShouldFailWhenConnectionIsClosedAfterPingStart() { var inPipe = new BufferedPipe(1024); var outPipe = new BufferedPipe(1024); var http2Con = await ConnectionUtils.BuildEstablishedConnection( true, inPipe, outPipe, loggerProvider); // Request ping var pingTask = http2Con.PingAsync(); // Expect ping emission var fh = await outPipe.ReadFrameHeaderWithTimeout(); Assert.Equal(FrameType.Ping, fh.Type); Assert.Equal(8, fh.Length); Assert.Equal(0, fh.Flags); Assert.Equal(0u, fh.StreamId); var pingData = new ArraySegment <byte>(new byte[8]); await outPipe.ReadAllWithTimeout(pingData); // Close the connection await inPipe.CloseAsync(); await outPipe.AssertStreamEnd(); // Await ping to finish with exception var timeoutTask = Task.Delay(200); Assert.True( pingTask == await Task.WhenAny(pingTask, timeoutTask), "Expected pingTask to finish"); await Assert.ThrowsAsync <ConnectionClosedException>( async() => await pingTask); }
public async Task PingAsyncShouldSendPingAndWaitForAssociatedAck() { var inPipe = new BufferedPipe(1024); var outPipe = new BufferedPipe(1024); var http2Con = await ConnectionUtils.BuildEstablishedConnection( true, inPipe, outPipe, loggerProvider); // Request ping var pingTask = http2Con.PingAsync(); // Expect ping emission var fh = await outPipe.ReadFrameHeaderWithTimeout(); Assert.Equal(FrameType.Ping, fh.Type); Assert.Equal(8, fh.Length); Assert.Equal(0, fh.Flags); Assert.Equal(0u, fh.StreamId); var pingData = new ArraySegment <byte>(new byte[8]); await outPipe.ReadAllWithTimeout(pingData); // Respond with pong fh.Flags = (byte)PingFrameFlags.Ack; await inPipe.WriteFrameHeader(fh); await inPipe.WriteAsync(pingData); // Await ping task to finish Assert.True( pingTask == await Task.WhenAny(pingTask, Task.Delay(200)), "Expected pingTask to finish"); }
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 CreatingStreamShouldEmitHeaders() { var inPipe = new BufferedPipe(1024); var outPipe = new BufferedPipe(1024); var conn = await ConnectionUtils.BuildEstablishedConnection( false, inPipe, outPipe, loggerProvider); var stream1Task = conn.CreateStreamAsync(DefaultGetHeaders, false); var stream1 = await stream1Task; Assert.Equal(StreamState.Open, stream1.State); Assert.Equal(1u, stream1.Id); var fh = await outPipe.ReadFrameHeaderWithTimeout(); Assert.Equal(1u, fh.StreamId); Assert.Equal(FrameType.Headers, fh.Type); Assert.Equal((byte)HeadersFrameFlags.EndOfHeaders, fh.Flags); Assert.Equal(EncodedDefaultGetHeaders.Length, fh.Length); var hdrData = new byte[fh.Length]; await outPipe.ReadWithTimeout(new ArraySegment <byte>(hdrData)); Assert.Equal(EncodedDefaultGetHeaders, hdrData); var stream3Task = conn.CreateStreamAsync(DefaultGetHeaders, true); var stream3 = await stream3Task; Assert.Equal(StreamState.HalfClosedLocal, stream3.State); Assert.Equal(3u, stream3.Id); fh = await outPipe.ReadFrameHeaderWithTimeout(); Assert.Equal(3u, fh.StreamId); Assert.Equal(FrameType.Headers, fh.Type); Assert.Equal( (byte)(HeadersFrameFlags.EndOfHeaders | HeadersFrameFlags.EndOfStream), fh.Flags); Assert.Equal(EncodedIndexedDefaultGetHeaders.Length, fh.Length); var hdrData3 = new byte[fh.Length]; await outPipe.ReadWithTimeout(new ArraySegment <byte>(hdrData3)); Assert.Equal(EncodedIndexedDefaultGetHeaders, hdrData3); }
public async Task CreatedStreamsShouldAlwaysUseIncreasedStreamIds( int nrStreams) { // This test checks if there are race conditions in the stream // establish code var inPipe = new BufferedPipe(10 * 1024); var outPipe = new BufferedPipe(10 * 1024); var conn = await ConnectionUtils.BuildEstablishedConnection( false, inPipe, outPipe, loggerProvider); var createStreamTasks = new Task <IStream> [nrStreams]; for (var i = 0; i < nrStreams; i++) { // Create the task in the threadpool var t = Task.Run( () => conn.CreateStreamAsync(DefaultGetHeaders, false)); createStreamTasks[i] = t; } // Wait until all streams are open await Task.WhenAll(createStreamTasks); var streams = createStreamTasks.Select(t => t.Result).ToList(); // Check output data // Sequence IDs must be always increasing var buffer = new byte[Settings.Default.MaxFrameSize]; for (var i = 0; i < nrStreams; i++) { var expectedId = 1u + 2 * i; var fh = await outPipe.ReadFrameHeaderWithTimeout(); Assert.Equal(expectedId, fh.StreamId); Assert.Equal(FrameType.Headers, fh.Type); Assert.Equal((byte)HeadersFrameFlags.EndOfHeaders, fh.Flags); // Discard header data await outPipe.ReadWithTimeout( new ArraySegment <byte>(buffer, 0, fh.Length)); } }
public async Task ResponseHeadersShouldBeCorrectlySent( bool useEndOfStream) { var inPipe = new BufferedPipe(1024); var outPipe = new BufferedPipe(1024); var r = await StreamCreator.CreateConnectionAndStream( StreamState.Open, loggerProvider, inPipe, outPipe); await r.stream.WriteHeadersAsync(DefaultStatusHeaders, useEndOfStream); // Check the received headers var fh = await outPipe.ReadFrameHeaderWithTimeout(); Assert.Equal(FrameType.Headers, fh.Type); Assert.Equal(1u, fh.StreamId); var expectedFlags = HeadersFrameFlags.EndOfHeaders; if (useEndOfStream) expectedFlags |= HeadersFrameFlags.EndOfStream; Assert.Equal((byte)expectedFlags, fh.Flags); Assert.InRange(fh.Length, 1, 1024); var headerData = new byte[fh.Length]; await outPipe.ReadAllWithTimeout(new ArraySegment<byte>(headerData)); Assert.Equal(EncodedDefaultStatusHeaders, headerData); }
public async Task PingAsyncShouldNotCompleteWhenWrongPingResponseIsReceived() { var inPipe = new BufferedPipe(1024); var outPipe = new BufferedPipe(1024); var http2Con = await ConnectionUtils.BuildEstablishedConnection( true, inPipe, outPipe, loggerProvider); // Request ping var pingTask = http2Con.PingAsync(); // Expect ping emission var fh = await outPipe.ReadFrameHeaderWithTimeout(); Assert.Equal(FrameType.Ping, fh.Type); Assert.Equal(8, fh.Length); Assert.Equal(0, fh.Flags); Assert.Equal(0u, fh.StreamId); var pingData = new ArraySegment <byte>(new byte[8]); await outPipe.ReadAllWithTimeout(pingData); // Respond with pong and manipulated data fh.Flags = (byte)PingFrameFlags.Ack; for (var i = 0; i < pingData.Count; i++) { pingData.Array[i] = 0xFF; } await inPipe.WriteFrameHeader(fh); await inPipe.WriteAsync(pingData); // Await ping not to finish var timeoutTask = Task.Delay(200); Assert.True( timeoutTask == await Task.WhenAny(pingTask, timeoutTask), "Expected pingTask not to finish"); }
public async Task PingAsyncShouldUseIncreasingIds() { var inPipe = new BufferedPipe(1024); var outPipe = new BufferedPipe(1024); var http2Con = await ConnectionUtils.BuildEstablishedConnection( true, inPipe, outPipe, loggerProvider); for (var expectedId = 0u; expectedId < 3u; expectedId++) { // Request ping var pingTask = http2Con.PingAsync(); // Expect ping emission with that ID var fh = await outPipe.ReadFrameHeaderWithTimeout(); Assert.Equal(FrameType.Ping, fh.Type); Assert.Equal(8, fh.Length); Assert.Equal(0, fh.Flags); Assert.Equal(0u, fh.StreamId); var pingData = new ArraySegment <byte>(new byte[8]); await outPipe.ReadAllWithTimeout(pingData); Assert.Equal(expectedId, BitConverter.ToUInt64(pingData.Array, 0)); } }
public async Task DataShouldBeCorrectlySent( bool isServer, int[] dataLength) { var inPipe = new BufferedPipe(1024); var outPipe = new BufferedPipe(1024); var r = await StreamCreator.CreateConnectionAndStream( isServer, loggerProvider, inPipe, outPipe); var totalToSend = dataLength.Aggregate(0, (sum, n) => sum + n); var writeTask = Task.Run(async() => { byte nr = 0; for (var i = 0; i < dataLength.Length; i++) { var toSend = dataLength[i]; var isEndOfStream = i == (dataLength.Length - 1); var buffer = new byte[toSend]; for (var j = 0; j < toSend; j++) { buffer[j] = nr; nr++; if (nr > 122) { nr = 0; } } await r.stream.WriteAsync( new ArraySegment <byte>(buffer), isEndOfStream); } }); var data = new byte[totalToSend]; var offset = 0; for (var i = 0; i < dataLength.Length; i++) { var fh = await outPipe.ReadFrameHeaderWithTimeout(); Assert.Equal(FrameType.Data, fh.Type); Assert.Equal(1u, fh.StreamId); var expectEOS = i == dataLength.Length - 1; var gotEOS = (fh.Flags & (byte)DataFrameFlags.EndOfStream) != 0; Assert.Equal(expectEOS, gotEOS); Assert.Equal(dataLength[i], fh.Length); var part = new byte[fh.Length]; await outPipe.ReadAllWithTimeout(new ArraySegment <byte>(part)); Array.Copy(part, 0, data, offset, fh.Length); offset += fh.Length; } var doneTask = await Task.WhenAny(writeTask, Task.Delay(250)); Assert.True(writeTask == doneTask, "Expected write task to finish"); // Check if the correct data was received var expected = 0; for (var j = 0; j < totalToSend; j++) { Assert.Equal(expected, data[j]); expected++; if (expected > 122) { expected = 0; } } }
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 }
[InlineData(65535, 100 * 1024, 2, 7)] // Continuations required public async Task OutgoingHeadersShouldBeFragmentedIntoContinuationsAccordingToFrameSize( int maxFrameSize, int totalHeaderBytes, int expectedMinNrOfFrames, int expectedMaxNrOfFrames) { var inPipe = new BufferedPipe(1024); var outPipe = new BufferedPipe(1024); var remoteSettings = Settings.Default; remoteSettings.MaxFrameSize = (uint)maxFrameSize; var res = await ServerStreamTests.StreamCreator.CreateConnectionAndStream( StreamState.Open, loggerProvider, inPipe, outPipe, remoteSettings : remoteSettings, huffmanStrategy : HuffmanStrategy.Never); // Create the list of headers that should be sent // This must be big enough in order to send multiple frames var headers = new List <HeaderField>(); // The :status header is necessary to avoid an exception on send headers.AddRange( DefaultStatusHeaders.TakeWhile(sh => sh.Name.StartsWith(":"))); const int headerLen = 3 + 10 + 8; // The calculated size of one header field // Calculate the amount of headers of the given size that are needed // to be sent based on the required totalHeaderBytes number. int requiredHeaderData = totalHeaderBytes - 1; // -1 for :status field int amountHeaders = requiredHeaderData / headerLen; for (var i = 0; i < amountHeaders; i++) { var headerField = new HeaderField { Name = "hd" + i.ToString("D8"), Value = i.ToString("D8"), Sensitive = true, }; headers.Add(headerField); } // Send the headers in background task var writeHeaderTask = Task.Run(async() => { await res.stream.WriteHeadersAsync(headers, false); }); var hDecoder = new Decoder(); var hBuf = new byte[maxFrameSize]; var expectCont = false; var rcvdFrames = 0; var rcvdHeaders = new List <HeaderField>(); while (true) { var fh = await outPipe.ReadFrameHeaderWithTimeout(); Assert.Equal(expectCont ? FrameType.Continuation : FrameType.Headers, fh.Type); Assert.Equal(1u, fh.StreamId); Assert.InRange(fh.Length, 1, maxFrameSize); // Read header block fragment data await outPipe.ReadAllWithTimeout( new ArraySegment <byte>(hBuf, 0, fh.Length)); // Decode it var lastHeadersCount = rcvdHeaders.Count; var decodeResult = hDecoder.DecodeHeaderBlockFragment( new ArraySegment <byte>(hBuf, 0, fh.Length), int.MaxValue, rcvdHeaders); Assert.True( rcvdHeaders.Count > lastHeadersCount, "Expected to retrieve at least one header per frame"); Assert.Equal(DecoderExtensions.DecodeStatus.Success, decodeResult.Status); expectCont = true; rcvdFrames++; if ((fh.Flags & (byte)ContinuationFrameFlags.EndOfHeaders) != 0) { break; } } Assert.InRange(rcvdFrames, expectedMinNrOfFrames, expectedMaxNrOfFrames); Assert.Equal(headers.Count, rcvdHeaders.Count); Assert.True(rcvdHeaders.SequenceEqual(headers)); }