Forbid + obsolete context.Authentication

This commit is contained in:
Hao Kung 2017-05-25 18:26:36 -07:00
parent 9428d1778d
commit b1f92fb6bc
18 changed files with 91 additions and 251 deletions

View File

@ -64,18 +64,7 @@ namespace Microsoft.AspNetCore.Authentication
/// <param name="properties">The <see cref="AuthenticationProperties"/> properties.</param> /// <param name="properties">The <see cref="AuthenticationProperties"/> properties.</param>
/// <returns>The task.</returns> /// <returns>The task.</returns>
public static Task ChallengeAsync(this HttpContext context, string scheme, AuthenticationProperties properties) => public static Task ChallengeAsync(this HttpContext context, string scheme, AuthenticationProperties properties) =>
context.ChallengeAsync(scheme, properties: properties, behavior: ChallengeBehavior.Automatic); context.RequestServices.GetRequiredService<IAuthenticationService>().ChallengeAsync(context, scheme, properties);
/// <summary>
/// Extension method for Challenge.
/// </summary>
/// <param name="context">The <see cref="HttpContext"/> context.</param>
/// <param name="scheme">The name of the authentication scheme.</param>
/// <param name="properties">The <see cref="AuthenticationProperties"/> properties.</param>
/// <param name="behavior">The <see cref="ChallengeBehavior"/> behavior.</param>
/// <returns>The task.</returns>
public static Task ChallengeAsync(this HttpContext context, string scheme, AuthenticationProperties properties, ChallengeBehavior behavior) =>
context.RequestServices.GetRequiredService<IAuthenticationService>().ChallengeAsync(context, scheme, properties, behavior);
/// <summary> /// <summary>
/// Extension method for Forbid. /// Extension method for Forbid.
@ -111,7 +100,7 @@ namespace Microsoft.AspNetCore.Authentication
/// <param name="properties">The <see cref="AuthenticationProperties"/> properties.</param> /// <param name="properties">The <see cref="AuthenticationProperties"/> properties.</param>
/// <returns>The task.</returns> /// <returns>The task.</returns>
public static Task ForbidAsync(this HttpContext context, string scheme, AuthenticationProperties properties) => public static Task ForbidAsync(this HttpContext context, string scheme, AuthenticationProperties properties) =>
context.RequestServices.GetRequiredService<IAuthenticationService>().ChallengeAsync(context, scheme, properties, ChallengeBehavior.Forbidden); context.RequestServices.GetRequiredService<IAuthenticationService>().ForbidAsync(context, scheme, properties);
/// <summary> /// <summary>
/// Extension method for SignIn. /// Extension method for SignIn.

View File

@ -58,7 +58,7 @@ namespace Microsoft.AspNetCore.Authentication
public string DefaultSignInScheme { get; set; } public string DefaultSignInScheme { get; set; }
/// <summary> /// <summary>
/// Used by as the default scheme by <see cref="IAuthenticationService.ChallengeAsync(HttpContext, string, AuthenticationProperties, ChallengeBehavior)"/>. /// Used by as the default scheme by <see cref="IAuthenticationService.ChallengeAsync(HttpContext, string, AuthenticationProperties)"/>.
/// </summary> /// </summary>
public string DefaultChallengeScheme { get; set; } public string DefaultChallengeScheme { get; set; }
} }

View File

@ -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
{
/// <summary>
/// Base context for authentication.
/// </summary>
public abstract class BaseAuthenticationContext : BaseContext
{
/// <summary>
/// Constructor.
/// </summary>
/// <param name="context">The context.</param>
/// <param name="authenticationScheme">The name of the scheme.</param>
/// <param name="properties">The properties.</param>
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();
}
/// <summary>
/// The name of the scheme.
/// </summary>
public string AuthenticationScheme { get; }
/// <summary>
/// Contains the extra meta-data arriving with the authentication. May be altered.
/// </summary>
public AuthenticationProperties Properties { get; protected set; }
}
}

View File

