diff --git a/src/Microsoft.AspNetCore.Authentication.Abstractions/AuthenticationHttpContextExtensions.cs b/src/Microsoft.AspNetCore.Authentication.Abstractions/AuthenticationHttpContextExtensions.cs
index 238251a60d..cb8f1fb480 100644
--- a/src/Microsoft.AspNetCore.Authentication.Abstractions/AuthenticationHttpContextExtensions.cs
+++ b/src/Microsoft.AspNetCore.Authentication.Abstractions/AuthenticationHttpContextExtensions.cs
@@ -64,18 +64,7 @@ namespace Microsoft.AspNetCore.Authentication
/// The properties.
/// The task.
public static Task ChallengeAsync(this HttpContext context, string scheme, AuthenticationProperties properties) =>
- context.ChallengeAsync(scheme, properties: properties, behavior: ChallengeBehavior.Automatic);
-
- ///
- /// Extension method for Challenge.
- ///
- /// The context.
- /// The name of the authentication scheme.
- /// The properties.
- /// The behavior.
- /// The task.
- public static Task ChallengeAsync(this HttpContext context, string scheme, AuthenticationProperties properties, ChallengeBehavior behavior) =>
- context.RequestServices.GetRequiredService().ChallengeAsync(context, scheme, properties, behavior);
+ context.RequestServices.GetRequiredService().ChallengeAsync(context, scheme, properties);
///
/// Extension method for Forbid.
@@ -111,7 +100,7 @@ namespace Microsoft.AspNetCore.Authentication
/// The properties.
/// The task.
public static Task ForbidAsync(this HttpContext context, string scheme, AuthenticationProperties properties) =>
- context.RequestServices.GetRequiredService().ChallengeAsync(context, scheme, properties, ChallengeBehavior.Forbidden);
+ context.RequestServices.GetRequiredService().ForbidAsync(context, scheme, properties);
///
/// Extension method for SignIn.
diff --git a/src/Microsoft.AspNetCore.Authentication.Abstractions/AuthenticationOptions.cs b/src/Microsoft.AspNetCore.Authentication.Abstractions/AuthenticationOptions.cs
index 4d90d3d6f2..08df75a2ad 100644
--- a/src/Microsoft.AspNetCore.Authentication.Abstractions/AuthenticationOptions.cs
+++ b/src/Microsoft.AspNetCore.Authentication.Abstractions/AuthenticationOptions.cs
@@ -58,7 +58,7 @@ namespace Microsoft.AspNetCore.Authentication
public string DefaultSignInScheme { get; set; }
///
- /// Used by as the default scheme by .
+ /// Used by as the default scheme by .
///
public string DefaultChallengeScheme { get; set; }
}
diff --git a/src/Microsoft.AspNetCore.Authentication.Abstractions/BaseAuthenticationContext.cs b/src/Microsoft.AspNetCore.Authentication.Abstractions/BaseAuthenticationContext.cs
deleted file mode 100644
index cfe5809c5a..0000000000
--- a/src/Microsoft.AspNetCore.Authentication.Abstractions/BaseAuthenticationContext.cs
+++ /dev/null
@@ -1,41 +0,0 @@
-// 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.
-
-using System;
-using Microsoft.AspNetCore.Http;
-
-namespace Microsoft.AspNetCore.Authentication
-{
- ///
- /// Base context for authentication.
- ///
- public abstract class BaseAuthenticationContext : BaseContext
- {
- ///
- /// Constructor.
- ///
- /// The context.
- /// The name of the scheme.
- /// The properties.
- protected BaseAuthenticationContext(HttpContext context, string authenticationScheme, AuthenticationProperties properties) : base(context)
- {
- if (string.IsNullOrEmpty(authenticationScheme))
- {
- throw new ArgumentException(nameof(authenticationScheme));
- }
-
- AuthenticationScheme = authenticationScheme;
- Properties = properties ?? new AuthenticationProperties();
- }
-
- ///
- /// The name of the scheme.
- ///
- public string AuthenticationScheme { get; }
-
- ///
- /// Contains the extra meta-data arriving with the authentication. May be altered.
- ///
- public AuthenticationProperties Properties { get; protected set; }
- }
-}
diff --git a/src/Microsoft.AspNetCore.Authentication.Abstractions/BaseContext.cs b/src/Microsoft.AspNetCore.Authentication.Abstractions/BaseContext.cs
deleted file mode 100644
index 3d65f0dd75..0000000000
--- a/src/Microsoft.AspNetCore.Authentication.Abstractions/BaseContext.cs
+++ /dev/null
@@ -1,49 +0,0 @@
-// 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.
-
-using System;
-using Microsoft.AspNetCore.Http;
-
-namespace Microsoft.AspNetCore.Authentication
-{
- ///
- /// Base class used by other context classes.
- ///
- public abstract class BaseContext
- {
- ///
- /// Constructor.
- ///
- /// The request context.
- protected BaseContext(HttpContext context)
- {
- if (context == null)
- {
- throw new ArgumentNullException(nameof(context));
- }
-
- HttpContext = context;
- }
-
- ///
- /// The context.
- ///
- public HttpContext HttpContext { get; }
-
- ///
- /// The request.
- ///
- public HttpRequest Request
- {
- get { return HttpContext.Request; }
- }
-
- ///
- /// The response.
- ///
- public HttpResponse Response
- {
- get { return HttpContext.Response; }
- }
- }
-}
diff --git a/src/Microsoft.AspNetCore.Authentication.Abstractions/ChallengeBehavior.cs b/src/Microsoft.AspNetCore.Authentication.Abstractions/ChallengeBehavior.cs
deleted file mode 100644
index 1506021dd4..0000000000
--- a/src/Microsoft.AspNetCore.Authentication.Abstractions/ChallengeBehavior.cs
+++ /dev/null
@@ -1,15 +0,0 @@
-// 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.
-
-namespace Microsoft.AspNetCore.Authentication
-{
- ///
- /// Controls how challenge will behave (i.e. 401 vs 403).
- ///
- public enum ChallengeBehavior
- {
- Automatic,
- Unauthorized,
- Forbidden
- }
-}
\ No newline at end of file
diff --git a/src/Microsoft.AspNetCore.Authentication.Abstractions/ChallengeContext.cs b/src/Microsoft.AspNetCore.Authentication.Abstractions/ChallengeContext.cs
deleted file mode 100644
index ee2392eb04..0000000000
--- a/src/Microsoft.AspNetCore.Authentication.Abstractions/ChallengeContext.cs
+++ /dev/null
@@ -1,45 +0,0 @@
-// 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.
-
-using System;
-using Microsoft.AspNetCore.Http;
-
-namespace Microsoft.AspNetCore.Authentication
-{
- ///
- /// Context used for challenges.
- ///
- public class ChallengeContext : BaseAuthenticationContext
- {
- ///
- /// Constructor.
- ///
- /// The context.
- /// The name of the scheme.
- public ChallengeContext(HttpContext httpContext, string authenticationScheme)
- : this(httpContext, authenticationScheme, properties: null, behavior: ChallengeBehavior.Automatic)
- { }
-
- ///
- /// Constructor
- ///
- /// The context.
- /// The name of the scheme.
- /// The properties.
- /// The challenge behavior.
- public ChallengeContext(HttpContext httpContext, string authenticationScheme, AuthenticationProperties properties, ChallengeBehavior behavior)
- : base(httpContext, authenticationScheme, properties)
- {
- if (string.IsNullOrEmpty(authenticationScheme))
- {
- throw new ArgumentException(nameof(authenticationScheme));
- }
- Behavior = behavior;
- }
-
- ///
- /// The challenge behavior.
- ///
- public ChallengeBehavior Behavior { get; }
- }
-}
\ No newline at end of file
diff --git a/src/Microsoft.AspNetCore.Authentication.Abstractions/IAuthenticationHandler.cs b/src/Microsoft.AspNetCore.Authentication.Abstractions/IAuthenticationHandler.cs
index 49d5f498af..af92cc7659 100644
--- a/src/Microsoft.AspNetCore.Authentication.Abstractions/IAuthenticationHandler.cs
+++ b/src/Microsoft.AspNetCore.Authentication.Abstractions/IAuthenticationHandler.cs
@@ -1,6 +1,7 @@
// 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.
+using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
@@ -28,22 +29,30 @@ namespace Microsoft.AspNetCore.Authentication
///
/// Challenge behavior.
///
- /// The context.
+ /// The that contains the extra meta-data arriving with the authentication.
/// A task.
- Task ChallengeAsync(ChallengeContext context);
+ Task ChallengeAsync(AuthenticationProperties properties);
+
+ ///
+ /// Forbid behavior.
+ ///
+ /// The that contains the extra meta-data arriving with the authentication.
+ /// A task.
+ Task ForbidAsync(AuthenticationProperties properties);
///
/// Handle sign in.
///
- /// The context.
+ /// The user.
+ /// The that contains the extra meta-data arriving with the authentication.
/// A task.
- Task SignInAsync(SignInContext context);
+ Task SignInAsync(ClaimsPrincipal user, AuthenticationProperties properties);
///
/// Signout behavior.
///
- /// The context.
+ /// The that contains the extra meta-data arriving with the authentication.
/// A task.
- Task SignOutAsync(SignOutContext context);
+ Task SignOutAsync(AuthenticationProperties properties);
}
}
diff --git a/src/Microsoft.AspNetCore.Authentication.Abstractions/IAuthenticationSchemeProvider.cs b/src/Microsoft.AspNetCore.Authentication.Abstractions/IAuthenticationSchemeProvider.cs
index 861f3dda49..5fa08c977a 100644
--- a/src/Microsoft.AspNetCore.Authentication.Abstractions/IAuthenticationSchemeProvider.cs
+++ b/src/Microsoft.AspNetCore.Authentication.Abstractions/IAuthenticationSchemeProvider.cs
@@ -34,11 +34,11 @@ namespace Microsoft.AspNetCore.Authentication
Task GetDefaultAuthenticateSchemeAsync();
///
- /// Returns the scheme that will be used by default for .
+ /// Returns the scheme that will be used by default for .
/// This is typically specified via .
/// Otherwise, if only a single scheme exists, that will be used, if more than one exists, null will be returned.
///
- /// The scheme that will be used by default for .
+ /// The scheme that will be used by default for .
Task GetDefaultChallengeSchemeAsync();
///
diff --git a/src/Microsoft.AspNetCore.Authentication.Abstractions/IAuthenticationService.cs b/src/Microsoft.AspNetCore.Authentication.Abstractions/IAuthenticationService.cs
index ec54325ea4..e5d5336016 100644
--- a/src/Microsoft.AspNetCore.Authentication.Abstractions/IAuthenticationService.cs
+++ b/src/Microsoft.AspNetCore.Authentication.Abstractions/IAuthenticationService.cs
@@ -26,9 +26,17 @@ namespace Microsoft.AspNetCore.Authentication
/// The .
/// The name of the authentication scheme.
/// The .
- /// The .
/// A task.
- Task ChallengeAsync(HttpContext context, string scheme, AuthenticationProperties properties, ChallengeBehavior behavior);
+ Task ChallengeAsync(HttpContext context, string scheme, AuthenticationProperties properties);
+
+ ///
+ /// Forbids the specified authentication scheme.
+ ///
+ /// The .
+ /// The name of the authentication scheme.
+ /// The .
+ /// A task.
+ Task ForbidAsync(HttpContext context, string scheme, AuthenticationProperties properties);
///
/// Sign a principal in for the specified authentication scheme.
diff --git a/src/Microsoft.AspNetCore.Authentication.Abstractions/SignInContext.cs b/src/Microsoft.AspNetCore.Authentication.Abstractions/SignInContext.cs
deleted file mode 100644
index e89b663a7d..0000000000
--- a/src/Microsoft.AspNetCore.Authentication.Abstractions/SignInContext.cs
+++ /dev/null
@@ -1,37 +0,0 @@
-// 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.
-
-using System;
-using System.Security.Claims;
-using Microsoft.AspNetCore.Http;
-
-namespace Microsoft.AspNetCore.Authentication
-{
- ///
- /// Context used for sign out.
- ///
- public class SignInContext : BaseAuthenticationContext
- {
- ///
- /// Constructor.
- ///
- /// The context.
- /// The name of the authentication scheme.
- /// The user to sign in.
- /// The properties.
- public SignInContext(HttpContext context, string authenticationScheme, ClaimsPrincipal principal, AuthenticationProperties properties)
- : base(context, authenticationScheme, properties)
- {
- if (principal == null)
- {
- throw new ArgumentNullException(nameof(principal));
- }
- Principal = principal;
- }
-
- ///
- /// The user to sign in.
- ///
- public ClaimsPrincipal Principal { get; }
- }
-}
\ No newline at end of file
diff --git a/src/Microsoft.AspNetCore.Authentication.Abstractions/SignOutContext.cs b/src/Microsoft.AspNetCore.Authentication.Abstractions/SignOutContext.cs
deleted file mode 100644
index 307a3af875..0000000000
--- a/src/Microsoft.AspNetCore.Authentication.Abstractions/SignOutContext.cs
+++ /dev/null
@@ -1,23 +0,0 @@
-// 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.
-
-using Microsoft.AspNetCore.Http;
-
-namespace Microsoft.AspNetCore.Authentication
-{
- ///
- /// Context used to sign out.
- ///
- public class SignOutContext : BaseAuthenticationContext
- {
- ///
- /// Constructor.
- ///
- /// The context.
- /// The name of the authentication scheme.
- /// The properties.
- public SignOutContext(HttpContext context, string authenticationScheme, AuthenticationProperties properties)
- : base(context, authenticationScheme, properties)
- { }
- }
-}
\ No newline at end of file
diff --git a/src/Microsoft.AspNetCore.Authentication.Core/AuthenticationSchemeProvider.cs b/src/Microsoft.AspNetCore.Authentication.Core/AuthenticationSchemeProvider.cs
index 91e68f2ca0..2ce3801b5b 100644
--- a/src/Microsoft.AspNetCore.Authentication.Core/AuthenticationSchemeProvider.cs
+++ b/src/Microsoft.AspNetCore.Authentication.Core/AuthenticationSchemeProvider.cs
@@ -57,11 +57,11 @@ namespace Microsoft.AspNetCore.Authentication
}
///
- /// Returns the scheme that will be used by default for .
+ /// Returns the scheme that will be used by default for .
/// This is typically specified via .
/// Otherwise, if only a single scheme exists, that will be used, if more than one exists, null will be returned.
///
- /// The scheme that will be used by default for .
+ /// The scheme that will be used by default for .
public Task GetDefaultChallengeSchemeAsync()
{
if (_options.DefaultChallengeScheme != null)
diff --git a/src/Microsoft.AspNetCore.Authentication.Core/AuthenticationService.cs b/src/Microsoft.AspNetCore.Authentication.Core/AuthenticationService.cs
index 9196e62cf6..326b277f9c 100644
--- a/src/Microsoft.AspNetCore.Authentication.Core/AuthenticationService.cs
+++ b/src/Microsoft.AspNetCore.Authentication.Core/AuthenticationService.cs
@@ -80,9 +80,8 @@ namespace Microsoft.AspNetCore.Authentication
/// The .
/// The name of the authentication scheme.
/// The .
- /// The .
/// A task.
- public virtual async Task ChallengeAsync(HttpContext context, string scheme, AuthenticationProperties properties, ChallengeBehavior behavior)
+ public virtual async Task ChallengeAsync(HttpContext context, string scheme, AuthenticationProperties properties)
{
if (scheme == null)
{
@@ -100,8 +99,35 @@ namespace Microsoft.AspNetCore.Authentication
throw new InvalidOperationException($"No authentication handler is configured to handle the scheme: {scheme}");
}
- var challengeContext = new ChallengeContext(context, scheme, properties, behavior);
- await handler.ChallengeAsync(challengeContext);
+ await handler.ChallengeAsync(properties);
+ }
+
+ ///
+ /// Forbid the specified authentication scheme.
+ ///
+ /// The .
+ /// The name of the authentication scheme.
+ /// The .
+ /// A task.
+ public virtual async Task ForbidAsync(HttpContext context, string scheme, AuthenticationProperties properties)
+ {
+ if (scheme == null)
+ {
+ var defaultChallengeScheme = await Schemes.GetDefaultChallengeSchemeAsync();
+ scheme = defaultChallengeScheme?.Name;
+ if (scheme == null)
+ {
+ throw new InvalidOperationException($"No authenticationScheme was specified, and there was no DefaultChallengeScheme found.");
+ }
+ }
+
+ var handler = await Handlers.GetHandlerAsync(context, scheme);
+ if (handler == null)
+ {
+ throw new InvalidOperationException($"No authentication handler is configured to handle the scheme: {scheme}");
+ }
+
+ await handler.ForbidAsync(properties);
}
///
@@ -135,8 +161,7 @@ namespace Microsoft.AspNetCore.Authentication
throw new InvalidOperationException($"No authentication handler is configured to handle the scheme: {scheme}");
}
- var signInContext = new SignInContext(context, scheme, principal, properties);
- await handler.SignInAsync(signInContext);
+ await handler.SignInAsync(principal, properties);
}
///
@@ -159,8 +184,7 @@ namespace Microsoft.AspNetCore.Authentication
throw new InvalidOperationException($"No authentication handler is configured to handle the scheme: {scheme}");
}
- var signOutContext = new SignOutContext(context, scheme, properties);
- await handler.SignOutAsync(signOutContext);
+ await handler.SignOutAsync(properties);
}
}
}
diff --git a/src/Microsoft.AspNetCore.Http.Abstractions/HttpContext.cs b/src/Microsoft.AspNetCore.Http.Abstractions/HttpContext.cs
index 7f72dcd8bb..60c938db0a 100644
--- a/src/Microsoft.AspNetCore.Http.Abstractions/HttpContext.cs
+++ b/src/Microsoft.AspNetCore.Http.Abstractions/HttpContext.cs
@@ -41,8 +41,11 @@ namespace Microsoft.AspNetCore.Http
public abstract WebSocketManager WebSockets { get; }
///
- /// Gets an object that facilitates authentication for this request.
+ /// This is obsolete and will be removed in a future version.
+ /// The recommended alternative is to use Microsoft.AspNetCore.Authentication.AuthenticationHttpContextExtensions.
+ /// See https://go.microsoft.com/fwlink/?linkid=845470.
///
+ [Obsolete("This is obsolete and will be removed in a future version. The recommended alternative is to use Microsoft.AspNetCore.Authentication.AuthenticationHttpContextExtensions. See https://go.microsoft.com/fwlink/?linkid=845470.")]
public abstract AuthenticationManager Authentication { get; }
///
diff --git a/src/Microsoft.AspNetCore.Http/DefaultHttpContext.cs b/src/Microsoft.AspNetCore.Http/DefaultHttpContext.cs
index d1e431c7fa..cdd1263043 100644
--- a/src/Microsoft.AspNetCore.Http/DefaultHttpContext.cs
+++ b/src/Microsoft.AspNetCore.Http/DefaultHttpContext.cs
@@ -111,6 +111,12 @@ namespace Microsoft.AspNetCore.Http
public override ConnectionInfo Connection => _connection ?? (_connection = InitializeConnectionInfo());
+ ///
+ /// This is obsolete and will be removed in a future version.
+ /// The recommended alternative is to use Microsoft.AspNetCore.Authentication.AuthenticationHttpContextExtensions.
+ /// See https://go.microsoft.com/fwlink/?linkid=845470.
+ ///
+ [Obsolete("This is obsolete and will be removed in a future version. The recommended alternative is to use Microsoft.AspNetCore.Authentication.AuthenticationHttpContextExtensions. See https://go.microsoft.com/fwlink/?linkid=845470.")]
public override AuthenticationManager Authentication => _authenticationManager ?? (_authenticationManager = InitializeAuthenticationManager());
public override WebSocketManager WebSockets => _websockets ?? (_websockets = InitializeWebSocketManager());
diff --git a/test/Microsoft.AspNetCore.Authentication.Core.Test/TokenExtensionTests.cs b/test/Microsoft.AspNetCore.Authentication.Core.Test/TokenExtensionTests.cs
index a203afadf0..d8f25f9509 100644
--- a/test/Microsoft.AspNetCore.Authentication.Core.Test/TokenExtensionTests.cs
+++ b/test/Microsoft.AspNetCore.Authentication.Core.Test/TokenExtensionTests.cs
@@ -166,9 +166,14 @@ namespace Microsoft.AspNetCore.Authentication
return Task.FromResult(AuthenticateResult.Success(new AuthenticationTicket(new ClaimsPrincipal(), props, "simple")));
}
- public Task ChallengeAsync(ChallengeContext context)
+ public Task ChallengeAsync(AuthenticationProperties properties)
{
- return Task.FromResult(0);
+ throw new NotImplementedException();
+ }
+
+ public Task ForbidAsync(AuthenticationProperties properties)
+ {
+ throw new NotImplementedException();
}
public Task InitializeAsync(AuthenticationScheme scheme, HttpContext context)
@@ -176,14 +181,14 @@ namespace Microsoft.AspNetCore.Authentication
return Task.FromResult(0);
}
- public Task SignInAsync(SignInContext context)
+ public Task SignInAsync(ClaimsPrincipal user, AuthenticationProperties properties)
{
- return Task.FromResult(0);
+ throw new NotImplementedException();
}
- public Task SignOutAsync(SignOutContext context)
+ public Task SignOutAsync(AuthenticationProperties properties)
{
- return Task.FromResult(0);
+ throw new NotImplementedException();
}
}
diff --git a/test/Microsoft.AspNetCore.Http.Tests/Authentication/DefaultAuthenticationManagerTests.cs b/test/Microsoft.AspNetCore.Http.Tests/Authentication/DefaultAuthenticationManagerTests.cs
index 85968a9425..101f2b19eb 100644
--- a/test/Microsoft.AspNetCore.Http.Tests/Authentication/DefaultAuthenticationManagerTests.cs
+++ b/test/Microsoft.AspNetCore.Http.Tests/Authentication/DefaultAuthenticationManagerTests.cs
@@ -1,6 +1,7 @@
// 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.
+#pragma warning disable CS0618 // Type or member is obsolete
using System;
using System.Security.Claims;
using System.Threading.Tasks;
@@ -100,3 +101,4 @@ namespace Microsoft.AspNetCore.Http.Authentication.Internal
}
}
}
+#pragma warning restore CS0618 // Type or member is obsolete
diff --git a/test/Microsoft.AspNetCore.Http.Tests/DefaultHttpContextTests.cs b/test/Microsoft.AspNetCore.Http.Tests/DefaultHttpContextTests.cs
index 13b9ee65cc..2327db1ce9 100644
--- a/test/Microsoft.AspNetCore.Http.Tests/DefaultHttpContextTests.cs
+++ b/test/Microsoft.AspNetCore.Http.Tests/DefaultHttpContextTests.cs
@@ -191,7 +191,9 @@ namespace Microsoft.AspNetCore.Http
TestCachedFeaturesAreNull(context, features);
TestCachedFeaturesAreNull(context.Request, features);
TestCachedFeaturesAreNull(context.Response, features);
+#pragma warning disable CS0618 // Type or member is obsolete
TestCachedFeaturesAreNull(context.Authentication, features);
+#pragma warning restore CS0618 // Type or member is obsolete
TestCachedFeaturesAreNull(context.Connection, features);
TestCachedFeaturesAreNull(context.WebSockets, features);
}
@@ -220,7 +222,9 @@ namespace Microsoft.AspNetCore.Http
TestCachedFeaturesAreSet(context, features);
TestCachedFeaturesAreSet(context.Request, features);
TestCachedFeaturesAreSet(context.Response, features);
+#pragma warning disable CS0618 // Type or member is obsolete
TestCachedFeaturesAreSet(context.Authentication, features);
+#pragma warning restore CS0618 // Type or member is obsolete
TestCachedFeaturesAreSet(context.Connection, features);
TestCachedFeaturesAreSet(context.WebSockets, features);
}