Allow headers to match on ReferenceEquals before OrdinalIgnoreCase (#9341)

This commit is contained in:
Ben Adams 2019-04-18 20:00:01 +01:00 committed by Stephen Halter
parent 52b3b19e06
commit 8fcadf72cd
35 changed files with 7465 additions and 3809 deletions

View File

@ -263,12 +263,12 @@ namespace Microsoft.AspNetCore.Antiforgery
_tokenStore.SaveCookieToken(httpContext, cookieToken); _tokenStore.SaveCookieToken(httpContext, cookieToken);
} }
if (!_options.SuppressXFrameOptionsHeader && !httpContext.Response.Headers.ContainsKey("X-Frame-Options")) if (!_options.SuppressXFrameOptionsHeader && !httpContext.Response.Headers.ContainsKey(HeaderNames.XFrameOptions))
{ {
// Adding X-Frame-Options header to prevent ClickJacking. See // Adding X-Frame-Options header to prevent ClickJacking. See
// http://tools.ietf.org/html/draft-ietf-websec-x-frame-options-10 // http://tools.ietf.org/html/draft-ietf-websec-x-frame-options-10
// for more information. // for more information.
httpContext.Response.Headers["X-Frame-Options"] = "SAMEORIGIN"; httpContext.Response.Headers[HeaderNames.XFrameOptions] = "SAMEORIGIN";
} }
} }

View File

@ -20,6 +20,7 @@ using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Microsoft.Extensions.StackTrace.Sources; using Microsoft.Extensions.StackTrace.Sources;
using Microsoft.Net.Http.Headers;
namespace Microsoft.AspNetCore.Hosting.Internal namespace Microsoft.AspNetCore.Hosting.Internal
{ {
@ -184,7 +185,7 @@ namespace Microsoft.AspNetCore.Hosting.Internal
return context => return context =>
{ {
context.Response.StatusCode = 500; context.Response.StatusCode = 500;
context.Response.Headers["Cache-Control"] = "no-cache"; context.Response.Headers[HeaderNames.CacheControl] = "no-cache";
return errorPage.ExecuteAsync(context); return errorPage.ExecuteAsync(context);
}; };
} }

View File

