using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Net; using System.Net.Sockets; using EmbedIO.Net.Internal; using Swan.Logging; namespace EmbedIO.Net { /// /// Represents the EndPoint Manager. /// public static class EndPointManager { private static readonly ConcurrentDictionary> IPToEndpoints = new (); /// /// Gets or sets a value indicating whether [use IPv6]. By default, this flag is set. /// /// /// true if [use IPv6]; otherwise, false. /// public static bool UseIpv6 { get; set; } = true; internal static void AddListener(HttpListener listener) { var added = new List(); try { foreach (var prefix in listener.Prefixes) { AddPrefix(prefix, listener); added.Add(prefix); } } catch (Exception ex) { ex.Log(nameof(AddListener)); foreach (var prefix in added) { RemovePrefix(prefix, listener); } throw; } } internal static void RemoveEndPoint(EndPointListener epl, IPEndPoint ep) { if (IPToEndpoints.TryGetValue(ep.Address, out var p)) { if (p.TryRemove(ep.Port, out _) && p.Count == 0) { _ = IPToEndpoints.TryRemove(ep.Address, out _); } } epl.Dispose(); } internal static void RemoveListener(HttpListener listener) { foreach (var prefix in listener.Prefixes) { RemovePrefix(prefix, listener); } } internal static void AddPrefix(string p, HttpListener listener) { var lp = new ListenerPrefix(p); if (!lp.IsValid()) { throw new HttpListenerException(400, "Invalid path."); } // listens on all the interfaces if host name cannot be parsed by IPAddress. var epl = GetEpListener(lp.Host, lp.Port, listener, lp.Secure); epl.AddPrefix(lp, listener); } private static EndPointListener GetEpListener(string host, int port, HttpListener listener, bool secure = false) { var address = ResolveAddress(host); var p = IPToEndpoints.GetOrAdd(address, x => new ConcurrentDictionary()); return p.GetOrAdd(port, x => new EndPointListener(listener, address, x, secure)); } private static IPAddress ResolveAddress(string host) { if (host == "*" || host == "+" || host == "0.0.0.0") { return UseIpv6 ? IPAddress.IPv6Any : IPAddress.Any; } if (IPAddress.TryParse(host, out var address)) { return address; } try { var hostEntry = new IPHostEntry { HostName = host, AddressList = Dns.GetHostAddresses(host), }; return hostEntry.AddressList[0]; } catch { return UseIpv6 ? IPAddress.IPv6Any : IPAddress.Any; } } private static void RemovePrefix(string prefix, HttpListener listener) { try { var lp = new ListenerPrefix(prefix); if (!lp.IsValid()) { return; } var epl = GetEpListener(lp.Host, lp.Port, listener, lp.Secure); epl.RemovePrefix(lp); } catch (SocketException) { // ignored } } } }