From ffaa2f1cb6da35bb6971ecde260a60b773745867 Mon Sep 17 00:00:00 2001 From: Taylor Southwick Date: Tue, 16 Jan 2024 11:40:39 -0800 Subject: [PATCH 1/5] Prefer IHttpContextAccessor over HttpContextAccessor This change will check if we're currently in a runtime environment that contains a IHttpContextAccessor; if we are, then we'll prefer that over HttpContextAccessor. This allows people to potentially customize what it means to get and set a HttpContext. --- .../HttpContext.cs | 20 ++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/src/Microsoft.AspNetCore.SystemWebAdapters/HttpContext.cs b/src/Microsoft.AspNetCore.SystemWebAdapters/HttpContext.cs index 81fa01dad7..91ae4f9cfa 100644 --- a/src/Microsoft.AspNetCore.SystemWebAdapters/HttpContext.cs +++ b/src/Microsoft.AspNetCore.SystemWebAdapters/HttpContext.cs @@ -6,6 +6,7 @@ using System.Linq; using System.Security.Principal; using System.Web.Caching; +using System.Web.Hosting; using System.Web.SessionState; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; @@ -20,7 +21,7 @@ namespace System.Web; public class HttpContext : IServiceProvider { - private static readonly HttpContextAccessor _accessor = new(); + private static readonly HttpContextAccessor _defaultHttpContextAccessor = new(); private HttpRequest? _request; private HttpResponse? _response; @@ -30,8 +31,21 @@ public class HttpContext : IServiceProvider public static HttpContext? Current { - get => _accessor.HttpContext?.AsSystemWeb(); - set => _accessor.HttpContext = value?.AsAspNetCore(); + get => Accessor.HttpContext?.AsSystemWeb(); + set => Accessor.HttpContext = value?.AsAspNetCore(); + } + + private static IHttpContextAccessor Accessor + { + get + { + if (HostingEnvironmentAccessor.TryGet(out var current) && current.Services.GetService() is { } accessor) + { + return accessor; + } + + return _defaultHttpContextAccessor; + } } internal HttpContext(HttpContextCore context) From 98661f526fb73d3c8729a4497974328c1211babc Mon Sep 17 00:00:00 2001 From: Taylor Southwick Date: Tue, 16 Jan 2024 12:39:09 -0800 Subject: [PATCH 2/5] Add tests --- .../HttpContextCurrentTests.cs | 70 +++++++++++++++++++ .../ResponseHeaderTests.cs | 1 + 2 files changed, 71 insertions(+) create mode 100644 test/Microsoft.AspNetCore.SystemWebAdapters.CoreServices.Tests/HttpContextCurrentTests.cs diff --git a/test/Microsoft.AspNetCore.SystemWebAdapters.CoreServices.Tests/HttpContextCurrentTests.cs b/test/Microsoft.AspNetCore.SystemWebAdapters.CoreServices.Tests/HttpContextCurrentTests.cs new file mode 100644 index 0000000000..c91493a06a --- /dev/null +++ b/test/Microsoft.AspNetCore.SystemWebAdapters.CoreServices.Tests/HttpContextCurrentTests.cs @@ -0,0 +1,70 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Threading.Tasks; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.TestHost; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Moq; +using Xunit; + +namespace Microsoft.AspNetCore.SystemWebAdapters.CoreServices.Tests; + +[Collection(nameof(SelfHostedTests))] +public class HttpContextCurrentTests +{ + [Fact] + public void CurrentReturnsNullByDefault() + { + Assert.Null(HttpContext.Current); + } + + [Fact] + public void SavesByDefaultToHttpContextAccessor() + { + // Arrange + var accessor = new HttpContextAccessor(); + var context = new DefaultHttpContext(); + + // Act + HttpContext.Current = context; + + // Assert + Assert.Same(accessor.HttpContext, context); + } + + [Fact] + public async Task UsesIHttpContextAccessor() + { + // Arrange + var defaultAccessor = new HttpContextAccessor(); + var accessor = new Mock(); + accessor.SetupAllProperties(); + + using var host = await new HostBuilder() + .ConfigureWebHost(webBuilder => + { + webBuilder + .UseTestServer() + .ConfigureServices(services => + { + services.AddSingleton(accessor.Object); + services.AddSystemWebAdapters(); + }) + .Configure(app => + { + }); + }) + .StartAsync(); + var context = new DefaultHttpContext(); + + // Act + HttpContext.Current = context; + + // Assert + Assert.Same(context, accessor.Object.HttpContext); + Assert.Null(defaultAccessor.HttpContext); + } +} diff --git a/test/Microsoft.AspNetCore.SystemWebAdapters.CoreServices.Tests/ResponseHeaderTests.cs b/test/Microsoft.AspNetCore.SystemWebAdapters.CoreServices.Tests/ResponseHeaderTests.cs index 93c98f9ac2..1e225f2220 100644 --- a/test/Microsoft.AspNetCore.SystemWebAdapters.CoreServices.Tests/ResponseHeaderTests.cs +++ b/test/Microsoft.AspNetCore.SystemWebAdapters.CoreServices.Tests/ResponseHeaderTests.cs @@ -13,6 +13,7 @@ using Microsoft.Extensions.Hosting; using Microsoft.Net.Http.Headers; using Xunit; + using SameSiteMode = System.Web.SameSiteMode; namespace Microsoft.AspNetCore.SystemWebAdapters.CoreServices.Tests; From 9806c47acfabf3ef8cd1dd404a2593a8de0f94d0 Mon Sep 17 00:00:00 2001 From: Taylor Southwick Date: Tue, 16 Jan 2024 12:39:38 -0800 Subject: [PATCH 3/5] inline --- .../HttpContextCurrentTests.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/Microsoft.AspNetCore.SystemWebAdapters.CoreServices.Tests/HttpContextCurrentTests.cs b/test/Microsoft.AspNetCore.SystemWebAdapters.CoreServices.Tests/HttpContextCurrentTests.cs index c91493a06a..3e587e0676 100644 --- a/test/Microsoft.AspNetCore.SystemWebAdapters.CoreServices.Tests/HttpContextCurrentTests.cs +++ b/test/Microsoft.AspNetCore.SystemWebAdapters.CoreServices.Tests/HttpContextCurrentTests.cs @@ -39,7 +39,6 @@ public void SavesByDefaultToHttpContextAccessor() public async Task UsesIHttpContextAccessor() { // Arrange - var defaultAccessor = new HttpContextAccessor(); var accessor = new Mock(); accessor.SetupAllProperties(); @@ -65,6 +64,6 @@ public async Task UsesIHttpContextAccessor() // Assert Assert.Same(context, accessor.Object.HttpContext); - Assert.Null(defaultAccessor.HttpContext); + Assert.Null(new HttpContextAccessor().HttpContext); } } From 7712b59b9df795f9e70b0a139e3cc3e8060881bf Mon Sep 17 00:00:00 2001 From: Taylor Southwick Date: Tue, 16 Jan 2024 12:53:53 -0800 Subject: [PATCH 4/5] move to runtime --- .../Hosting/HostingEnvironmentAccessor.cs | 10 ++++++++++ .../HttpContext.cs | 20 ++----------------- 2 files changed, 12 insertions(+), 18 deletions(-) diff --git a/src/Microsoft.AspNetCore.SystemWebAdapters/Hosting/HostingEnvironmentAccessor.cs b/src/Microsoft.AspNetCore.SystemWebAdapters/Hosting/HostingEnvironmentAccessor.cs index 96cab330c8..c857ccd2d7 100644 --- a/src/Microsoft.AspNetCore.SystemWebAdapters/Hosting/HostingEnvironmentAccessor.cs +++ b/src/Microsoft.AspNetCore.SystemWebAdapters/Hosting/HostingEnvironmentAccessor.cs @@ -2,20 +2,25 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics.CodeAnalysis; +using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.SystemWebAdapters; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; namespace System.Web.Hosting; internal sealed class HostingEnvironmentAccessor { + private static readonly HttpContextAccessor _defaultHttpContextAccessor = new(); private static HostingEnvironmentAccessor? _current; private readonly IOptions _options; + private readonly IHttpContextAccessor? _accessor; public HostingEnvironmentAccessor(IServiceProvider services, IOptions options) { Services = services; + _accessor = services.GetService(); _options = options; } @@ -42,5 +47,10 @@ public static bool TryGet([MaybeNullWhen(false)] out HostingEnvironmentAccessor return current is not null; } + /// + /// Gets an that is either registered to the current hosting runtime or the default one via . + /// + public static IHttpContextAccessor HttpContextAccessor => _current?._accessor ?? _defaultHttpContextAccessor; + internal SystemWebAdaptersOptions Options => _options.Value; } diff --git a/src/Microsoft.AspNetCore.SystemWebAdapters/HttpContext.cs b/src/Microsoft.AspNetCore.SystemWebAdapters/HttpContext.cs index 91ae4f9cfa..d3a85a36e5 100644 --- a/src/Microsoft.AspNetCore.SystemWebAdapters/HttpContext.cs +++ b/src/Microsoft.AspNetCore.SystemWebAdapters/HttpContext.cs @@ -9,7 +9,6 @@ using System.Web.Hosting; using System.Web.SessionState; using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Features.Authentication; using Microsoft.AspNetCore.SystemWebAdapters; using Microsoft.AspNetCore.SystemWebAdapters.Features; @@ -21,8 +20,6 @@ namespace System.Web; public class HttpContext : IServiceProvider { - private static readonly HttpContextAccessor _defaultHttpContextAccessor = new(); - private HttpRequest? _request; private HttpResponse? _response; private HttpServerUtility? _server; @@ -31,21 +28,8 @@ public class HttpContext : IServiceProvider public static HttpContext? Current { - get => Accessor.HttpContext?.AsSystemWeb(); - set => Accessor.HttpContext = value?.AsAspNetCore(); - } - - private static IHttpContextAccessor Accessor - { - get - { - if (HostingEnvironmentAccessor.TryGet(out var current) && current.Services.GetService() is { } accessor) - { - return accessor; - } - - return _defaultHttpContextAccessor; - } + get => HostingEnvironmentAccessor.HttpContextAccessor.HttpContext?.AsSystemWeb(); + set => HostingEnvironmentAccessor.HttpContextAccessor.HttpContext = value?.AsAspNetCore(); } internal HttpContext(HttpContextCore context) From e40f5b4a6e2b8fde30c1ae982e5dcae21367c889 Mon Sep 17 00:00:00 2001 From: Taylor Southwick Date: Wed, 17 Jan 2024 15:50:37 -0800 Subject: [PATCH 5/5] lazily load --- .../Hosting/HostingEnvironmentAccessor.cs | 21 +++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/src/Microsoft.AspNetCore.SystemWebAdapters/Hosting/HostingEnvironmentAccessor.cs b/src/Microsoft.AspNetCore.SystemWebAdapters/Hosting/HostingEnvironmentAccessor.cs index c857ccd2d7..18d5b9cc2a 100644 --- a/src/Microsoft.AspNetCore.SystemWebAdapters/Hosting/HostingEnvironmentAccessor.cs +++ b/src/Microsoft.AspNetCore.SystemWebAdapters/Hosting/HostingEnvironmentAccessor.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics.CodeAnalysis; +using System.Threading; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.SystemWebAdapters; using Microsoft.Extensions.DependencyInjection; @@ -11,7 +12,7 @@ namespace System.Web.Hosting; internal sealed class HostingEnvironmentAccessor { - private static readonly HttpContextAccessor _defaultHttpContextAccessor = new(); + private static HttpContextAccessor? _defaultHttpContextAccessor; private static HostingEnvironmentAccessor? _current; private readonly IOptions _options; @@ -50,7 +51,23 @@ public static bool TryGet([MaybeNullWhen(false)] out HostingEnvironmentAccessor /// /// Gets an that is either registered to the current hosting runtime or the default one via . /// - public static IHttpContextAccessor HttpContextAccessor => _current?._accessor ?? _defaultHttpContextAccessor; + public static IHttpContextAccessor HttpContextAccessor + { + get + { + if (_current?._accessor is { } current) + { + return current; + } + + if (_defaultHttpContextAccessor is null) + { + Interlocked.CompareExchange(ref _defaultHttpContextAccessor, new(), null); + } + + return _defaultHttpContextAccessor; + } + } internal SystemWebAdaptersOptions Options => _options.Value; }