@ -22,11 +22,6 @@ namespace Microsoft.AspNetCore.Hosting.Internal
private const string DeprecatedDiagnosticsEndRequestKey = "Microsoft.AspNetCore.Hosting.EndRequest"; private const string DeprecatedDiagnosticsEndRequestKey = "Microsoft.AspNetCore.Hosting.EndRequest";
private const string DiagnosticsUnhandledExceptionKey = "Microsoft.AspNetCore.Hosting.UnhandledException"; private const string DiagnosticsUnhandledExceptionKey = "Microsoft.AspNetCore.Hosting.UnhandledException";
private const string RequestIdHeaderName = "Request-Id";
private const string CorrelationContextHeaderName = "Correlation-Context";
private const string TraceParentHeaderName = "traceparent";
private const string TraceStateHeaderName = "tracestate";
private readonly DiagnosticListener _diagnosticListener; private readonly DiagnosticListener _diagnosticListener;
private readonly ILogger _logger; private readonly ILogger _logger;
@ -238,22 +233,22 @@ namespace Microsoft.AspNetCore.Hosting.Internal
{ {
var activity = new Activity(ActivityName); var activity = new Activity(ActivityName);
if (!httpContext.Request.Headers.TryGetValue(TraceParentHeaderName, out var requestId)) if (!httpContext.Request.Headers.TryGetValue(HeaderNames.TraceParent, out var requestId))
{ {
httpContext.Request.Headers.TryGetValue(RequestIdHeaderName, out requestId); httpContext.Request.Headers.TryGetValue(HeaderNames.RequestId, out requestId);
} }
if (!StringValues.IsNullOrEmpty(requestId)) if (!StringValues.IsNullOrEmpty(requestId))
{ {
activity.SetParentId(requestId); activity.SetParentId(requestId);
if (httpContext.Request.Headers.TryGetValue(TraceStateHeaderName, out var traceState)) if (httpContext.Request.Headers.TryGetValue(HeaderNames.TraceState, out var traceState))
{ {
activity.TraceStateString = traceState; activity.TraceStateString = traceState;
} }
// We expect baggage to be empty by default // We expect baggage to be empty by default
// Only very advanced users will be using it in near future, we encourage them to keep baggage small (few items) // Only very advanced users will be using it in near future, we encourage them to keep baggage small (few items)
string[] baggage = httpContext.Request.Headers.GetCommaSeparatedValues(CorrelationContextHeaderName); string[] baggage = httpContext.Request.Headers.GetCommaSeparatedValues(HeaderNames.CorrelationContext);
if (baggage.Length > 0) if (baggage.Length > 0)
{ {
foreach (var item in baggage) foreach (var item in baggage)

View File

@ -22,6 +22,7 @@ using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.StackTrace.Sources; using Microsoft.Extensions.StackTrace.Sources;
using Microsoft.Net.Http.Headers;
namespace Microsoft.AspNetCore.Hosting.Internal namespace Microsoft.AspNetCore.Hosting.Internal
{ {
@ -276,7 +277,7 @@ namespace Microsoft.AspNetCore.Hosting.Internal
return context => return context =>
{ {
context.Response.StatusCode = 500; context.Response.StatusCode = 500;
context.Response.Headers["Cache-Control"] = "no-cache"; context.Response.Headers[HeaderNames.CacheControl] = "no-cache";
return errorPage.ExecuteAsync(context); return errorPage.ExecuteAsync(context);
}; };
} }

View File

@ -118,74 +118,87 @@ namespace Microsoft.Net.Http.Headers
} }
public static partial class HeaderNames public static partial class HeaderNames
{ {
public const string Accept = "Accept"; public static readonly string Accept;
public const string AcceptCharset = "Accept-Charset"; public static readonly string AcceptCharset;
public const string AcceptEncoding = "Accept-Encoding"; public static readonly string AcceptEncoding;
public const string AcceptLanguage = "Accept-Language"; public static readonly string AcceptLanguage;
public const string AcceptRanges = "Accept-Ranges"; public static readonly string AcceptRanges;
public const string AccessControlAllowCredentials = "Access-Control-Allow-Credentials"; public static readonly string AccessControlAllowCredentials;
public const string AccessControlAllowHeaders = "Access-Control-Allow-Headers"; public static readonly string AccessControlAllowHeaders;
public const string AccessControlAllowMethods = "Access-Control-Allow-Methods"; public static readonly string AccessControlAllowMethods;
public const string AccessControlAllowOrigin = "Access-Control-Allow-Origin"; public static readonly string AccessControlAllowOrigin;
public const string AccessControlExposeHeaders = "Access-Control-Expose-Headers"; public static readonly string AccessControlExposeHeaders;
public const string AccessControlMaxAge = "Access-Control-Max-Age"; public static readonly string AccessControlMaxAge;
public const string AccessControlRequestHeaders = "Access-Control-Request-Headers"; public static readonly string AccessControlRequestHeaders;
public const string AccessControlRequestMethod = "Access-Control-Request-Method"; public static readonly string AccessControlRequestMethod;
public const string Age = "Age"; public static readonly string Age;
public const string Allow = "Allow"; public static readonly string Allow;
public const string Authority = ":authority"; public static readonly string Authority;
public const string Authorization = "Authorization"; public static readonly string Authorization;
public const string CacheControl = "Cache-Control"; public static readonly string CacheControl;
public const string Connection = "Connection"; public static readonly string Connection;
public const string ContentDisposition = "Content-Disposition"; public static readonly string ContentDisposition;
public const string ContentEncoding = "Content-Encoding"; public static readonly string ContentEncoding;
public const string ContentLanguage = "Content-Language"; public static readonly string ContentLanguage;
public const string ContentLength = "Content-Length"; public static readonly string ContentLength;
public const string ContentLocation = "Content-Location"; public static readonly string ContentLocation;
public const string ContentMD5 = "Content-MD5"; public static readonly string ContentMD5;
public const string ContentRange = "Content-Range"; public static readonly string ContentRange;
public const string ContentSecurityPolicy = "Content-Security-Policy"; public static readonly string ContentSecurityPolicy;
public const string ContentSecurityPolicyReportOnly = "Content-Security-Policy-Report-Only"; public static readonly string ContentSecurityPolicyReportOnly;
public const string ContentType = "Content-Type"; public static readonly string ContentType;
public const string Cookie = "Cookie"; public static readonly string Cookie;
public const string Date = "Date"; public static readonly string CorrelationContext;
public const string ETag = "ETag"; public static readonly string Date;
public const string Expect = "Expect"; public static readonly string DNT;
public const string Expires = "Expires"; public static readonly string ETag;
public const string From = "From"; public static readonly string Expect;
public const string Host = "Host"; public static readonly string Expires;
public const string IfMatch = "If-Match"; public static readonly string From;
public const string IfModifiedSince = "If-Modified-Since"; public static readonly string Host;
public const string IfNoneMatch = "If-None-Match"; public static readonly string IfMatch;
public const string IfRange = "If-Range"; public static readonly string IfModifiedSince;
public const string IfUnmodifiedSince = "If-Unmodified-Since"; public static readonly string IfNoneMatch;
public const string LastModified = "Last-Modified"; public static readonly string IfRange;
public const string Location = "Location"; public static readonly string IfUnmodifiedSince;
public const string MaxForwards = "Max-Forwards"; public static readonly string KeepAlive;
public const string Method = ":method"; public static readonly string LastModified;
public const string Origin = "Origin"; public static readonly string Location;
public const string Path = ":path"; public static readonly string MaxForwards;
public const string Pragma = "Pragma"; public static readonly string Method;
public const string ProxyAuthenticate = "Proxy-Authenticate"; public static readonly string Origin;
public const string ProxyAuthorization = "Proxy-Authorization"; public static readonly string Path;
public const string Range = "Range"; public static readonly string Pragma;
public const string Referer = "Referer"; public static readonly string ProxyAuthenticate;
public const string RetryAfter = "Retry-After"; public static readonly string ProxyAuthorization;
public const string Scheme = ":scheme"; public static readonly string Range;
public const string Server = "Server"; public static readonly string Referer;
public const string SetCookie = "Set-Cookie"; public static readonly string RequestId;
public const string Status = ":status"; public static readonly string RetryAfter;
public const string StrictTransportSecurity = "Strict-Transport-Security"; public static readonly string Scheme;
public const string TE = "TE"; public static readonly string SecWebSocketAccept;
public const string Trailer = "Trailer"; public static readonly string SecWebSocketKey;
public const string TransferEncoding = "Transfer-Encoding"; public static readonly string SecWebSocketProtocol;
public const string Upgrade = "Upgrade"; public static readonly string SecWebSocketVersion;
public const string UserAgent = "User-Agent"; public static readonly string Server;
public const string Vary = "Vary"; public static readonly string SetCookie;
public const string Via = "Via"; public static readonly string Status;
public const string Warning = "Warning"; public static readonly string StrictTransportSecurity;
public const string WebSocketSubProtocols = "Sec-WebSocket-Protocol"; public static readonly string TE;
public const string WWWAuthenticate = "WWW-Authenticate"; public static readonly string TraceParent;
public static readonly string TraceState;
public static readonly string Trailer;
public static readonly string TransferEncoding;
public static readonly string Translate;
public static readonly string Upgrade;
public static readonly string UpgradeInsecureRequests;
public static readonly string UserAgent;
public static readonly string Vary;
public static readonly string Via;
public static readonly string Warning;
public static readonly string WebSocketSubProtocols;
public static readonly string WWWAuthenticate;
public static readonly string XFrameOptions;
} }
public static partial class HeaderQuality public static partial class HeaderQuality
{ {

View File

@ -5,73 +5,87 @@ namespace Microsoft.Net.Http.Headers
{ {
public static class HeaderNames public static class HeaderNames
{ {
public const string Accept = "Accept"; // Use readonly statics rather than constants so ReferenceEquals works
public const string AcceptCharset = "Accept-Charset"; public static readonly string Accept = "Accept";
public const string AcceptEncoding = "Accept-Encoding"; public static readonly string AcceptCharset = "Accept-Charset";
public const string AcceptLanguage = "Accept-Language"; public static readonly string AcceptEncoding = "Accept-Encoding";
public const string AcceptRanges = "Accept-Ranges"; public static readonly string AcceptLanguage = "Accept-Language";
public const string AccessControlAllowCredentials = "Access-Control-Allow-Credentials"; public static readonly string AcceptRanges = "Accept-Ranges";
public const string AccessControlAllowHeaders = "Access-Control-Allow-Headers"; public static readonly string AccessControlAllowCredentials = "Access-Control-Allow-Credentials";
public const string AccessControlAllowMethods = "Access-Control-Allow-Methods"; public static readonly string AccessControlAllowHeaders = "Access-Control-Allow-Headers";
public const string AccessControlAllowOrigin = "Access-Control-Allow-Origin"; public static readonly string AccessControlAllowMethods = "Access-Control-Allow-Methods";
public const string AccessControlExposeHeaders = "Access-Control-Expose-Headers"; public static readonly string AccessControlAllowOrigin = "Access-Control-Allow-Origin";
public const string AccessControlMaxAge = "Access-Control-Max-Age"; public static readonly string AccessControlExposeHeaders = "Access-Control-Expose-Headers";
public const string AccessControlRequestHeaders = "Access-Control-Request-Headers"; public static readonly string AccessControlMaxAge = "Access-Control-Max-Age";
public const string AccessControlRequestMethod = "Access-Control-Request-Method"; public static readonly string AccessControlRequestHeaders = "Access-Control-Request-Headers";
public const string Age = "Age"; public static readonly string AccessControlRequestMethod = "Access-Control-Request-Method";
public const string Allow = "Allow"; public static readonly string Age = "Age";
public const string Authority = ":authority"; public static readonly string Allow = "Allow";
public const string Authorization = "Authorization"; public static readonly string Authority = ":authority";
public const string CacheControl = "Cache-Control"; public static readonly string Authorization = "Authorization";
public const string Connection = "Connection"; public static readonly string CacheControl = "Cache-Control";
public const string ContentDisposition = "Content-Disposition"; public static readonly string Connection = "Connection";
public const string ContentEncoding = "Content-Encoding"; public static readonly string ContentDisposition = "Content-Disposition";
public const string ContentLanguage = "Content-Language"; public static readonly string ContentEncoding = "Content-Encoding";
public const string ContentLength = "Content-Length"; public static readonly string ContentLanguage = "Content-Language";
public const string ContentLocation = "Content-Location"; public static readonly string ContentLength = "Content-Length";
public const string ContentMD5 = "Content-MD5"; public static readonly string ContentLocation = "Content-Location";
public const string ContentRange = "Content-Range"; public static readonly string ContentMD5 = "Content-MD5";
public const string ContentSecurityPolicy = "Content-Security-Policy"; public static readonly string ContentRange = "Content-Range";
public const string ContentSecurityPolicyReportOnly = "Content-Security-Policy-Report-Only"; public static readonly string ContentSecurityPolicy = "Content-Security-Policy";
public const string ContentType = "Content-Type"; public static readonly string ContentSecurityPolicyReportOnly = "Content-Security-Policy-Report-Only";
public const string Cookie = "Cookie"; public static readonly string ContentType = "Content-Type";
public const string Date = "Date"; public static readonly string CorrelationContext = "Correlation-Context";
public const string ETag = "ETag"; public static readonly string Cookie = "Cookie";
public const string Expires = "Expires"; public static readonly string Date = "Date";
public const string Expect = "Expect"; public static readonly string DNT = "DNT";
public const string From = "From"; public static readonly string ETag = "ETag";
public const string Host = "Host"; public static readonly string Expires = "Expires";
public const string IfMatch = "If-Match"; public static readonly string Expect = "Expect";
public const string IfModifiedSince = "If-Modified-Since"; public static readonly string From = "From";
public const string IfNoneMatch = "If-None-Match"; public static readonly string Host = "Host";
public const string IfRange = "If-Range"; public static readonly string KeepAlive = "Keep-Alive";
public const string IfUnmodifiedSince = "If-Unmodified-Since"; public static readonly string IfMatch = "If-Match";
public const string LastModified = "Last-Modified"; public static readonly string IfModifiedSince = "If-Modified-Since";
public const string Location = "Location"; public static readonly string IfNoneMatch = "If-None-Match";
public const string MaxForwards = "Max-Forwards"; public static readonly string IfRange = "If-Range";
public const string Method = ":method"; public static readonly string IfUnmodifiedSince = "If-Unmodified-Since";
public const string Origin = "Origin"; public static readonly string LastModified = "Last-Modified";
public const string Path = ":path"; public static readonly string Location = "Location";
public const string Pragma = "Pragma"; public static readonly string MaxForwards = "Max-Forwards";
public const string ProxyAuthenticate = "Proxy-Authenticate"; public static readonly string Method = ":method";
public const string ProxyAuthorization = "Proxy-Authorization"; public static readonly string Origin = "Origin";
public const string Range = "Range"; public static readonly string Path = ":path";
public const string Referer = "Referer"; public static readonly string Pragma = "Pragma";
public const string RetryAfter = "Retry-After"; public static readonly string ProxyAuthenticate = "Proxy-Authenticate";
public const string Scheme = ":scheme"; public static readonly string ProxyAuthorization = "Proxy-Authorization";
public const string Server = "Server"; public static readonly string Range = "Range";
public const string SetCookie = "Set-Cookie"; public static readonly string Referer = "Referer";
public const string Status = ":status"; public static readonly string RetryAfter = "Retry-After";
public const string StrictTransportSecurity = "Strict-Transport-Security"; public static readonly string RequestId = "Request-Id";
public const string TE = "TE"; public static readonly string Scheme = ":scheme";
public const string Trailer = "Trailer"; public static readonly string SecWebSocketAccept = "Sec-WebSocket-Accept";
public const string TransferEncoding = "Transfer-Encoding"; public static readonly string SecWebSocketKey = "Sec-WebSocket-Key";
public const string Upgrade = "Upgrade"; public static readonly string SecWebSocketProtocol = "Sec-WebSocket-Protocol";
public const string UserAgent = "User-Agent"; public static readonly string SecWebSocketVersion = "Sec-WebSocket-Version";
public const string Vary = "Vary"; public static readonly string Server = "Server";
public const string Via = "Via"; public static readonly string SetCookie = "Set-Cookie";
public const string Warning = "Warning"; public static readonly string Status = ":status";
public const string WebSocketSubProtocols = "Sec-WebSocket-Protocol"; public static readonly string StrictTransportSecurity = "Strict-Transport-Security";
public const string WWWAuthenticate = "WWW-Authenticate"; public static readonly string TE = "TE";
public static readonly string Trailer = "Trailer";
public static readonly string TransferEncoding = "Transfer-Encoding";
public static readonly string Translate = "Translate";
public static readonly string TraceParent = "traceparent";
public static readonly string TraceState = "tracestate";
public static readonly string Upgrade = "Upgrade";
public static readonly string UpgradeInsecureRequests = "Upgrade-Insecure-Requests";
public static readonly string UserAgent = "User-Agent";
public static readonly string Vary = "Vary";
public static readonly string Via = "Via";
public static readonly string Warning = "Warning";
public static readonly string WebSocketSubProtocols = "Sec-WebSocket-Protocol";
public static readonly string WWWAuthenticate = "WWW-Authenticate";
public static readonly string XFrameOptions = "X-Frame-Options";
} }
} }

View File

@ -7,6 +7,7 @@
<Compile Include="Microsoft.AspNetCore.Http.Abstractions.netcoreapp3.0.cs" /> <Compile Include="Microsoft.AspNetCore.Http.Abstractions.netcoreapp3.0.cs" />
<Reference Include="Microsoft.AspNetCore.Http.Features" /> <Reference Include="Microsoft.AspNetCore.Http.Features" />
<Reference Include="Microsoft.Extensions.ActivatorUtilities.Sources" /> <Reference Include="Microsoft.Extensions.ActivatorUtilities.Sources" />
<Reference Include="Microsoft.Net.Http.Headers" />
<Reference Include="System.Text.Encodings.Web" /> <Reference Include="System.Text.Encodings.Web" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -4,13 +4,12 @@
using System; using System;
using Microsoft.AspNetCore.Http.Features; using Microsoft.AspNetCore.Http.Features;
using Microsoft.Extensions.Primitives; using Microsoft.Extensions.Primitives;
using Microsoft.Net.Http.Headers;
namespace Microsoft.AspNetCore.Http namespace Microsoft.AspNetCore.Http
{ {
public static class ResponseTrailerExtensions public static class ResponseTrailerExtensions
{ {
private const string Trailer = "Trailer";
/// <summary> /// <summary>
/// Adds the given trailer name to the 'Trailer' response header. This must happen before the response headers are sent. /// Adds the given trailer name to the 'Trailer' response header. This must happen before the response headers are sent.
/// </summary> /// </summary>
@ -18,7 +17,7 @@ namespace Microsoft.AspNetCore.Http
/// <param name="trailerName"></param> /// <param name="trailerName"></param>
public static void DeclareTrailer(this HttpResponse response, string trailerName) public static void DeclareTrailer(this HttpResponse response, string trailerName)
{ {
response.Headers.AppendCommaSeparatedValues(Trailer, trailerName); response.Headers.AppendCommaSeparatedValues(HeaderNames.Trailer, trailerName);
} }
/// <summary> /// <summary>

View File

@ -22,6 +22,7 @@ Microsoft.AspNetCore.Http.HttpResponse</Description>
<ItemGroup> <ItemGroup>
<Reference Include="Microsoft.AspNetCore.Http.Features" /> <Reference Include="Microsoft.AspNetCore.Http.Features" />
<Reference Include="Microsoft.Extensions.ActivatorUtilities.Sources" /> <Reference Include="Microsoft.Extensions.ActivatorUtilities.Sources" />
<Reference Include="Microsoft.Net.Http.Headers" />
<Reference Include="System.Text.Encodings.Web" /> <Reference Include="System.Text.Encodings.Web" />
</ItemGroup> </ItemGroup>

View File

@ -116,8 +116,8 @@ namespace Microsoft.AspNetCore.Http.Internal
public override HostString Host public override HostString Host
{ {
get { return HostString.FromUriComponent(Headers["Host"]); } get { return HostString.FromUriComponent(Headers[HeaderNames.Host]); }
set { Headers["Host"] = value.ToUriComponent(); } set { Headers[HeaderNames.Host] = value.ToUriComponent(); }
} }
public override IQueryCollection Query public override IQueryCollection Query

View File

@ -5,6 +5,7 @@ using System;
using System.IO; using System.IO;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.Net.Http.Headers;
namespace Microsoft.AspNetCore.Http.Internal namespace Microsoft.AspNetCore.Http.Internal
{ {
@ -30,8 +31,8 @@ namespace Microsoft.AspNetCore.Http.Internal
/// </summary> /// </summary>
public string ContentDisposition public string ContentDisposition
{ {
get { return Headers["Content-Disposition"]; } get { return Headers[HeaderNames.ContentDisposition]; }
set { Headers["Content-Disposition"] = value; } set { Headers[HeaderNames.ContentDisposition] = value; }
} }
/// <summary> /// <summary>
@ -39,8 +40,8 @@ namespace Microsoft.AspNetCore.Http.Internal
/// </summary> /// </summary>
public string ContentType public string ContentType
{ {
get { return Headers["Content-Type"]; } get { return Headers[HeaderNames.ContentType]; }
set { Headers["Content-Type"] = value; } set { Headers[HeaderNames.ContentType] = value; }
} }
/// <summary> /// <summary>

View File

@ -391,7 +391,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
// Prevent ArgumentException from duplicate key if header already added, such as when the // Prevent ArgumentException from duplicate key if header already added, such as when the
// request is re-executed by an error handler (see https://github.com/aspnet/AspNetCore/issues/6415) // request is re-executed by an error handler (see https://github.com/aspnet/AspNetCore/issues/6415)
context.Response.Headers["Allow"] = allow; context.Response.Headers[HeaderNames.Allow] = allow;
return Task.CompletedTask; return Task.CompletedTask;
}, },

View File

@ -4,6 +4,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using Microsoft.Extensions.Primitives; using Microsoft.Extensions.Primitives;
using Microsoft.Net.Http.Headers;
namespace Microsoft.AspNetCore.WebUtilities namespace Microsoft.AspNetCore.WebUtilities
{ {
@ -14,7 +15,7 @@ namespace Microsoft.AspNetCore.WebUtilities
get get
{ {
StringValues values; StringValues values;
if (Headers.TryGetValue("Content-Type", out values)) if (Headers.TryGetValue(HeaderNames.ContentType, out values))
{ {
return values; return values;
} }
@ -27,7 +28,7 @@ namespace Microsoft.AspNetCore.WebUtilities
get get
{ {
StringValues values; StringValues values;
if (Headers.TryGetValue("Content-Disposition", out values)) if (Headers.TryGetValue(HeaderNames.ContentDisposition, out values))
{ {
return values; return values;
} }
@ -45,4 +46,4 @@ namespace Microsoft.AspNetCore.WebUtilities
/// </summary> /// </summary>
public long? BaseStreamOffset { get; set; } public long? BaseStreamOffset { get; set; }
} }
} }

View File

@ -11,6 +11,7 @@ using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features; using Microsoft.AspNetCore.Http.Features;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Microsoft.Net.Http.Headers;
namespace Microsoft.AspNetCore.Session namespace Microsoft.AspNetCore.Session
{ {
@ -159,9 +160,9 @@ namespace Microsoft.AspNetCore.Session
_context.Response.Cookies.Append(_options.Cookie.Name, _cookieValue, cookieOptions); _context.Response.Cookies.Append(_options.Cookie.Name, _cookieValue, cookieOptions);
_context.Response.Headers["Cache-Control"] = "no-cache"; _context.Response.Headers[HeaderNames.CacheControl] = "no-cache";
_context.Response.Headers["Pragma"] = "no-cache"; _context.Response.Headers[HeaderNames.Pragma] = "no-cache";
_context.Response.Headers["Expires"] = "-1"; _context.Response.Headers[HeaderNames.Expires] = "-1";
} }
// Returns true if the session has already been established, or if it still can be because the response has not been sent. // Returns true if the session has already been established, or if it still can be because the response has not been sent.
@ -171,4 +172,4 @@ namespace Microsoft.AspNetCore.Session
} }
} }
} }
} }

View File

@ -35,21 +35,3 @@ namespace Microsoft.AspNetCore.WebSockets
public static Microsoft.Extensions.DependencyInjection.IServiceCollection AddWebSockets(this Microsoft.Extensions.DependencyInjection.IServiceCollection services, System.Action<Microsoft.AspNetCore.Builder.WebSocketOptions> configure) { throw null; } public static Microsoft.Extensions.DependencyInjection.IServiceCollection AddWebSockets(this Microsoft.Extensions.DependencyInjection.IServiceCollection services, System.Action<Microsoft.AspNetCore.Builder.WebSocketOptions> configure) { throw null; }
} }
} }
namespace Microsoft.AspNetCore.WebSockets.Internal
{
public static partial class Constants
{
public static partial class Headers
{
public const string Connection = "Connection";
public const string ConnectionUpgrade = "Upgrade";
public const string SecWebSocketAccept = "Sec-WebSocket-Accept";
public const string SecWebSocketKey = "Sec-WebSocket-Key";
public const string SecWebSocketProtocol = "Sec-WebSocket-Protocol";
public const string SecWebSocketVersion = "Sec-WebSocket-Version";
public const string SupportedVersion = "13";
public const string Upgrade = "Upgrade";
public const string UpgradeWebSocket = "websocket";
}
}
}

