File: Internal\ServerAddressesCollection.cs
Web Access
Project: src\src\Servers\Kestrel\Core\src\Microsoft.AspNetCore.Server.Kestrel.Core.csproj (Microsoft.AspNetCore.Server.Kestrel.Core)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Collections;
using System.Diagnostics;
using System.Linq;
using Microsoft.AspNetCore.Hosting.Server.Features;
 
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal;
 
[DebuggerDisplay("Count = {Count}")]
[DebuggerTypeProxy(typeof(ServerAddressesCollectionDebugView))]
internal sealed class ServerAddressesCollection : ICollection<string>
{
    private readonly List<string> _addresses = new List<string>();
    private readonly PublicServerAddressesCollection _publicCollection;
 
    public ServerAddressesCollection()
    {
        _publicCollection = new PublicServerAddressesCollection(this);
    }
 
    public ICollection<string> PublicCollection => _publicCollection;
 
    public bool IsReadOnly => false;
 
    public int Count
    {
        get
        {
            lock (_addresses)
            {
                return _addresses.Count;
            }
        }
    }
 
    public void PreventPublicMutation()
    {
        lock (_addresses)
        {
            _publicCollection.IsReadOnly = true;
        }
    }
 
    public void Add(string item)
    {
        lock (_addresses)
        {
            _addresses.Add(item);
        }
    }
 
    public bool Remove(string item)
    {
        lock (_addresses)
        {
            return _addresses.Remove(item);
        }
    }
 
    public void Clear()
    {
        lock (_addresses)
        {
            _addresses.Clear();
        }
    }
 
    public bool Contains(string item)
    {
        lock (_addresses)
        {
            return _addresses.Contains(item);
        }
    }
 
    public void CopyTo(string[] array, int arrayIndex)
    {
        lock (_addresses)
        {
            _addresses.CopyTo(array, arrayIndex);
        }
    }
 
    public IEnumerator<string> GetEnumerator()
    {
        lock (_addresses)
        {
            // Copy inside the lock.
            return new List<string>(_addresses).GetEnumerator();
        }
    }
 
    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
 
    [DebuggerDisplay("Count = {Count}")]
    [DebuggerTypeProxy(typeof(PublicServerAddressesCollectionDebugView))]
    private sealed class PublicServerAddressesCollection : ICollection<string>
    {
        private readonly ServerAddressesCollection _addressesCollection;
        private readonly object _addressesLock;
 
        public PublicServerAddressesCollection(ServerAddressesCollection addresses)
        {
            _addressesCollection = addresses;
            _addressesLock = addresses._addresses;
        }
 
        public bool IsReadOnly { get; set; }
 
        public int Count => _addressesCollection.Count;
 
        public void Add(string item)
        {
            lock (_addressesLock)
            {
                ThrowIfReadonly();
                _addressesCollection.Add(item);
            }
        }
 
        public bool Remove(string item)
        {
            lock (_addressesLock)
            {
                ThrowIfReadonly();
                return _addressesCollection.Remove(item);
            }
        }
 
        public void Clear()
        {
            lock (_addressesLock)
            {
                ThrowIfReadonly();
                _addressesCollection.Clear();
            }
        }
 
        public bool Contains(string item)
        {
            return _addressesCollection.Contains(item);
        }
 
        public void CopyTo(string[] array, int arrayIndex)
        {
            _addressesCollection.CopyTo(array, arrayIndex);
        }
 
        public IEnumerator<string> GetEnumerator()
        {
            return _addressesCollection.GetEnumerator();
        }
 
        IEnumerator IEnumerable.GetEnumerator()
        {
            return _addressesCollection.GetEnumerator();
        }
 
        [StackTraceHidden]
        private void ThrowIfReadonly()
        {
            if (IsReadOnly)
            {
                throw new InvalidOperationException($"{nameof(IServerAddressesFeature)}.{nameof(IServerAddressesFeature.Addresses)} cannot be modified after the server has started.");
            }
        }
    }
 
    private sealed class PublicServerAddressesCollectionDebugView(PublicServerAddressesCollection publicCollection)
    {
        private readonly PublicServerAddressesCollection _collection = publicCollection;
        [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
        public string[] Items => _collection.ToArray();
    }
 
    private sealed class ServerAddressesCollectionDebugView(ServerAddressesCollection collection)
    {
        private readonly ServerAddressesCollection _collection = collection;
 
        [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
        public string[] Items => _collection.ToArray();
    }
}