diff --git a/samples/RemoteAuth/Identity/MvcCoreApp/Controllers/TestController.cs b/samples/RemoteAuth/Identity/MvcCoreApp/Controllers/TestController.cs
index cad5aa8218..389d8153c4 100644
--- a/samples/RemoteAuth/Identity/MvcCoreApp/Controllers/TestController.cs
+++ b/samples/RemoteAuth/Identity/MvcCoreApp/Controllers/TestController.cs
@@ -1,3 +1,4 @@
+using System.Web.SessionState;
using ClassLibrary;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.SystemWebAdapters;
@@ -9,7 +10,7 @@ namespace MvcCoreApp.Controllers
public class TestController : Controller
{
[HttpGet]
- [Session(IsReadOnly = true)]
+ [Session(SessionBehavior = SessionStateBehavior.ReadOnly)]
[Route("/api/test/request/info")]
public void Get([FromQuery] bool? suppress = false) => RequestInfo.WriteRequestInfo(suppress ?? false);
diff --git a/src/Microsoft.AspNetCore.SystemWebAdapters.CoreServices/SessionAttribute.cs b/src/Microsoft.AspNetCore.SystemWebAdapters.CoreServices/SessionAttribute.cs
index 0e83316f26..4a9152be24 100644
--- a/src/Microsoft.AspNetCore.SystemWebAdapters.CoreServices/SessionAttribute.cs
+++ b/src/Microsoft.AspNetCore.SystemWebAdapters.CoreServices/SessionAttribute.cs
@@ -2,13 +2,51 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System;
+using System.Web.SessionState;
namespace Microsoft.AspNetCore.SystemWebAdapters;
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public sealed class SessionAttribute : Attribute
{
- public SessionBehavior Behavior { get; set; } = SessionBehavior.Preload;
+ [Obsolete("Prefer SessionBehavior instead.")]
+ public SessionBehavior Behavior
+ {
+ get
+ {
+ if (SessionBehavior is SessionStateBehavior.Disabled)
+ {
+ return SystemWebAdapters.SessionBehavior.None;
+ }
- public bool IsReadOnly { get; set; }
+ return IsPreLoad ? SystemWebAdapters.SessionBehavior.Preload : SystemWebAdapters.SessionBehavior.OnDemand;
+ }
+ set
+ {
+ SessionBehavior = value switch
+ {
+ SystemWebAdapters.SessionBehavior.None => SessionStateBehavior.Disabled,
+ SystemWebAdapters.SessionBehavior.Preload => SessionStateBehavior.Required,
+ SystemWebAdapters.SessionBehavior.OnDemand => SessionStateBehavior.Required,
+ _ => throw new ArgumentOutOfRangeException(nameof(value)),
+ };
+ }
+ }
+
+ public SessionStateBehavior SessionBehavior { get; set; }
+
+ public bool IsPreLoad { get; set; } = true;
+
+ public bool IsReadOnly
+ {
+ get => SessionBehavior is SessionStateBehavior.ReadOnly;
+ [Obsolete("Prefer SessionBehavior property")]
+ set
+ {
+ if (value)
+ {
+ SessionBehavior = SessionStateBehavior.ReadOnly;
+ }
+ }
+ }
}
diff --git a/src/Microsoft.AspNetCore.SystemWebAdapters.CoreServices/SessionBehavior.cs b/src/Microsoft.AspNetCore.SystemWebAdapters.CoreServices/SessionBehavior.cs
index b55c8eca07..a3f499aae9 100644
--- a/src/Microsoft.AspNetCore.SystemWebAdapters.CoreServices/SessionBehavior.cs
+++ b/src/Microsoft.AspNetCore.SystemWebAdapters.CoreServices/SessionBehavior.cs
@@ -5,6 +5,10 @@
namespace Microsoft.AspNetCore.SystemWebAdapters;
+///
+/// Instead of this, please use .
+///
+[Obsolete("Prefer System.Web.SessionState")]
public enum SessionBehavior
{
///
diff --git a/src/Microsoft.AspNetCore.SystemWebAdapters.CoreServices/SessionMiddleware.cs b/src/Microsoft.AspNetCore.SystemWebAdapters.CoreServices/SessionMiddleware.cs
index cd6b027c72..f49cdb9554 100644
--- a/src/Microsoft.AspNetCore.SystemWebAdapters.CoreServices/SessionMiddleware.cs
+++ b/src/Microsoft.AspNetCore.SystemWebAdapters.CoreServices/SessionMiddleware.cs
@@ -6,65 +6,65 @@
using System.Threading.Tasks;
using System.Web.SessionState;
using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.SystemWebAdapters.Features;
using Microsoft.AspNetCore.SystemWebAdapters.SessionState;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
namespace Microsoft.AspNetCore.SystemWebAdapters;
-internal partial class SessionMiddleware
+internal partial class SessionLoadMiddleware
{
private readonly RequestDelegate _next;
- private readonly ILogger _logger;
+ private readonly ILogger _logger;
[LoggerMessage(EventId = 0, Level = LogLevel.Trace, Message = "Initializing session state: {Behavior}")]
- private partial void LogMessage(SessionBehavior behavior);
+ private partial void LogMessage(SessionStateBehavior behavior);
[LoggerMessage(EventId = 1, Level = LogLevel.Warning, Message = "Creating session on demand by synchronously waiting on a potential asynchronous connection")]
private partial void LogOnDemand();
private readonly TimeSpan CommitTimeout = TimeSpan.FromMinutes(1);
- public SessionMiddleware(RequestDelegate next, ILogger logger)
+ public SessionLoadMiddleware(RequestDelegate next, ILogger logger)
{
_next = next;
_logger = logger;
}
public Task InvokeAsync(HttpContextCore context)
- => context.GetEndpoint()?.Metadata.GetMetadata() is { Behavior: not SessionBehavior.None } metadata
- ? ManageStateAsync(context, metadata)
+ => context.Features.GetRequired() is { Behavior: not SessionStateBehavior.Disabled and not SessionStateBehavior.Default } feature
+ ? ManageStateAsync(context, feature)
: _next(context);
- private async Task ManageStateAsync(HttpContextCore context, SessionAttribute metadata)
+ private async Task ManageStateAsync(HttpContextCore context, ISessionStateFeature feature)
{
- LogMessage(metadata.Behavior);
+ LogMessage(feature.Behavior);
var manager = context.RequestServices.GetRequiredService();
+ var details = new SessionAttribute { SessionBehavior = feature.Behavior, IsPreLoad = feature.IsPreLoad };
- using var state = metadata.Behavior switch
- {
+ using var state = !feature.IsPreLoad
#pragma warning disable CA2000 // False positive for CA2000 here
-#pragma warning disable CS0618 // Type or member is obsolete
- SessionBehavior.OnDemand => new LazySessionState(context, LogOnDemand, metadata, manager),
-#pragma warning restore CS0618 // Type or member is obsolete
+ ? new LazySessionState(context, LogOnDemand, details, manager)
#pragma warning restore CA2000 // Dispose objects before losing scope
+ : await manager.CreateAsync(context, details);
- SessionBehavior.Preload => await manager.CreateAsync(context, metadata),
- var behavior => throw new InvalidOperationException($"Unknown session behavior {behavior}"),
- };
-
- context.Features.Set(new HttpSessionState(state));
+ feature.State = state;
try
{
await _next(context);
using var cts = new CancellationTokenSource(CommitTimeout);
- await state.CommitAsync(cts.Token);
+
+ if (!details.IsReadOnly)
+ {
+ await state.CommitAsync(cts.Token);
+ }
}
finally
{
- context.Features.Set(null);
+ feature.State = null;
}
}
diff --git a/src/Microsoft.AspNetCore.SystemWebAdapters.CoreServices/SessionStateMiddleware.cs b/src/Microsoft.AspNetCore.SystemWebAdapters.CoreServices/SessionStateMiddleware.cs
new file mode 100644
index 0000000000..1ac73c5def
--- /dev/null
+++ b/src/Microsoft.AspNetCore.SystemWebAdapters.CoreServices/SessionStateMiddleware.cs
@@ -0,0 +1,51 @@
+// 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 System.Web.SessionState;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.SystemWebAdapters.Features;
+using Microsoft.AspNetCore.SystemWebAdapters.SessionState;
+
+namespace Microsoft.AspNetCore.SystemWebAdapters;
+
+internal sealed class SessionStateMiddleware
+{
+ private readonly RequestDelegate _next;
+
+ public SessionStateMiddleware(RequestDelegate next) => _next = next;
+
+ public async Task InvokeAsync(HttpContext context)
+ {
+ context.Features.Set(new SessionStateFeature(context));
+ await _next(context);
+ context.Features.Set(null);
+ }
+
+ private sealed class SessionStateFeature : ISessionStateFeature
+ {
+ private readonly HttpContext _context;
+ private SessionStateBehavior? _behavior;
+ private HttpSessionState? _session;
+
+ public SessionStateFeature(HttpContext context)
+ {
+ _context = context;
+ }
+
+ public SessionStateBehavior Behavior
+ {
+ get => _behavior is { } behavior ? behavior : GetExisting()?.SessionBehavior ?? default;
+ set => _behavior = value;
+ }
+
+ public HttpSessionState? Session => State is null ? null : _session ??= new(this);
+
+ public ISessionState? State { get; set; }
+
+ public bool IsPreLoad => GetExisting()?.IsPreLoad ?? true;
+
+ private SessionAttribute? GetExisting()
+ => _context.GetEndpoint()?.Metadata.GetMetadata();
+ }
+}
diff --git a/src/Microsoft.AspNetCore.SystemWebAdapters.CoreServices/SystemWebAdaptersExtensions.cs b/src/Microsoft.AspNetCore.SystemWebAdapters.CoreServices/SystemWebAdaptersExtensions.cs
index 966d5e2205..78ce0fc840 100644
--- a/src/Microsoft.AspNetCore.SystemWebAdapters.CoreServices/SystemWebAdaptersExtensions.cs
+++ b/src/Microsoft.AspNetCore.SystemWebAdapters.CoreServices/SystemWebAdaptersExtensions.cs
@@ -106,7 +106,7 @@ public static void UseSystemWebAdapters(this IApplicationBuilder app)
ApplicationEvent.PostUpdateRequestCache,
});
- app.UseMiddleware();
+ app.UseMiddleware();
if (app.AreHttpApplicationEventsRequired())
{
@@ -146,6 +146,7 @@ public Action Configure(Action next)
{
builder.UseMiddleware();
builder.UseMiddleware();
+ builder.UseMiddleware();
if (builder.AreHttpApplicationEventsRequired())
{
diff --git a/src/Microsoft.AspNetCore.SystemWebAdapters/Adapters/Features/ISessionStateFeature.cs b/src/Microsoft.AspNetCore.SystemWebAdapters/Adapters/Features/ISessionStateFeature.cs
new file mode 100644
index 0000000000..b006f85739
--- /dev/null
+++ b/src/Microsoft.AspNetCore.SystemWebAdapters/Adapters/Features/ISessionStateFeature.cs
@@ -0,0 +1,26 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#if NETCOREAPP
+using System.Diagnostics.CodeAnalysis;
+using System.Web;
+using System.Web.SessionState;
+using Microsoft.AspNetCore.SystemWebAdapters.SessionState;
+
+namespace Microsoft.AspNetCore.SystemWebAdapters.Features;
+
+///
+/// Represents session state for System.Web
+///
+[Experimental(Constants.ExperimentalFeatures.DiagnosticId)]
+public interface ISessionStateFeature
+{
+ SessionStateBehavior Behavior { get; set; }
+
+ bool IsPreLoad { get; }
+
+ HttpSessionState? Session { get; }
+
+ ISessionState? State { get; set; }
+}
+#endif
diff --git a/src/Microsoft.AspNetCore.SystemWebAdapters/Generated/Ref.Standard.cs b/src/Microsoft.AspNetCore.SystemWebAdapters/Generated/Ref.Standard.cs
index c4e79b8abe..0e0dd4416d 100644
--- a/src/Microsoft.AspNetCore.SystemWebAdapters/Generated/Ref.Standard.cs
+++ b/src/Microsoft.AspNetCore.SystemWebAdapters/Generated/Ref.Standard.cs
@@ -158,6 +158,7 @@ internal HttpContext() { }
public void RewritePath(string path, bool rebaseClientPath) { throw new System.PlatformNotSupportedException("Only supported when running on ASP.NET Core or System.Web");}
public void RewritePath(string filePath, string pathInfo, string queryString) { throw new System.PlatformNotSupportedException("Only supported when running on ASP.NET Core or System.Web");}
public void RewritePath(string filePath, string pathInfo, string queryString, bool setClientFilePath) { throw new System.PlatformNotSupportedException("Only supported when running on ASP.NET Core or System.Web");}
+ public void SetSessionStateBehavior(System.Web.SessionState.SessionStateBehavior sessionStateBehavior) { throw new System.PlatformNotSupportedException("Only supported when running on ASP.NET Core or System.Web");}
object System.IServiceProvider.GetService(System.Type service) { throw new System.PlatformNotSupportedException("Only supported when running on ASP.NET Core or System.Web");}
}
public partial class HttpContextBase : System.IServiceProvider
@@ -185,6 +186,7 @@ public partial class HttpContextBase : System.IServiceProvider
public virtual void RewritePath(string path, bool rebaseClientPath) { throw new System.PlatformNotSupportedException("Only supported when running on ASP.NET Core or System.Web");}
public virtual void RewritePath(string filePath, string pathInfo, string queryString) { throw new System.PlatformNotSupportedException("Only supported when running on ASP.NET Core or System.Web");}
public virtual void RewritePath(string filePath, string pathInfo, string queryString, bool setClientFilePath) { throw new System.PlatformNotSupportedException("Only supported when running on ASP.NET Core or System.Web");}
+ public virtual void SetSessionStateBehavior(System.Web.SessionState.SessionStateBehavior sessionStateBehavior) { throw new System.PlatformNotSupportedException("Only supported when running on ASP.NET Core or System.Web");}
}
public partial class HttpContextWrapper : System.Web.HttpContextBase
{
@@ -210,6 +212,7 @@ public partial class HttpContextWrapper : System.Web.HttpContextBase
public override void RewritePath(string path, bool rebaseClientPath) { throw new System.PlatformNotSupportedException("Only supported when running on ASP.NET Core or System.Web");}
public override void RewritePath(string filePath, string pathInfo, string queryString) { throw new System.PlatformNotSupportedException("Only supported when running on ASP.NET Core or System.Web");}
public override void RewritePath(string filePath, string pathInfo, string queryString, bool setClientFilePath) { throw new System.PlatformNotSupportedException("Only supported when running on ASP.NET Core or System.Web");}
+ public override void SetSessionStateBehavior(System.Web.SessionState.SessionStateBehavior sessionStateBehavior) { throw new System.PlatformNotSupportedException("Only supported when running on ASP.NET Core or System.Web");}
}
public sealed partial class HttpCookie
{
@@ -855,6 +858,13 @@ internal HttpSessionState() { }
public void Remove(string name) { throw new System.PlatformNotSupportedException("Only supported when running on ASP.NET Core or System.Web");}
public void RemoveAll() { throw new System.PlatformNotSupportedException("Only supported when running on ASP.NET Core or System.Web");}
}
+ public enum SessionStateBehavior
+ {
+ Default = 0,
+ Disabled = 3,
+ ReadOnly = 2,
+ Required = 1,
+ }
public enum SessionStateMode
{
Custom = 4,
diff --git a/src/Microsoft.AspNetCore.SystemWebAdapters/Generated/TypeForwards.Framework.cs b/src/Microsoft.AspNetCore.SystemWebAdapters/Generated/TypeForwards.Framework.cs
index 8d529cc2d7..4ec96364a4 100644
--- a/src/Microsoft.AspNetCore.SystemWebAdapters/Generated/TypeForwards.Framework.cs
+++ b/src/Microsoft.AspNetCore.SystemWebAdapters/Generated/TypeForwards.Framework.cs
@@ -69,4 +69,5 @@
[assembly:System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Web.Configuration.HttpCapabilitiesBase))]
[assembly:System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Web.Hosting.HostingEnvironment))]
[assembly:System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Web.SessionState.HttpSessionState))]
+[assembly:System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Web.SessionState.SessionStateBehavior))]
[assembly:System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Web.SessionState.SessionStateMode))]
diff --git a/src/Microsoft.AspNetCore.SystemWebAdapters/HttpContext.cs b/src/Microsoft.AspNetCore.SystemWebAdapters/HttpContext.cs
index 2a106c6c21..76f30ebedd 100644
--- a/src/Microsoft.AspNetCore.SystemWebAdapters/HttpContext.cs
+++ b/src/Microsoft.AspNetCore.SystemWebAdapters/HttpContext.cs
@@ -93,7 +93,10 @@ public IPrincipal User
}
}
- public HttpSessionState? Session => _context.Features.Get();
+ public HttpSessionState? Session => _context.Features.Get()?.Session;
+
+ public void SetSessionStateBehavior(SessionStateBehavior sessionStateBehavior)
+ => _context.Features.GetRequired().Behavior = sessionStateBehavior;
public DateTime Timestamp => _context.Features.GetRequired().Timestamp.DateTime;
diff --git a/src/Microsoft.AspNetCore.SystemWebAdapters/HttpContextBase.cs b/src/Microsoft.AspNetCore.SystemWebAdapters/HttpContextBase.cs
index 136378b31a..0ac23f7165 100644
--- a/src/Microsoft.AspNetCore.SystemWebAdapters/HttpContextBase.cs
+++ b/src/Microsoft.AspNetCore.SystemWebAdapters/HttpContextBase.cs
@@ -4,6 +4,7 @@
using System.Collections;
using System.Security.Principal;
using System.Diagnostics.CodeAnalysis;
+using System.Web.SessionState;
using Microsoft.AspNetCore.SystemWebAdapters;
namespace System.Web
@@ -67,5 +68,7 @@ public virtual IPrincipal User
public virtual void RewritePath(string filePath, string pathInfo, string? queryString) => throw new NotImplementedException();
public virtual void RewritePath(string filePath, string pathInfo, string? queryString, bool setClientFilePath) => throw new NotImplementedException();
+
+ public virtual void SetSessionStateBehavior(SessionStateBehavior sessionStateBehavior) => throw new NotImplementedException();
}
}
diff --git a/src/Microsoft.AspNetCore.SystemWebAdapters/HttpContextWrapper.cs b/src/Microsoft.AspNetCore.SystemWebAdapters/HttpContextWrapper.cs
index be4766cc25..5a7388547e 100644
--- a/src/Microsoft.AspNetCore.SystemWebAdapters/HttpContextWrapper.cs
+++ b/src/Microsoft.AspNetCore.SystemWebAdapters/HttpContextWrapper.cs
@@ -79,5 +79,7 @@ public override IPrincipal User
public override void RewritePath(string filePath, string pathInfo, string? queryString) => _context.RewritePath(filePath, pathInfo, queryString);
public override void RewritePath(string filePath, string pathInfo, string? queryString, bool setClientFilePath) => _context.RewritePath(filePath, pathInfo, queryString, setClientFilePath);
+
+ public override void SetSessionStateBehavior(SessionStateBehavior sessionStateBehavior) => _context.SetSessionStateBehavior(sessionStateBehavior);
}
}
diff --git a/src/Microsoft.AspNetCore.SystemWebAdapters/SessionState/HttpSessionState.cs b/src/Microsoft.AspNetCore.SystemWebAdapters/SessionState/HttpSessionState.cs
index 73a2ff7212..b961716c99 100644
--- a/src/Microsoft.AspNetCore.SystemWebAdapters/SessionState/HttpSessionState.cs
+++ b/src/Microsoft.AspNetCore.SystemWebAdapters/SessionState/HttpSessionState.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System.Collections;
+using Microsoft.AspNetCore.SystemWebAdapters.Features;
using Microsoft.AspNetCore.SystemWebAdapters.SessionState;
namespace System.Web.SessionState;
@@ -10,12 +11,19 @@ namespace System.Web.SessionState;
[Diagnostics.CodeAnalysis.SuppressMessage("Naming", "CA1710:Identifiers should have correct suffix", Justification = Constants.ApiFromAspNet)]
public class HttpSessionState : ICollection
{
+ private readonly Func _state;
+
+ internal HttpSessionState(ISessionStateFeature feature)
+ {
+ _state = () => feature.State ?? throw new InvalidOperationException("Session state is no longer available");
+ }
+
public HttpSessionState(ISessionState container)
{
- State = container;
+ _state = () => container;
}
- internal ISessionState State { get; }
+ internal ISessionState State => _state();
public string SessionID => State.SessionID;
diff --git a/src/Microsoft.AspNetCore.SystemWebAdapters/SessionState/SessionStateBehavior.cs b/src/Microsoft.AspNetCore.SystemWebAdapters/SessionState/SessionStateBehavior.cs
new file mode 100644
index 0000000000..52ceb19daa
--- /dev/null
+++ b/src/Microsoft.AspNetCore.SystemWebAdapters/SessionState/SessionStateBehavior.cs
@@ -0,0 +1,12 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace System.Web.SessionState;
+
+public enum SessionStateBehavior
+{
+ Default = 0,
+ Required = 1,
+ ReadOnly = 2,
+ Disabled = 3
+};
diff --git a/test/Microsoft.AspNetCore.SystemWebAdapters.CoreServices.Tests/SessionState/SessionIntegrationTests.cs b/test/Microsoft.AspNetCore.SystemWebAdapters.CoreServices.Tests/SessionState/SessionIntegrationTests.cs
new file mode 100644
index 0000000000..0c095d68d1
--- /dev/null
+++ b/test/Microsoft.AspNetCore.SystemWebAdapters.CoreServices.Tests/SessionState/SessionIntegrationTests.cs
@@ -0,0 +1,143 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+
+using System;
+using System.Net.Http;
+using System.Threading.Tasks;
+using System.Web.SessionState;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.AspNetCore.TestHost;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Hosting;
+using Xunit;
+
+namespace Microsoft.AspNetCore.SystemWebAdapters.CoreServices.Tests.SessionState;
+
+[Collection(nameof(SelfHostedTests))]
+public class SessionIntegrationTests
+{
+ [InlineData("/?override=disabled", "Session:null")]
+ [InlineData("/?override=readonly", "ReadOnly:True")]
+ [InlineData("/?override=required", "ReadOnly:False")]
+ [InlineData("/?override=default", "Session:null")]
+ [Theory]
+ public async Task TestSetSessionStateBehavior(string endpoint, string expected)
+ {
+ var actual = await GetAsync(endpoint);
+ Assert.Equal(expected, actual);
+ }
+
+ [InlineData("/disabled", "Session:null")]
+ [InlineData("/readonly", "ReadOnly:True")]
+ [InlineData("/required", "ReadOnly:False")]
+ [InlineData("/default", "Session:null")]
+ [Theory]
+ public async Task TestSessionAttribute(string endpoint, string expected)
+ {
+ var actual = await GetAsync(endpoint);
+ Assert.Equal(expected, actual);
+ }
+
+ [InlineData("/disabled?override=required", "ReadOnly:False")]
+ [InlineData("/disabled?override=readonly", "ReadOnly:True")]
+ [InlineData("/readonly?override=required", "ReadOnly:False")]
+ [InlineData("/default?override=disabled", "Session:null")]
+ [Theory]
+ public async Task TestOverrideSessionStateBehavior(string endpoint, string expected)
+ {
+ var actual = await GetAsync(endpoint);
+ Assert.Equal(expected, actual);
+ }
+
+ private static async Task GetAsync(string endpoint)
+ {
+ using var host = await new HostBuilder()
+ .ConfigureWebHost(webBuilder =>
+ {
+ webBuilder
+ .UseTestServer(options =>
+ {
+ options.AllowSynchronousIO = true;
+ })
+ .ConfigureServices(services =>
+ {
+ services.AddRouting();
+ services.AddControllers();
+ services.AddSystemWebAdapters()
+ .WrapAspNetCoreSession();
+ services.AddDistributedMemoryCache();
+ })
+ .Configure(app =>
+ {
+ app.UseRouting();
+ app.Use((ctx, next) =>
+ {
+ SetOverrideSessionBehavior(ctx);
+ return next(ctx);
+ });
+ app.UseSession();
+ app.UseSystemWebAdapters();
+ app.UseEndpoints(endpoints =>
+ {
+ endpoints.MapGet("/", (context) => GetSessionStatus(context));
+ endpoints.MapGet("/disabled", (context) => GetSessionStatus(context)).WithMetadata(new SessionAttribute { SessionBehavior = SessionStateBehavior.Disabled });
+ endpoints.MapGet("/readonly", (context) => GetSessionStatus(context)).WithMetadata(new SessionAttribute { SessionBehavior = SessionStateBehavior.ReadOnly });
+ endpoints.MapGet("/required", (context) => GetSessionStatus(context)).WithMetadata(new SessionAttribute { SessionBehavior = SessionStateBehavior.Required });
+ endpoints.MapGet("/default", (context) => GetSessionStatus(context)).WithMetadata(new SessionAttribute { SessionBehavior = SessionStateBehavior.Default });
+ });
+ });
+ })
+ .StartAsync();
+
+ var uri = new Uri(endpoint, UriKind.Relative);
+
+ try
+ {
+ return await host.GetTestClient().GetStringAsync(uri).ConfigureAwait(false);
+ }
+ finally
+ {
+ await host.StopAsync();
+ }
+ }
+
+ private static void SetOverrideSessionBehavior(HttpContext context)
+ {
+ string? overrideValue = context.Request.QueryString["override"];
+
+ switch (overrideValue)
+ {
+ case "disabled":
+ context.SetSessionStateBehavior(SessionStateBehavior.Disabled);
+ break;
+ case "readonly":
+ context.SetSessionStateBehavior(SessionStateBehavior.ReadOnly);
+ break;
+ case "required":
+ context.SetSessionStateBehavior(SessionStateBehavior.Required);
+ break;
+ case "default":
+ context.SetSessionStateBehavior(SessionStateBehavior.Default);
+ break;
+ default:
+ break;
+ }
+ }
+
+ private static Task GetSessionStatus(HttpContext context)
+ {
+ var session = context.Session;
+ if (session == null)
+ {
+ context.Response.Write("Session:null");
+ }
+ else
+ {
+ context.Response.Write($"ReadOnly:{session.IsReadOnly}"); ;
+ }
+
+ return Task.CompletedTask;
+ }
+}
diff --git a/test/Microsoft.AspNetCore.SystemWebAdapters.CoreServices.Tests/SessionState/Wrapped/AspNetCoreSessionState.cs b/test/Microsoft.AspNetCore.SystemWebAdapters.CoreServices.Tests/SessionState/Wrapped/AspNetCoreSessionStateTests.cs
similarity index 97%
rename from test/Microsoft.AspNetCore.SystemWebAdapters.CoreServices.Tests/SessionState/Wrapped/AspNetCoreSessionState.cs
rename to test/Microsoft.AspNetCore.SystemWebAdapters.CoreServices.Tests/SessionState/Wrapped/AspNetCoreSessionStateTests.cs
index 2f7d3f06fc..808e6c3ff7 100644
--- a/test/Microsoft.AspNetCore.SystemWebAdapters.CoreServices.Tests/SessionState/Wrapped/AspNetCoreSessionState.cs
+++ b/test/Microsoft.AspNetCore.SystemWebAdapters.CoreServices.Tests/SessionState/Wrapped/AspNetCoreSessionStateTests.cs
@@ -4,6 +4,7 @@
using System;
using System.Linq;
using System.Threading.Tasks;
+using System.Web.SessionState;
using AutoFixture;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.SystemWebAdapters.SessionState.Serialization;
@@ -286,7 +287,9 @@ private static async Task CreateSessionStateFromSessionManager(Mo
var options = Options.Create(sessionSerializerOptions);
var aspNetCoreSessionManager = new AspNetCoreSessionManager(new Composite(serializer.Object), loggerFactory.Object, options);
- return await aspNetCoreSessionManager.CreateAsync(httpContextCore.Object, new SessionAttribute() { IsReadOnly = isReadOnly });
+ var behavior = isReadOnly ? SessionStateBehavior.ReadOnly : SessionStateBehavior.Required;
+
+ return await aspNetCoreSessionManager.CreateAsync(httpContextCore.Object, new SessionAttribute() { SessionBehavior = behavior });
}
private sealed class Composite : ICompositeSessionKeySerializer
diff --git a/test/Microsoft.AspNetCore.SystemWebAdapters.Tests/HttpContextTests.cs b/test/Microsoft.AspNetCore.SystemWebAdapters.Tests/HttpContextTests.cs
index 2570312e5e..74170b1779 100644
--- a/test/Microsoft.AspNetCore.SystemWebAdapters.Tests/HttpContextTests.cs
+++ b/test/Microsoft.AspNetCore.SystemWebAdapters.Tests/HttpContextTests.cs
@@ -501,5 +501,28 @@ public void SetHttpContextToNull()
// Assert
Assert.Null(HttpContext.Current);
}
+
+ [InlineData(SessionStateBehavior.Required)]
+ [InlineData(SessionStateBehavior.Default)]
+ [InlineData(SessionStateBehavior.Disabled)]
+ [InlineData(SessionStateBehavior.ReadOnly)]
+ [Theory]
+ public void SetSessionStateBehavior(SessionStateBehavior behavior)
+ {
+ // Arrange
+ var coreContext = new DefaultHttpContext();
+ var context = new HttpContext(coreContext);
+
+ var feature = new Mock();
+ feature.SetupAllProperties();
+
+ coreContext.Features.Set(feature.Object);
+
+ // Act
+ context.SetSessionStateBehavior(behavior);
+
+ // Assert
+ feature.VerifySet(f => f.Behavior = behavior);
+ }
}
}