View File

@ -3,18 +3,12 @@
namespace Microsoft.AspNetCore.WebSockets.Internal namespace Microsoft.AspNetCore.WebSockets.Internal
{ {
public static class Constants internal static class Constants
{ {
public static class Headers public static class Headers
{ {
public const string Upgrade = "Upgrade"; public const string UpgradeWebSocket = "websocket";
public const string UpgradeWebSocket = "websocket";
public const string Connection = "Connection";
public const string ConnectionUpgrade = "Upgrade"; public const string ConnectionUpgrade = "Upgrade";
public const string SecWebSocketKey = "Sec-WebSocket-Key";
public const string SecWebSocketVersion = "Sec-WebSocket-Version";
public const string SecWebSocketProtocol = "Sec-WebSocket-Protocol";
public const string SecWebSocketAccept = "Sec-WebSocket-Accept";
public const string SupportedVersion = "13"; public const string SupportedVersion = "13";
} }
} }

View File

@ -6,6 +6,7 @@ using System.Collections.Generic;
using System.Security.Cryptography; using System.Security.Cryptography;
using System.Text; using System.Text;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.Net.Http.Headers;
namespace Microsoft.AspNetCore.WebSockets.Internal namespace Microsoft.AspNetCore.WebSockets.Internal
{ {
@ -16,10 +17,10 @@ namespace Microsoft.AspNetCore.WebSockets.Internal
/// </summary> /// </summary>
public static readonly IEnumerable<string> NeededHeaders = new[] public static readonly IEnumerable<string> NeededHeaders = new[]
{ {
Constants.Headers.Upgrade, HeaderNames.Upgrade,
Constants.Headers.Connection, HeaderNames.Connection,
Constants.Headers.SecWebSocketKey, HeaderNames.SecWebSocketKey,
Constants.Headers.SecWebSocketVersion HeaderNames.SecWebSocketVersion
}; };
// Verify Method, Upgrade, Connection, version, key, etc.. // Verify Method, Upgrade, Connection, version, key, etc..
@ -34,28 +35,28 @@ namespace Microsoft.AspNetCore.WebSockets.Internal
foreach (var pair in headers) foreach (var pair in headers)
{ {
if (string.Equals(Constants.Headers.Connection, pair.Key, StringComparison.OrdinalIgnoreCase)) if (string.Equals(HeaderNames.Connection, pair.Key, StringComparison.OrdinalIgnoreCase))
{ {
if (string.Equals(Constants.Headers.ConnectionUpgrade, pair.Value, StringComparison.OrdinalIgnoreCase)) if (string.Equals(Constants.Headers.ConnectionUpgrade, pair.Value, StringComparison.OrdinalIgnoreCase))
{ {
validConnection = true; validConnection = true;
} }
} }
else if (string.Equals(Constants.Headers.Upgrade, pair.Key, StringComparison.OrdinalIgnoreCase)) else if (string.Equals(HeaderNames.Upgrade, pair.Key, StringComparison.OrdinalIgnoreCase))
{ {
if (string.Equals(Constants.Headers.UpgradeWebSocket, pair.Value, StringComparison.OrdinalIgnoreCase)) if (string.Equals(Constants.Headers.UpgradeWebSocket, pair.Value, StringComparison.OrdinalIgnoreCase))
{ {
validUpgrade = true; validUpgrade = true;
} }
} }
else if (string.Equals(Constants.Headers.SecWebSocketVersion, pair.Key, StringComparison.OrdinalIgnoreCase)) else if (string.Equals(HeaderNames.SecWebSocketVersion, pair.Key, StringComparison.OrdinalIgnoreCase))
{ {
if (string.Equals(Constants.Headers.SupportedVersion, pair.Value, StringComparison.OrdinalIgnoreCase)) if (string.Equals(Constants.Headers.SupportedVersion, pair.Value, StringComparison.OrdinalIgnoreCase))
{ {
validVersion = true; validVersion = true;
} }
} }
else if (string.Equals(Constants.Headers.SecWebSocketKey, pair.Key, StringComparison.OrdinalIgnoreCase)) else if (string.Equals(HeaderNames.SecWebSocketKey, pair.Key, StringComparison.OrdinalIgnoreCase))
{ {
validKey = IsRequestKeyValid(pair.Value); validKey = IsRequestKeyValid(pair.Value);
} }
@ -66,12 +67,12 @@ namespace Microsoft.AspNetCore.WebSockets.Internal
public static void GenerateResponseHeaders(string key, string subProtocol, IHeaderDictionary headers) public static void GenerateResponseHeaders(string key, string subProtocol, IHeaderDictionary headers)
{ {
headers[Constants.Headers.Connection] = Constants.Headers.ConnectionUpgrade; headers[HeaderNames.Connection] = Constants.Headers.ConnectionUpgrade;
headers[Constants.Headers.Upgrade] = Constants.Headers.UpgradeWebSocket; headers[HeaderNames.Upgrade] = Constants.Headers.UpgradeWebSocket;
headers[Constants.Headers.SecWebSocketAccept] = CreateResponseKey(key); headers[HeaderNames.SecWebSocketAccept] = CreateResponseKey(key);
if (!string.IsNullOrWhiteSpace(subProtocol)) if (!string.IsNullOrWhiteSpace(subProtocol))
{ {
headers[Constants.Headers.SecWebSocketProtocol] = subProtocol; headers[HeaderNames.SecWebSocketProtocol] = subProtocol;
} }
} }
@ -118,4 +119,4 @@ namespace Microsoft.AspNetCore.WebSockets.Internal
} }
} }
} }
} }

