示例#1
0
        public async Task ReceivingHeadersWithPaddingAndPriorityShouldBeSupported(
            int?numPadding, bool hasPrio)
        {
            const int nrStreams         = 10;
            var       inPipe            = new BufferedPipe(1024);
            var       outPipe           = new BufferedPipe(1024);
            int       nrAcceptedStreams = 0;
            var       handlerDone       = new SemaphoreSlim(0);
            uint      streamId          = 1;
            var       headersOk         = false;
            var       streamIdOk        = false;
            var       streamStateOk     = false;

            Func <IStream, bool> listener = (s) =>
            {
                Interlocked.Increment(ref nrAcceptedStreams);
                Task.Run(async() =>
                {
                    var rcvdHeaders = await s.ReadHeadersAsync();
                    headersOk       = DefaultGetHeaders.SequenceEqual(rcvdHeaders);
                    streamIdOk      = s.Id == streamId;
                    streamStateOk   = s.State == StreamState.Open;
                    handlerDone.Release();
                });
                return(true);
            };
            var http2Con = await ConnectionUtils.BuildEstablishedConnection(
                true, inPipe, outPipe, loggerProvider, listener);

            var hEncoder = new Encoder();
            var outBuf   = new byte[Settings.Default.MaxFrameSize];

            for (var i = 0; i < nrStreams; i++)
            {
                headersOk     = false;
                streamIdOk    = false;
                streamStateOk = false;

                var headerOffset = 0;
                if (numPadding != null)
                {
                    outBuf[0]     = (byte)numPadding;
                    headerOffset += 1;
                }
                if (hasPrio)
                {
                    // TODO: Initialize the priority data in this test properly
                    // if priority data checking gets inserted later on
                    headerOffset += 5;
                }
                var result = hEncoder.EncodeInto(
                    new ArraySegment <byte>(outBuf, headerOffset, outBuf.Length - headerOffset),
                    DefaultGetHeaders);
                var totalLength = headerOffset + result.UsedBytes;
                if (numPadding != null)
                {
                    totalLength += numPadding.Value;
                }

                var flags = (byte)HeadersFrameFlags.EndOfHeaders;
                if (numPadding != null)
                {
                    flags |= (byte)HeadersFrameFlags.Padded;
                }
                if (hasPrio)
                {
                    flags |= (byte)HeadersFrameFlags.Priority;
                }

                var fh = new FrameHeader
                {
                    Type     = FrameType.Headers,
                    Length   = totalLength,
                    Flags    = (byte)flags,
                    StreamId = streamId,
                };
                await inPipe.WriteFrameHeader(fh);

                await inPipe.WriteAsync(new ArraySegment <byte>(outBuf, 0, totalLength));

                var requestDone = await handlerDone.WaitAsync(ReadableStreamTestExtensions.ReadTimeout);

                Assert.True(requestDone, "Expected handler to complete within timeout");
                Assert.True(headersOk);
                Assert.True(streamIdOk);
                Assert.True(streamStateOk);
                streamId += 2;
            }
            Assert.Equal(nrStreams, nrAcceptedStreams);
        }
