diff --git a/src/Microsoft.AspNetCore.Server.Kestrel/Http/FrameHeaders.Generated.cs b/src/Microsoft.AspNetCore.Server.Kestrel/Http/FrameHeaders.Generated.cs index 3d9ccbaf3c..bf47376156 100644 --- a/src/Microsoft.AspNetCore.Server.Kestrel/Http/FrameHeaders.Generated.cs +++ b/src/Microsoft.AspNetCore.Server.Kestrel/Http/FrameHeaders.Generated.cs @@ -2038,6 +2038,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http } protected override void SetValueFast(string key, StringValues value) { + switch (key.Length) { case 13: @@ -2424,10 +2425,12 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http } break; } + Unknown[key] = value; } protected override void AddValueFast(string key, StringValues value) { + switch (key.Length) { case 13: @@ -2990,6 +2993,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http } break; } + Unknown.Add(key, value); } protected override bool RemoveFast(string key) @@ -7181,6 +7185,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http } protected override void SetValueFast(string key, StringValues value) { + ValidateHeaderCharacters(value); switch (key.Length) { case 13: @@ -7512,10 +7517,12 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http } break; } + ValidateHeaderCharacters(key); Unknown[key] = value; } protected override void AddValueFast(string key, StringValues value) { + ValidateHeaderCharacters(value); switch (key.Length) { case 13: @@ -7991,6 +7998,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http } break; } + ValidateHeaderCharacters(key); Unknown.Add(key, value); } protected override bool RemoveFast(string key) diff --git a/src/Microsoft.AspNetCore.Server.Kestrel/Http/FrameHeaders.cs b/src/Microsoft.AspNetCore.Server.Kestrel/Http/FrameHeaders.cs index 596b973575..4bba23dbd3 100644 --- a/src/Microsoft.AspNetCore.Server.Kestrel/Http/FrameHeaders.cs +++ b/src/Microsoft.AspNetCore.Server.Kestrel/Http/FrameHeaders.cs @@ -39,21 +39,18 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http { get { + // Unlike the IHeaderDictionary version, this getter will throw a KeyNotFoundException. return GetValueFast(key); } set { - if (_isReadOnly) - { - ThrowReadOnlyException(); - } - SetValueFast(key, value); + ((IHeaderDictionary)this)[key] = value; } } protected void ThrowReadOnlyException() { - throw new InvalidOperationException("Headers are readonly, reponse has already started."); + throw new InvalidOperationException("Headers are read-only, response has already started."); } protected void ThrowArgumentException() @@ -140,11 +137,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http void ICollection>.Add(KeyValuePair item) { - if (_isReadOnly) - { - ThrowReadOnlyException(); - } - AddValueFast(item.Key, item.Value); + ((IDictionary)this).Add(item.Key, item.Value); } void IDictionary.Add(string key, StringValues value) @@ -216,5 +209,27 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http { return TryGetValueFast(key, out value); } + + public static void ValidateHeaderCharacters(StringValues headerValues) + { + foreach (var value in headerValues) + { + ValidateHeaderCharacters(value); + } + } + + public static void ValidateHeaderCharacters(string headerCharacters) + { + if (headerCharacters != null) + { + foreach (var ch in headerCharacters) + { + if (ch < 0x20) + { + throw new InvalidOperationException(string.Format("Invalid control character in header: 0x{0:X2}", (byte)ch)); + } + } + } + } } } diff --git a/tools/Microsoft.AspNetCore.Server.Kestrel.GeneratedCode/KnownHeaders.cs b/tools/Microsoft.AspNetCore.Server.Kestrel.GeneratedCode/KnownHeaders.cs index 9f47fdfae0..a51039d540 100644 --- a/tools/Microsoft.AspNetCore.Server.Kestrel.GeneratedCode/KnownHeaders.cs +++ b/tools/Microsoft.AspNetCore.Server.Kestrel.GeneratedCode/KnownHeaders.cs @@ -299,6 +299,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http }} protected override void SetValueFast(string key, StringValues value) {{ + {(loop.ClassName == "FrameResponseHeaders" ? "ValidateHeaderCharacters(value);" : "")} switch (key.Length) {{{Each(loop.HeadersByLength, byLength => $@" case {byLength.Key}: @@ -313,10 +314,12 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http ")}}} break; ")}}} + {(loop.ClassName == "FrameResponseHeaders" ? "ValidateHeaderCharacters(key);" : "")} Unknown[key] = value; }} protected override void AddValueFast(string key, StringValues value) {{ + {(loop.ClassName == "FrameResponseHeaders" ? "ValidateHeaderCharacters(value);" : "")} switch (key.Length) {{{Each(loop.HeadersByLength, byLength => $@" case {byLength.Key}: @@ -335,6 +338,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Http ")}}} break; ")}}} + {(loop.ClassName == "FrameResponseHeaders" ? "ValidateHeaderCharacters(key);" : "")} Unknown.Add(key, value); }} protected override bool RemoveFast(string key)