View File

@ -147,7 +147,7 @@ namespace Microsoft.AspNetCore.WebSockets
} }
} }
string key = string.Join(", ", _context.Request.Headers[Constants.Headers.SecWebSocketKey]); string key = string.Join(", ", _context.Request.Headers[HeaderNames.SecWebSocketKey]);
HandshakeHelpers.GenerateResponseHeaders(key, subProtocol, _context.Response.Headers); HandshakeHelpers.GenerateResponseHeaders(key, subProtocol, _context.Response.Headers);

View File

@ -12,6 +12,7 @@ using Microsoft.AspNetCore.Testing;
using Microsoft.AspNetCore.Testing.xunit; using Microsoft.AspNetCore.Testing.xunit;
using Microsoft.AspNetCore.WebSockets.Internal; using Microsoft.AspNetCore.WebSockets.Internal;
using Microsoft.Extensions.Logging.Testing; using Microsoft.Extensions.Logging.Testing;
using Microsoft.Net.Http.Headers;
using Xunit; using Xunit;
namespace Microsoft.AspNetCore.WebSockets.Test namespace Microsoft.AspNetCore.WebSockets.Test
@ -537,11 +538,11 @@ namespace Microsoft.AspNetCore.WebSockets.Test
request.Headers.Connection.Clear(); request.Headers.Connection.Clear();
request.Headers.Connection.Add("Upgrade"); request.Headers.Connection.Add("Upgrade");
request.Headers.Upgrade.Add(new System.Net.Http.Headers.ProductHeaderValue("websocket")); request.Headers.Upgrade.Add(new System.Net.Http.Headers.ProductHeaderValue("websocket"));
request.Headers.Add(Constants.Headers.SecWebSocketVersion, Constants.Headers.SupportedVersion); request.Headers.Add(HeaderNames.SecWebSocketVersion, "13");
// SecWebSocketKey required to be 16 bytes // SecWebSocketKey required to be 16 bytes
request.Headers.Add(Constants.Headers.SecWebSocketKey, Convert.ToBase64String(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 }, Base64FormattingOptions.None)); request.Headers.Add(HeaderNames.SecWebSocketKey, Convert.ToBase64String(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 }, Base64FormattingOptions.None));
request.Headers.Add("Origin", "http://example.com"); request.Headers.Add(HeaderNames.Origin, "http://example.com");
var response = await client.SendAsync(request); var response = await client.SendAsync(request);
Assert.Equal(expectedCode, response.StatusCode); Assert.Equal(expectedCode, response.StatusCode);