@ -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
{
/// <summary>
/// Base class used by other context classes.
/// </summary>
public abstract class BaseContext
{
/// <summary>
/// Constructor.
/// </summary>
/// <param name="context">The request context.</param>
protected BaseContext(HttpContext context)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
HttpContext = context;
}
/// <summary>
/// The context.
/// </summary>
public HttpContext HttpContext { get; }
/// <summary>
/// The request.
/// </summary>
public HttpRequest Request
{
get { return HttpContext.Request; }
}
/// <summary>
/// The response.
/// </summary>
public HttpResponse Response
{
get { return HttpContext.Response; }
}
}
}

View File

@ -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
{
/// <summary>
/// Controls how challenge will behave (i.e. 401 vs 403).
/// </summary>
public enum ChallengeBehavior
{
Automatic,
Unauthorized,
Forbidden
}
}

View File

@ -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
{
/// <summary>
/// Context used for challenges.
/// </summary>
public class ChallengeContext : BaseAuthenticationContext
{
/// <summary>
/// Constructor.
/// </summary>
/// <param name="httpContext">The context.</param>
/// <param name="authenticationScheme">The name of the scheme.</param>
public ChallengeContext(HttpContext httpContext, string authenticationScheme)
: this(httpContext, authenticationScheme, properties: null, behavior: ChallengeBehavior.Automatic)
{ }
/// <summary>
/// Constructor
/// </summary>
/// <param name="httpContext">The context.</param>
/// <param name="authenticationScheme">The name of the scheme.</param>
/// <param name="properties">The properties.</param>
/// <param name="behavior">The challenge behavior.</param>
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;
}
/// <summary>
/// The challenge behavior.
/// </summary>
public ChallengeBehavior Behavior { get; }
}
}

View File

@ -1,6 +1,7 @@
// Copyright (c) .NET Foundation. All rights reserved. // 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. // 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 System.Threading.Tasks;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
@ -28,22 +29,30 @@ namespace Microsoft.AspNetCore.Authentication
/// <summary> /// <summary>
/// Challenge behavior. /// Challenge behavior.
/// </summary> /// </summary>
/// <param name="context">The <see cref="ChallengeContext"/> context.</param> /// <param name="properties">The <see cref="AuthenticationProperties"/> that contains the extra meta-data arriving with the authentication.</param>
/// <returns>A task.</returns> /// <returns>A task.</returns>
Task ChallengeAsync(ChallengeContext context); Task ChallengeAsync(AuthenticationProperties properties);
/// <summary>
/// Forbid behavior.
/// </summary>
/// <param name="properties">The <see cref="AuthenticationProperties"/> that contains the extra meta-data arriving with the authentication.</param>
/// <returns>A task.</returns>
Task ForbidAsync(AuthenticationProperties properties);
/// <summary> /// <summary>
/// Handle sign in. /// Handle sign in.
/// </summary> /// </summary>
/// <param name="context">The <see cref="SignInContext"/> context.</param> /// <param name="user">The <see cref="ClaimsPrincipal"/> user.</param>
/// <param name="properties">The <see cref="AuthenticationProperties"/> that contains the extra meta-data arriving with the authentication.</param>
/// <returns>A task.</returns> /// <returns>A task.</returns>
Task SignInAsync(SignInContext context); Task SignInAsync(ClaimsPrincipal user, AuthenticationProperties properties);
/// <summary> /// <summary>
/// Signout behavior. /// Signout behavior.
/// </summary> /// </summary>
/// <param name="context">The <see cref="SignOutContext"/> context.</param> /// <param name="properties">The <see cref="AuthenticationProperties"/> that contains the extra meta-data arriving with the authentication.</param>
/// <returns>A task.</returns> /// <returns>A task.</returns>
Task SignOutAsync(SignOutContext context); Task SignOutAsync(AuthenticationProperties properties);
} }
} }

View File

