Пример #1
0
        public void NullCharactersRejectedInUTF8AndLatin1Mode(bool useLatin1, KnownHeader header)
        {
            var headers = new HttpRequestHeaders(encodingSelector: useLatin1 ? _ => Encoding.Latin1 : (Func <string, Encoding>)null);

            var valueArray = new char[127]; // 64 + 32 + 16 + 8 + 4 + 2 + 1

            for (var i = 0; i < valueArray.Length; i++)
            {
                valueArray[i] = 'a';
            }

            for (var i = 1; i < valueArray.Length; i++)
            {
                // Set non-ascii Latin char that is valid Utf16 when widened; but not a valid utf8 -> utf16 conversion.
                valueArray[i] = '\0';
                string valueString = new string(valueArray);

                headers.Reset();

                Assert.Throws <InvalidOperationException>(() =>
                {
                    var headerName = Encoding.ASCII.GetBytes(header.Name).AsSpan();
                    var valueSpan  = Encoding.ASCII.GetBytes(valueString).AsSpan();

                    headers.Append(headerName, valueSpan);
                });

                valueArray[i] = 'a';
            }
        }
Пример #2
0
        public void ValueReuseOnlyWhenAllowed(bool reuseValue, KnownHeader header)
        {
            const string HeaderValue = "Hello";

            var headers = new HttpRequestHeaders(reuseHeaderValues: reuseValue);

            for (var i = 0; i < 6; i++)
            {
                var prevName = ChangeNameCase(header.Name, variant: i);
                var nextName = ChangeNameCase(header.Name, variant: i + 1);

                var values = GetHeaderValues(headers, prevName, nextName, HeaderValue, HeaderValue);

                Assert.Equal(HeaderValue, values.PrevHeaderValue);
                Assert.NotSame(HeaderValue, values.PrevHeaderValue);

                Assert.Equal(HeaderValue, values.NextHeaderValue);
                Assert.NotSame(HeaderValue, values.NextHeaderValue);

                Assert.Equal(values.PrevHeaderValue, values.NextHeaderValue);
                if (reuseValue)
                {
                    // When materialized string is reused previous and new should be the same object
                    Assert.Same(values.PrevHeaderValue, values.NextHeaderValue);
                }
                else
                {
                    // When materialized string is not reused previous and new should be the different objects
                    Assert.NotSame(values.PrevHeaderValue, values.NextHeaderValue);
                }
            }
        }
Пример #3
0
    public void ValueReuseLatin1NotConfusedForUtf16AndStillRejected(bool reuseValue, KnownHeader header)
    {
        var headers = new HttpRequestHeaders(reuseHeaderValues: reuseValue);

        var headerValue = new char[127]; // 64 + 32 + 16 + 8 + 4 + 2 + 1

        for (var i = 0; i < headerValue.Length; i++)
        {
            headerValue[i] = 'a';
        }

        for (var i = 0; i < headerValue.Length; i++)
        {
            // Set non-ascii Latin char that is valid Utf16 when widened; but not a valid utf8 -> utf16 conversion.
            headerValue[i] = '\u00a3';

            for (var mode = 0; mode <= 1; mode++)
            {
                string headerValueUtf16Latin1CrossOver;
                if (mode == 0)
                {
                    // Full length
                    headerValueUtf16Latin1CrossOver = new string(headerValue);
                }
                else
                {
                    // Truncated length (to ensure different paths from changing lengths in matching)
                    headerValueUtf16Latin1CrossOver = new string(headerValue.AsSpan().Slice(0, i + 1));
                }

                headers.Reset();

                var headerName = Encoding.ASCII.GetBytes(header.Name).AsSpan();
                var prevSpan   = Encoding.UTF8.GetBytes(headerValueUtf16Latin1CrossOver).AsSpan();

                headers.Append(headerName, prevSpan, checkForNewlineChars: false);
                headers.OnHeadersComplete();
                var prevHeaderValue = ((IHeaderDictionary)headers)[header.Name].ToString();

                Assert.Equal(headerValueUtf16Latin1CrossOver, prevHeaderValue);
                Assert.NotSame(headerValueUtf16Latin1CrossOver, prevHeaderValue);

                headers.Reset();

                Assert.Throws <InvalidOperationException>(() =>
                {
                    var headerName = Encoding.ASCII.GetBytes(header.Name).AsSpan();
                    var nextSpan   = Encoding.Latin1.GetBytes(headerValueUtf16Latin1CrossOver).AsSpan();

                    Assert.False(nextSpan.SequenceEqual(Encoding.ASCII.GetBytes(headerValueUtf16Latin1CrossOver)));

                    headers.Append(headerName, nextSpan, checkForNewlineChars: false);
                });
            }

            // Reset back to Ascii
            headerValue[i] = 'a';
        }
    }