示例#2
0
            public static async Task <Result> CreateConnectionAndStream(
                StreamState state,
                ILoggerProvider loggerProvider,
                IBufferedPipe iPipe, IBufferedPipe oPipe,
                Settings?localSettings          = null,
                Settings?remoteSettings         = null,
                HuffmanStrategy huffmanStrategy = HuffmanStrategy.Never)
            {
                if (state == StreamState.Idle)
                {
                    throw new Exception("Not supported");
                }

                var hEncoder = new Encoder();
                var conn     = await ConnectionUtils.BuildEstablishedConnection(
                    false, iPipe, oPipe, loggerProvider, null,
                    localSettings : localSettings,
                    remoteSettings : remoteSettings,
                    huffmanStrategy : huffmanStrategy);

                var endOfStream = false;

                if (state == StreamState.HalfClosedLocal ||
                    state == StreamState.Closed)
                {
                    endOfStream = true;
                }
                var stream = await conn.CreateStreamAsync(
                    DefaultGetHeaders, endOfStream : endOfStream);

                await oPipe.ReadAndDiscardHeaders(1u, endOfStream);

                if (state == StreamState.HalfClosedRemote ||
                    state == StreamState.Closed)
                {
                    var outBuf = new byte[Settings.Default.MaxFrameSize];
                    var result = hEncoder.EncodeInto(
                        new ArraySegment <byte>(outBuf),
                        DefaultStatusHeaders);
                    await iPipe.WriteFrameHeaderWithTimeout(
                        new FrameHeader
                    {
                        Type  = FrameType.Headers,
                        Flags = (byte)(HeadersFrameFlags.EndOfHeaders |
                                       HeadersFrameFlags.EndOfStream),
                        StreamId = 1u,
                        Length   = result.UsedBytes,
                    });

                    await iPipe.WriteAsync(new ArraySegment <byte>(outBuf, 0, result.UsedBytes));

                    var readHeadersTask = stream.ReadHeadersAsync();
                    var combined        = await Task.WhenAny(readHeadersTask, Task.Delay(
                                                                 ReadableStreamTestExtensions.ReadTimeout));

                    Assert.True(readHeadersTask == combined, "Expected to receive headers");
                    var headers = await readHeadersTask;
                    Assert.True(headers.SequenceEqual(DefaultStatusHeaders));
                    // Consume the data - which should be empty
                    var data = await stream.ReadAllToArrayWithTimeout();

                    Assert.Equal(0, data.Length);
                }
                else if (state == StreamState.Reset)
                {
                    await iPipe.WriteResetStream(1u, ErrorCode.Cancel);

                    await Assert.ThrowsAsync <StreamResetException>(
                        () => stream.ReadHeadersAsync());
                }

                return(new Result
                {
                    hEncoder = hEncoder,
                    conn = conn,
                    stream = stream,
                });
            }
示例#3
0
        public async Task ContinuationFrameHeadersShouldBeAddedToTotalHeaders(
            int[] nrHeadersInFrame)
        {
            var     inPipe  = new BufferedPipe(1024);
            var     outPipe = new BufferedPipe(1024);
            IStream stream  = null;
            IEnumerable <HeaderField> receivedHeaders = null;
            SemaphoreSlim             handlerDone     = new SemaphoreSlim(0);

            Func <IStream, bool> listener = (s) =>
            {
                stream = s;
                Task.Run(async() =>
                {
                    receivedHeaders = await s.ReadHeadersAsync();
                    handlerDone.Release();
                });
                return(true);
            };
            var http2Con = await ConnectionUtils.BuildEstablishedConnection(
                true, inPipe, outPipe, loggerProvider, listener);

            var hEncoder       = new Encoder();
            var totalHeaders   = DefaultGetHeaders;
            var isContinuation = false;
            var toSkip         = 0;
            var isEndOfHeaders = false;

            for (var frameNr = 0; frameNr <= nrHeadersInFrame.Length; frameNr++)
            {
                var headersToSend = totalHeaders.Skip(toSkip);
                if (frameNr != nrHeadersInFrame.Length)
                {
                    var toSend = nrHeadersInFrame[frameNr];
                    headersToSend = headersToSend.Take(toSend);
                    toSkip       += toSend;
                }
                else
                {
                    // send remaining headers
                    isEndOfHeaders = true;
                }
                if (!isContinuation)
                {
                    await inPipe.WriteHeaders(
                        hEncoder, 1, false, headersToSend, isEndOfHeaders);

                    isContinuation = true;
                }
                else
                {
                    await inPipe.WriteContinuation(
                        hEncoder, 1, headersToSend, isEndOfHeaders);
                }
            }
            var handlerCompleted = await handlerDone.WaitAsync(
                ReadableStreamTestExtensions.ReadTimeout);

            Assert.True(handlerCompleted, "Expected stream handler to complete");
            Assert.True(
                totalHeaders.SequenceEqual(receivedHeaders),
                "Expected to receive all sent headers");
        }
示例#4
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
        }
示例#5
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();
            }
        }