View File

@ -7,6 +7,7 @@ using System.Net;
using System.Net.Http; using System.Net.Http;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
using Microsoft.Net.Http.Headers;
using Xunit; using Xunit;
namespace Microsoft.AspNetCore.Mvc.FunctionalTests namespace Microsoft.AspNetCore.Mvc.FunctionalTests
@ -41,7 +42,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
// Arrange // Arrange
var requestId = Guid.NewGuid().ToString(); var requestId = Guid.NewGuid().ToString();
var request = new HttpRequestMessage(HttpMethod.Get, url); var request = new HttpRequestMessage(HttpMethod.Get, url);
request.Headers.TryAddWithoutValidation("RequestId", requestId); request.Headers.TryAddWithoutValidation(HeaderNames.RequestId, requestId);
// Act // Act
var response = await Client.SendAsync(request); var response = await Client.SendAsync(request);
@ -64,7 +65,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
{ {
var requestId = Guid.NewGuid().ToString(); var requestId = Guid.NewGuid().ToString();
var request = new HttpRequestMessage(HttpMethod.Get, url); var request = new HttpRequestMessage(HttpMethod.Get, url);
request.Headers.TryAddWithoutValidation("RequestId", requestId); request.Headers.TryAddWithoutValidation(HeaderNames.RequestId, requestId);
var response = await Client.SendAsync(request); var response = await Client.SendAsync(request);
@ -84,7 +85,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
// Act & Assert // Act & Assert
var requestId1 = "b40f6ec1-8a6b-41c1-b3fe-928f581ebaf5"; var requestId1 = "b40f6ec1-8a6b-41c1-b3fe-928f581ebaf5";
var request1 = new HttpRequestMessage(HttpMethod.Get, url); var request1 = new HttpRequestMessage(HttpMethod.Get, url);
request1.Headers.TryAddWithoutValidation("RequestId", requestId1); request1.Headers.TryAddWithoutValidation(HeaderNames.RequestId, requestId1);
var response1 = await Client.SendAsync(request1); var response1 = await Client.SendAsync(request1);
@ -93,7 +94,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
var requestId2 = Guid.NewGuid().ToString(); var requestId2 = Guid.NewGuid().ToString();
var request2 = new HttpRequestMessage(HttpMethod.Get, url); var request2 = new HttpRequestMessage(HttpMethod.Get, url);
request2.Headers.TryAddWithoutValidation("RequestId", requestId2); request2.Headers.TryAddWithoutValidation(HeaderNames.RequestId, requestId2);
var response2 = await Client.SendAsync(request2); var response2 = await Client.SendAsync(request2);
Assert.Equal(HttpStatusCode.NotFound, response2.StatusCode); Assert.Equal(HttpStatusCode.NotFound, response2.StatusCode);

View File

@ -5,6 +5,7 @@ using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Net.Http.Headers;
namespace BasicWebSite namespace BasicWebSite
{ {
@ -26,7 +27,7 @@ namespace BasicWebSite
throw new InvalidOperationException("RequestId should be null here"); throw new InvalidOperationException("RequestId should be null here");
} }
var requestId = context.Request.Headers["RequestId"]; var requestId = context.Request.Headers[HeaderNames.RequestId];
requestIdService.RequestId = requestId; requestIdService.RequestId = requestId;
return _next(context); return _next(context);

View File

@ -4,6 +4,7 @@
using System; using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.Net.Http.Headers;
namespace Microsoft.AspNetCore.Authentication.Cookies namespace Microsoft.AspNetCore.Authentication.Cookies
{ {
@ -41,7 +42,7 @@ namespace Microsoft.AspNetCore.Authentication.Cookies
{ {
if (IsAjaxRequest(context.Request)) if (IsAjaxRequest(context.Request))
{ {
context.Response.Headers["Location"] = context.RedirectUri; context.Response.Headers[HeaderNames.Location] = context.RedirectUri;
context.Response.StatusCode = 401; context.Response.StatusCode = 401;
} }
else else
@ -58,7 +59,7 @@ namespace Microsoft.AspNetCore.Authentication.Cookies
{ {
if (IsAjaxRequest(context.Request)) if (IsAjaxRequest(context.Request))
{ {
context.Response.Headers["Location"] = context.RedirectUri; context.Response.Headers[HeaderNames.Location] = context.RedirectUri;
context.Response.StatusCode = 403; context.Response.StatusCode = 403;
} }
else else
@ -75,7 +76,7 @@ namespace Microsoft.AspNetCore.Authentication.Cookies
{ {
if (IsAjaxRequest(context.Request)) if (IsAjaxRequest(context.Request))
{ {
context.Response.Headers["Location"] = context.RedirectUri; context.Response.Headers[HeaderNames.Location] = context.RedirectUri;
} }
else else
{ {
@ -91,7 +92,7 @@ namespace Microsoft.AspNetCore.Authentication.Cookies
{ {
if (IsAjaxRequest(context.Request)) if (IsAjaxRequest(context.Request))
{ {
context.Response.Headers["Location"] = context.RedirectUri; context.Response.Headers[HeaderNames.Location] = context.RedirectUri;
} }
else else
{ {
@ -155,4 +156,4 @@ namespace Microsoft.AspNetCore.Authentication.Cookies
/// <param name="context">Contains information about the event</param> /// <param name="context">Contains information about the event</param>
public virtual Task RedirectToAccessDenied(RedirectContext<CookieAuthenticationOptions> context) => OnRedirectToAccessDenied(context); public virtual Task RedirectToAccessDenied(RedirectContext<CookieAuthenticationOptions> context) => OnRedirectToAccessDenied(context);
} }
} }

View File

@ -63,7 +63,7 @@ namespace Microsoft.AspNetCore.Authentication.JwtBearer
if (string.IsNullOrEmpty(token)) if (string.IsNullOrEmpty(token))
{ {
string authorization = Request.Headers["Authorization"]; string authorization = Request.Headers[HeaderNames.Authorization];
// If no authorization header found, nothing to process further // If no authorization header found, nothing to process further
if (string.IsNullOrEmpty(authorization)) if (string.IsNullOrEmpty(authorization))

View File

@ -127,6 +127,12 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
return StringValues.Concat(existing, append); return StringValues.Concat(existing, append);
} }
[MethodImpl(MethodImplOptions.NoInlining)]
protected bool TryGetUnknown(string key, ref StringValues value) => MaybeUnknown?.TryGetValue(key, out value) ?? false;
[MethodImpl(MethodImplOptions.NoInlining)]
protected bool RemoveUnknown(string key) => MaybeUnknown?.Remove(key) ?? false;
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
protected static int BitCount(long value) protected static int BitCount(long value)
{ {

View File

@ -13,6 +13,7 @@ using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Server.Kestrel.Core.Features; using Microsoft.AspNetCore.Server.Kestrel.Core.Features;
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure;
using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal; using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal;
using Microsoft.Net.Http.Headers;
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
{ {
@ -302,7 +303,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
StatusCode = StatusCodes.Status101SwitchingProtocols; StatusCode = StatusCodes.Status101SwitchingProtocols;
ReasonPhrase = "Switching Protocols"; ReasonPhrase = "Switching Protocols";
ResponseHeaders["Connection"] = "Upgrade"; ResponseHeaders[HeaderNames.Connection] = "Upgrade";
await FlushAsync(); await FlushAsync();

View File

@ -20,6 +20,7 @@ using Microsoft.AspNetCore.Internal;
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Primitives; using Microsoft.Extensions.Primitives;
using Microsoft.Net.Http.Headers;
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
{ {
@ -903,7 +904,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
} }
if (_httpVersion != Http.HttpVersion.Http10 && if (_httpVersion != Http.HttpVersion.Http10 &&
RequestHeaders.TryGetValue("Expect", out var expect) && RequestHeaders.TryGetValue(HeaderNames.Expect, out var expect) &&
(expect.FirstOrDefault() ?? "").Equals("100-continue", StringComparison.OrdinalIgnoreCase)) (expect.FirstOrDefault() ?? "").Equals("100-continue", StringComparison.OrdinalIgnoreCase))
{ {
Output.Write100ContinueAsync().GetAwaiter().GetResult(); Output.Write100ContinueAsync().GetAwaiter().GetResult();

View File

@ -92,6 +92,14 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
Unknown[key] = value; Unknown[key] = value;
} }
[MethodImpl(MethodImplOptions.NoInlining)]
private bool AddValueUnknown(string key, StringValues value)
{
Unknown.Add(key, value);
// Return true, above will throw and exit for false
return true;
}
[MethodImpl(MethodImplOptions.NoInlining)] [MethodImpl(MethodImplOptions.NoInlining)]
private unsafe void AppendUnknownHeaders(Span<byte> name, string valueString) private unsafe void AppendUnknownHeaders(Span<byte> name, string valueString)
{ {

View File

@ -78,6 +78,15 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
Unknown[key] = value; Unknown[key] = value;
} }
[MethodImpl(MethodImplOptions.NoInlining)]
private bool AddValueUnknown(string key, StringValues value)
{
ValidateHeaderNameCharacters(key);
Unknown.Add(key, value);
// Return true, above will throw and exit for false
return true;
}
public partial struct Enumerator : IEnumerator<KeyValuePair<string, StringValues>> public partial struct Enumerator : IEnumerator<KeyValuePair<string, StringValues>>
{ {
private readonly HttpResponseHeaders _collection; private readonly HttpResponseHeaders _collection;

View File

@ -28,6 +28,15 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
Unknown[key] = value; Unknown[key] = value;
} }
[MethodImpl(MethodImplOptions.NoInlining)]
private bool AddValueUnknown(string key, StringValues value)
{
ValidateHeaderNameCharacters(key);
Unknown.Add(key, value);
// Return true, above will throw and exit for false
return true;
}
public partial struct Enumerator : IEnumerator<KeyValuePair<string, StringValues>> public partial struct Enumerator : IEnumerator<KeyValuePair<string, StringValues>>
{ {
private readonly HttpResponseTrailers _collection; private readonly HttpResponseTrailers _collection;

View File

@ -1,4 +1,4 @@
// Copyright (c) .NET Foundation. All rights reserved. // 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. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System; using System;
@ -13,12 +13,13 @@ using Microsoft.AspNetCore.Server.Kestrel.Core;
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal;
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http;
using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal; using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal;
using Microsoft.Net.Http.Headers;
namespace Microsoft.AspNetCore.Server.Kestrel.Performance namespace Microsoft.AspNetCore.Server.Kestrel.Performance
{ {
public class ResponseHeaderCollectionBenchmark public class ResponseHeaderCollectionBenchmark
{ {
private const int InnerLoopCount = 512; private const int InnerLoopCount = 128;
private static readonly byte[] _bytesServer = Encoding.ASCII.GetBytes("\r\nServer: Kestrel"); private static readonly byte[] _bytesServer = Encoding.ASCII.GetBytes("\r\nServer: Kestrel");
private static readonly DateHeaderValueManager _dateHeaderValueManager = new DateHeaderValueManager(); private static readonly DateHeaderValueManager _dateHeaderValueManager = new DateHeaderValueManager();
@ -84,7 +85,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Performance
{ {
_responseHeadersDirect.Reset(); _responseHeadersDirect.Reset();
_response.Headers["Content-Length"] = "0"; _response.Headers[HeaderNames.ContentLength] = "0";
} }
} }
@ -118,21 +119,21 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Performance
var headers = _response.Headers; var headers = _response.Headers;
headers["Connection"] = "Close"; headers[HeaderNames.Connection] = "Close";
headers["Cache-Control"] = "public, max-age=30672000"; headers[HeaderNames.CacheControl] = "public, max-age=30672000";
headers["Vary"] = "Accept-Encoding"; headers[HeaderNames.Vary] = "Accept-Encoding";
headers["Content-Encoding"] = "gzip"; headers[HeaderNames.ContentEncoding] = "gzip";
headers["Expires"] = "Fri, 12 Jan 2018 22:01:55 GMT"; headers[HeaderNames.Expires] = "Fri, 12 Jan 2018 22:01:55 GMT";
headers["Last-Modified"] = "Wed, 22 Jun 2016 20:08:29 GMT"; headers[HeaderNames.LastModified] = "Wed, 22 Jun 2016 20:08:29 GMT";
headers["Set-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"; headers[HeaderNames.SetCookie] = "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";
headers["ETag"] = "\"54ef7954-1078\""; headers[HeaderNames.ETag] = "\"54ef7954-1078\"";
headers["Transfer-Encoding"] = "chunked"; headers[HeaderNames.TransferEncoding] = "chunked";
headers["Content-Language"] = "en-gb"; headers[HeaderNames.ContentLanguage] = "en-gb";
headers["Upgrade"] = "websocket"; headers[HeaderNames.Upgrade] = "websocket";
headers["Via"] = "1.1 varnish"; headers[HeaderNames.Via] = "1.1 varnish";
headers["Access-Control-Allow-Origin"] = "*"; headers[HeaderNames.AccessControlAllowOrigin] = "*";
headers["Access-Control-Allow-credentials"] = "true"; headers[HeaderNames.AccessControlAllowCredentials] = "true";
headers["Access-Control-Expose-Headers"] = "Client-Protocol, Content-Length, Content-Type, X-Bandwidth-Est, X-Bandwidth-Est2, X-Bandwidth-Est-Comp, X-Bandwidth-Avg, X-Walltime-Ms, X-Sequence-Num"; headers[HeaderNames.AccessControlExposeHeaders] = "Client-Protocol, Content-Length, Content-Type, X-Bandwidth-Est, X-Bandwidth-Est2, X-Bandwidth-Est-Comp, X-Bandwidth-Avg, X-Walltime-Ms, X-Sequence-Num";
var dateHeaderValues = _dateHeaderValueManager.GetDateHeaderValues(); var dateHeaderValues = _dateHeaderValueManager.GetDateHeaderValues();
_responseHeadersDirect.SetRawDate(dateHeaderValues.String, dateHeaderValues.Bytes); _responseHeadersDirect.SetRawDate(dateHeaderValues.String, dateHeaderValues.Bytes);
@ -159,8 +160,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Performance
headers["X-Content-Type-Options"] = "nosniff"; headers["X-Content-Type-Options"] = "nosniff";
headers["X-Xss-Protection"] = "1; mode=block"; headers["X-Xss-Protection"] = "1; mode=block";
headers["X-Frame-Options"] = "SAMEORIGIN"; headers["X-Frame-Options"] = "SAMEORIGIN";
headers["Strict-Transport-Security"] = "max-age=31536000; includeSubDomains; preload"; headers[HeaderNames.StrictTransportSecurity] = "max-age=31536000; includeSubDomains; preload";
headers["Content-Security-Policy"] = "default-src 'none'; script-src 'self' cdnjs.cloudflare.com code.jquery.com scotthelme.disqus.com a.disquscdn.com www.google-analytics.com go.disqus.com platform.twitter.com cdn.syndication.twimg.com; style-src 'self' a.disquscdn.com fonts.googleapis.com cdnjs.cloudflare.com platform.twitter.com; img-src 'self' data: www.gravatar.com www.google-analytics.com links.services.disqus.com referrer.disqus.com a.disquscdn.com cdn.syndication.twimg.com syndication.twitter.com pbs.twimg.com platform.twitter.com abs.twimg.com; child-src fusiontables.googleusercontent.com fusiontables.google.com www.google.com disqus.com www.youtube.com syndication.twitter.com platform.twitter.com; frame-src fusiontables.googleusercontent.com fusiontables.google.com www.google.com disqus.com www.youtube.com syndication.twitter.com platform.twitter.com; connect-src 'self' links.services.disqus.com; font-src 'self' cdnjs.cloudflare.com fonts.gstatic.com fonts.googleapis.com; form-action 'self'; upgrade-insecure-requests;"; headers[HeaderNames.ContentSecurityPolicy] = "default-src 'none'; script-src 'self' cdnjs.cloudflare.com code.jquery.com scotthelme.disqus.com a.disquscdn.com www.google-analytics.com go.disqus.com platform.twitter.com cdn.syndication.twimg.com; style-src 'self' a.disquscdn.com fonts.googleapis.com cdnjs.cloudflare.com platform.twitter.com; img-src 'self' data: www.gravatar.com www.google-analytics.com links.services.disqus.com referrer.disqus.com a.disquscdn.com cdn.syndication.twimg.com syndication.twitter.com pbs.twimg.com platform.twitter.com abs.twimg.com; child-src fusiontables.googleusercontent.com fusiontables.google.com www.google.com disqus.com www.youtube.com syndication.twitter.com platform.twitter.com; frame-src fusiontables.googleusercontent.com fusiontables.google.com www.google.com disqus.com www.youtube.com syndication.twitter.com platform.twitter.com; connect-src 'self' links.services.disqus.com; font-src 'self' cdnjs.cloudflare.com fonts.gstatic.com fonts.googleapis.com; form-action 'self'; upgrade-insecure-requests;";
var dateHeaderValues = _dateHeaderValueManager.GetDateHeaderValues(); var dateHeaderValues = _dateHeaderValueManager.GetDateHeaderValues();
_responseHeadersDirect.SetRawDate(dateHeaderValues.String, dateHeaderValues.Bytes); _responseHeadersDirect.SetRawDate(dateHeaderValues.String, dateHeaderValues.Bytes);

View File

@ -11,6 +11,7 @@ using BenchmarkDotNet.Attributes;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http;
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure;
using Microsoft.Net.Http.Headers;
namespace Microsoft.AspNetCore.Server.Kestrel.Performance namespace Microsoft.AspNetCore.Server.Kestrel.Performance
{ {
@ -82,7 +83,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Performance
var responseHeaders = _responseHeaders; var responseHeaders = _responseHeaders;
responseHeaders.HeaderContentEncoding = "gzip"; responseHeaders.HeaderContentEncoding = "gzip";
responseHeaders.HeaderContentType = "text/html; charset=utf-8"; responseHeaders.HeaderContentType = "text/html; charset=utf-8";
_responseHeadersDict["Strict-Transport-Security"] = "max-age=31536000; includeSubdomains"; _responseHeadersDict[HeaderNames.StrictTransportSecurity] = "max-age=31536000; includeSubdomains";
responseHeaders.HeaderVary = "Accept-Encoding"; responseHeaders.HeaderVary = "Accept-Encoding";
_responseHeadersDict["X-Powered-By"] = "ASP.NET"; _responseHeadersDict["X-Powered-By"] = "ASP.NET";

View File

@ -94,7 +94,11 @@ namespace CodeGenerator
"Translate", "Translate",
"User-Agent", "User-Agent",
"DNT", "DNT",
"Upgrade-Insecure-Requests" "Upgrade-Insecure-Requests",
"Request-Id",
"Correlation-Context",
"TraceParent",
"TraceState"
}) })
.Concat(corsRequestHeaders) .Concat(corsRequestHeaders)
.Select((header, index) => new KnownHeader .Select((header, index) => new KnownHeader
@ -539,21 +543,21 @@ namespace CodeGenerator
new new
{ {
Headers = requestHeaders, Headers = requestHeaders,
HeadersByLength = requestHeaders.GroupBy(x => x.Name.Length), HeadersByLength = requestHeaders.OrderBy(x => x.Name.Length).GroupBy(x => x.Name.Length),
ClassName = "HttpRequestHeaders", ClassName = "HttpRequestHeaders",
Bytes = default(byte[]) Bytes = default(byte[])
}, },
new new
{ {
Headers = responseHeaders, Headers = responseHeaders,
HeadersByLength = responseHeaders.GroupBy(x => x.Name.Length), HeadersByLength = responseHeaders.OrderBy(x => x.Name.Length).GroupBy(x => x.Name.Length),
ClassName = "HttpResponseHeaders", ClassName = "HttpResponseHeaders",
Bytes = responseHeaders.SelectMany(header => header.Bytes).ToArray() Bytes = responseHeaders.SelectMany(header => header.Bytes).ToArray()
}, },
new new
{ {
Headers = responseTrailers, Headers = responseTrailers,
HeadersByLength = responseTrailers.GroupBy(x => x.Name.Length), HeadersByLength = responseTrailers.OrderBy(x => x.Name.Length).GroupBy(x => x.Name.Length),
ClassName = "HttpResponseTrailers", ClassName = "HttpResponseTrailers",
Bytes = responseTrailers.SelectMany(header => header.Bytes).ToArray() Bytes = responseTrailers.SelectMany(header => header.Bytes).ToArray()
} }
@ -647,27 +651,43 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
switch (key.Length) switch (key.Length)
{{{Each(loop.HeadersByLength, byLength => $@" {{{Each(loop.HeadersByLength, byLength => $@"
case {byLength.Key}: case {byLength.Key}:
{{{Each(byLength, header => $@" {{{Each(byLength.OrderBy(h => !h.PrimaryHeader), header => $@"
if (""{header.Name}"".Equals(key, StringComparison.OrdinalIgnoreCase)) if (ReferenceEquals(HeaderNames.{header.Identifier}, key))
{{{(header.Identifier == "ContentLength" ? @" {{{(header.Identifier == "ContentLength" ? @"
if (_contentLength.HasValue) if (_contentLength.HasValue)
{ {
value = HeaderUtilities.FormatNonNegativeInt64(_contentLength.Value); value = HeaderUtilities.FormatNonNegativeInt64(_contentLength.Value);
return true; return true;
} }
return false;" : $@" return false;" : $@"
if ({header.TestBit()}) if ({header.TestBit()})
{{ {{
value = _headers._{header.Identifier}; value = _headers._{header.Identifier};
return true; return true;
}} }}
return false;")} return false;")}
}}")} }}")}
}} {Each(byLength.OrderBy(h => !h.PrimaryHeader), header => $@"
break;")} if (HeaderNames.{header.Identifier}.Equals(key, StringComparison.OrdinalIgnoreCase))
{{{(header.Identifier == "ContentLength" ? @"
if (_contentLength.HasValue)
{
value = HeaderUtilities.FormatNonNegativeInt64(_contentLength.Value);
return true;
}
return false;" : $@"
if ({header.TestBit()})
{{
value = _headers._{header.Identifier};
return true;
}}
return false;")}
}}")}
break;
}}")}
}} }}
return MaybeUnknown?.TryGetValue(key, out value) ?? false; return TryGetUnknown(key, ref value);
}} }}
protected override void SetValueFast(string key, StringValues value) protected override void SetValueFast(string key, StringValues value)
@ -676,17 +696,26 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
switch (key.Length) switch (key.Length)
{{{Each(loop.HeadersByLength, byLength => $@" {{{Each(loop.HeadersByLength, byLength => $@"
case {byLength.Key}: case {byLength.Key}:
{{{Each(byLength, header => $@" {{{Each(byLength.OrderBy(h => !h.PrimaryHeader), header => $@"
if (""{header.Name}"".Equals(key, StringComparison.OrdinalIgnoreCase)) if (ReferenceEquals(HeaderNames.{header.Identifier}, key))
{{{(header.Identifier == "ContentLength" ? $@" {{{(header.Identifier == "ContentLength" ? $@"
_contentLength = ParseContentLength(value.ToString());" : $@" _contentLength = ParseContentLength(value.ToString());" : $@"
{header.SetBit()}; {header.SetBit()};
_headers._{header.Identifier} = value;{(header.EnhancedSetter == false ? "" : $@" _headers._{header.Identifier} = value;{(header.EnhancedSetter == false ? "" : $@"
_headers._raw{header.Identifier} = null;")}")} _headers._raw{header.Identifier} = null;")}")}
return; return;
}}")} }}")}
}} {Each(byLength.OrderBy(h => !h.PrimaryHeader), header => $@"
break;")} if (HeaderNames.{header.Identifier}.Equals(key, StringComparison.OrdinalIgnoreCase))
{{{(header.Identifier == "ContentLength" ? $@"
_contentLength = ParseContentLength(value.ToString());" : $@"
{header.SetBit()};
_headers._{header.Identifier} = value;{(header.EnhancedSetter == false ? "" : $@"
_headers._raw{header.Identifier} = null;")}")}
return;
}}")}
break;
}}")}
}} }}
SetValueUnknown(key, value); SetValueUnknown(key, value);
@ -698,32 +727,47 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
switch (key.Length) switch (key.Length)
{{{Each(loop.HeadersByLength, byLength => $@" {{{Each(loop.HeadersByLength, byLength => $@"
case {byLength.Key}: case {byLength.Key}:
{{{Each(byLength, header => $@" {{{Each(byLength.OrderBy(h => !h.PrimaryHeader), header => $@"
if (""{header.Name}"".Equals(key, StringComparison.OrdinalIgnoreCase)) if (ReferenceEquals(HeaderNames.{header.Identifier}, key))
{{{(header.Identifier == "ContentLength" ? $@" {{{(header.Identifier == "ContentLength" ? $@"
if (!_contentLength.HasValue) if (!_contentLength.HasValue)
{{ {{
_contentLength = ParseContentLength(value); _contentLength = ParseContentLength(value);
return true; return true;
}} }}
return false;" : $@" return false;" : $@"
if ({header.TestNotBit()}) if ({header.TestNotBit()})
{{ {{
{header.SetBit()}; {header.SetBit()};
_headers._{header.Identifier} = value;{(header.EnhancedSetter == false ? "" : $@" _headers._{header.Identifier} = value;{(header.EnhancedSetter == false ? "" : $@"
_headers._raw{header.Identifier} = null;")} _headers._raw{header.Identifier} = null;")}
return true; return true;
}} }}
return false;")} return false;")}
}}")} }}")}
}} {Each(byLength.OrderBy(h => !h.PrimaryHeader), header => $@"
break;")} if (HeaderNames.{header.Identifier}.Equals(key, StringComparison.OrdinalIgnoreCase))
{{{(header.Identifier == "ContentLength" ? $@"
if (!_contentLength.HasValue)
{{
_contentLength = ParseContentLength(value);
return true;
}}
return false;" : $@"
if ({header.TestNotBit()})
{{
{header.SetBit()};
_headers._{header.Identifier} = value;{(header.EnhancedSetter == false ? "" : $@"
_headers._raw{header.Identifier} = null;")}
return true;
}}
return false;")}
}}")}
break;
}}")}
}} }}
{(loop.ClassName == "HttpResponseHeaders" ? @"
ValidateHeaderNameCharacters(key);" : "")} return AddValueUnknown(key, value);
Unknown.Add(key, value);
// Return true, above will throw and exit for false
return true;
}} }}
protected override bool RemoveFast(string key) protected override bool RemoveFast(string key)
@ -731,29 +775,47 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
switch (key.Length) switch (key.Length)
{{{Each(loop.HeadersByLength, byLength => $@" {{{Each(loop.HeadersByLength, byLength => $@"
case {byLength.Key}: case {byLength.Key}:
{{{Each(byLength, header => $@" {{{Each(byLength.OrderBy(h => !h.PrimaryHeader), header => $@"
if (""{header.Name}"".Equals(key, StringComparison.OrdinalIgnoreCase)) if (ReferenceEquals(HeaderNames.{header.Identifier}, key))
{{{(header.Identifier == "ContentLength" ? @" {{{(header.Identifier == "ContentLength" ? @"
if (_contentLength.HasValue) if (_contentLength.HasValue)
{ {
_contentLength = null; _contentLength = null;
return true; return true;
} }
return false;" : $@" return false;" : $@"
if ({header.TestBit()}) if ({header.TestBit()})
{{ {{
{header.ClearBit()}; {header.ClearBit()};
_headers._{header.Identifier} = default(StringValues);{(header.EnhancedSetter == false ? "" : $@" _headers._{header.Identifier} = default(StringValues);{(header.EnhancedSetter == false ? "" : $@"
_headers._raw{header.Identifier} = null;")} _headers._raw{header.Identifier} = null;")}
return true; return true;
}} }}
return false;")} return false;")}
}}")} }}")}
}} {Each(byLength.OrderBy(h => !h.PrimaryHeader), header => $@"
break;")} if (HeaderNames.{header.Identifier}.Equals(key, StringComparison.OrdinalIgnoreCase))
{{{(header.Identifier == "ContentLength" ? @"
if (_contentLength.HasValue)
{
_contentLength = null;
return true;
}
return false;" : $@"
if ({header.TestBit()})
{{
{header.ClearBit()};
_headers._{header.Identifier} = default(StringValues);{(header.EnhancedSetter == false ? "" : $@"
_headers._raw{header.Identifier} = null;")}
return true;
}}
return false;")}
}}")}
break;
}}")}
}} }}
return MaybeUnknown?.Remove(key) ?? false; return RemoveUnknown(key);
}} }}
{(loop.ClassName != "HttpRequestHeaders" ? {(loop.ClassName != "HttpRequestHeaders" ?
$@" protected override void ClearFast() $@" protected override void ClearFast()
@ -770,7 +832,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
{Each(loop.Headers.Where(header => header.Identifier != "ContentLength").OrderBy(h => !h.PrimaryHeader), header => $@" {Each(loop.Headers.Where(header => header.Identifier != "ContentLength").OrderBy(h => !h.PrimaryHeader), header => $@"
if ({header.TestTempBit()}) if ({header.TestTempBit()})
{{ {{
_headers._{header.Identifier} = default(StringValues); _headers._{header.Identifier} = default;
if({header.TestNotTempBit()}) if({header.TestNotTempBit()})
{{ {{
return; return;
@ -786,7 +848,7 @@ $@" private void Clear(long bitsToClear)
{Each(loop.Headers.Where(header => header.Identifier != "ContentLength").OrderBy(h => !h.PrimaryHeader), header => $@" {Each(loop.Headers.Where(header => header.Identifier != "ContentLength").OrderBy(h => !h.PrimaryHeader), header => $@"
if ({header.TestTempBit()}) if ({header.TestTempBit()})
{{ {{
_headers._{header.Identifier} = default(StringValues); _headers._{header.Identifier} = default;
if({header.TestNotTempBit()}) if({header.TestNotTempBit()})
{{ {{
return; return;
@ -809,7 +871,7 @@ $@" private void Clear(long bitsToClear)
{{ {{
return false; return false;
}} }}
array[arrayIndex] = new KeyValuePair<string, StringValues>(""{header.Name}"", _headers._{header.Identifier}); array[arrayIndex] = new KeyValuePair<string, StringValues>(HeaderNames.{header.Identifier}, _headers._{header.Identifier});
++arrayIndex; ++arrayIndex;
}}")} }}")}
if (_contentLength.HasValue) if (_contentLength.HasValue)
@ -818,7 +880,7 @@ $@" private void Clear(long bitsToClear)
{{ {{
return false; return false;
}} }}
array[arrayIndex] = new KeyValuePair<string, StringValues>(""Content-Length"", HeaderUtilities.FormatNonNegativeInt64(_contentLength.Value)); array[arrayIndex] = new KeyValuePair<string, StringValues>(HeaderNames.ContentLength, HeaderUtilities.FormatNonNegativeInt64(_contentLength.Value));
++arrayIndex; ++arrayIndex;
}} }}
((ICollection<KeyValuePair<string, StringValues>>)MaybeUnknown)?.CopyTo(array, arrayIndex); ((ICollection<KeyValuePair<string, StringValues>>)MaybeUnknown)?.CopyTo(array, arrayIndex);
@ -970,14 +1032,14 @@ $@" private void Clear(long bitsToClear)
Header{header.Identifier}: // case {header.Index} Header{header.Identifier}: // case {header.Index}
if ({header.TestBit()}) if ({header.TestBit()})
{{ {{
_current = new KeyValuePair<string, StringValues>(""{header.Name}"", _collection._headers._{header.Identifier}); _current = new KeyValuePair<string, StringValues>(HeaderNames.{header.Identifier}, _collection._headers._{header.Identifier});
_next = {header.Index + 1}; _next = {header.Index + 1};
return true; return true;
}}")} }}")}
{(!loop.ClassName.Contains("Trailers") ? $@"HeaderContentLength: // case {loop.Headers.Count() - 1} {(!loop.ClassName.Contains("Trailers") ? $@"HeaderContentLength: // case {loop.Headers.Count() - 1}
if (_collection._contentLength.HasValue) if (_collection._contentLength.HasValue)
{{ {{
_current = new KeyValuePair<string, StringValues>(""Content-Length"", HeaderUtilities.FormatNonNegativeInt64(_collection._contentLength.Value)); _current = new KeyValuePair<string, StringValues>(HeaderNames.ContentLength, HeaderUtilities.FormatNonNegativeInt64(_collection._contentLength.Value));
_next = {loop.Headers.Count()}; _next = {loop.Headers.Count()};
return true; return true;
}}" : "")} }}" : "")}

View File

@ -273,8 +273,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
} }
[Theory] [Theory]
[InlineData(HeaderNames.Path, "/")] [InlineData(":path", "/")]
[InlineData(HeaderNames.Scheme, "http")] [InlineData(":scheme", "http")]
public async Task HEADERS_Received_CONNECTMethod_WithSchemeOrPath_Reset(string headerName, string value) public async Task HEADERS_Received_CONNECTMethod_WithSchemeOrPath_Reset(string headerName, string value)
{ {
await InitializeConnectionAsync(_noopApplication); await InitializeConnectionAsync(_noopApplication);

View File

@ -7,6 +7,7 @@ using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Http.Features; using Microsoft.AspNetCore.Http.Features;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Net.Http.Headers;
namespace Microsoft.AspNetCore.Http.Connections.Internal.Transports namespace Microsoft.AspNetCore.Http.Connections.Internal.Transports
{ {
@ -26,13 +27,13 @@ namespace Microsoft.AspNetCore.Http.Connections.Internal.Transports
public async Task ProcessRequestAsync(HttpContext context, CancellationToken token) public async Task ProcessRequestAsync(HttpContext context, CancellationToken token)
{ {
context.Response.ContentType = "text/event-stream"; context.Response.ContentType = "text/event-stream";
context.Response.Headers["Cache-Control"] = "no-cache"; context.Response.Headers[HeaderNames.CacheControl] = "no-cache";
// Make sure we disable all response buffering for SSE // Make sure we disable all response buffering for SSE
var bufferingFeature = context.Features.Get<IHttpBufferingFeature>(); var bufferingFeature = context.Features.Get<IHttpBufferingFeature>();
bufferingFeature?.DisableResponseBuffering(); bufferingFeature?.DisableResponseBuffering();
context.Response.Headers["Content-Encoding"] = "identity"; context.Response.Headers[HeaderNames.ContentEncoding] = "identity";
// Workaround for a Firefox bug where EventSource won't fire the open event // Workaround for a Firefox bug where EventSource won't fire the open event
// until it receives some data // until it receives some data