Пример #4
0
        internal HttpHeader(KnownHeader name, KnownHeader value)
        {
            nameString = name.String;
            NameData   = name.String8;

            valueString = value.String;
            ValueData   = value.String8;
        }
Пример #5
0
        internal HttpHeader(KnownHeader name, string value)
        {
            nameString = name.String;
            NameData   = name.String8;

            valueString = value.Trim();
            ValueData   = valueString.GetByteString();
        }
Пример #6
0
            static void Validate(string name, KnownHeader h)
            {
                Assert.NotNull(h);
                Assert.Same(h, KnownHeaders.TryGetKnownHeader(name));

                Assert.Same(h, h.Descriptor.KnownHeader);
                Assert.Equal(name, h.Name, StringComparer.OrdinalIgnoreCase);
                Assert.Equal(name, h.Descriptor.Name, StringComparer.OrdinalIgnoreCase);
            }
Пример #7
0
        public void Latin1ValuesAcceptedInLatin1ModeButNotReused(bool reuseValue, KnownHeader header)
        {
            var headers = new HttpRequestHeaders(reuseHeaderValues: reuseValue, _ => Encoding.Latin1);

            var headerValue = new char[127]; // 64 + 32 + 16 + 8 + 4 + 2 + 1

            for (var i = 0; i < headerValue.Length; i++)
            {
                headerValue[i] = 'a';
            }

            for (var i = 0; i < headerValue.Length; i++)
            {
                // Set non-ascii Latin char that is valid Utf16 when widened; but not a valid utf8 -> utf16 conversion.
                headerValue[i] = '\u00a3';

                for (var mode = 0; mode <= 1; mode++)
                {
                    string headerValueUtf16Latin1CrossOver;
                    if (mode == 0)
                    {
                        // Full length
                        headerValueUtf16Latin1CrossOver = new string(headerValue);
                    }
                    else
                    {
                        // Truncated length (to ensure different paths from changing lengths in matching)
                        headerValueUtf16Latin1CrossOver = new string(headerValue.AsSpan().Slice(0, i + 1));
                    }

                    var headerName     = Encoding.ASCII.GetBytes(header.Name).AsSpan();
                    var latinValueSpan = Encoding.Latin1.GetBytes(headerValueUtf16Latin1CrossOver).AsSpan();

                    Assert.False(latinValueSpan.SequenceEqual(Encoding.ASCII.GetBytes(headerValueUtf16Latin1CrossOver)));

                    headers.Reset();
                    headers.Append(headerName, latinValueSpan);
                    headers.OnHeadersComplete();
                    var parsedHeaderValue1 = ((IHeaderDictionary)headers)[header.Name].ToString();

                    headers.Reset();
                    headers.Append(headerName, latinValueSpan);
                    headers.OnHeadersComplete();
                    var parsedHeaderValue2 = ((IHeaderDictionary)headers)[header.Name].ToString();

                    Assert.Equal(headerValueUtf16Latin1CrossOver, parsedHeaderValue1);
                    Assert.Equal(parsedHeaderValue1, parsedHeaderValue2);
                    Assert.NotSame(parsedHeaderValue1, parsedHeaderValue2);
                }

                // Reset back to Ascii
                headerValue[i] = 'a';
            }
        }
