Example #1
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
        }
Example #2
0
        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();
        }
Example #3
0
        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();
        }
Example #4
0
        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);
        }
Example #5
0
        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();
        }
Example #6
0
        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();
        }
Example #7
0
        public async Task ConnectionShouldIgnoreAndAcknowledgeUnknownSettings()
        {
            var inPipe   = new BufferedPipe(1024);
            var outPipe  = new BufferedPipe(1024);
            var http2Con = BuildConnection(true, Settings.Default, inPipe, outPipe);

            await ClientPreface.WriteAsync(inPipe);

            await outPipe.ReadAndDiscardSettings();

            var settings = Settings.Default;
            // Create a buffer for normal settings plus 3 unknown ones
            var settingsBuffer = new byte[settings.RequiredSize + 18];

            settings.EncodeInto(new ArraySegment <byte>(
                                    settingsBuffer, 0, settings.RequiredSize));
            // Use some unknown settings IDs
            settingsBuffer[settings.RequiredSize]      = 0;
            settingsBuffer[settings.RequiredSize + 1]  = 10;
            settingsBuffer[settings.RequiredSize + 6]  = 10;
            settingsBuffer[settings.RequiredSize + 7]  = 20;
            settingsBuffer[settings.RequiredSize + 12] = 0xFF;
            settingsBuffer[settings.RequiredSize + 13] = 0xFF;
            var settingsHeader = new FrameHeader
            {
                Type     = FrameType.Settings,
                StreamId = 0,
                Flags    = 0,
                Length   = settingsBuffer.Length,
            };
            await inPipe.WriteFrameHeader(settingsHeader);

            await inPipe.WriteAsync(new ArraySegment <byte>(settingsBuffer));

            // Check if the connection ACKs these settings
            await outPipe.AssertSettingsAck();
        }
Example #8
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
        }