Add custom request header decoder API to Kestrel (#23233)
This commit is contained in:
parent
bfbb8b0159
commit
b446ab7c6f
|
|
@ -137,6 +137,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core
|
|||
public bool DisableStringReuse { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } }
|
||||
public bool EnableAltSvc { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } }
|
||||
public Microsoft.AspNetCore.Server.Kestrel.Core.KestrelServerLimits Limits { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } }
|
||||
public System.Func<string, System.Text.Encoding> RequestHeaderEncodingSelector { get { throw null; } set { } }
|
||||
public Microsoft.AspNetCore.Server.Kestrel.KestrelConfigurationLoader Configure() { throw null; }
|
||||
public Microsoft.AspNetCore.Server.Kestrel.KestrelConfigurationLoader Configure(Microsoft.Extensions.Configuration.IConfiguration config) { throw null; }
|
||||
public Microsoft.AspNetCore.Server.Kestrel.KestrelConfigurationLoader Configure(Microsoft.Extensions.Configuration.IConfiguration config, bool reloadOnChange) { throw null; }
|
||||
|
|
|
|||
|
|
@ -18,14 +18,12 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
|
|||
private const string EndpointDefaultsKey = "EndpointDefaults";
|
||||
private const string EndpointsKey = "Endpoints";
|
||||
private const string UrlKey = "Url";
|
||||
private const string Latin1RequestHeadersKey = "Latin1RequestHeaders";
|
||||
|
||||
private readonly IConfiguration _configuration;
|
||||
|
||||
private IDictionary<string, CertificateConfig> _certificates;
|
||||
private EndpointDefaults _endpointDefaults;
|
||||
private IEnumerable<EndpointConfig> _endpoints;
|
||||
private bool? _latin1RequestHeaders;
|
||||
|
||||
public ConfigurationReader(IConfiguration configuration)
|
||||
{
|
||||
|
|
@ -35,7 +33,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
|
|||
public IDictionary<string, CertificateConfig> Certificates => _certificates ??= ReadCertificates();
|
||||
public EndpointDefaults EndpointDefaults => _endpointDefaults ??= ReadEndpointDefaults();
|
||||
public IEnumerable<EndpointConfig> Endpoints => _endpoints ??= ReadEndpoints();
|
||||
public bool Latin1RequestHeaders => _latin1RequestHeaders ??= _configuration.GetValue<bool>(Latin1RequestHeadersKey);
|
||||
|
||||
private IDictionary<string, CertificateConfig> ReadCertificates()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -6270,6 +6270,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
|
|||
public unsafe void Append(ReadOnlySpan<byte> name, ReadOnlySpan<byte> value)
|
||||
{
|
||||
ref byte nameStart = ref MemoryMarshal.GetReference(name);
|
||||
var nameStr = string.Empty;
|
||||
ref StringValues values = ref Unsafe.AsRef<StringValues>(null);
|
||||
var flag = 0L;
|
||||
|
||||
|
|
@ -6281,6 +6282,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
|
|||
{
|
||||
flag = 0x20000000000L;
|
||||
values = ref _headers._TE;
|
||||
nameStr = HeaderNames.TE;
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
|
|
@ -6289,11 +6291,13 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
|
|||
{
|
||||
flag = 0x100000000000L;
|
||||
values = ref _headers._DNT;
|
||||
nameStr = HeaderNames.DNT;
|
||||
}
|
||||
else if ((firstTerm3 == 0x4956u) && ((Unsafe.AddByteOffset(ref nameStart, (IntPtr)2) & 0xdfu) == 0x41u))
|
||||
{
|
||||
flag = 0x100L;
|
||||
values = ref _headers._Via;
|
||||
nameStr = HeaderNames.Via;
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
|
|
@ -6302,16 +6306,19 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
|
|||
{
|
||||
flag = 0x80000000L;
|
||||
values = ref _headers._Host;
|
||||
nameStr = HeaderNames.Host;
|
||||
}
|
||||
else if ((firstTerm4 == 0x45544144u))
|
||||
{
|
||||
flag = 0x4L;
|
||||
values = ref _headers._Date;
|
||||
nameStr = HeaderNames.Date;
|
||||
}
|
||||
else if ((firstTerm4 == 0x4d4f5246u))
|
||||
{
|
||||
flag = 0x40000000L;
|
||||
values = ref _headers._From;
|
||||
nameStr = HeaderNames.From;
|
||||
}
|
||||
break;
|
||||
case 5:
|
||||
|
|
@ -6319,16 +6326,19 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
|
|||
{
|
||||
flag = 0x200000L;
|
||||
values = ref _headers._Path;
|
||||
nameStr = HeaderNames.Path;
|
||||
}
|
||||
else if (((Unsafe.ReadUnaligned<uint>(ref nameStart) & 0xdfdfdfdfu) == 0x4f4c4c41u) && ((Unsafe.AddByteOffset(ref nameStart, (IntPtr)4) & 0xdfu) == 0x57u))
|
||||
{
|
||||
flag = 0x400L;
|
||||
values = ref _headers._Allow;
|
||||
nameStr = HeaderNames.Allow;
|
||||
}
|
||||
else if (((Unsafe.ReadUnaligned<uint>(ref nameStart) & 0xdfdfdfdfu) == 0x474e4152u) && ((Unsafe.AddByteOffset(ref nameStart, (IntPtr)4) & 0xdfu) == 0x45u))
|
||||
{
|
||||
flag = 0x10000000000L;
|
||||
values = ref _headers._Range;
|
||||
nameStr = HeaderNames.Range;
|
||||
}
|
||||
break;
|
||||
case 6:
|
||||
|
|
@ -6337,26 +6347,31 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
|
|||
{
|
||||
flag = 0x800000L;
|
||||
values = ref _headers._Accept;
|
||||
nameStr = HeaderNames.Accept;
|
||||
}
|
||||
else if ((firstTerm6 == 0x4b4f4f43u) && ((Unsafe.ReadUnaligned<ushort>(ref Unsafe.AddByteOffset(ref nameStart, (IntPtr)(2 * sizeof(ushort)))) & 0xdfdfu) == 0x4549u))
|
||||
{
|
||||
flag = 0x10000000L;
|
||||
values = ref _headers._Cookie;
|
||||
nameStr = HeaderNames.Cookie;
|
||||
}
|
||||
else if ((firstTerm6 == 0x45505845u) && ((Unsafe.ReadUnaligned<ushort>(ref Unsafe.AddByteOffset(ref nameStart, (IntPtr)(2 * sizeof(ushort)))) & 0xdfdfu) == 0x5443u))
|
||||
{
|
||||
flag = 0x20000000L;
|
||||
values = ref _headers._Expect;
|
||||
nameStr = HeaderNames.Expect;
|
||||
}
|
||||
else if ((firstTerm6 == 0x4749524fu) && ((Unsafe.ReadUnaligned<ushort>(ref Unsafe.AddByteOffset(ref nameStart, (IntPtr)(2 * sizeof(ushort)))) & 0xdfdfu) == 0x4e49u))
|
||||
{
|
||||
flag = 0x4000000000000L;
|
||||
values = ref _headers._Origin;
|
||||
nameStr = HeaderNames.Origin;
|
||||
}
|
||||
else if ((firstTerm6 == 0x47415250u) && ((Unsafe.ReadUnaligned<ushort>(ref Unsafe.AddByteOffset(ref nameStart, (IntPtr)(2 * sizeof(ushort)))) & 0xdfdfu) == 0x414du))
|
||||
{
|
||||
flag = 0x10L;
|
||||
values = ref _headers._Pragma;
|
||||
nameStr = HeaderNames.Pragma;
|
||||
}
|
||||
break;
|
||||
case 7:
|
||||
|
|
@ -6364,36 +6379,43 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
|
|||
{
|
||||
flag = 0x100000L;
|
||||
values = ref _headers._Method;
|
||||
nameStr = HeaderNames.Method;
|
||||
}
|
||||
else if (((Unsafe.ReadUnaligned<uint>(ref nameStart) & 0xdfdfdfffu) == 0x4843533au) && ((Unsafe.ReadUnaligned<ushort>(ref Unsafe.AddByteOffset(ref nameStart, (IntPtr)(2 * sizeof(ushort)))) & 0xdfdfu) == 0x4d45u) && ((Unsafe.AddByteOffset(ref nameStart, (IntPtr)6) & 0xdfu) == 0x45u))
|
||||
{
|
||||
flag = 0x400000L;
|
||||
values = ref _headers._Scheme;
|
||||
nameStr = HeaderNames.Scheme;
|
||||
}
|
||||
else if (((Unsafe.ReadUnaligned<uint>(ref nameStart) & 0xdfdfdfdfu) == 0x49505845u) && ((Unsafe.ReadUnaligned<ushort>(ref Unsafe.AddByteOffset(ref nameStart, (IntPtr)(2 * sizeof(ushort)))) & 0xdfdfu) == 0x4552u) && ((Unsafe.AddByteOffset(ref nameStart, (IntPtr)6) & 0xdfu) == 0x53u))
|
||||
{
|
||||
flag = 0x20000L;
|
||||
values = ref _headers._Expires;
|
||||
nameStr = HeaderNames.Expires;
|
||||
}
|
||||
else if (((Unsafe.ReadUnaligned<uint>(ref nameStart) & 0xdfdfdfdfu) == 0x45464552u) && ((Unsafe.ReadUnaligned<ushort>(ref Unsafe.AddByteOffset(ref nameStart, (IntPtr)(2 * sizeof(ushort)))) & 0xdfdfu) == 0x4552u) && ((Unsafe.AddByteOffset(ref nameStart, (IntPtr)6) & 0xdfu) == 0x52u))
|
||||
{
|
||||
flag = 0x8000000000L;
|
||||
values = ref _headers._Referer;
|
||||
nameStr = HeaderNames.Referer;
|
||||
}
|
||||
else if (((Unsafe.ReadUnaligned<uint>(ref nameStart) & 0xdfdfdfdfu) == 0x49415254u) && ((Unsafe.ReadUnaligned<ushort>(ref Unsafe.AddByteOffset(ref nameStart, (IntPtr)(2 * sizeof(ushort)))) & 0xdfdfu) == 0x454cu) && ((Unsafe.AddByteOffset(ref nameStart, (IntPtr)6) & 0xdfu) == 0x52u))
|
||||
{
|
||||
flag = 0x20L;
|
||||
values = ref _headers._Trailer;
|
||||
nameStr = HeaderNames.Trailer;
|
||||
}
|
||||
else if (((Unsafe.ReadUnaligned<uint>(ref nameStart) & 0xdfdfdfdfu) == 0x52475055u) && ((Unsafe.ReadUnaligned<ushort>(ref Unsafe.AddByteOffset(ref nameStart, (IntPtr)(2 * sizeof(ushort)))) & 0xdfdfu) == 0x4441u) && ((Unsafe.AddByteOffset(ref nameStart, (IntPtr)6) & 0xdfu) == 0x45u))
|
||||
{
|
||||
flag = 0x80L;
|
||||
values = ref _headers._Upgrade;
|
||||
nameStr = HeaderNames.Upgrade;
|
||||
}
|
||||
else if (((Unsafe.ReadUnaligned<uint>(ref nameStart) & 0xdfdfdfdfu) == 0x4e524157u) && ((Unsafe.ReadUnaligned<ushort>(ref Unsafe.AddByteOffset(ref nameStart, (IntPtr)(2 * sizeof(ushort)))) & 0xdfdfu) == 0x4e49u) && ((Unsafe.AddByteOffset(ref nameStart, (IntPtr)6) & 0xdfu) == 0x47u))
|
||||
{
|
||||
flag = 0x200L;
|
||||
values = ref _headers._Warning;
|
||||
nameStr = HeaderNames.Warning;
|
||||
}
|
||||
break;
|
||||
case 8:
|
||||
|
|
@ -6402,11 +6424,13 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
|
|||
{
|
||||
flag = 0x100000000L;
|
||||
values = ref _headers._IfMatch;
|
||||
nameStr = HeaderNames.IfMatch;
|
||||
}
|
||||
else if ((firstTerm8 == 0x45474e41522d4649uL))
|
||||
{
|
||||
flag = 0x800000000L;
|
||||
values = ref _headers._IfRange;
|
||||
nameStr = HeaderNames.IfRange;
|
||||
}
|
||||
break;
|
||||
case 9:
|
||||
|
|
@ -6414,6 +6438,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
|
|||
{
|
||||
flag = 0x40000000000L;
|
||||
values = ref _headers._Translate;
|
||||
nameStr = HeaderNames.Translate;
|
||||
}
|
||||
break;
|
||||
case 10:
|
||||
|
|
@ -6421,31 +6446,37 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
|
|||
{
|
||||
flag = 0x80000L;
|
||||
values = ref _headers._Authority;
|
||||
nameStr = HeaderNames.Authority;
|
||||
}
|
||||
else if (((Unsafe.ReadUnaligned<ulong>(ref nameStart) & 0xdfdfdfdfdfdfdfdfuL) == 0x495443454e4e4f43uL) && ((Unsafe.ReadUnaligned<ushort>(ref Unsafe.AddByteOffset(ref nameStart, (IntPtr)(4 * sizeof(ushort)))) & 0xdfdfu) == 0x4e4fu))
|
||||
{
|
||||
flag = 0x2L;
|
||||
values = ref _headers._Connection;
|
||||
nameStr = HeaderNames.Connection;
|
||||
}
|
||||
else if (((Unsafe.ReadUnaligned<ulong>(ref nameStart) & 0xdfdfdfffdfdfdfdfuL) == 0x4547412d52455355uL) && ((Unsafe.ReadUnaligned<ushort>(ref Unsafe.AddByteOffset(ref nameStart, (IntPtr)(4 * sizeof(ushort)))) & 0xdfdfu) == 0x544eu))
|
||||
{
|
||||
flag = 0x80000000000L;
|
||||
values = ref _headers._UserAgent;
|
||||
nameStr = HeaderNames.UserAgent;
|
||||
}
|
||||
else if (((Unsafe.ReadUnaligned<ulong>(ref nameStart) & 0xdfdfdfffdfdfdfdfuL) == 0x494c412d5045454buL) && ((Unsafe.ReadUnaligned<ushort>(ref Unsafe.AddByteOffset(ref nameStart, (IntPtr)(4 * sizeof(ushort)))) & 0xdfdfu) == 0x4556u))
|
||||
{
|
||||
flag = 0x8L;
|
||||
values = ref _headers._KeepAlive;
|
||||
nameStr = HeaderNames.KeepAlive;
|
||||
}
|
||||
else if (((Unsafe.ReadUnaligned<ulong>(ref nameStart) & 0xffdfdfdfdfdfdfdfuL) == 0x2d54534555514552uL) && ((Unsafe.ReadUnaligned<ushort>(ref Unsafe.AddByteOffset(ref nameStart, (IntPtr)(4 * sizeof(ushort)))) & 0xdfdfu) == 0x4449u))
|
||||
{
|
||||
flag = 0x400000000000L;
|
||||
values = ref _headers._RequestId;
|
||||
nameStr = HeaderNames.RequestId;
|
||||
}
|
||||
else if (((Unsafe.ReadUnaligned<ulong>(ref nameStart) & 0xdfdfdfdfdfdfdfdfuL) == 0x4154534543415254uL) && ((Unsafe.ReadUnaligned<ushort>(ref Unsafe.AddByteOffset(ref nameStart, (IntPtr)(4 * sizeof(ushort)))) & 0xdfdfu) == 0x4554u))
|
||||
{
|
||||
flag = 0x2000000000000L;
|
||||
values = ref _headers._TraceState;
|
||||
nameStr = HeaderNames.TraceState;
|
||||
}
|
||||
break;
|
||||
case 11:
|
||||
|
|
@ -6453,11 +6484,13 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
|
|||
{
|
||||
flag = 0x8000L;
|
||||
values = ref _headers._ContentMD5;
|
||||
nameStr = HeaderNames.ContentMD5;
|
||||
}
|
||||
else if (((Unsafe.ReadUnaligned<ulong>(ref nameStart) & 0xdfdfdfdfdfdfdfdfuL) == 0x5241504543415254uL) && ((Unsafe.ReadUnaligned<ushort>(ref Unsafe.AddByteOffset(ref nameStart, (IntPtr)(4 * sizeof(ushort)))) & 0xdfdfu) == 0x4e45u) && ((Unsafe.AddByteOffset(ref nameStart, (IntPtr)10) & 0xdfu) == 0x54u))
|
||||
{
|
||||
flag = 0x1000000000000L;
|
||||
values = ref _headers._TraceParent;
|
||||
nameStr = HeaderNames.TraceParent;
|
||||
}
|
||||
break;
|
||||
case 12:
|
||||
|
|
@ -6465,11 +6498,13 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
|
|||
{
|
||||
flag = 0x800L;
|
||||
values = ref _headers._ContentType;
|
||||
nameStr = HeaderNames.ContentType;
|
||||
}
|
||||
else if (((Unsafe.ReadUnaligned<ulong>(ref nameStart) & 0xdfdfdfdfffdfdfdfuL) == 0x57524f462d58414duL) && ((Unsafe.ReadUnaligned<uint>(ref Unsafe.AddByteOffset(ref nameStart, (IntPtr)(2 * sizeof(uint)))) & 0xdfdfdfdfu) == 0x53445241u))
|
||||
{
|
||||
flag = 0x2000000000L;
|
||||
values = ref _headers._MaxForwards;
|
||||
nameStr = HeaderNames.MaxForwards;
|
||||
}
|
||||
break;
|
||||
case 13:
|
||||
|
|
@ -6477,26 +6512,31 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
|
|||
{
|
||||
flag = 0x8000000L;
|
||||
values = ref _headers._Authorization;
|
||||
nameStr = HeaderNames.Authorization;
|
||||
}
|
||||
else if (((Unsafe.ReadUnaligned<ulong>(ref nameStart) & 0xdfdfffdfdfdfdfdfuL) == 0x4f432d4548434143uL) && ((Unsafe.ReadUnaligned<uint>(ref Unsafe.AddByteOffset(ref nameStart, (IntPtr)(2 * sizeof(uint)))) & 0xdfdfdfdfu) == 0x4f52544eu) && ((Unsafe.AddByteOffset(ref nameStart, (IntPtr)12) & 0xdfu) == 0x4cu))
|
||||
{
|
||||
flag = 0x1L;
|
||||
values = ref _headers._CacheControl;
|
||||
nameStr = HeaderNames.CacheControl;
|
||||
}
|
||||
else if (((Unsafe.ReadUnaligned<ulong>(ref nameStart) & 0xffdfdfdfdfdfdfdfuL) == 0x2d544e45544e4f43uL) && ((Unsafe.ReadUnaligned<uint>(ref Unsafe.AddByteOffset(ref nameStart, (IntPtr)(2 * sizeof(uint)))) & 0xdfdfdfdfu) == 0x474e4152u) && ((Unsafe.AddByteOffset(ref nameStart, (IntPtr)12) & 0xdfu) == 0x45u))
|
||||
{
|
||||
flag = 0x10000L;
|
||||
values = ref _headers._ContentRange;
|
||||
nameStr = HeaderNames.ContentRange;
|
||||
}
|
||||
else if (((Unsafe.ReadUnaligned<ulong>(ref nameStart) & 0xffdfdfdfdfffdfdfuL) == 0x2d454e4f4e2d4649uL) && ((Unsafe.ReadUnaligned<uint>(ref Unsafe.AddByteOffset(ref nameStart, (IntPtr)(2 * sizeof(uint)))) & 0xdfdfdfdfu) == 0x4354414du) && ((Unsafe.AddByteOffset(ref nameStart, (IntPtr)12) & 0xdfu) == 0x48u))
|
||||
{
|
||||
flag = 0x400000000L;
|
||||
values = ref _headers._IfNoneMatch;
|
||||
nameStr = HeaderNames.IfNoneMatch;
|
||||
}
|
||||
else if (((Unsafe.ReadUnaligned<ulong>(ref nameStart) & 0xdfdfdfffdfdfdfdfuL) == 0x444f4d2d5453414cuL) && ((Unsafe.ReadUnaligned<uint>(ref Unsafe.AddByteOffset(ref nameStart, (IntPtr)(2 * sizeof(uint)))) & 0xdfdfdfdfu) == 0x45494649u) && ((Unsafe.AddByteOffset(ref nameStart, (IntPtr)12) & 0xdfu) == 0x44u))
|
||||
{
|
||||
flag = 0x40000L;
|
||||
values = ref _headers._LastModified;
|
||||
nameStr = HeaderNames.LastModified;
|
||||
}
|
||||
break;
|
||||
case 14:
|
||||
|
|
@ -6504,10 +6544,18 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
|
|||
{
|
||||
flag = 0x1000000L;
|
||||
values = ref _headers._AcceptCharset;
|
||||
nameStr = HeaderNames.AcceptCharset;
|
||||
}
|
||||
else if (((Unsafe.ReadUnaligned<ulong>(ref nameStart) & 0xffdfdfdfdfdfdfdfuL) == 0x2d544e45544e4f43uL) && ((Unsafe.ReadUnaligned<uint>(ref Unsafe.AddByteOffset(ref nameStart, (IntPtr)(2 * sizeof(uint)))) & 0xdfdfdfdfu) == 0x474e454cu) && ((Unsafe.ReadUnaligned<ushort>(ref Unsafe.AddByteOffset(ref nameStart, (IntPtr)(6 * sizeof(ushort)))) & 0xdfdfu) == 0x4854u))
|
||||
{
|
||||
AppendContentLength(value);
|
||||
if (ReferenceEquals(EncodingSelector, KestrelServerOptions.DefaultRequestHeaderEncodingSelector))
|
||||
{
|
||||
AppendContentLength(value);
|
||||
}
|
||||
else
|
||||
{
|
||||
AppendContentLengthCustomEncoding(value, EncodingSelector(HeaderNames.ContentLength));
|
||||
}
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
|
@ -6517,11 +6565,13 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
|
|||
{
|
||||
flag = 0x2000000L;
|
||||
values = ref _headers._AcceptEncoding;
|
||||
nameStr = HeaderNames.AcceptEncoding;
|
||||
}
|
||||
else if ((firstTerm15 == 0x4c2d545045434341uL) && ((Unsafe.ReadUnaligned<uint>(ref Unsafe.AddByteOffset(ref nameStart, (IntPtr)(2 * sizeof(uint)))) & 0xdfdfdfdfu) == 0x55474e41u) && ((Unsafe.ReadUnaligned<ushort>(ref Unsafe.AddByteOffset(ref nameStart, (IntPtr)(6 * sizeof(ushort)))) & 0xdfdfu) == 0x4741u) && ((Unsafe.AddByteOffset(ref nameStart, (IntPtr)14) & 0xdfu) == 0x45u))
|
||||
{
|
||||
flag = 0x4000000L;
|
||||
values = ref _headers._AcceptLanguage;
|
||||
nameStr = HeaderNames.AcceptLanguage;
|
||||
}
|
||||
break;
|
||||
case 16:
|
||||
|
|
@ -6532,16 +6582,19 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
|
|||
{
|
||||
flag = 0x1000L;
|
||||
values = ref _headers._ContentEncoding;
|
||||
nameStr = HeaderNames.ContentEncoding;
|
||||
}
|
||||
else if (((Unsafe.ReadUnaligned<ulong>(ref Unsafe.AddByteOffset(ref nameStart, (IntPtr)sizeof(ulong))) & 0xdfdfdfdfdfdfdfdfuL) == 0x45474155474e414cuL))
|
||||
{
|
||||
flag = 0x2000L;
|
||||
values = ref _headers._ContentLanguage;
|
||||
nameStr = HeaderNames.ContentLanguage;
|
||||
}
|
||||
else if (((Unsafe.ReadUnaligned<ulong>(ref Unsafe.AddByteOffset(ref nameStart, (IntPtr)sizeof(ulong))) & 0xdfdfdfdfdfdfdfdfuL) == 0x4e4f495441434f4cuL))
|
||||
{
|
||||
flag = 0x4000L;
|
||||
values = ref _headers._ContentLocation;
|
||||
nameStr = HeaderNames.ContentLocation;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
|
@ -6550,11 +6603,13 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
|
|||
{
|
||||
flag = 0x200000000L;
|
||||
values = ref _headers._IfModifiedSince;
|
||||
nameStr = HeaderNames.IfModifiedSince;
|
||||
}
|
||||
else if (((Unsafe.ReadUnaligned<ulong>(ref nameStart) & 0xdfdfdfdfdfdfdfdfuL) == 0x524546534e415254uL) && ((Unsafe.ReadUnaligned<ulong>(ref Unsafe.AddByteOffset(ref nameStart, (IntPtr)sizeof(ulong))) & 0xdfdfdfdfdfdfdfffuL) == 0x4e49444f434e452duL) && ((Unsafe.AddByteOffset(ref nameStart, (IntPtr)16) & 0xdfu) == 0x47u))
|
||||
{
|
||||
flag = 0x40L;
|
||||
values = ref _headers._TransferEncoding;
|
||||
nameStr = HeaderNames.TransferEncoding;
|
||||
}
|
||||
break;
|
||||
case 19:
|
||||
|
|
@ -6562,16 +6617,19 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
|
|||
{
|
||||
flag = 0x800000000000L;
|
||||
values = ref _headers._CorrelationContext;
|
||||
nameStr = HeaderNames.CorrelationContext;
|
||||
}
|
||||
else if (((Unsafe.ReadUnaligned<ulong>(ref nameStart) & 0xdfdfdfdfdfffdfdfuL) == 0x444f4d4e552d4649uL) && ((Unsafe.ReadUnaligned<ulong>(ref Unsafe.AddByteOffset(ref nameStart, (IntPtr)sizeof(ulong))) & 0xdfdfffdfdfdfdfdfuL) == 0x49532d4445494649uL) && ((Unsafe.ReadUnaligned<ushort>(ref Unsafe.AddByteOffset(ref nameStart, (IntPtr)(8 * sizeof(ushort)))) & 0xdfdfu) == 0x434eu) && ((Unsafe.AddByteOffset(ref nameStart, (IntPtr)18) & 0xdfu) == 0x45u))
|
||||
{
|
||||
flag = 0x1000000000L;
|
||||
values = ref _headers._IfUnmodifiedSince;
|
||||
nameStr = HeaderNames.IfUnmodifiedSince;
|
||||
}
|
||||
else if (((Unsafe.ReadUnaligned<ulong>(ref nameStart) & 0xdfdfffdfdfdfdfdfuL) == 0x55412d59584f5250uL) && ((Unsafe.ReadUnaligned<ulong>(ref Unsafe.AddByteOffset(ref nameStart, (IntPtr)sizeof(ulong))) & 0xdfdfdfdfdfdfdfdfuL) == 0x54415a49524f4854uL) && ((Unsafe.ReadUnaligned<ushort>(ref Unsafe.AddByteOffset(ref nameStart, (IntPtr)(8 * sizeof(ushort)))) & 0xdfdfu) == 0x4f49u) && ((Unsafe.AddByteOffset(ref nameStart, (IntPtr)18) & 0xdfu) == 0x4eu))
|
||||
{
|
||||
flag = 0x4000000000L;
|
||||
values = ref _headers._ProxyAuthorization;
|
||||
nameStr = HeaderNames.ProxyAuthorization;
|
||||
}
|
||||
break;
|
||||
case 25:
|
||||
|
|
@ -6579,6 +6637,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
|
|||
{
|
||||
flag = 0x200000000000L;
|
||||
values = ref _headers._UpgradeInsecureRequests;
|
||||
nameStr = HeaderNames.UpgradeInsecureRequests;
|
||||
}
|
||||
break;
|
||||
case 29:
|
||||
|
|
@ -6586,6 +6645,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
|
|||
{
|
||||
flag = 0x8000000000000L;
|
||||
values = ref _headers._AccessControlRequestMethod;
|
||||
nameStr = HeaderNames.AccessControlRequestMethod;
|
||||
}
|
||||
break;
|
||||
case 30:
|
||||
|
|
@ -6593,6 +6653,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
|
|||
{
|
||||
flag = 0x10000000000000L;
|
||||
values = ref _headers._AccessControlRequestHeaders;
|
||||
nameStr = HeaderNames.AccessControlRequestHeaders;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
@ -6622,7 +6683,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
|
|||
}
|
||||
|
||||
// We didn't have a previous matching header value, or have already added a header, so get the string for this value.
|
||||
var valueStr = value.GetRequestHeaderStringNonNullCharacters(UseLatin1);
|
||||
var valueStr = value.GetRequestHeaderString(nameStr, EncodingSelector);
|
||||
if ((_bits & flag) == 0)
|
||||
{
|
||||
// We didn't already have a header set, so add a new one.
|
||||
|
|
@ -6640,8 +6701,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
|
|||
// The header was not one of the "known" headers.
|
||||
// Convert value to string first, because passing two spans causes 8 bytes stack zeroing in
|
||||
// this method with rep stosd, which is slower than necessary.
|
||||
var valueStr = value.GetRequestHeaderStringNonNullCharacters(UseLatin1);
|
||||
AppendUnknownHeaders(name, valueStr);
|
||||
nameStr = name.GetHeaderName();
|
||||
var valueStr = value.GetRequestHeaderString(nameStr, EncodingSelector);
|
||||
AppendUnknownHeaders(nameStr, valueStr);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -369,7 +369,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
|
|||
ConnectionIdFeature = ConnectionId;
|
||||
|
||||
HttpRequestHeaders.Reset();
|
||||
HttpRequestHeaders.UseLatin1 = ServerOptions.Latin1RequestHeaders;
|
||||
HttpRequestHeaders.EncodingSelector = ServerOptions.RequestHeaderEncodingSelector;
|
||||
HttpRequestHeaders.ReuseHeaderValues = !ServerOptions.DisableStringReuse;
|
||||
HttpResponseHeaders.Reset();
|
||||
RequestHeaders = HttpRequestHeaders;
|
||||
|
|
@ -532,7 +532,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
|
|||
}
|
||||
|
||||
string key = name.GetHeaderName();
|
||||
var valueStr = value.GetRequestHeaderStringNonNullCharacters(ServerOptions.Latin1RequestHeaders);
|
||||
var valueStr = value.GetRequestHeaderString(key, HttpRequestHeaders.EncodingSelector);
|
||||
RequestTrailers.Append(key, valueStr);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,9 @@ using System;
|
|||
using System.Buffers.Text;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
using Microsoft.Net.Http.Headers;
|
||||
|
|
@ -17,12 +19,12 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
|
|||
private long _previousBits = 0;
|
||||
|
||||
public bool ReuseHeaderValues { get; set; }
|
||||
public bool UseLatin1 { get; set; }
|
||||
public Func<string, Encoding> EncodingSelector { get; set; }
|
||||
|
||||
public HttpRequestHeaders(bool reuseHeaderValues = true, bool useLatin1 = false)
|
||||
public HttpRequestHeaders(bool reuseHeaderValues = true, Func<string, Encoding> encodingSelector = null)
|
||||
{
|
||||
ReuseHeaderValues = reuseHeaderValues;
|
||||
UseLatin1 = useLatin1;
|
||||
EncodingSelector = encodingSelector ?? KestrelServerOptions.DefaultRequestHeaderEncodingSelector;
|
||||
}
|
||||
|
||||
public void OnHeadersComplete()
|
||||
|
|
@ -87,7 +89,30 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
|
|||
parsed < 0 ||
|
||||
consumed != value.Length)
|
||||
{
|
||||
KestrelBadHttpRequestException.Throw(RequestRejectionReason.InvalidContentLength, value.GetRequestHeaderStringNonNullCharacters(UseLatin1));
|
||||
KestrelBadHttpRequestException.Throw(RequestRejectionReason.InvalidContentLength, value.GetRequestHeaderString(HeaderNames.ContentLength, EncodingSelector));
|
||||
}
|
||||
|
||||
_contentLength = parsed;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
private void AppendContentLengthCustomEncoding(ReadOnlySpan<byte> value, Encoding customEncoding)
|
||||
{
|
||||
if (_contentLength.HasValue)
|
||||
{
|
||||
KestrelBadHttpRequestException.Throw(RequestRejectionReason.MultipleContentLengths);
|
||||
}
|
||||
|
||||
// long.MaxValue = 9223372036854775807 (19 chars)
|
||||
Span<char> decodedChars = stackalloc char[20];
|
||||
var numChars = customEncoding.GetChars(value, decodedChars);
|
||||
long parsed = -1;
|
||||
|
||||
if (numChars > 19 ||
|
||||
!long.TryParse(decodedChars.Slice(0, numChars), NumberStyles.Integer, CultureInfo.InvariantCulture, out parsed) ||
|
||||
parsed < 0)
|
||||
{
|
||||
KestrelBadHttpRequestException.Throw(RequestRejectionReason.InvalidContentLength, value.GetRequestHeaderString(HeaderNames.ContentLength, EncodingSelector));
|
||||
}
|
||||
|
||||
_contentLength = parsed;
|
||||
|
|
@ -108,11 +133,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
|
|||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
private unsafe void AppendUnknownHeaders(ReadOnlySpan<byte> name, string valueString)
|
||||
private unsafe void AppendUnknownHeaders(string name, string valueString)
|
||||
{
|
||||
string key = name.GetHeaderName();
|
||||
Unknown.TryGetValue(key, out var existing);
|
||||
Unknown[key] = AppendValue(existing, valueString);
|
||||
Unknown.TryGetValue(name, out var existing);
|
||||
Unknown[name] = AppendValue(existing, valueString);
|
||||
}
|
||||
|
||||
public Enumerator GetEnumerator()
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure
|
|||
private const ulong _http10VersionLong = 3471766442030158920; // GetAsciiStringAsLong("HTTP/1.0"); const results in better codegen
|
||||
private const ulong _http11VersionLong = 3543824036068086856; // GetAsciiStringAsLong("HTTP/1.1"); const results in better codegen
|
||||
|
||||
private static readonly UTF8EncodingSealed HeaderValueEncoding = new UTF8EncodingSealed();
|
||||
private static readonly UTF8EncodingSealed DefaultRequestHeaderEncoding = new UTF8EncodingSealed();
|
||||
private static readonly SpanAction<char, IntPtr> _getHeaderName = GetHeaderName;
|
||||
private static readonly SpanAction<char, IntPtr> _getAsciiStringNonNullCharacters = GetAsciiStringNonNullCharacters;
|
||||
|
||||
|
|
@ -120,11 +120,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure
|
|||
}
|
||||
}
|
||||
|
||||
public static string GetAsciiOrUTF8StringNonNullCharacters(this Span<byte> span)
|
||||
=> GetAsciiOrUTF8StringNonNullCharacters((ReadOnlySpan<byte>)span);
|
||||
|
||||
public static string GetAsciiOrUTF8StringNonNullCharacters(this ReadOnlySpan<byte> span)
|
||||
=> StringUtilities.GetAsciiOrUTF8StringNonNullCharacters(span, HeaderValueEncoding);
|
||||
=> StringUtilities.GetAsciiOrUTF8StringNonNullCharacters(span, DefaultRequestHeaderEncoding);
|
||||
|
||||
private static unsafe void GetAsciiStringNonNullCharacters(Span<char> buffer, IntPtr state)
|
||||
{
|
||||
|
|
@ -139,8 +136,34 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure
|
|||
}
|
||||
}
|
||||
|
||||
public static string GetRequestHeaderStringNonNullCharacters(this ReadOnlySpan<byte> span, bool useLatin1) =>
|
||||
useLatin1 ? span.GetLatin1StringNonNullCharacters() : span.GetAsciiOrUTF8StringNonNullCharacters(HeaderValueEncoding);
|
||||
public static string GetRequestHeaderString(this ReadOnlySpan<byte> span, string name, Func<string, Encoding> encodingSelector)
|
||||
{
|
||||
if (ReferenceEquals(KestrelServerOptions.DefaultRequestHeaderEncodingSelector, encodingSelector))
|
||||
{
|
||||
return span.GetAsciiOrUTF8StringNonNullCharacters(DefaultRequestHeaderEncoding);
|
||||
}
|
||||
|
||||
var encoding = encodingSelector(name);
|
||||
|
||||
if (encoding is null)
|
||||
{
|
||||
return span.GetAsciiOrUTF8StringNonNullCharacters(DefaultRequestHeaderEncoding);
|
||||
}
|
||||
|
||||
if (ReferenceEquals(encoding, Encoding.Latin1))
|
||||
{
|
||||
return span.GetLatin1StringNonNullCharacters();
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
return encoding.GetString(span);
|
||||
}
|
||||
catch (DecoderFallbackException ex)
|
||||
{
|
||||
throw new InvalidOperationException(ex.Message, ex);
|
||||
}
|
||||
}
|
||||
|
||||
public static string GetAsciiStringEscaped(this ReadOnlySpan<byte> span, int maxChars)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -255,8 +255,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel
|
|||
|
||||
ConfigurationReader = new ConfigurationReader(Configuration);
|
||||
|
||||
Options.Latin1RequestHeaders = ConfigurationReader.Latin1RequestHeaders;
|
||||
|
||||
LoadDefaultCert(ConfigurationReader);
|
||||
|
||||
foreach (var endpoint in ConfigurationReader.Endpoints)
|
||||
|
|
|
|||
|
|
@ -35,12 +35,19 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core
|
|||
|
||||
private IDisposable _configChangedRegistration;
|
||||
|
||||
public KestrelServer(IOptions<KestrelServerOptions> options, IEnumerable<IConnectionListenerFactory> transportFactories, ILoggerFactory loggerFactory)
|
||||
public KestrelServer(
|
||||
IOptions<KestrelServerOptions> options,
|
||||
IEnumerable<IConnectionListenerFactory> transportFactories,
|
||||
ILoggerFactory loggerFactory)
|
||||
: this(transportFactories, null, CreateServiceContext(options, loggerFactory))
|
||||
{
|
||||
}
|
||||
|
||||
public KestrelServer(IOptions<KestrelServerOptions> options, IEnumerable<IConnectionListenerFactory> transportFactories, IEnumerable<IMultiplexedConnectionListenerFactory> multiplexedFactories, ILoggerFactory loggerFactory)
|
||||
public KestrelServer(
|
||||
IOptions<KestrelServerOptions> options,
|
||||
IEnumerable<IConnectionListenerFactory> transportFactories,
|
||||
IEnumerable<IMultiplexedConnectionListenerFactory> multiplexedFactories,
|
||||
ILoggerFactory loggerFactory)
|
||||
: this(transportFactories, multiplexedFactories, CreateServiceContext(options, loggerFactory))
|
||||
{
|
||||
}
|
||||
|
|
@ -52,7 +59,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core
|
|||
}
|
||||
|
||||
// For testing
|
||||
internal KestrelServer(IEnumerable<IConnectionListenerFactory> transportFactories, IEnumerable<IMultiplexedConnectionListenerFactory> multiplexedFactories, ServiceContext serviceContext)
|
||||
internal KestrelServer(
|
||||
IEnumerable<IConnectionListenerFactory> transportFactories,
|
||||
IEnumerable<IMultiplexedConnectionListenerFactory> multiplexedFactories,
|
||||
ServiceContext serviceContext)
|
||||
{
|
||||
if (transportFactories == null)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ using System.IO;
|
|||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Text;
|
||||
using Microsoft.AspNetCore.Certificates.Generation;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal;
|
||||
|
|
@ -22,6 +23,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core
|
|||
/// </summary>
|
||||
public class KestrelServerOptions
|
||||
{
|
||||
// internal to fast-path header decoding when RequestHeaderEncodingSelector is unchanged.
|
||||
internal static readonly Func<string, Encoding> DefaultRequestHeaderEncodingSelector = _ => null;
|
||||
|
||||
private Func<string, Encoding> _requestHeaderEncodingSelector = DefaultRequestHeaderEncodingSelector;
|
||||
|
||||
// The following two lists configure the endpoints that Kestrel should listen to. If both lists are empty, the "urls" config setting (e.g. UseUrls) is used.
|
||||
internal List<ListenOptions> CodeBackedListenOptions { get; } = new List<ListenOptions>();
|
||||
internal List<ListenOptions> ConfigurationBackedListenOptions { get; } = new List<ListenOptions>();
|
||||
|
|
@ -65,6 +71,24 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core
|
|||
/// </remarks>
|
||||
public bool DisableStringReuse { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// Controls whether to return the AltSvcHeader from on an HTTP/2 or lower response for HTTP/3
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Defaults to false.
|
||||
/// </remarks>
|
||||
public bool EnableAltSvc { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a callback that returns the <see cref="Encoding"/> to decode the value for the specified request header name,
|
||||
/// or <see langword="null"/> to use the default <see cref="UTF8Encoding"/>.
|
||||
/// </summary>
|
||||
public Func<string, Encoding> RequestHeaderEncodingSelector
|
||||
{
|
||||
get => _requestHeaderEncodingSelector;
|
||||
set => _requestHeaderEncodingSelector = value ?? throw new ArgumentNullException(nameof(value));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enables the Listen options callback to resolve and use services registered by the application during startup.
|
||||
/// Typically initialized by UseKestrel()"/>.
|
||||
|
|
@ -78,15 +102,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core
|
|||
|
||||
/// <summary>
|
||||
/// Provides a configuration source where endpoints will be loaded from on server start.
|
||||
/// The default is null.
|
||||
/// The default is <see langword="null"/>.
|
||||
/// </summary>
|
||||
public KestrelConfigurationLoader ConfigurationLoader { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Controls whether to return the AltSvcHeader from on an HTTP/2 or lower response for HTTP/3
|
||||
/// </summary>
|
||||
public bool EnableAltSvc { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// A default configuration action for all endpoints. Use for Listen, configuration, the default url, and URLs.
|
||||
/// </summary>
|
||||
|
|
@ -107,11 +126,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core
|
|||
/// </summary>
|
||||
internal bool IsDevCertLoaded { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Treat request headers as Latin-1 or ISO/IEC 8859-1 instead of UTF-8.
|
||||
/// </summary>
|
||||
internal bool Latin1RequestHeaders { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Specifies a configuration Action to run for each newly created endpoint. Calling this again will replace
|
||||
/// the prior action.
|
||||
|
|
@ -159,7 +173,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core
|
|||
if (DefaultCertificate == null && !IsDevCertLoaded)
|
||||
{
|
||||
IsDevCertLoaded = true; // Only try once
|
||||
var logger = ApplicationServices.GetRequiredService<ILogger<KestrelServer>>();
|
||||
var logger = ApplicationServices!.GetRequiredService<ILogger<KestrelServer>>();
|
||||
try
|
||||
{
|
||||
DefaultCertificate = CertificateManager.Instance.ListCertificates(StoreName.My, StoreLocation.CurrentUser, isValid: true)
|
||||
|
|
@ -220,7 +234,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core
|
|||
/// </summary>
|
||||
/// <param name="config">The configuration section for Kestrel.</param>
|
||||
/// <param name="reloadOnChange">
|
||||
/// If <see langword="true" />, Kestrel will dynamically update endpoint bindings when configuration changes.
|
||||
/// If <see langword="true"/>, Kestrel will dynamically update endpoint bindings when configuration changes.
|
||||
/// This will only reload endpoints defined in the "Endpoints" section of your <paramref name="config"/>. Endpoints defined in code will not be reloaded.
|
||||
/// </param>
|
||||
/// <returns>A <see cref="KestrelConfigurationLoader"/> for further endpoint configuration.</returns>
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ using System.Text;
|
|||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
using Microsoft.Net.Http.Headers;
|
||||
using Xunit;
|
||||
using static CodeGenerator.KnownHeaders;
|
||||
|
||||
|
|
@ -307,11 +308,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
|
|||
var headers = new HttpRequestHeaders();
|
||||
const string key = "\u00141\u00F3d\017c";
|
||||
|
||||
var encoding = Encoding.GetEncoding("iso-8859-1");
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
var exception = Assert.Throws<BadHttpRequestException>(
|
||||
#pragma warning restore CS0618 // Type or member is obsolete
|
||||
() => headers.Append(encoding.GetBytes(key), Encoding.ASCII.GetBytes("value")));
|
||||
() => headers.Append(Encoding.Latin1.GetBytes(key), Encoding.ASCII.GetBytes("value")));
|
||||
Assert.Equal(StatusCodes.Status400BadRequest, exception.StatusCode);
|
||||
}
|
||||
|
||||
|
|
@ -473,7 +473,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
|
|||
Assert.Throws<InvalidOperationException>(() =>
|
||||
{
|
||||
var headerName = Encoding.ASCII.GetBytes(header.Name).AsSpan();
|
||||
var nextSpan = Encoding.GetEncoding("iso-8859-1").GetBytes(headerValueUtf16Latin1CrossOver).AsSpan();
|
||||
var nextSpan = Encoding.Latin1.GetBytes(headerValueUtf16Latin1CrossOver).AsSpan();
|
||||
|
||||
Assert.False(nextSpan.SequenceEqual(Encoding.ASCII.GetBytes(headerValueUtf16Latin1CrossOver)));
|
||||
|
||||
|
|
@ -490,7 +490,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
|
|||
[MemberData(nameof(KnownRequestHeaders))]
|
||||
public void Latin1ValuesAcceptedInLatin1ModeButNotReused(bool reuseValue, KnownHeader header)
|
||||
{
|
||||
var headers = new HttpRequestHeaders(reuseHeaderValues: reuseValue, useLatin1: true);
|
||||
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++)
|
||||
|
|
@ -517,19 +517,24 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
|
|||
headerValueUtf16Latin1CrossOver = new string(headerValue.AsSpan().Slice(0, i + 1));
|
||||
}
|
||||
|
||||
headers.Reset();
|
||||
|
||||
var headerName = Encoding.ASCII.GetBytes(header.Name).AsSpan();
|
||||
var latinValueSpan = Encoding.GetEncoding("iso-8859-1").GetBytes(headerValueUtf16Latin1CrossOver).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 parsedHeaderValue = ((IHeaderDictionary)headers)[header.Name].ToString();
|
||||
var parsedHeaderValue1 = ((IHeaderDictionary)headers)[header.Name].ToString();
|
||||
|
||||
Assert.Equal(headerValueUtf16Latin1CrossOver, parsedHeaderValue);
|
||||
Assert.NotSame(headerValueUtf16Latin1CrossOver, parsedHeaderValue);
|
||||
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
|
||||
|
|
@ -541,7 +546,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
|
|||
[MemberData(nameof(KnownRequestHeaders))]
|
||||
public void NullCharactersRejectedInUTF8AndLatin1Mode(bool useLatin1, KnownHeader header)
|
||||
{
|
||||
var headers = new HttpRequestHeaders(useLatin1: useLatin1);
|
||||
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++)
|
||||
|
|
@ -569,6 +574,53 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
|
|||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CanSpecifyEncodingBasedOnHeaderName()
|
||||
{
|
||||
const string headerValue = "Hello \u03a0";
|
||||
var acceptNameBytes = Encoding.ASCII.GetBytes(HeaderNames.Accept);
|
||||
var cookieNameBytes = Encoding.ASCII.GetBytes(HeaderNames.Cookie);
|
||||
var headerValueBytes = Encoding.UTF8.GetBytes(headerValue);
|
||||
|
||||
var headers = new HttpRequestHeaders(encodingSelector: headerName =>
|
||||
{
|
||||
// For known headers, the HeaderNames value is passed in.
|
||||
if (ReferenceEquals(headerName, HeaderNames.Accept))
|
||||
{
|
||||
return Encoding.GetEncoding("ASCII", EncoderFallback.ExceptionFallback, DecoderFallback.ExceptionFallback);
|
||||
}
|
||||
|
||||
return Encoding.UTF8;
|
||||
});
|
||||
|
||||
Assert.Throws<InvalidOperationException>(() => headers.Append(acceptNameBytes, headerValueBytes));
|
||||
headers.Append(cookieNameBytes, headerValueBytes);
|
||||
headers.OnHeadersComplete();
|
||||
|
||||
var parsedAcceptHeaderValue = ((IHeaderDictionary)headers)[HeaderNames.Accept].ToString();
|
||||
var parsedCookieHeaderValue = ((IHeaderDictionary)headers)[HeaderNames.Cookie].ToString();
|
||||
|
||||
Assert.Empty(parsedAcceptHeaderValue);
|
||||
Assert.Equal(headerValue, parsedCookieHeaderValue);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CanSpecifyEncodingForContentLength()
|
||||
{
|
||||
var contentLengthNameBytes = Encoding.ASCII.GetBytes(HeaderNames.ContentLength);
|
||||
// Always 32 bits per code point, so not a superset of ASCII
|
||||
var contentLengthValueBytes = Encoding.UTF32.GetBytes("1337");
|
||||
|
||||
var headers = new HttpRequestHeaders(encodingSelector: _ => Encoding.UTF32);
|
||||
headers.Append(contentLengthNameBytes, contentLengthValueBytes);
|
||||
headers.OnHeadersComplete();
|
||||
|
||||
Assert.Equal(1337, headers.ContentLength);
|
||||
|
||||
Assert.Throws<InvalidOperationException>(() =>
|
||||
new HttpRequestHeaders().Append(contentLengthNameBytes, contentLengthValueBytes));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ValueReuseNeverWhenUnknownHeader()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
// 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.Net;
|
||||
using Xunit;
|
||||
|
||||
|
|
@ -60,5 +61,14 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
|
|||
// https://github.com/dotnet/aspnetcore/issues/21423
|
||||
options.ListenLocalhost(5000);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SettingRequestHeaderEncodingSelecterThrowsArgumentNullException()
|
||||
{
|
||||
var options = new KestrelServerOptions();
|
||||
|
||||
var ex = Assert.Throws<ArgumentNullException>(() => options.RequestHeaderEncodingSelector = null);
|
||||
Assert.Equal("value", ex.ParamName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ using System;
|
|||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure;
|
||||
using Microsoft.Net.Http.Headers;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
|
||||
|
|
@ -17,7 +18,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
|
|||
[InlineData(new byte[] { 0xef, 0xbf, 0xbd })] // 3 bytes: Replacement character, highest UTF-8 character currently encoded in the UTF-8 code page
|
||||
private void FullUTF8RangeSupported(byte[] encodedBytes)
|
||||
{
|
||||
var s = HttpUtilities.GetRequestHeaderStringNonNullCharacters(encodedBytes.AsSpan(), useLatin1: false);
|
||||
var s = HttpUtilities.GetRequestHeaderString(encodedBytes.AsSpan(), HeaderNames.Accept, KestrelServerOptions.DefaultRequestHeaderEncodingSelector);
|
||||
|
||||
Assert.Equal(1, s.Length);
|
||||
}
|
||||
|
|
@ -35,7 +36,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
|
|||
var byteRange = Enumerable.Range(1, length).Select(x => (byte)x).ToArray();
|
||||
Array.Copy(bytes, 0, byteRange, position, bytes.Length);
|
||||
|
||||
Assert.Throws<InvalidOperationException>(() => HttpUtilities.GetRequestHeaderStringNonNullCharacters(byteRange.AsSpan(), useLatin1: false));
|
||||
Assert.Throws<InvalidOperationException>(() =>
|
||||
HttpUtilities.GetRequestHeaderString(byteRange.AsSpan(), HeaderNames.Accept, KestrelServerOptions.DefaultRequestHeaderEncodingSelector));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ using System.IO;
|
|||
using System.Linq;
|
||||
using System.Security.Authentication;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Text;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Core;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Https;
|
||||
|
|
@ -587,27 +588,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Tests
|
|||
Assert.True(ran1);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Latin1RequestHeadersReadFromConfig()
|
||||
{
|
||||
var options = CreateServerOptions();
|
||||
var config = new ConfigurationBuilder().AddInMemoryCollection().Build();
|
||||
|
||||
Assert.False(options.Latin1RequestHeaders);
|
||||
options.Configure(config).Load();
|
||||
Assert.False(options.Latin1RequestHeaders);
|
||||
|
||||
options = CreateServerOptions();
|
||||
config = new ConfigurationBuilder().AddInMemoryCollection(new[]
|
||||
{
|
||||
new KeyValuePair<string, string>("Latin1RequestHeaders", "true"),
|
||||
}).Build();
|
||||
|
||||
Assert.False(options.Latin1RequestHeaders);
|
||||
options.Configure(config).Load();
|
||||
Assert.True(options.Latin1RequestHeaders);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Reload_IdentifiesEndpointsToStartAndStop()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -2,7 +2,9 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Core;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure;
|
||||
using Microsoft.Net.Http.Headers;
|
||||
|
||||
namespace Microsoft.AspNetCore.Server.Kestrel.Performance
|
||||
{
|
||||
|
|
@ -10,6 +12,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Performance
|
|||
{
|
||||
private const int Iterations = 50;
|
||||
|
||||
private string _headerName;
|
||||
private byte[] _asciiBytes;
|
||||
private byte[] _utf8Bytes;
|
||||
|
||||
|
|
@ -27,24 +30,28 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Performance
|
|||
switch (Type)
|
||||
{
|
||||
case BenchmarkTypes.KeepAlive:
|
||||
_headerName = HeaderNames.Connection;
|
||||
// keep-alive
|
||||
_asciiBytes = new byte[] { 0x6b, 0x65, 0x65, 0x70, 0x2d, 0x61, 0x6c, 0x69, 0x76, 0x65 };
|
||||
// kéép-álivé
|
||||
_utf8Bytes = new byte[] { 0x6b, 0xc3, 0xa9, 0xc3, 0xa9, 0x70, 0x2d, 0xc3, 0xa1, 0x6c, 0x69, 0x76, 0xc3, 0xa9 };
|
||||
break;
|
||||
case BenchmarkTypes.Accept:
|
||||
_headerName = HeaderNames.Accept;
|
||||
// text/plain,text/html;q=0.9,application/xhtml+xml;q=0.9,application/xml;q=0.8,*/*;q=0.7
|
||||
_asciiBytes = new byte[] { 0x74, 0x65, 0x78, 0x74, 0x2f, 0x70, 0x6c, 0x61, 0x69, 0x6e, 0x2c, 0x74, 0x65, 0x78, 0x74, 0x2f, 0x68, 0x74, 0x6d, 0x6c, 0x3b, 0x71, 0x3d, 0x30, 0x2e, 0x39, 0x2c, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x78, 0x68, 0x74, 0x6d, 0x6c, 0x2b, 0x78, 0x6d, 0x6c, 0x3b, 0x71, 0x3d, 0x30, 0x2e, 0x39, 0x2c, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x78, 0x6d, 0x6c, 0x3b, 0x71, 0x3d, 0x30, 0x2e, 0x38, 0x2c, 0x2a, 0x2f, 0x2a, 0x3b, 0x71, 0x3d, 0x30, 0x2e, 0x37 };
|
||||
// téxt/pláin,téxt/html;q=0.9,ápplicátion/xhtml+xml;q=0.9,ápplicátion/xml;q=0.8,*/*;q=0.7
|
||||
_utf8Bytes = new byte[] { 0x74, 0xc3, 0xa9, 0x78, 0x74, 0x2f, 0x70, 0x6c, 0xc3, 0xa1, 0x69, 0x6e, 0x2c, 0x74, 0xc3, 0xa9, 0x78, 0x74, 0x2f, 0x68, 0x74, 0x6d, 0x6c, 0x3b, 0x71, 0x3d, 0x30, 0x2e, 0x39, 0x2c, 0xc3, 0xa1, 0x70, 0x70, 0x6c, 0x69, 0x63, 0xc3, 0xa1, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x78, 0x68, 0x74, 0x6d, 0x6c, 0x2b, 0x78, 0x6d, 0x6c, 0x3b, 0x71, 0x3d, 0x30, 0x2e, 0x39, 0x2c, 0xc3, 0xa1, 0x70, 0x70, 0x6c, 0x69, 0x63, 0xc3, 0xa1, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x78, 0x6d, 0x6c, 0x3b, 0x71, 0x3d, 0x30, 0x2e, 0x38, 0x2c, 0x2a, 0x2f, 0x2a, 0x3b, 0x71, 0x3d, 0x30, 0x2e, 0x37 };
|
||||
break;
|
||||
case BenchmarkTypes.UserAgent:
|
||||
_headerName = HeaderNames.UserAgent;
|
||||
// Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36
|
||||
_asciiBytes = new byte[] { 0x4d, 0x6f, 0x7a, 0x69, 0x6c, 0x6c, 0x61, 0x2f, 0x35, 0x2e, 0x30, 0x20, 0x28, 0x57, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x73, 0x20, 0x4e, 0x54, 0x20, 0x31, 0x30, 0x2e, 0x30, 0x3b, 0x20, 0x57, 0x4f, 0x57, 0x36, 0x34, 0x29, 0x20, 0x41, 0x70, 0x70, 0x6c, 0x65, 0x57, 0x65, 0x62, 0x4b, 0x69, 0x74, 0x2f, 0x35, 0x33, 0x37, 0x2e, 0x33, 0x36, 0x20, 0x28, 0x4b, 0x48, 0x54, 0x4d, 0x4c, 0x2c, 0x20, 0x6c, 0x69, 0x6b, 0x65, 0x20, 0x47, 0x65, 0x63, 0x6b, 0x6f, 0x29, 0x20, 0x43, 0x68, 0x72, 0x6f, 0x6d, 0x65, 0x2f, 0x35, 0x34, 0x2e, 0x30, 0x2e, 0x32, 0x38, 0x34, 0x30, 0x2e, 0x39, 0x39, 0x20, 0x53, 0x61, 0x66, 0x61, 0x72, 0x69, 0x2f, 0x35, 0x33, 0x37, 0x2e, 0x33, 0x36 };
|
||||
// Mozillá/5.0 (Windows NT 10.0; WOW64) áppléWébKit/537.36 (KHTML, liké Gécko) Chromé/54.0.2840.99 Sáfári/537.36
|
||||
_utf8Bytes = new byte[] { 0x4d, 0x6f, 0x7a, 0x69, 0x6c, 0x6c, 0xc3, 0xa1, 0x2f, 0x35, 0x2e, 0x30, 0x20, 0x28, 0x57, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x73, 0x20, 0x4e, 0x54, 0x20, 0x31, 0x30, 0x2e, 0x30, 0x3b, 0x20, 0x57, 0x4f, 0x57, 0x36, 0x34, 0x29, 0x20, 0xc3, 0xa1, 0x70, 0x70, 0x6c, 0xc3, 0xa9, 0x57, 0xc3, 0xa9, 0x62, 0x4b, 0x69, 0x74, 0x2f, 0x35, 0x33, 0x37, 0x2e, 0x33, 0x36, 0x20, 0x28, 0x4b, 0x48, 0x54, 0x4d, 0x4c, 0x2c, 0x20, 0x6c, 0x69, 0x6b, 0xc3, 0xa9, 0x20, 0x47, 0xc3, 0xa9, 0x63, 0x6b, 0x6f, 0x29, 0x20, 0x43, 0x68, 0x72, 0x6f, 0x6d, 0xc3, 0xa9, 0x2f, 0x35, 0x34, 0x2e, 0x30, 0x2e, 0x32, 0x38, 0x34, 0x30, 0x2e, 0x39, 0x39, 0x20, 0x53, 0xc3, 0xa1, 0x66, 0xc3, 0xa1, 0x72, 0x69, 0x2f, 0x35, 0x33, 0x37, 0x2e, 0x33, 0x36 };
|
||||
break;
|
||||
case BenchmarkTypes.Cookie:
|
||||
_headerName = HeaderNames.Cookie;
|
||||
// prov=20629ccd-8b0f-e8ef-2935-cd26609fc0bc; __qca=P0-1591065732-1479167353442; _ga=GA1.2.1298898376.1479167354; _gat=1; sgt=id=9519gfde_3347_4762_8762_df51458c8ec2; acct=t=why-is-%e0%a5%a7%e0%a5%a8%e0%a5%a9-numeric&s=why-is-%e0%a5%a7%e0%a5%a8%e0%a5%a9-numeric
|
||||
_asciiBytes = new byte[] { 0x70, 0x72, 0x6f, 0x76, 0x3d, 0x32, 0x30, 0x36, 0x32, 0x39, 0x63, 0x63, 0x64, 0x2d, 0x38, 0x62, 0x30, 0x66, 0x2d, 0x65, 0x38, 0x65, 0x66, 0x2d, 0x32, 0x39, 0x33, 0x35, 0x2d, 0x63, 0x64, 0x32, 0x36, 0x36, 0x30, 0x39, 0x66, 0x63, 0x30, 0x62, 0x63, 0x3b, 0x20, 0x5f, 0x5f, 0x71, 0x63, 0x61, 0x3d, 0x50, 0x30, 0x2d, 0x31, 0x35, 0x39, 0x31, 0x30, 0x36, 0x35, 0x37, 0x33, 0x32, 0x2d, 0x31, 0x34, 0x37, 0x39, 0x31, 0x36, 0x37, 0x33, 0x35, 0x33, 0x34, 0x34, 0x32, 0x3b, 0x20, 0x5f, 0x67, 0x61, 0x3d, 0x47, 0x41, 0x31, 0x2e, 0x32, 0x2e, 0x31, 0x32, 0x39, 0x38, 0x38, 0x39, 0x38, 0x33, 0x37, 0x36, 0x2e, 0x31, 0x34, 0x37, 0x39, 0x31, 0x36, 0x37, 0x33, 0x35, 0x34, 0x3b, 0x20, 0x5f, 0x67, 0x61, 0x74, 0x3d, 0x31, 0x3b, 0x20, 0x73, 0x67, 0x74, 0x3d, 0x69, 0x64, 0x3d, 0x39, 0x35, 0x31, 0x39, 0x67, 0x66, 0x64, 0x65, 0x5f, 0x33, 0x33, 0x34, 0x37, 0x5f, 0x34, 0x37, 0x36, 0x32, 0x5f, 0x38, 0x37, 0x36, 0x32, 0x5f, 0x64, 0x66, 0x35, 0x31, 0x34, 0x35, 0x38, 0x63, 0x38, 0x65, 0x63, 0x32, 0x3b, 0x20, 0x61, 0x63, 0x63, 0x74, 0x3d, 0x74, 0x3d, 0x77, 0x68, 0x79, 0x2d, 0x69, 0x73, 0x2d, 0x25, 0x65, 0x30, 0x25, 0x61, 0x35, 0x25, 0x61, 0x37, 0x25, 0x65, 0x30, 0x25, 0x61, 0x35, 0x25, 0x61, 0x38, 0x25, 0x65, 0x30, 0x25, 0x61, 0x35, 0x25, 0x61, 0x39, 0x2d, 0x6e, 0x75, 0x6d, 0x65, 0x72, 0x69, 0x63, 0x26, 0x73, 0x3d, 0x77, 0x68, 0x79, 0x2d, 0x69, 0x73, 0x2d, 0x25, 0x65, 0x30, 0x25, 0x61, 0x35, 0x25, 0x61, 0x37, 0x25, 0x65, 0x30, 0x25, 0x61, 0x35, 0x25, 0x61, 0x38, 0x25, 0x65, 0x30, 0x25, 0x61, 0x35, 0x25, 0x61, 0x39, 0x2d, 0x6e, 0x75, 0x6d, 0x65, 0x72, 0x69, 0x63 };
|
||||
// prov=20629ccd-8b0f-é8éf-2935-cd26609fc0bc; __qcá=P0-1591065732-1479167353442; _gá=Gá1.2.1298898376.1479167354; _gát=1; sgt=id=9519gfdé_3347_4762_8762_df51458c8éc2; ácct=t=why-is-%é0%á5%á7%é0%á5%á8%é0%á5%á9-numéric&s=why-is-%é0%á5%á7%é0%á5%á8%é0%á5%á9-numéric
|
||||
|
|
@ -67,7 +74,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Performance
|
|||
{
|
||||
for (uint i = 0; i < Iterations; i++)
|
||||
{
|
||||
HttpUtilities.GetRequestHeaderStringNonNullCharacters(_utf8Bytes, useLatin1: false);
|
||||
HttpUtilities.GetRequestHeaderString(_utf8Bytes, _headerName, KestrelServerOptions.DefaultRequestHeaderEncodingSelector);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ using System.Collections.Generic;
|
|||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Microsoft.Net.Http.Headers;
|
||||
|
||||
namespace CodeGenerator
|
||||
{
|
||||
|
|
@ -230,23 +231,39 @@ namespace CodeGenerator
|
|||
firstTermVar = "";
|
||||
}
|
||||
|
||||
string GenerateIfBody(KnownHeader header, string extraIndent = "")
|
||||
{
|
||||
if (header.Identifier == "ContentLength")
|
||||
{
|
||||
return $@"
|
||||
{extraIndent}if (ReferenceEquals(EncodingSelector, KestrelServerOptions.DefaultRequestHeaderEncodingSelector))
|
||||
{extraIndent}{{
|
||||
{extraIndent} AppendContentLength(value);
|
||||
{extraIndent}}}
|
||||
{extraIndent}else
|
||||
{extraIndent}{{
|
||||
{extraIndent} AppendContentLengthCustomEncoding(value, EncodingSelector(HeaderNames.ContentLength));
|
||||
{extraIndent}}}
|
||||
{extraIndent}return;";
|
||||
}
|
||||
else
|
||||
{
|
||||
return $@"
|
||||
{extraIndent}flag = {header.FlagBit()};
|
||||
{extraIndent}values = ref _headers._{header.Identifier};
|
||||
{extraIndent}nameStr = HeaderNames.{header.Identifier};";
|
||||
}
|
||||
}
|
||||
|
||||
var groups = values.GroupBy(header => header.EqualIgnoreCaseBytesFirstTerm());
|
||||
return start + $@"{Each(groups, (byFirstTerm, i) => $@"{(byFirstTerm.Count() == 1 ? $@"{Each(byFirstTerm, header => $@"
|
||||
{(i > 0 ? "else " : "")}if ({header.EqualIgnoreCaseBytes(firstTermVar)})
|
||||
{{{(header.Identifier == "ContentLength" ? $@"
|
||||
AppendContentLength(value);
|
||||
return;" : $@"
|
||||
flag = {header.FlagBit()};
|
||||
values = ref _headers._{header.Identifier};")}
|
||||
{{{GenerateIfBody(header)}
|
||||
}}")}" : $@"
|
||||
if ({byFirstTerm.Key.Replace(firstTermVarExpression, firstTermVar)})
|
||||
{{{Each(byFirstTerm, (header, i) => $@"
|
||||
{(i > 0 ? "else " : "")}if ({header.EqualIgnoreCaseBytesSecondTermOnwards()})
|
||||
{{{(header.Identifier == "ContentLength" ? $@"
|
||||
AppendContentLength(value);
|
||||
return;" : $@"
|
||||
flag = {header.FlagBit()};
|
||||
values = ref _headers._{header.Identifier};")}
|
||||
{{{GenerateIfBody(header, extraIndent: " ")}
|
||||
}}")}
|
||||
}}")}")}";
|
||||
}
|
||||
|
|
@ -986,6 +1003,7 @@ $@" private void Clear(long bitsToClear)
|
|||
public unsafe void Append(ReadOnlySpan<byte> name, ReadOnlySpan<byte> value)
|
||||
{{
|
||||
ref byte nameStart = ref MemoryMarshal.GetReference(name);
|
||||
var nameStr = string.Empty;
|
||||
ref StringValues values = ref Unsafe.AsRef<StringValues>(null);
|
||||
var flag = 0L;
|
||||
|
||||
|
|
@ -1017,7 +1035,7 @@ $@" private void Clear(long bitsToClear)
|
|||
}}
|
||||
|
||||
// We didn't have a previous matching header value, or have already added a header, so get the string for this value.
|
||||
var valueStr = value.GetRequestHeaderStringNonNullCharacters(UseLatin1);
|
||||
var valueStr = value.GetRequestHeaderString(nameStr, EncodingSelector);
|
||||
if ((_bits & flag) == 0)
|
||||
{{
|
||||
// We didn't already have a header set, so add a new one.
|
||||
|
|
@ -1035,8 +1053,9 @@ $@" private void Clear(long bitsToClear)
|
|||
// The header was not one of the ""known"" headers.
|
||||
// Convert value to string first, because passing two spans causes 8 bytes stack zeroing in
|
||||
// this method with rep stosd, which is slower than necessary.
|
||||
var valueStr = value.GetRequestHeaderStringNonNullCharacters(UseLatin1);
|
||||
AppendUnknownHeaders(name, valueStr);
|
||||
nameStr = name.GetHeaderName();
|
||||
var valueStr = value.GetRequestHeaderString(nameStr, EncodingSelector);
|
||||
AppendUnknownHeaders(nameStr, valueStr);
|
||||
}}
|
||||
}}" : "")}
|
||||
|
||||
|
|
|
|||
|
|
@ -4609,7 +4609,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
|
|||
[Fact]
|
||||
public async Task HEADERS_Received_Latin1_AcceptedWhenLatin1OptionIsConfigured()
|
||||
{
|
||||
_serviceContext.ServerOptions.Latin1RequestHeaders = true;
|
||||
_serviceContext.ServerOptions.RequestHeaderEncodingSelector = _ => Encoding.Latin1;
|
||||
|
||||
await InitializeConnectionAsync(context =>
|
||||
{
|
||||
|
|
|
|||
|
|
@ -406,7 +406,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
|
|||
|
||||
void IHttpHeadersHandler.OnHeader(ReadOnlySpan<byte> name, ReadOnlySpan<byte> value)
|
||||
{
|
||||
_decodedHeaders[name.GetAsciiStringNonNullCharacters()] = value.GetRequestHeaderStringNonNullCharacters(useLatin1: _serviceContext.ServerOptions.Latin1RequestHeaders);
|
||||
var nameStr = name.GetHeaderName();
|
||||
_decodedHeaders[nameStr] = value.GetRequestHeaderString(nameStr, _serviceContext.ServerOptions.RequestHeaderEncodingSelector);
|
||||
}
|
||||
|
||||
void IHttpHeadersHandler.OnHeadersComplete(bool endStream) { }
|
||||
|
|
|
|||
|
|
@ -356,7 +356,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
|
|||
public void OnStaticIndexedHeader(int index)
|
||||
{
|
||||
var knownHeader = H3StaticTable.GetHeaderFieldAt(index);
|
||||
_decodedHeaders[((Span<byte>)knownHeader.Name).GetAsciiStringNonNullCharacters()] = HttpUtilities.GetAsciiOrUTF8StringNonNullCharacters(knownHeader.Value);
|
||||
_decodedHeaders[((Span<byte>)knownHeader.Name).GetAsciiStringNonNullCharacters()] = HttpUtilities.GetAsciiOrUTF8StringNonNullCharacters((ReadOnlySpan<byte>)knownHeader.Value);
|
||||
}
|
||||
|
||||
public void OnStaticIndexedHeader(int index, ReadOnlySpan<byte> value)
|
||||
|
|
|
|||
|
|
@ -2001,7 +2001,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests
|
|||
{
|
||||
var testContext = new TestServiceContext(LoggerFactory);
|
||||
|
||||
testContext.ServerOptions.Latin1RequestHeaders = true;
|
||||
testContext.ServerOptions.RequestHeaderEncodingSelector = _ => Encoding.Latin1;
|
||||
|
||||
await using (var server = new TestServer(context =>
|
||||
{
|
||||
|
|
@ -2059,6 +2059,42 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests
|
|||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CustomRequestHeaderEncodingSelectorCanBeConfigured()
|
||||
{
|
||||
var testContext = new TestServiceContext(LoggerFactory);
|
||||
|
||||
testContext.ServerOptions.RequestHeaderEncodingSelector = _ => Encoding.UTF32;
|
||||
|
||||
await using (var server = new TestServer(context =>
|
||||
{
|
||||
Assert.Equal("£", context.Request.Headers["X-Test"]);
|
||||
return Task.CompletedTask;
|
||||
}, testContext))
|
||||
{
|
||||
using (var connection = server.CreateConnection())
|
||||
{
|
||||
await connection.Send(
|
||||
"GET / HTTP/1.1",
|
||||
"Host:",
|
||||
"X-Test: ");
|
||||
|
||||
await connection.Stream.WriteAsync(Encoding.UTF32.GetBytes("£")).DefaultTimeout();
|
||||
|
||||
await connection.Send("",
|
||||
"",
|
||||
"");
|
||||
|
||||
await connection.Receive(
|
||||
"HTTP/1.1 200 OK",
|
||||
$"Date: {testContext.DateHeaderValue}",
|
||||
"Content-Length: 0",
|
||||
"",
|
||||
"");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static TheoryData<string, string> HostHeaderData => HttpParsingData.HostHeaderData;
|
||||
|
||||
private class IntAsClass
|
||||
|
|
|
|||
|
|
@ -15,6 +15,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure
|
|||
{
|
||||
internal static class StringUtilities
|
||||
{
|
||||
private static readonly SpanAction<char, IntPtr> s_getAsciiOrUtf8StringNonNullCharacters = GetAsciiStringNonNullCharacters;
|
||||
|
||||
private static string GetAsciiOrUTF8StringNonNullCharacters(this Span<byte> span, Encoding defaultEncoding)
|
||||
=> GetAsciiOrUTF8StringNonNullCharacters((ReadOnlySpan<byte>)span, defaultEncoding);
|
||||
|
||||
|
|
@ -52,15 +54,13 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure
|
|||
}
|
||||
}
|
||||
|
||||
private static readonly SpanAction<char, IntPtr> s_getAsciiOrUtf8StringNonNullCharacters = GetAsciiOrUTF8StringNonNullCharacters;
|
||||
|
||||
private static unsafe void GetAsciiOrUTF8StringNonNullCharacters(Span<char> buffer, IntPtr state)
|
||||
private static unsafe void GetAsciiStringNonNullCharacters(Span<char> buffer, IntPtr state)
|
||||
{
|
||||
fixed (char* output = &MemoryMarshal.GetReference(buffer))
|
||||
{
|
||||
// This version if AsciiUtilities returns null if there are any null (0 byte) characters
|
||||
// in the string
|
||||
if (!StringUtilities.TryGetAsciiString((byte*)state.ToPointer(), output, buffer.Length))
|
||||
// This version if AsciiUtilities returns false if there are any null ('\0') or non-Ascii
|
||||
// character (> 127) in the string.
|
||||
if (!TryGetAsciiString((byte*)state.ToPointer(), output, buffer.Length))
|
||||
{
|
||||
// Mark resultString for UTF-8 encoding
|
||||
output[0] = '\0';
|
||||
|
|
|
|||
Loading…
Reference in New Issue