Пример #8
0
        public void MultiValueReuseEmptyAfterReset(bool reuseValue, KnownHeader header)
        {
            const string HeaderValue1 = "Hello1";
            const string HeaderValue2 = "Hello2";

            var headers    = new HttpRequestHeaders(reuseHeaderValues: reuseValue);
            var headerName = Encoding.ASCII.GetBytes(header.Name).AsSpan();
            var prevSpan1  = Encoding.UTF8.GetBytes(HeaderValue1).AsSpan();
            var prevSpan2  = Encoding.UTF8.GetBytes(HeaderValue2).AsSpan();

            headers.Append(headerName, prevSpan1);
            headers.Append(headerName, prevSpan2);
            headers.OnHeadersComplete();
            var prevHeaderValue = ((IHeaderDictionary)headers)[header.Name];

            Assert.Equal(2, prevHeaderValue.Count);

            Assert.NotEqual(string.Empty, prevHeaderValue.ToString());
            Assert.Single(headers);
            var count = headers.Count;

            Assert.Equal(1, count);

            headers.Reset();

            // Empty after reset
            var nextHeaderValue = ((IHeaderDictionary)headers)[header.Name].ToString();

            Assert.NotNull(nextHeaderValue);
            Assert.Equal(string.Empty, nextHeaderValue);
            Assert.Empty(headers);
            count = headers.Count;
            Assert.Equal(0, count);

            headers.OnHeadersComplete();

            // Still empty after complete
            nextHeaderValue = ((IHeaderDictionary)headers)[header.Name].ToString();

            Assert.NotNull(nextHeaderValue);
            Assert.Equal(string.Empty, nextHeaderValue);
            Assert.Empty(headers);
            count = headers.Count;
            Assert.Equal(0, count);
        }
Пример #9
0
        public void ValueReuseEmptyAfterReset(bool reuseValue, KnownHeader header)
        {
            const string HeaderValue = "Hello";

            var headers    = new HttpRequestHeaders(reuseHeaderValues: reuseValue);
            var headerName = Encoding.ASCII.GetBytes(header.Name).AsSpan();
            var prevSpan   = Encoding.UTF8.GetBytes(HeaderValue).AsSpan();

            headers.Append(headerName, prevSpan, checkForNewlineChars: false);
            headers.OnHeadersComplete();
            var prevHeaderValue = ((IHeaderDictionary)headers)[header.Name].ToString();

            Assert.NotNull(prevHeaderValue);
            Assert.NotEqual(string.Empty, prevHeaderValue);
            Assert.Equal(HeaderValue, prevHeaderValue);
            Assert.NotSame(HeaderValue, prevHeaderValue);
            Assert.Single(headers);
            var count = headers.Count;

            Assert.Equal(1, count);

            headers.Reset();

            // Empty after reset
            var nextHeaderValue = ((IHeaderDictionary)headers)[header.Name].ToString();

            Assert.NotNull(nextHeaderValue);
            Assert.Equal(string.Empty, nextHeaderValue);
            Assert.NotEqual(HeaderValue, nextHeaderValue);
            Assert.Empty(headers);
            count = headers.Count;
            Assert.Equal(0, count);

            headers.OnHeadersComplete();

            // Still empty after complete
            nextHeaderValue = ((IHeaderDictionary)headers)[header.Name].ToString();

            Assert.NotNull(nextHeaderValue);
            Assert.Equal(string.Empty, nextHeaderValue);
            Assert.NotEqual(HeaderValue, nextHeaderValue);
            Assert.Empty(headers);
            count = headers.Count;
            Assert.Equal(0, count);
        }
Пример #10
0
        public void ValueReuseMissingValuesClear(bool reuseValue, KnownHeader header)
        {
            const string HeaderValue1 = "Hello1";
            var          headers      = new HttpRequestHeaders(reuseHeaderValues: reuseValue);

            for (var i = 0; i < 6; i++)
            {
                var prevName = ChangeNameCase(header.Name, variant: i);
                var nextName = ChangeNameCase(header.Name, variant: i + 1);

                var values = GetHeaderValues(headers, prevName, nextName, HeaderValue1, nextValue: null);

                Assert.Equal(HeaderValue1, values.PrevHeaderValue);
                Assert.NotSame(HeaderValue1, values.PrevHeaderValue);

                Assert.Equal(string.Empty, values.NextHeaderValue);

                Assert.NotEqual(values.PrevHeaderValue, values.NextHeaderValue);
            }
        }
