From 55a77f44350e8403d0dc795bef04c9572a9b66a7 Mon Sep 17 00:00:00 2001 From: Phil McCloghry-Laing Date: Wed, 15 Jul 2020 12:54:54 +1000 Subject: [PATCH] Add CORS support to MiniProfiler --- .../MiniProfilerMiddleware.cs | 37 +++++++++++++++++++ .../MiniProfilerOptions.cs | 6 +++ 2 files changed, 43 insertions(+) diff --git a/src/MiniProfiler.AspNetCore/MiniProfilerMiddleware.cs b/src/MiniProfiler.AspNetCore/MiniProfilerMiddleware.cs index 46372d085..1f93bc30b 100644 --- a/src/MiniProfiler.AspNetCore/MiniProfilerMiddleware.cs +++ b/src/MiniProfiler.AspNetCore/MiniProfilerMiddleware.cs @@ -197,6 +197,12 @@ private async Task SetHeadersAndState(HttpContext context, MiniProfiler current) } } + // Expose X-MiniProfiler-Ids header if this is a CORS request + if (context.Request.Headers.ContainsKey("Origin")) + { + context.Response.Headers.Add("Access-Control-Expose-Headers", "X-MiniProfiler-Ids"); + } + // Set the state to use in RenderIncludes() down the pipe later new RequestState { IsAuthorized = isAuthorized, RequestIDs = profilerIds }.Store(context); } @@ -205,6 +211,18 @@ private async Task SetHeadersAndState(HttpContext context, MiniProfiler current) private async Task HandleRequest(HttpContext context, PathString subPath) { + // Is this a CORS request + if (context.Request.Headers.TryGetValue("Origin", out var originValues) + && originValues.Any()) + { + SetCorsHeaders(context.Response, originValues); + if (context.Request.Method == "OPTIONS") + { + await HandleCorsOptionsRequest(context.Response); + return; + } + } + context.Response.StatusCode = StatusCodes.Status200OK; string result = null; @@ -406,5 +424,24 @@ private async Task GetSingleProfilerResultAsync(HttpContext context) return Render.SingleResultHtml(profiler, context.Request.PathBase + Options.RouteBasePath.Value.EnsureTrailingSlash()); } } + + private void SetCorsHeaders(HttpResponse response, string origin) + { + response.Headers.Add("Vary", "Origin"); + + if (_options.Value.CorsOrigins != null + && _options.Value.CorsOrigins.Contains(origin, StringComparer.OrdinalIgnoreCase)) + { + response.Headers.Add("Access-Control-Allow-Origin", origin); + } + } + + private async Task HandleCorsOptionsRequest(HttpResponse response) + { + response.StatusCode = 200; + response.Headers.Add("Access-Control-Allow-Headers", "Content-Type"); + response.Headers.Add("Access-Control-Allow-Methods", "OPTIONS, GET"); + await response.WriteAsync("").ConfigureAwait(false); + } } } diff --git a/src/MiniProfiler.AspNetCore/MiniProfilerOptions.cs b/src/MiniProfiler.AspNetCore/MiniProfilerOptions.cs index 71dc1d2d3..8d615fd35 100644 --- a/src/MiniProfiler.AspNetCore/MiniProfilerOptions.cs +++ b/src/MiniProfiler.AspNetCore/MiniProfilerOptions.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; using StackExchange.Profiling.Internal; @@ -55,6 +56,11 @@ public class MiniProfilerOptions : MiniProfilerBaseOptions /// public Func UserIdProvider { get; set; } = request => request.HttpContext.Connection.RemoteIpAddress?.ToString(); + /// + /// Optional list of origins allowed for CORS requests to MiniProfiler + /// + public IEnumerable CorsOrigins { get; set; } + #if NETCOREAPP3_0 /// /// Whether to add a Server-Timing header after profiling a request. Only supported in .NET Core 3.0 and higher.