Exemplo n.º 1
0
        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 ConnectionShouldIgnorePriorityData(
            bool isServer, uint streamId,
            uint streamDependency, bool isExclusive, byte weight)
        {
            var inPipe   = new BufferedPipe(1024);
            var outPipe  = new BufferedPipe(1024);
            var http2Con = await ConnectionUtils.BuildEstablishedConnection(
                isServer, inPipe, outPipe, loggerProvider);

            var prioData = new PriorityData
            {
                StreamDependency            = streamDependency,
                StreamDependencyIsExclusive = isExclusive,
                Weight = weight,
            };
            await inPipe.WritePriority(streamId, prioData);

            // Send a ping afterwards
            // If we get a response the priority frame in between was ignored
            var pingData = new byte[8];

            for (var i = 0; i < 8; i++)
            {
                pingData[i] = (byte)i;
            }
            await inPipe.WritePing(pingData, false);

            await outPipe.ReadAndDiscardPong();
        }
        public async Task ConnectionShouldIgnoreResetsforUnknownStreams()
        {
            var inPipe  = new BufferedPipe(1024);
            var outPipe = new BufferedPipe(1024);

            Func <IStream, bool> listener = (s) => true;
            var conn = await ConnectionUtils.BuildEstablishedConnection(
                true, inPipe, outPipe, loggerProvider, listener);

            var hEncoder = new Encoder();

            var streamId = 7u;
            await inPipe.WriteHeaders(
                hEncoder, streamId, false, TestHeaders.DefaultGetHeaders);

            await inPipe.WriteResetStream(streamId - 2, ErrorCode.RefusedStream);

            await inPipe.WriteResetStream(streamId - 4, ErrorCode.Cancel);

            // Send a ping afterwards
            // If we get a response the reset frame in between was ignored
            await inPipe.WritePing(new byte[8], false);

            await outPipe.ReadAndDiscardPong();
        }
Exemplo n.º 4
0
        public async Task ShouldGoAwayWhenStreamWindowIsOverflowedThroughSettingsUpdate()
        {
            var inPipe  = new BufferedPipe(1024);
            var outPipe = new BufferedPipe(1024);

            var res = await ServerStreamTests.StreamCreator.CreateConnectionAndStream(
                StreamState.Open, loggerProvider, inPipe, outPipe);

            // Let the remote increase the flow control window of the stream
            await inPipe.WriteWindowUpdate(
                1u,
                (int)(int.MaxValue - Settings.Default.InitialWindowSize));

            // Should be still alive
            await inPipe.WritePing(new byte[8], false);

            await outPipe.ReadAndDiscardPong();

            // And now write new settings which overflow the window
            var newSettings = Settings.Default;

            newSettings.InitialWindowSize++;
            await inPipe.WriteSettings(newSettings);

            // Dead through overflow
            await outPipe.AssertGoAwayReception(ErrorCode.FlowControlError, 1u);
        }
Exemplo n.º 5
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);
        }
Exemplo n.º 6
0
        public async Task ShouldAllowToSetTheMaxPossibleConnectionFlowControlWindowSize()
        {
            var inPipe  = new BufferedPipe(1024);
            var outPipe = new BufferedPipe(1024);

            var http2Con = await ConnectionUtils.BuildEstablishedConnection(
                true, inPipe, outPipe, loggerProvider, null);

            var amount = int.MaxValue - 65535;
            await inPipe.WriteWindowUpdate(0, amount);

            // Check aliveness with ping/pong
            await inPipe.WritePing(new byte[8], false);

            await outPipe.ReadAndDiscardPong();
        }
Exemplo n.º 7
0
        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();
        }
Exemplo n.º 8
0
        public async Task ShouldAllowToSetTheMaxPossibleStreamFlowControlWindowSize()
        {
            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 hEncoder = new Encoder();
            await inPipe.WriteHeaders(hEncoder, 1, false, DefaultGetHeaders);

            var amount = int.MaxValue - 65535;
            await inPipe.WriteWindowUpdate(1, amount);

            // Check aliveness with ping/pong
            await inPipe.WritePing(new byte[8], false);

            await outPipe.ReadAndDiscardPong();
        }
Exemplo n.º 9
0
        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();
            }
        }
Exemplo n.º 10
0
        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();
        }
Exemplo n.º 11
0
        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 ConnectionShouldIgnoreUnknownFrames(
            bool isServer, int payloadLength)
        {
            var inPipe   = new BufferedPipe(1024);
            var outPipe  = new BufferedPipe(1024);
            var http2Con = await ConnectionUtils.BuildEstablishedConnection(
                isServer, inPipe, outPipe, loggerProvider);

            // send an undefined frame type
            var fh = new FrameHeader
            {
                Type     = (FrameType)100,
                Flags    = 33,
                Length   = payloadLength,
                StreamId = 0,
            };
            await inPipe.WriteFrameHeader(fh);

            if (payloadLength != 0)
            {
                var payload = new byte[payloadLength];
                await inPipe.WriteAsync(new ArraySegment <byte>(payload));
            }

            // Send a ping afterwards
            // If we get a response the unknown frame in between was ignored
            var pingData = new byte[8];

            for (var i = 0; i < 8; i++)
            {
                pingData[i] = (byte)i;
            }
            await inPipe.WritePing(pingData, false);

            await outPipe.ReadAndDiscardPong();
        }