示例#6
0
        public async Task IncompleteHeaderBlocksShouldBeDetected(
            int nrContinuations)
        {
            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();
            // Construct a proper set of header here, where a single headerfield could
            // by splitted over more than 1 fragment
            var totalHeaders = DefaultGetHeaders.TakeWhile(
                h => h.Name.StartsWith(":"));

            totalHeaders = totalHeaders.Append(
                new HeaderField {
                Name = "longname", Value = "longvalue"
            });
            totalHeaders = totalHeaders.Append(
                new HeaderField {
                Name = "abc", Value = "xyz"
            });

            // Encode all headers in a single header block
            var hBuf      = new byte[32 * 1024];
            var encodeRes =
                hEncoder.EncodeInto(new ArraySegment <byte>(hBuf), totalHeaders);

            Assert.Equal(totalHeaders.Count(), encodeRes.FieldCount);
            Assert.True(
                encodeRes.UsedBytes >= 5,
                "Test must encode headers with at least 5 bytes to work properly");

            // Write header data in multiple parts
            // The last part will miss one byte
            var offset = 0;
            var length = encodeRes.UsedBytes;

            var isLast     = nrContinuations == 0;
            var dataLength = isLast ? (length - 1) : 3;
            var fh         = new FrameHeader
            {
                Type     = FrameType.Headers,
                Length   = dataLength,
                StreamId = 1u,
                Flags    = (byte)(isLast ? HeadersFrameFlags.EndOfHeaders : 0),
            };
            await inPipe.WriteFrameHeader(fh);

            await inPipe.WriteAsync(new ArraySegment <byte>(hBuf, offset, dataLength));

            offset += dataLength;
            length -= dataLength;

            for (var i = 0; i < nrContinuations; i++)
            {
                isLast     = i == nrContinuations - 1;
                dataLength = isLast ? (length - 1) : 1;
                fh         = new FrameHeader
                {
                    Type     = FrameType.Continuation,
                    Length   = dataLength,
                    StreamId = 1u,
                    Flags    = (byte)(isLast ? ContinuationFrameFlags.EndOfHeaders : 0),
                };
                await inPipe.WriteFrameHeader(fh);

                await inPipe.WriteAsync(new ArraySegment <byte>(hBuf, offset, dataLength));

                offset += dataLength;
                length -= dataLength;
            }

            Assert.True(1 == length, "Expected to send all but 1 byte");

            // Expect a GoAway as reaction to incomplete headers
            await outPipe.AssertGoAwayReception(ErrorCode.CompressionError, 0u);

            await outPipe.AssertStreamEnd();
        }
示例#7
0
        public async Task ContinuationsWherePartsDontContainFullHeaderMustBeSupported(
            int nrContinuations)
        {
            var     inPipe  = new BufferedPipe(1024);
            var     outPipe = new BufferedPipe(1024);
            IStream stream  = null;
            IEnumerable <HeaderField> receivedHeaders = null;
            SemaphoreSlim             handlerDone     = new SemaphoreSlim(0);

            Func <IStream, bool> listener = (s) =>
            {
                stream = s;
                Task.Run(async() =>
                {
                    receivedHeaders = await s.ReadHeadersAsync();
                    handlerDone.Release();
                });
                return(true);
            };
            var http2Con = await ConnectionUtils.BuildEstablishedConnection(
                true, inPipe, outPipe, loggerProvider, listener);

            var hEncoder = new Encoder();
            // Construct a proper set of header here, where a single headerfield could
            // by splitted over more than 1 fragment
            var totalHeaders = DefaultGetHeaders.TakeWhile(
                h => h.Name.StartsWith(":"));

            totalHeaders = totalHeaders.Append(
                new HeaderField {
                Name = "longname", Value = "longvalue"
            });
            totalHeaders = totalHeaders.Append(
                new HeaderField {
                Name = "abc", Value = "xyz"
            });

            // Encode all headers in a single header block
            var hBuf      = new byte[32 * 1024];
            var encodeRes =
                hEncoder.EncodeInto(new ArraySegment <byte>(hBuf), totalHeaders);

            Assert.Equal(totalHeaders.Count(), encodeRes.FieldCount);
            Assert.True(
                encodeRes.UsedBytes >= 5,
                "Test must encode headers with at least 5 bytes to work properly");

            // Write the first 3 bytes in a header frame
            // These cover all pseudoheaders, which wouldn't be affected by
            // possible bugs anyway
            var fh = new FrameHeader
            {
                Type     = FrameType.Headers,
                Length   = 3,
                StreamId = 1u,
                Flags    = (byte)0,
            };
            await inPipe.WriteFrameHeader(fh);

            await inPipe.WriteAsync(new ArraySegment <byte>(hBuf, 0, 3));

            var offset = 3;
            var length = encodeRes.UsedBytes - 3;

            for (var i = 0; i < nrContinuations; i++)
            {
                var isLast     = i == nrContinuations - 1;
                var dataLength = isLast ? length : 1;
                fh = new FrameHeader
                {
                    Type     = FrameType.Continuation,
                    Length   = dataLength,
                    StreamId = 1u,
                    Flags    = (byte)(isLast ? ContinuationFrameFlags.EndOfHeaders : 0),
                };
                await inPipe.WriteFrameHeader(fh);

                await inPipe.WriteAsync(new ArraySegment <byte>(hBuf, offset, dataLength));

                offset += dataLength;
                length -= dataLength;
            }

            var handlerCompleted = await handlerDone.WaitAsync(
                ReadableStreamTestExtensions.ReadTimeout);

            Assert.True(handlerCompleted, "Expected stream handler to complete");
            Assert.True(
                totalHeaders.SequenceEqual(receivedHeaders),
                "Expected to receive all sent headers");
        }
