From 19ce3728871f462601510605c99b5d359190b19d Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Mon, 22 Apr 2019 04:37:56 +0100 Subject: [PATCH] Cache some Headers lookups (#9618) --- .../CORS/src/Infrastructure/CorsService.cs | 24 +++++++++-------- .../ExceptionHandlerMiddleware.cs | 12 ++++----- .../src/ForwardedHeadersMiddleware.cs | 27 ++++++++++--------- .../Internal/ResponseCachingKeyProvider.cs | 14 +++++----- .../Internal/ResponseCachingPolicyProvider.cs | 8 +++--- .../Session/src/SessionMiddleware.cs | 10 ++++--- 6 files changed, 51 insertions(+), 44 deletions(-) diff --git a/src/Middleware/CORS/src/Infrastructure/CorsService.cs b/src/Middleware/CORS/src/Infrastructure/CorsService.cs index a240ae22c9..26c3bb042b 100644 --- a/src/Middleware/CORS/src/Infrastructure/CorsService.cs +++ b/src/Middleware/CORS/src/Infrastructure/CorsService.cs @@ -79,8 +79,8 @@ namespace Microsoft.AspNetCore.Cors.Infrastructure throw new ArgumentException(Resources.InsecureConfiguration, nameof(policy)); } - var origin = context.Request.Headers[CorsConstants.Origin]; var requestHeaders = context.Request.Headers; + var origin = requestHeaders[CorsConstants.Origin]; var isOptionsRequest = string.Equals(context.Request.Method, CorsConstants.PreflightHttpMethod, StringComparison.OrdinalIgnoreCase); var isPreflightRequest = isOptionsRequest && requestHeaders.ContainsKey(CorsConstants.AccessControlRequestMethod); @@ -110,6 +110,7 @@ namespace Microsoft.AspNetCore.Cors.Infrastructure private static void PopulateResult(HttpContext context, CorsPolicy policy, CorsResult result) { + var headers = context.Request.Headers; if (policy.AllowAnyOrigin) { result.AllowedOrigin = CorsConstants.AnyOrigin; @@ -117,7 +118,7 @@ namespace Microsoft.AspNetCore.Cors.Infrastructure } else { - var origin = context.Request.Headers[CorsConstants.Origin]; + var origin = headers[CorsConstants.Origin]; result.AllowedOrigin = origin; result.VaryByOrigin = policy.Origins.Count > 1; } @@ -129,12 +130,12 @@ namespace Microsoft.AspNetCore.Cors.Infrastructure AddHeaderValues(result.AllowedExposedHeaders, policy.ExposedHeaders); var allowedMethods = policy.AllowAnyMethod ? - new[] { result.IsPreflightRequest ? (string)context.Request.Headers[CorsConstants.AccessControlRequestMethod] : context.Request.Method } : + new[] { result.IsPreflightRequest ? (string)headers[CorsConstants.AccessControlRequestMethod] : context.Request.Method } : policy.Methods; AddHeaderValues(result.AllowedMethods, allowedMethods); var allowedHeaders = policy.AllowAnyHeader ? - context.Request.Headers.GetCommaSeparatedValues(CorsConstants.AccessControlRequestHeaders) : + headers.GetCommaSeparatedValues(CorsConstants.AccessControlRequestHeaders) : policy.Headers; AddHeaderValues(result.AllowedHeaders, allowedHeaders); } @@ -169,11 +170,12 @@ namespace Microsoft.AspNetCore.Cors.Infrastructure return; } - response.Headers[CorsConstants.AccessControlAllowOrigin] = result.AllowedOrigin; + var headers = response.Headers; + headers[CorsConstants.AccessControlAllowOrigin] = result.AllowedOrigin; if (result.SupportsCredentials) { - response.Headers[CorsConstants.AccessControlAllowCredentials] = "true"; + headers[CorsConstants.AccessControlAllowCredentials] = "true"; } if (result.IsPreflightRequest) @@ -184,17 +186,17 @@ namespace Microsoft.AspNetCore.Cors.Infrastructure // `Access-Control-Allow-Methods`, `Access-Control-Allow-Headers`, `Access-Control-Max-Age` if (result.AllowedHeaders.Count > 0) { - response.Headers.SetCommaSeparatedValues(CorsConstants.AccessControlAllowHeaders, result.AllowedHeaders.ToArray()); + headers.SetCommaSeparatedValues(CorsConstants.AccessControlAllowHeaders, result.AllowedHeaders.ToArray()); } if (result.AllowedMethods.Count > 0) { - response.Headers.SetCommaSeparatedValues(CorsConstants.AccessControlAllowMethods, result.AllowedMethods.ToArray()); + headers.SetCommaSeparatedValues(CorsConstants.AccessControlAllowMethods, result.AllowedMethods.ToArray()); } if (result.PreflightMaxAge.HasValue) { - response.Headers[CorsConstants.AccessControlMaxAge] = result.PreflightMaxAge.Value.TotalSeconds.ToString(CultureInfo.InvariantCulture); + headers[CorsConstants.AccessControlMaxAge] = result.PreflightMaxAge.Value.TotalSeconds.ToString(CultureInfo.InvariantCulture); } } else @@ -203,13 +205,13 @@ namespace Microsoft.AspNetCore.Cors.Infrastructure // `Access-Control-Expose-Headers` if (result.AllowedExposedHeaders.Count > 0) { - response.Headers.SetCommaSeparatedValues(CorsConstants.AccessControlExposeHeaders, result.AllowedExposedHeaders.ToArray()); + headers.SetCommaSeparatedValues(CorsConstants.AccessControlExposeHeaders, result.AllowedExposedHeaders.ToArray()); } } if (result.VaryByOrigin) { - response.Headers.Append("Vary", "Origin"); + headers.Append("Vary", "Origin"); } } diff --git a/src/Middleware/Diagnostics/src/ExceptionHandler/ExceptionHandlerMiddleware.cs b/src/Middleware/Diagnostics/src/ExceptionHandler/ExceptionHandlerMiddleware.cs index 814c3991e8..ebdfcb0103 100644 --- a/src/Middleware/Diagnostics/src/ExceptionHandler/ExceptionHandlerMiddleware.cs +++ b/src/Middleware/Diagnostics/src/ExceptionHandler/ExceptionHandlerMiddleware.cs @@ -104,12 +104,12 @@ namespace Microsoft.AspNetCore.Diagnostics private Task ClearCacheHeaders(object state) { - var response = (HttpResponse)state; - response.Headers[HeaderNames.CacheControl] = "no-cache"; - response.Headers[HeaderNames.Pragma] = "no-cache"; - response.Headers[HeaderNames.Expires] = "-1"; - response.Headers.Remove(HeaderNames.ETag); + var headers = ((HttpResponse)state).Headers; + headers[HeaderNames.CacheControl] = "no-cache"; + headers[HeaderNames.Pragma] = "no-cache"; + headers[HeaderNames.Expires] = "-1"; + headers.Remove(HeaderNames.ETag); return Task.CompletedTask; } } -} \ No newline at end of file +} diff --git a/src/Middleware/HttpOverrides/src/ForwardedHeadersMiddleware.cs b/src/Middleware/HttpOverrides/src/ForwardedHeadersMiddleware.cs index f22fd90588..242ab8b1ca 100644 --- a/src/Middleware/HttpOverrides/src/ForwardedHeadersMiddleware.cs +++ b/src/Middleware/HttpOverrides/src/ForwardedHeadersMiddleware.cs @@ -150,17 +150,19 @@ namespace Microsoft.AspNetCore.HttpOverrides bool checkFor = false, checkProto = false, checkHost = false; int entryCount = 0; + var request = context.Request; + var requestHeaders = context.Request.Headers; if ((_options.ForwardedHeaders & ForwardedHeaders.XForwardedFor) == ForwardedHeaders.XForwardedFor) { checkFor = true; - forwardedFor = context.Request.Headers.GetCommaSeparatedValues(_options.ForwardedForHeaderName); + forwardedFor = requestHeaders.GetCommaSeparatedValues(_options.ForwardedForHeaderName); entryCount = Math.Max(forwardedFor.Length, entryCount); } if ((_options.ForwardedHeaders & ForwardedHeaders.XForwardedProto) == ForwardedHeaders.XForwardedProto) { checkProto = true; - forwardedProto = context.Request.Headers.GetCommaSeparatedValues(_options.ForwardedProtoHeaderName); + forwardedProto = requestHeaders.GetCommaSeparatedValues(_options.ForwardedProtoHeaderName); if (_options.RequireHeaderSymmetry && checkFor && forwardedFor.Length != forwardedProto.Length) { _logger.LogWarning(1, "Parameter count mismatch between X-Forwarded-For and X-Forwarded-Proto."); @@ -172,7 +174,7 @@ namespace Microsoft.AspNetCore.HttpOverrides if ((_options.ForwardedHeaders & ForwardedHeaders.XForwardedHost) == ForwardedHeaders.XForwardedHost) { checkHost = true; - forwardedHost = context.Request.Headers.GetCommaSeparatedValues(_options.ForwardedHostHeaderName); + forwardedHost = requestHeaders.GetCommaSeparatedValues(_options.ForwardedHostHeaderName); if (_options.RequireHeaderSymmetry && ((checkFor && forwardedFor.Length != forwardedHost.Length) || (checkProto && forwardedProto.Length != forwardedHost.Length))) @@ -212,7 +214,6 @@ namespace Microsoft.AspNetCore.HttpOverrides // Gather initial values var connection = context.Connection; - var request = context.Request; var currentValues = new SetOfForwarders() { RemoteIpAndPort = connection.RemoteIpAddress != null ? new IPEndPoint(connection.RemoteIpAddress, connection.RemotePort) : null, @@ -293,17 +294,17 @@ namespace Microsoft.AspNetCore.HttpOverrides if (connection.RemoteIpAddress != null) { // Save the original - request.Headers[_options.OriginalForHeaderName] = new IPEndPoint(connection.RemoteIpAddress, connection.RemotePort).ToString(); + requestHeaders[_options.OriginalForHeaderName] = new IPEndPoint(connection.RemoteIpAddress, connection.RemotePort).ToString(); } if (forwardedFor.Length > entriesConsumed) { // Truncate the consumed header values - request.Headers[_options.ForwardedForHeaderName] = forwardedFor.Take(forwardedFor.Length - entriesConsumed).ToArray(); + requestHeaders[_options.ForwardedForHeaderName] = forwardedFor.Take(forwardedFor.Length - entriesConsumed).ToArray(); } else { // All values were consumed - request.Headers.Remove(_options.ForwardedForHeaderName); + requestHeaders.Remove(_options.ForwardedForHeaderName); } connection.RemoteIpAddress = currentValues.RemoteIpAndPort.Address; connection.RemotePort = currentValues.RemoteIpAndPort.Port; @@ -312,16 +313,16 @@ namespace Microsoft.AspNetCore.HttpOverrides if (checkProto && currentValues.Scheme != null) { // Save the original - request.Headers[_options.OriginalProtoHeaderName] = request.Scheme; + requestHeaders[_options.OriginalProtoHeaderName] = request.Scheme; if (forwardedProto.Length > entriesConsumed) { // Truncate the consumed header values - request.Headers[_options.ForwardedProtoHeaderName] = forwardedProto.Take(forwardedProto.Length - entriesConsumed).ToArray(); + requestHeaders[_options.ForwardedProtoHeaderName] = forwardedProto.Take(forwardedProto.Length - entriesConsumed).ToArray(); } else { // All values were consumed - request.Headers.Remove(_options.ForwardedProtoHeaderName); + requestHeaders.Remove(_options.ForwardedProtoHeaderName); } request.Scheme = currentValues.Scheme; } @@ -329,16 +330,16 @@ namespace Microsoft.AspNetCore.HttpOverrides if (checkHost && currentValues.Host != null) { // Save the original - request.Headers[_options.OriginalHostHeaderName] = request.Host.ToString(); + requestHeaders[_options.OriginalHostHeaderName] = request.Host.ToString(); if (forwardedHost.Length > entriesConsumed) { // Truncate the consumed header values - request.Headers[_options.ForwardedHostHeaderName] = forwardedHost.Take(forwardedHost.Length - entriesConsumed).ToArray(); + requestHeaders[_options.ForwardedHostHeaderName] = forwardedHost.Take(forwardedHost.Length - entriesConsumed).ToArray(); } else { // All values were consumed - request.Headers.Remove(_options.ForwardedHostHeaderName); + requestHeaders.Remove(_options.ForwardedHostHeaderName); } request.Host = HostString.FromUriComponent(currentValues.Host); } diff --git a/src/Middleware/ResponseCaching/src/Internal/ResponseCachingKeyProvider.cs b/src/Middleware/ResponseCaching/src/Internal/ResponseCachingKeyProvider.cs index 05a0e9a0f4..6b9b654404 100644 --- a/src/Middleware/ResponseCaching/src/Internal/ResponseCachingKeyProvider.cs +++ b/src/Middleware/ResponseCaching/src/Internal/ResponseCachingKeyProvider.cs @@ -110,19 +110,21 @@ namespace Microsoft.AspNetCore.ResponseCaching.Internal builder.Append(varyByRules.VaryByKeyPrefix); // Vary by headers - if (varyByRules?.Headers.Count > 0) + var headersCount = varyByRules?.Headers.Count ?? 0; + if (headersCount > 0) { // Append a group separator for the header segment of the cache key builder.Append(KeyDelimiter) .Append('H'); - for (var i = 0; i < varyByRules.Headers.Count; i++) + var requestHeaders = context.HttpContext.Request.Headers; + for (var i = 0; i < headersCount; i++) { var header = varyByRules.Headers[i]; - var headerValues = context.HttpContext.Request.Headers[header]; + var headerValues = requestHeaders[header]; builder.Append(KeyDelimiter) .Append(header) - .Append("="); + .Append('='); var headerValuesArray = headerValues.ToArray(); Array.Sort(headerValuesArray, StringComparer.Ordinal); @@ -152,7 +154,7 @@ namespace Microsoft.AspNetCore.ResponseCaching.Internal { builder.Append(KeyDelimiter) .AppendUpperInvariant(queryArray[i].Key) - .Append("="); + .Append('='); var queryValueArray = queryArray[i].Value.ToArray(); Array.Sort(queryValueArray, StringComparer.Ordinal); @@ -176,7 +178,7 @@ namespace Microsoft.AspNetCore.ResponseCaching.Internal var queryKeyValues = context.HttpContext.Request.Query[queryKey]; builder.Append(KeyDelimiter) .Append(queryKey) - .Append("="); + .Append('='); var queryValueArray = queryKeyValues.ToArray(); Array.Sort(queryValueArray, StringComparer.Ordinal); diff --git a/src/Middleware/ResponseCaching/src/Internal/ResponseCachingPolicyProvider.cs b/src/Middleware/ResponseCaching/src/Internal/ResponseCachingPolicyProvider.cs index 97f074cf00..54a045c5af 100644 --- a/src/Middleware/ResponseCaching/src/Internal/ResponseCachingPolicyProvider.cs +++ b/src/Middleware/ResponseCaching/src/Internal/ResponseCachingPolicyProvider.cs @@ -33,12 +33,12 @@ namespace Microsoft.AspNetCore.ResponseCaching.Internal public virtual bool AllowCacheLookup(ResponseCachingContext context) { - var request = context.HttpContext.Request; + var requestHeaders = context.HttpContext.Request.Headers; // Verify request cache-control parameters - if (!StringValues.IsNullOrEmpty(request.Headers[HeaderNames.CacheControl])) + if (!StringValues.IsNullOrEmpty(requestHeaders[HeaderNames.CacheControl])) { - if (HeaderUtilities.ContainsCacheDirective(request.Headers[HeaderNames.CacheControl], CacheControlHeaderValue.NoCacheString)) + if (HeaderUtilities.ContainsCacheDirective(requestHeaders[HeaderNames.CacheControl], CacheControlHeaderValue.NoCacheString)) { context.Logger.RequestWithNoCacheNotCacheable(); return false; @@ -47,7 +47,7 @@ namespace Microsoft.AspNetCore.ResponseCaching.Internal else { // Support for legacy HTTP 1.0 cache directive - if (HeaderUtilities.ContainsCacheDirective(request.Headers[HeaderNames.Pragma], CacheControlHeaderValue.NoCacheString)) + if (HeaderUtilities.ContainsCacheDirective(requestHeaders[HeaderNames.Pragma], CacheControlHeaderValue.NoCacheString)) { context.Logger.RequestWithPragmaNoCacheNotCacheable(); return false; diff --git a/src/Middleware/Session/src/SessionMiddleware.cs b/src/Middleware/Session/src/SessionMiddleware.cs index d3eceb4d2b..796bdbde79 100644 --- a/src/Middleware/Session/src/SessionMiddleware.cs +++ b/src/Middleware/Session/src/SessionMiddleware.cs @@ -158,11 +158,13 @@ namespace Microsoft.AspNetCore.Session { var cookieOptions = _options.Cookie.Build(_context); - _context.Response.Cookies.Append(_options.Cookie.Name, _cookieValue, cookieOptions); + var response = _context.Response; + response.Cookies.Append(_options.Cookie.Name, _cookieValue, cookieOptions); - _context.Response.Headers[HeaderNames.CacheControl] = "no-cache"; - _context.Response.Headers[HeaderNames.Pragma] = "no-cache"; - _context.Response.Headers[HeaderNames.Expires] = "-1"; + var responseHeaders = response.Headers; + responseHeaders[HeaderNames.CacheControl] = "no-cache"; + responseHeaders[HeaderNames.Pragma] = "no-cache"; + responseHeaders[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.