Exemplo n.º 13
0
        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);
            }
        }
Exemplo n.º 14
0
        public async Task MaxHeaderListSizeViolationsShouldBeDetected(
            uint maxHeaderListSize,
            int headersInFirstFrame,
            int headersInContFrame,
            bool shouldError)
        {
            var inPipe  = new BufferedPipe(1024);
            var outPipe = new BufferedPipe(1024);

            Func <IStream, bool> listener = (s) => true;
            var settings = Settings.Default;

            settings.MaxHeaderListSize = maxHeaderListSize;
            var http2Con = await ConnectionUtils.BuildEstablishedConnection(
                true, inPipe, outPipe, loggerProvider, listener,
                localSettings : settings,
                huffmanStrategy : HuffmanStrategy.Never);

            var hEncoder = new Encoder();
            var headers  = new List <HeaderField>();

            // Add the default headers
            // These take 123 bytes in store and 3 bytes in transmission
            headers.AddRange(new HeaderField[]
            {
                new HeaderField {
                    Name = ":method", Value = "GET"
                },
                new HeaderField {
                    Name = ":path", Value = "/"
                },
                new HeaderField {
                    Name = ":scheme", Value = "http"
                },
            });
            var currentHeadersLength = headers
                                       .Select(hf => hf.Name.Length + hf.Value.Length + 32)
                                       .Sum();
            // Create a header which takes 34 bytes in store and 5 bytes in transmission
            var extraHeader = new HeaderField
            {
                Name = "a", Value = "b", Sensitive = true
            };

            while (currentHeadersLength < headersInFirstFrame)
            {
                headers.Add(extraHeader);
                currentHeadersLength += 1 + 1 + 32;
            }

            await inPipe.WriteHeaders(
                hEncoder, 1, false,
                headers, headersInContFrame == 0);

            if (headersInContFrame != 0)
            {
                headers.Clear();
                currentHeadersLength = 0;
                while (currentHeadersLength < headersInContFrame)
                {
                    headers.Add(extraHeader);
                    currentHeadersLength += 1 + 1 + 32;
                }
                await inPipe.WriteContinuation(
                    hEncoder, 1,
                    headers, true);
            }

            if (shouldError)
            {
                // TODO: The spec says actually the remote should answer with
                // an HTTP431 error - but that happens on another layer
                await outPipe.AssertGoAwayReception(ErrorCode.ProtocolError, 0u);

                await outPipe.AssertStreamEnd();
            }
            else
            {
                await inPipe.WritePing(new byte[8], false);

                await outPipe.ReadAndDiscardPong();
            }
        }
Exemplo n.º 15
0
        public async Task IncomingStreamsAfterMaxConcurrentStreamsShouldBeRejected(
            int maxConcurrentStreams)
        {
            var inPipe = new BufferedPipe(1024);
            var outPipe = new BufferedPipe(1024);
            var acceptedStreams = new List<IStream>();

            Func<IStream, bool> listener = (s) =>
            {
                lock (acceptedStreams)
                {
                    acceptedStreams.Add(s);
                }
                return true;
            };
            var settings = Settings.Default;
            settings.MaxConcurrentStreams = (uint)maxConcurrentStreams;
            var http2Con = await ConnectionUtils.BuildEstablishedConnection(
                true, inPipe, outPipe, loggerProvider, listener,
                localSettings: settings);

            var hEncoder = new Encoder();
            // Open maxConcurrentStreams
            var streamId = 1u;
            for (var i = 0; i < maxConcurrentStreams; i++)
            {
                await inPipe.WriteHeaders(hEncoder, streamId, false, DefaultGetHeaders);
                streamId += 2;
            }
            // Assert no rejection and response so far
            await inPipe.WritePing(new byte[8], false);
            await outPipe.ReadAndDiscardPong();
            lock (acceptedStreams)
            {
                Assert.Equal(maxConcurrentStreams, acceptedStreams.Count);
            }
            // Try to open an additional stream
            await inPipe.WriteHeaders(hEncoder, streamId, false, DefaultGetHeaders);
            // This one should be rejected
            await outPipe.AssertResetStreamReception(streamId, ErrorCode.RefusedStream);
            lock (acceptedStreams)
            {
                Assert.Equal(maxConcurrentStreams, acceptedStreams.Count);
            }

            // Once a stream is closed a new one should be acceptable
            await inPipe.WriteResetStream(streamId-2, ErrorCode.Cancel);
            streamId += 2;
            await inPipe.WriteHeaders(hEncoder, streamId, false, DefaultGetHeaders);
            // Assert no error response
            await inPipe.WritePing(new byte[8], false);
            await outPipe.ReadAndDiscardPong();
            lock (acceptedStreams)
            {
                // +1 because the dead stream isn't removed
                Assert.Equal(maxConcurrentStreams+1, acceptedStreams.Count);
                // Check if the reset worked
                Assert.Equal(
                    StreamState.Reset,
                    acceptedStreams[acceptedStreams.Count-2].State);
            }
        }