Пример #1
0
        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);
        }
Пример #2
0
        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]);
            }
        }
Пример #3
0
        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);
        }
Пример #4
0
        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");
        }
Пример #5
0
        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
        }
Пример #6
0
        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);
        }
Пример #7
0
        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));
            }
        }
Пример #8
0
        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);
        }
Пример #9
0
        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");
        }
Пример #10
0
        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));
            }
        }
Пример #11
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;
                }
            }
        }
Пример #12
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
        }
Пример #13
0
        [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));
        }