@ -34,11 +34,11 @@ namespace Microsoft.AspNetCore.Authentication
Task<AuthenticationScheme> GetDefaultAuthenticateSchemeAsync(); Task<AuthenticationScheme> GetDefaultAuthenticateSchemeAsync();
/// <summary> /// <summary>
/// Returns the scheme that will be used by default for <see cref="IAuthenticationService.ChallengeAsync(HttpContext, string, AuthenticationProperties, ChallengeBehavior)"/>. /// Returns the scheme that will be used by default for <see cref="IAuthenticationService.ChallengeAsync(HttpContext, string, AuthenticationProperties)"/>.
/// This is typically specified via <see cref="AuthenticationOptions.DefaultChallengeScheme"/>. /// This is typically specified via <see cref="AuthenticationOptions.DefaultChallengeScheme"/>.
/// Otherwise, if only a single scheme exists, that will be used, if more than one exists, null will be returned. /// Otherwise, if only a single scheme exists, that will be used, if more than one exists, null will be returned.
/// </summary> /// </summary>
/// <returns>The scheme that will be used by default for <see cref="IAuthenticationService.ChallengeAsync(HttpContext, string, AuthenticationProperties, ChallengeBehavior)"/>.</returns> /// <returns>The scheme that will be used by default for <see cref="IAuthenticationService.ChallengeAsync(HttpContext, string, AuthenticationProperties)"/>.</returns>
Task<AuthenticationScheme> GetDefaultChallengeSchemeAsync(); Task<AuthenticationScheme> GetDefaultChallengeSchemeAsync();
/// <summary> /// <summary>

View File

@ -26,9 +26,17 @@ namespace Microsoft.AspNetCore.Authentication
/// <param name="context">The <see cref="HttpContext"/>.</param> /// <param name="context">The <see cref="HttpContext"/>.</param>
/// <param name="scheme">The name of the authentication scheme.</param> /// <param name="scheme">The name of the authentication scheme.</param>
/// <param name="properties">The <see cref="AuthenticationProperties"/>.</param> /// <param name="properties">The <see cref="AuthenticationProperties"/>.</param>
/// <param name="behavior">The <see cref="ChallengeBehavior"/>.</param>
/// <returns>A task.</returns> /// <returns>A task.</returns>
Task ChallengeAsync(HttpContext context, string scheme, AuthenticationProperties properties, ChallengeBehavior behavior); Task ChallengeAsync(HttpContext context, string scheme, AuthenticationProperties properties);
/// <summary>
/// Forbids the specified authentication scheme.
/// </summary>
/// <param name="context">The <see cref="HttpContext"/>.</param>
/// <param name="scheme">The name of the authentication scheme.</param>
/// <param name="properties">The <see cref="AuthenticationProperties"/>.</param>
/// <returns>A task.</returns>
Task ForbidAsync(HttpContext context, string scheme, AuthenticationProperties properties);
/// <summary> /// <summary>
/// Sign a principal in for the specified authentication scheme. /// Sign a principal in for the specified authentication scheme.

View File

@ -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
{
/// <summary>
/// Context used for sign out.
/// </summary>
public class SignInContext : BaseAuthenticationContext
{
/// <summary>
/// Constructor.
/// </summary>
/// <param name="context">The context.</param>
/// <param name="authenticationScheme">The name of the authentication scheme.</param>
/// <param name="principal">The user to sign in.</param>
/// <param name="properties">The properties.</param>
public SignInContext(HttpContext context, string authenticationScheme, ClaimsPrincipal principal, AuthenticationProperties properties)
: base(context, authenticationScheme, properties)
{
if (principal == null)
{
throw new ArgumentNullException(nameof(principal));
}
Principal = principal;
}
/// <summary>
/// The user to sign in.
/// </summary>
public ClaimsPrincipal Principal { get; }
}
}

View File

