public async Task ReceivingAnInformationalHeaderAfterANormalHeaderShouldBeAnError() { var inPipe = new BufferedPipe(1024); var outPipe = new BufferedPipe(1024); var res = await StreamCreator.CreateConnectionAndStream( StreamState.Open, loggerProvider, inPipe, outPipe); await inPipe.WriteHeaders(res.hEncoder, 1u, false, DefaultStatusHeaders); // Expect to receive the status headers var recvdHeaders = await res.stream.ReadHeadersAsync(); Assert.True(DefaultStatusHeaders.SequenceEqual(recvdHeaders)); // 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); // 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 ClientsShouldBeAbleToReceiveInformationalHeaders() { var inPipe = new BufferedPipe(1024); var outPipe = new BufferedPipe(1024); var res = await StreamCreator.CreateConnectionAndStream( StreamState.Open, loggerProvider, inPipe, outPipe); // Send and receive first set of informational headers var readInfoHeaders1Task = res.stream.ReadHeadersAsync(); Assert.False(readInfoHeaders1Task.IsCompleted); var infoHeaders1 = new HeaderField[] { new HeaderField { Name = ":status", Value = "100" }, new HeaderField { Name = "extension-field", Value = "bar" }, }; await inPipe.WriteHeaders(res.hEncoder, 1u, false, infoHeaders1); var recvdInfoHeaders1 = await readInfoHeaders1Task; Assert.True(infoHeaders1.SequenceEqual(recvdInfoHeaders1)); // Send and receive second set of informational headers var readInfoHeaders2Task = res.stream.ReadHeadersAsync(); Assert.False(readInfoHeaders2Task.IsCompleted); var infoHeaders2 = new HeaderField[] { new HeaderField { Name = ":status", Value = "108" }, new HeaderField { Name = "extension-field-b", Value = "bar2" }, }; await inPipe.WriteHeaders(res.hEncoder, 1u, false, infoHeaders2); var recvdInfoHeaders2 = await readInfoHeaders2Task; Assert.True(infoHeaders2.SequenceEqual(recvdInfoHeaders2)); // Send and receive final headers var recvHeadersTask = res.stream.ReadHeadersAsync(); Assert.False(recvHeadersTask.IsCompleted); await inPipe.WriteHeaders(res.hEncoder, 1u, true, DefaultStatusHeaders); var recvdHeaders = await recvHeadersTask; Assert.True(DefaultStatusHeaders.SequenceEqual(recvdHeaders)); }
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); }
[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)); }