示例#8
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);
            }
        }
示例#9
0
            public static async Task<Result> CreateConnectionAndStream(
                StreamState state,
                ILoggerProvider loggerProvider,
                IBufferedPipe iPipe, IBufferedPipe oPipe,
                Settings? localSettings = null,
                Settings? remoteSettings = null,
                HuffmanStrategy huffmanStrategy = HuffmanStrategy.Never)
            {
                IStream stream = null;
                var handlerDone = new SemaphoreSlim(0);
                if (state == StreamState.Idle)
                {
                    throw new Exception("Not supported");
                }

                Func<IStream, bool> listener = (s) =>
                {
                    Task.Run(async () =>
                    {
                        stream = s;
                        try
                        {
                            await s.ReadHeadersAsync();
                            if (state == StreamState.Reset)
                            {
                                s.Cancel();
                                return;
                            }

                            if (state == StreamState.HalfClosedRemote ||
                                state == StreamState.Closed)
                            {
                                await s.ReadAllToArrayWithTimeout();
                            }

                            if (state == StreamState.HalfClosedLocal ||
                                state == StreamState.Closed)
                            {
                                await s.WriteHeadersAsync(
                                    DefaultStatusHeaders, true);
                            }
                        }
                        finally
                        {
                            handlerDone.Release();
                        }
                    });
                    return true;
                };
                var conn = await ConnectionUtils.BuildEstablishedConnection(
                    true, iPipe, oPipe, loggerProvider, listener,
                    localSettings: localSettings,
                    remoteSettings: remoteSettings,
                    huffmanStrategy: huffmanStrategy);
                var hEncoder = new Encoder();

                await iPipe.WriteHeaders(
                    hEncoder, 1, false, DefaultGetHeaders);

                if (state == StreamState.HalfClosedRemote ||
                    state == StreamState.Closed)
                {
                    await iPipe.WriteData(1u, 0, endOfStream: true);
                }

                var ok = await handlerDone.WaitAsync(
                    ReadableStreamTestExtensions.ReadTimeout);
                if (!ok) throw new Exception("Stream handler did not finish");

                if (state == StreamState.HalfClosedLocal ||
                    state == StreamState.Closed)
                {
                    // Consume the sent headers and data
                    await oPipe.ReadAndDiscardHeaders(1u, true);
                }
                else if (state == StreamState.Reset)
                {
                    // Consume the sent reset frame
                    await oPipe.AssertResetStreamReception(1, ErrorCode.Cancel);
                }

                return new Result
                {
                    conn = conn,
                    stream = stream,
                    hEncoder = hEncoder,
                };
            }