@ -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
{
/// <summary>
/// Context used to sign out.
/// </summary>
public class SignOutContext : BaseAuthenticationContext
{
/// <summary>
/// Constructor.
/// </summary>
/// <param name="context">The context.</param>
/// <param name="authenticationScheme">The name of the authentication scheme.</param>
/// <param name="properties">The properties.</param>
public SignOutContext(HttpContext context, string authenticationScheme, AuthenticationProperties properties)
: base(context, authenticationScheme, properties)
{ }
}
}

View File

@ -57,11 +57,11 @@ namespace Microsoft.AspNetCore.Authentication
} }
/// <summary> /// <summary>
/// Returns the scheme that will be used by default for <see cref="IAuthenticationService.ChallengeAsync(HttpContext, string, AuthenticationProperties, ChallengeBehavior)"/>. /// Returns the scheme that will be used by default for <see cref="IAuthenticationService.ChallengeAsync(HttpContext, string, AuthenticationProperties)"/>.
/// This is typically specified via <see cref="AuthenticationOptions.DefaultChallengeScheme"/>. /// This is typically specified via <see cref="AuthenticationOptions.DefaultChallengeScheme"/>.
/// Otherwise, if only a single scheme exists, that will be used, if more than one exists, null will be returned. /// Otherwise, if only a single scheme exists, that will be used, if more than one exists, null will be returned.
/// </summary> /// </summary>
/// <returns>The scheme that will be used by default for <see cref="IAuthenticationService.ChallengeAsync(HttpContext, string, AuthenticationProperties, ChallengeBehavior)"/>.</returns> /// <returns>The scheme that will be used by default for <see cref="IAuthenticationService.ChallengeAsync(HttpContext, string, AuthenticationProperties)"/>.</returns>
public Task<AuthenticationScheme> GetDefaultChallengeSchemeAsync() public Task<AuthenticationScheme> GetDefaultChallengeSchemeAsync()
{ {
if (_options.DefaultChallengeScheme != null) if (_options.DefaultChallengeScheme != null)

View File

@ -80,9 +80,8 @@ namespace Microsoft.AspNetCore.Authentication
/// <param name="context">The <see cref="HttpContext"/>.</param> /// <param name="context">The <see cref="HttpContext"/>.</param>
/// <param name="scheme">The name of the authentication scheme.</param> /// <param name="scheme">The name of the authentication scheme.</param>
/// <param name="properties">The <see cref="AuthenticationProperties"/>.</param> /// <param name="properties">The <see cref="AuthenticationProperties"/>.</param>
/// <param name="behavior">The <see cref="ChallengeBehavior"/>.</param>
/// <returns>A task.</returns> /// <returns>A task.</returns>
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) if (scheme == null)
{ {
@ -100,8 +99,35 @@ namespace Microsoft.AspNetCore.Authentication
throw new InvalidOperationException($"No authentication handler is configured to handle the scheme: {scheme}"); throw new InvalidOperationException($"No authentication handler is configured to handle the scheme: {scheme}");
} }
var challengeContext = new ChallengeContext(context, scheme, properties, behavior); await handler.ChallengeAsync(properties);
await handler.ChallengeAsync(challengeContext); }
/// <summary>
/// Forbid the specified authentication scheme.
/// </summary>
/// <param name="context">The <see cref="HttpContext"/>.</param>
/// <param name="scheme">The name of the authentication scheme.</param>
/// <param name="properties">The <see cref="AuthenticationProperties"/>.</param>
/// <returns>A task.</returns>
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);
} }
/// <summary> /// <summary>
@ -135,8 +161,7 @@ namespace Microsoft.AspNetCore.Authentication
throw new InvalidOperationException($"No authentication handler is configured to handle the scheme: {scheme}"); throw new InvalidOperationException($"No authentication handler is configured to handle the scheme: {scheme}");
} }
var signInContext = new SignInContext(context, scheme, principal, properties); await handler.SignInAsync(principal, properties);
await handler.SignInAsync(signInContext);
} }
/// <summary> /// <summary>
@ -159,8 +184,7 @@ namespace Microsoft.AspNetCore.Authentication
throw new InvalidOperationException($"No authentication handler is configured to handle the scheme: {scheme}"); throw new InvalidOperationException($"No authentication handler is configured to handle the scheme: {scheme}");
} }
var signOutContext = new SignOutContext(context, scheme, properties); await handler.SignOutAsync(properties);
await handler.SignOutAsync(signOutContext);
} }
} }
} }

