// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.Text; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Server.Kestrel.Core; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http; using Microsoft.Extensions.Primitives; using Xunit; namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests { public class FrameRequestHeadersTests { [Fact] public void InitialDictionaryIsEmpty() { IDictionary headers = new FrameRequestHeaders(); Assert.Equal(0, headers.Count); Assert.False(headers.IsReadOnly); } [Fact] public void SettingUnknownHeadersWorks() { IDictionary headers = new FrameRequestHeaders(); headers["custom"] = new[] { "value" }; Assert.NotNull(headers["custom"]); Assert.Equal(1, headers["custom"].Count); Assert.Equal("value", headers["custom"][0]); } [Fact] public void SettingKnownHeadersWorks() { IDictionary headers = new FrameRequestHeaders(); headers["host"] = new[] { "value" }; headers["content-length"] = new[] { "0" }; Assert.NotNull(headers["host"]); Assert.NotNull(headers["content-length"]); Assert.Equal(1, headers["host"].Count); Assert.Equal(1, headers["content-length"].Count); Assert.Equal("value", headers["host"][0]); Assert.Equal("0", headers["content-length"][0]); } [Fact] public void KnownAndCustomHeaderCountAddedTogether() { IDictionary headers = new FrameRequestHeaders(); headers["host"] = new[] { "value" }; headers["custom"] = new[] { "value" }; headers["Content-Length"] = new[] { "0" }; Assert.Equal(3, headers.Count); } [Fact] public void TryGetValueWorksForKnownAndUnknownHeaders() { IDictionary headers = new FrameRequestHeaders(); StringValues value; Assert.False(headers.TryGetValue("host", out value)); Assert.False(headers.TryGetValue("custom", out value)); Assert.False(headers.TryGetValue("Content-Length", out value)); headers["host"] = new[] { "value" }; Assert.True(headers.TryGetValue("host", out value)); Assert.False(headers.TryGetValue("custom", out value)); Assert.False(headers.TryGetValue("Content-Length", out value)); headers["custom"] = new[] { "value" }; Assert.True(headers.TryGetValue("host", out value)); Assert.True(headers.TryGetValue("custom", out value)); Assert.False(headers.TryGetValue("Content-Length", out value)); headers["Content-Length"] = new[] { "0" }; Assert.True(headers.TryGetValue("host", out value)); Assert.True(headers.TryGetValue("custom", out value)); Assert.True(headers.TryGetValue("Content-Length", out value)); } [Fact] public void SameExceptionThrownForMissingKey() { IDictionary headers = new FrameRequestHeaders(); Assert.Throws(() => headers["custom"]); Assert.Throws(() => headers["host"]); Assert.Throws(() => headers["Content-Length"]); } [Fact] public void EntriesCanBeEnumerated() { IDictionary headers = new FrameRequestHeaders(); var v1 = new[] { "localhost" }; var v2 = new[] { "0" }; var v3 = new[] { "value" }; headers["host"] = v1; headers["Content-Length"] = v2; headers["custom"] = v3; Assert.Equal( new[] { new KeyValuePair("Host", v1), new KeyValuePair("Content-Length", v2), new KeyValuePair("custom", v3), }, headers); } [Fact] public void KeysAndValuesCanBeEnumerated() { IDictionary headers = new FrameRequestHeaders(); StringValues v1 = new[] { "localhost" }; StringValues v2 = new[] { "0" }; StringValues v3 = new[] { "value" }; headers["host"] = v1; headers["Content-Length"] = v2; headers["custom"] = v3; Assert.Equal( new[] { "Host", "Content-Length", "custom" }, headers.Keys); Assert.Equal( new[] { v1, v2, v3 }, headers.Values); } [Fact] public void ContainsAndContainsKeyWork() { IDictionary headers = new FrameRequestHeaders(); var kv1 = new KeyValuePair("host", new[] { "localhost" }); var kv2 = new KeyValuePair("custom", new[] { "value" }); var kv3 = new KeyValuePair("Content-Length", new[] { "0" }); var kv1b = new KeyValuePair("host", new[] { "not-localhost" }); var kv2b = new KeyValuePair("custom", new[] { "not-value" }); var kv3b = new KeyValuePair("Content-Length", new[] { "1" }); Assert.False(headers.ContainsKey("host")); Assert.False(headers.ContainsKey("custom")); Assert.False(headers.ContainsKey("Content-Length")); Assert.False(headers.Contains(kv1)); Assert.False(headers.Contains(kv2)); Assert.False(headers.Contains(kv3)); headers["host"] = kv1.Value; Assert.True(headers.ContainsKey("host")); Assert.False(headers.ContainsKey("custom")); Assert.False(headers.ContainsKey("Content-Length")); Assert.True(headers.Contains(kv1)); Assert.False(headers.Contains(kv2)); Assert.False(headers.Contains(kv3)); Assert.False(headers.Contains(kv1b)); Assert.False(headers.Contains(kv2b)); Assert.False(headers.Contains(kv3b)); headers["custom"] = kv2.Value; Assert.True(headers.ContainsKey("host")); Assert.True(headers.ContainsKey("custom")); Assert.False(headers.ContainsKey("Content-Length")); Assert.True(headers.Contains(kv1)); Assert.True(headers.Contains(kv2)); Assert.False(headers.Contains(kv3)); Assert.False(headers.Contains(kv1b)); Assert.False(headers.Contains(kv2b)); Assert.False(headers.Contains(kv3b)); headers["Content-Length"] = kv3.Value; Assert.True(headers.ContainsKey("host")); Assert.True(headers.ContainsKey("custom")); Assert.True(headers.ContainsKey("Content-Length")); Assert.True(headers.Contains(kv1)); Assert.True(headers.Contains(kv2)); Assert.True(headers.Contains(kv3)); Assert.False(headers.Contains(kv1b)); Assert.False(headers.Contains(kv2b)); Assert.False(headers.Contains(kv3b)); } [Fact] public void AddWorksLikeSetAndThrowsIfKeyExists() { IDictionary headers = new FrameRequestHeaders(); StringValues value; Assert.False(headers.TryGetValue("host", out value)); Assert.False(headers.TryGetValue("custom", out value)); Assert.False(headers.TryGetValue("Content-Length", out value)); headers.Add("host", new[] { "localhost" }); headers.Add("custom", new[] { "value" }); headers.Add("Content-Length", new[] { "0" }); Assert.True(headers.TryGetValue("host", out value)); Assert.True(headers.TryGetValue("custom", out value)); Assert.True(headers.TryGetValue("Content-Length", out value)); Assert.Throws(() => headers.Add("host", new[] { "localhost" })); Assert.Throws(() => headers.Add("custom", new[] { "value" })); Assert.Throws(() => headers.Add("Content-Length", new[] { "0" })); Assert.True(headers.TryGetValue("host", out value)); Assert.True(headers.TryGetValue("custom", out value)); Assert.True(headers.TryGetValue("Content-Length", out value)); } [Fact] public void ClearRemovesAllHeaders() { IDictionary headers = new FrameRequestHeaders(); headers.Add("host", new[] { "localhost" }); headers.Add("custom", new[] { "value" }); headers.Add("Content-Length", new[] { "0" }); StringValues value; Assert.Equal(3, headers.Count); Assert.True(headers.TryGetValue("host", out value)); Assert.True(headers.TryGetValue("custom", out value)); Assert.True(headers.TryGetValue("Content-Length", out value)); headers.Clear(); Assert.Equal(0, headers.Count); Assert.False(headers.TryGetValue("host", out value)); Assert.False(headers.TryGetValue("custom", out value)); Assert.False(headers.TryGetValue("Content-Length", out value)); } [Fact] public void RemoveTakesHeadersOutOfDictionary() { IDictionary headers = new FrameRequestHeaders(); headers.Add("host", new[] { "localhost" }); headers.Add("custom", new[] { "value" }); headers.Add("Content-Length", new[] { "0" }); StringValues value; Assert.Equal(3, headers.Count); Assert.True(headers.TryGetValue("host", out value)); Assert.True(headers.TryGetValue("custom", out value)); Assert.True(headers.TryGetValue("Content-Length", out value)); Assert.True(headers.Remove("host")); Assert.False(headers.Remove("host")); Assert.Equal(2, headers.Count); Assert.False(headers.TryGetValue("host", out value)); Assert.True(headers.TryGetValue("custom", out value)); Assert.True(headers.Remove("custom")); Assert.False(headers.Remove("custom")); Assert.Equal(1, headers.Count); Assert.False(headers.TryGetValue("host", out value)); Assert.False(headers.TryGetValue("custom", out value)); Assert.True(headers.TryGetValue("Content-Length", out value)); Assert.True(headers.Remove("Content-Length")); Assert.False(headers.Remove("Content-Length")); Assert.Equal(0, headers.Count); Assert.False(headers.TryGetValue("host", out value)); Assert.False(headers.TryGetValue("custom", out value)); Assert.False(headers.TryGetValue("Content-Length", out value)); } [Fact] public void CopyToMovesDataIntoArray() { IDictionary headers = new FrameRequestHeaders(); headers.Add("host", new[] { "localhost" }); headers.Add("Content-Length", new[] { "0" }); headers.Add("custom", new[] { "value" }); var entries = new KeyValuePair[5]; headers.CopyTo(entries, 1); Assert.Null(entries[0].Key); Assert.Equal(new StringValues(), entries[0].Value); Assert.Equal("Host", entries[1].Key); Assert.Equal(new[] { "localhost" }, entries[1].Value); Assert.Equal("Content-Length", entries[2].Key); Assert.Equal(new[] { "0" }, entries[2].Value); Assert.Equal("custom", entries[3].Key); Assert.Equal(new[] { "value" }, entries[3].Value); Assert.Null(entries[4].Key); Assert.Equal(new StringValues(), entries[4].Value); } [Fact] public void AppendThrowsWhenHeaderNameContainsNonASCIICharacters() { var headers = new FrameRequestHeaders(); const string key = "\u00141ód\017c"; var encoding = Encoding.GetEncoding("iso-8859-1"); var exception = Assert.Throws( () => headers.Append(encoding.GetBytes(key), "value")); Assert.Equal(StatusCodes.Status400BadRequest, exception.StatusCode); } } }