Пример #11
0
        public void ValueReuseChangedValuesOverwrite(bool reuseValue, KnownHeader header)
        {
            const string HeaderValue1 = "Hello1";
            const string HeaderValue2 = "Hello2";
            var          headers      = new HttpRequestHeaders(reuseHeaderValues: reuseValue);

            for (var i = 0; i < 6; i++)
            {
                var prevName = ChangeNameCase(header.Name, variant: i);
                var nextName = ChangeNameCase(header.Name, variant: i + 1);

                var values = GetHeaderValues(headers, prevName, nextName, HeaderValue1, HeaderValue2);

                Assert.Equal(HeaderValue1, values.PrevHeaderValue);
                Assert.NotSame(HeaderValue1, values.PrevHeaderValue);

                Assert.Equal(HeaderValue2, values.NextHeaderValue);
                Assert.NotSame(HeaderValue2, values.NextHeaderValue);

                Assert.NotEqual(values.PrevHeaderValue, values.NextHeaderValue);
            }
        }
Пример #12
0
        public void ValueReuseNeverWhenNotAscii(bool reuseValue, KnownHeader header)
        {
            const string HeaderValue = "Hello \u03a0";

            var headers = new HttpRequestHeaders(reuseHeaderValues: reuseValue);

            for (var i = 0; i < 6; i++)
            {
                var prevName = ChangeNameCase(header.Name, variant: i);
                var nextName = ChangeNameCase(header.Name, variant: i + 1);

                var values = GetHeaderValues(headers, prevName, nextName, HeaderValue, HeaderValue);

                Assert.Equal(HeaderValue, values.PrevHeaderValue);
                Assert.NotSame(HeaderValue, values.PrevHeaderValue);

                Assert.Equal(HeaderValue, values.NextHeaderValue);
                Assert.NotSame(HeaderValue, values.NextHeaderValue);

                Assert.Equal(values.PrevHeaderValue, values.NextHeaderValue);

                Assert.NotSame(values.PrevHeaderValue, values.NextHeaderValue);
            }
        }
Пример #13
0
 internal void SetValue(KnownHeader value)
 {
     valueString = value.String;
     ValueData   = value.String8;
 }
Пример #14
0
        // adapted from Header serialization code in Http2Connection.cs
        private static Memory <byte> HPackEncode(HttpHeaders headers)
        {
            var buffer = new ArrayBuffer(4);

            FillAvailableSpaceWithOnes(buffer);

            foreach (KeyValuePair <HeaderDescriptor, string[]> header in headers.GetHeaderDescriptorsAndValues())
            {
                KnownHeader knownHeader = header.Key.KnownHeader;
                if (knownHeader != null)
                {
                    // For all other known headers, send them via their pre-encoded name and the associated value.
                    WriteBytes(knownHeader.Http2EncodedName);
                    string separator = null;
                    if (header.Value.Length > 1)
                    {
                        HttpHeaderParser parser = header.Key.Parser;
                        if (parser != null && parser.SupportsMultipleValues)
                        {
                            separator = parser.Separator;
                        }
                        else
                        {
                            separator = HttpHeaderParser.DefaultSeparator;
                        }
                    }

                    WriteLiteralHeaderValues(header.Value, separator);
                }
                else
                {
                    // The header is not known: fall back to just encoding the header name and value(s).
                    WriteLiteralHeader(header.Key.Name, header.Value);
                }
            }

            return(buffer.ActiveMemory);

            void WriteBytes(ReadOnlySpan <byte> bytes)
            {
                if (bytes.Length > buffer.AvailableLength)
                {
                    buffer.EnsureAvailableSpace(bytes.Length);
                    FillAvailableSpaceWithOnes(buffer);
                }

                bytes.CopyTo(buffer.AvailableSpan);
                buffer.Commit(bytes.Length);
            }

            void WriteLiteralHeaderValues(string[] values, string separator)
            {
                int bytesWritten;

                while (!HPackEncoder.EncodeStringLiterals(values, separator, buffer.AvailableSpan, out bytesWritten))
                {
                    buffer.EnsureAvailableSpace(buffer.AvailableLength + 1);
                    FillAvailableSpaceWithOnes(buffer);
                }

                buffer.Commit(bytesWritten);
            }

            void WriteLiteralHeader(string name, string[] values)
            {
                int bytesWritten;

                while (!HPackEncoder.EncodeLiteralHeaderFieldWithoutIndexingNewName(name, values, HttpHeaderParser.DefaultSeparator, buffer.AvailableSpan, out bytesWritten))
                {
                    buffer.EnsureAvailableSpace(buffer.AvailableLength + 1);
                    FillAvailableSpaceWithOnes(buffer);
                }

                buffer.Commit(bytesWritten);
            }

            // force issues related to buffer not being zeroed out
            void FillAvailableSpaceWithOnes(ArrayBuffer buffer) => buffer.AvailableSpan.Fill(0xff);
        }
