File: TestObjects\DynamicEndpointDataSource.cs
Web Access
Project: src\src\Http\Routing\test\UnitTests\Microsoft.AspNetCore.Routing.Tests.csproj (Microsoft.AspNetCore.Routing.Tests)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Primitives;
 
namespace Microsoft.AspNetCore.Routing.TestObjects;
 
public class DynamicEndpointDataSource : EndpointDataSource
{
    private readonly List<Endpoint> _endpoints;
    private CancellationTokenSource _cts;
    private CancellationChangeToken _changeToken;
    private readonly object _lock;
 
    public DynamicEndpointDataSource(params Endpoint[] endpoints)
    {
        _endpoints = new List<Endpoint>();
        _endpoints.AddRange(endpoints);
        _lock = new object();
 
        CreateChangeToken();
    }
 
    public override IChangeToken GetChangeToken() => _changeToken;
 
    public override IReadOnlyList<Endpoint> Endpoints => _endpoints;
 
    // Trigger change
    public void AddEndpoint(Endpoint endpoint)
    {
        _endpoints.Add(endpoint);
 
        // Capture the old tokens so that we can raise the callbacks on them. This is important so that
        // consumers do not register callbacks on an inflight event causing a stackoverflow.
        var oldTokenSource = _cts;
        var oldToken = _changeToken;
 
        CreateChangeToken();
 
        // Raise consumer callbacks. Any new callback registration would happen on the new token
        // created in earlier step.
        oldTokenSource.Cancel();
    }
 
    private void CreateChangeToken()
    {
        _cts = new CancellationTokenSource();
        _changeToken = new CancellationChangeToken(_cts.Token);
    }
}