// 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; using Microsoft.AspNetCore.Server.Kestrel.Internal; using Microsoft.AspNetCore.Server.Kestrel.Internal.Http; using Microsoft.Extensions.Primitives; using Xunit; namespace Microsoft.AspNetCore.Server.KestrelTests { public class FrameResponseHeadersTests { [Fact] public void InitialDictionaryIsEmpty() { var serverOptions = new KestrelServerOptions(); var serviceContext = new ServiceContext { DateHeaderValueManager = new DateHeaderValueManager(), ServerOptions = serverOptions }; var listenerContext = new ListenerContext(serviceContext) { ServerAddress = ServerAddress.FromUrl("http://localhost:5000") }; var connectionContext = new ConnectionContext(listenerContext); var frame = new Frame(application: null, context: connectionContext); frame.InitializeHeaders(); IDictionary headers = frame.ResponseHeaders; Assert.Equal(0, headers.Count); Assert.False(headers.IsReadOnly); } [Theory] [InlineData("Server", "\r\nData")] [InlineData("Server", "\0Data")] [InlineData("Server", "Data\r")] [InlineData("Server", "Da\0ta")] [InlineData("Server", "Da\u001Fta")] [InlineData("Unknown-Header", "\r\nData")] [InlineData("Unknown-Header", "\0Data")] [InlineData("Unknown-Header", "Data\0")] [InlineData("Unknown-Header", "Da\nta")] [InlineData("\r\nServer", "Data")] [InlineData("Server\r", "Data")] [InlineData("Ser\0ver", "Data")] [InlineData("Server\r\n", "Data")] [InlineData("\u0000Server", "Data")] [InlineData("Server", "Data\u0000")] [InlineData("\u001FServer", "Data")] [InlineData("Unknown-Header\r\n", "Data")] [InlineData("\0Unknown-Header", "Data")] [InlineData("Unknown\r-Header", "Data")] [InlineData("Unk\nown-Header", "Data")] [InlineData("Server", "Da\u007Fta")] [InlineData("Unknown\u007F-Header", "Data")] [InlineData("Ser\u0080ver", "Data")] [InlineData("Server", "Da\u0080ta")] [InlineData("Unknown\u0080-Header", "Data")] [InlineData("Ser™ver", "Data")] [InlineData("Server", "Da™ta")] [InlineData("Unknown™-Header", "Data")] [InlineData("Ser™ver", "Data")] [InlineData("šerver", "Data")] [InlineData("Server", "Dašta")] [InlineData("Unknownš-Header", "Data")] [InlineData("Seršver", "Data")] public void AddingControlOrNonAsciiCharactersToHeadersThrows(string key, string value) { var responseHeaders = new FrameResponseHeaders(); Assert.Throws(() => { ((IHeaderDictionary)responseHeaders)[key] = value; }); Assert.Throws(() => { ((IHeaderDictionary)responseHeaders)[key] = new StringValues(new[] { "valid", value }); }); Assert.Throws(() => { ((IDictionary)responseHeaders)[key] = value; }); Assert.Throws(() => { var kvp = new KeyValuePair(key, value); ((ICollection>)responseHeaders).Add(kvp); }); Assert.Throws(() => { var kvp = new KeyValuePair(key, value); ((IDictionary)responseHeaders).Add(key, value); }); } [Fact] public void ThrowsWhenAddingHeaderAfterReadOnlyIsSet() { var headers = new FrameResponseHeaders(); headers.SetReadOnly(); Assert.Throws(() => ((IDictionary)headers).Add("my-header", new[] { "value" })); } [Fact] public void ThrowsWhenChangingHeaderAfterReadOnlyIsSet() { var headers = new FrameResponseHeaders(); var dictionary = (IDictionary)headers; dictionary.Add("my-header", new[] { "value" }); headers.SetReadOnly(); Assert.Throws(() => dictionary["my-header"] = "other-value"); } [Fact] public void ThrowsWhenRemovingHeaderAfterReadOnlyIsSet() { var headers = new FrameResponseHeaders(); var dictionary = (IDictionary)headers; dictionary.Add("my-header", new[] { "value" }); headers.SetReadOnly(); Assert.Throws(() => dictionary.Remove("my-header")); } [Fact] public void ThrowsWhenClearingHeadersAfterReadOnlyIsSet() { var headers = new FrameResponseHeaders(); var dictionary = (IDictionary)headers; dictionary.Add("my-header", new[] { "value" }); headers.SetReadOnly(); Assert.Throws(() => dictionary.Clear()); } [Fact] public void ThrowsWhenAddingContentLengthWithNonNumericValue() { var headers = new FrameResponseHeaders(); var dictionary = (IDictionary)headers; Assert.Throws(() => dictionary.Add("Content-Length", new[] { "bad" })); } [Fact] public void ThrowsWhenSettingContentLengthToNonNumericValue() { var headers = new FrameResponseHeaders(); var dictionary = (IDictionary)headers; Assert.Throws(() => ((IHeaderDictionary)headers)["Content-Length"] = "bad"); } [Fact] public void ThrowsWhenSettingRawContentLengthToNonNumericValue() { var headers = new FrameResponseHeaders(); Assert.Throws(() => headers.SetRawContentLength("bad", Encoding.ASCII.GetBytes("bad"))); } [Fact] public void ThrowsWhenAssigningHeaderContentLengthToNonNumericValue() { var headers = new FrameResponseHeaders(); Assert.Throws(() => headers.HeaderContentLength = "bad"); } [Fact] public void ContentLengthValueCanBeReadAsLongAfterAddingHeader() { var headers = new FrameResponseHeaders(); var dictionary = (IDictionary)headers; dictionary.Add("Content-Length", "42"); Assert.Equal(42, headers.HeaderContentLengthValue); } [Fact] public void ContentLengthValueCanBeReadAsLongAfterSettingHeader() { var headers = new FrameResponseHeaders(); var dictionary = (IDictionary)headers; dictionary["Content-Length"] = "42"; Assert.Equal(42, headers.HeaderContentLengthValue); } [Fact] public void ContentLengthValueCanBeReadAsLongAfterSettingRawHeader() { var headers = new FrameResponseHeaders(); headers.SetRawContentLength("42", Encoding.ASCII.GetBytes("42")); Assert.Equal(42, headers.HeaderContentLengthValue); } [Fact] public void ContentLengthValueCanBeReadAsLongAfterAssigningHeader() { var headers = new FrameResponseHeaders(); headers.HeaderContentLength = "42"; Assert.Equal(42, headers.HeaderContentLengthValue); } [Fact] public void ContentLengthValueClearedWhenHeaderIsRemoved() { var headers = new FrameResponseHeaders(); headers.HeaderContentLength = "42"; var dictionary = (IDictionary)headers; dictionary.Remove("Content-Length"); Assert.Equal(null, headers.HeaderContentLengthValue); } [Fact] public void ContentLengthValueClearedWhenHeadersCleared() { var headers = new FrameResponseHeaders(); headers.HeaderContentLength = "42"; var dictionary = (IDictionary)headers; dictionary.Clear(); Assert.Equal(null, headers.HeaderContentLengthValue); } } }