View File

@ -41,8 +41,11 @@ namespace Microsoft.AspNetCore.Http
public abstract WebSocketManager WebSockets { get; } public abstract WebSocketManager WebSockets { get; }
/// <summary> /// <summary>
/// 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.
/// </summary> /// </summary>
[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; } public abstract AuthenticationManager Authentication { get; }
/// <summary> /// <summary>

View File

@ -111,6 +111,12 @@ namespace Microsoft.AspNetCore.Http
public override ConnectionInfo Connection => _connection ?? (_connection = InitializeConnectionInfo()); public override ConnectionInfo Connection => _connection ?? (_connection = InitializeConnectionInfo());
/// <summary>
/// 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.
/// </summary>
[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 AuthenticationManager Authentication => _authenticationManager ?? (_authenticationManager = InitializeAuthenticationManager());
public override WebSocketManager WebSockets => _websockets ?? (_websockets = InitializeWebSocketManager()); public override WebSocketManager WebSockets => _websockets ?? (_websockets = InitializeWebSocketManager());

View File

@ -166,9 +166,14 @@ namespace Microsoft.AspNetCore.Authentication
return Task.FromResult(AuthenticateResult.Success(new AuthenticationTicket(new ClaimsPrincipal(), props, "simple"))); 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) public Task InitializeAsync(AuthenticationScheme scheme, HttpContext context)
@ -176,14 +181,14 @@ namespace Microsoft.AspNetCore.Authentication
return Task.FromResult(0); 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();
} }
} }

View File

@ -1,6 +1,7 @@
// Copyright (c) .NET Foundation. All rights reserved. // 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. // 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;
using System.Security.Claims; using System.Security.Claims;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -100,3 +101,4 @@ namespace Microsoft.AspNetCore.Http.Authentication.Internal
} }
} }
} }
#pragma warning restore CS0618 // Type or member is obsolete

View File

@ -191,7 +191,9 @@ namespace Microsoft.AspNetCore.Http
TestCachedFeaturesAreNull(context, features); TestCachedFeaturesAreNull(context, features);
TestCachedFeaturesAreNull(context.Request, features); TestCachedFeaturesAreNull(context.Request, features);
TestCachedFeaturesAreNull(context.Response, features); TestCachedFeaturesAreNull(context.Response, features);
#pragma warning disable CS0618 // Type or member is obsolete
TestCachedFeaturesAreNull(context.Authentication, features); TestCachedFeaturesAreNull(context.Authentication, features);
#pragma warning restore CS0618 // Type or member is obsolete
TestCachedFeaturesAreNull(context.Connection, features); TestCachedFeaturesAreNull(context.Connection, features);
TestCachedFeaturesAreNull(context.WebSockets, features); TestCachedFeaturesAreNull(context.WebSockets, features);
} }
@ -220,7 +222,9 @@ namespace Microsoft.AspNetCore.Http
TestCachedFeaturesAreSet(context, features); TestCachedFeaturesAreSet(context, features);
TestCachedFeaturesAreSet(context.Request, features); TestCachedFeaturesAreSet(context.Request, features);
TestCachedFeaturesAreSet(context.Response, features); TestCachedFeaturesAreSet(context.Response, features);
#pragma warning disable CS0618 // Type or member is obsolete
TestCachedFeaturesAreSet(context.Authentication, features); TestCachedFeaturesAreSet(context.Authentication, features);
#pragma warning restore CS0618 // Type or member is obsolete
TestCachedFeaturesAreSet(context.Connection, features); TestCachedFeaturesAreSet(context.Connection, features);
TestCachedFeaturesAreSet(context.WebSockets, features); TestCachedFeaturesAreSet(context.WebSockets, features);
} }