Пример #15
0
        // adapted from Header serialization code in Http2Connection.cs
        private static Memory <byte> HPackEncode(HttpHeaders headers, Encoding?valueEncoding)
        {
            var buffer = new ArrayBuffer(4);

            FillAvailableSpaceWithOnes(buffer);
            string[] headerValues = Array.Empty <string>();

            foreach (HeaderEntry header in headers.GetEntries())
            {
                int headerValuesCount = HttpHeaders.GetStoreValuesIntoStringArray(header.Key, header.Value, ref headerValues);
                Assert.InRange(headerValuesCount, 0, int.MaxValue);
                ReadOnlySpan <string> headerValuesSpan = headerValues.AsSpan(0, headerValuesCount);

                KnownHeader knownHeader = header.Key.KnownHeader;
                if (knownHeader != null)
                {
                    // For all other known headers, send them via their pre-encoded name and the associated value.
                    WriteBytes(knownHeader.Http2EncodedName);
                    string separator = null;
                    if (headerValuesSpan.Length > 1)
                    {
                        HttpHeaderParser parser = header.Key.Parser;
                        if (parser != null && parser.SupportsMultipleValues)
                        {
                            separator = parser.Separator;
                        }
                        else
                        {
                            separator = HttpHeaderParser.DefaultSeparator;
                        }
                    }

                    WriteLiteralHeaderValues(headerValuesSpan, separator);
                }
                else
                {
                    // The header is not known: fall back to just encoding the header name and value(s).
                    WriteLiteralHeader(header.Key.Name, headerValuesSpan);
                }
            }

            return(buffer.ActiveMemory);

            void WriteBytes(ReadOnlySpan <byte> bytes)
            {
                if (bytes.Length > buffer.AvailableLength)
                {
                    buffer.EnsureAvailableSpace(bytes.Length);
                    FillAvailableSpaceWithOnes(buffer);
                }

                bytes.CopyTo(buffer.AvailableSpan);
                buffer.Commit(bytes.Length);
            }

            void WriteLiteralHeaderValues(ReadOnlySpan <string> values, string separator)
            {
                int bytesWritten;

                while (!HPackEncoder.EncodeStringLiterals(values, separator, valueEncoding, buffer.AvailableSpan, out bytesWritten))
                {
                    buffer.EnsureAvailableSpace(buffer.AvailableLength + 1);
                    FillAvailableSpaceWithOnes(buffer);
                }

                buffer.Commit(bytesWritten);
            }

            void WriteLiteralHeader(string name, ReadOnlySpan <string> values)
            {
                int bytesWritten;

                while (!HPackEncoder.EncodeLiteralHeaderFieldWithoutIndexingNewName(name, values, HttpHeaderParser.DefaultSeparator, valueEncoding, buffer.AvailableSpan, out bytesWritten))
                {
                    buffer.EnsureAvailableSpace(buffer.AvailableLength + 1);
                    FillAvailableSpaceWithOnes(buffer);
                }

                buffer.Commit(bytesWritten);
            }

            // force issues related to buffer not being zeroed out
            void FillAvailableSpaceWithOnes(ArrayBuffer buffer) => buffer.AvailableSpan.Fill(0xff);
        }