< Summary

Information
Class: IceRpc.ServerAddress
Assembly: IceRpc
File(s): /home/runner/work/icerpc-csharp/icerpc-csharp/src/IceRpc/ServerAddress.cs
Tag: 275_13775359185
Line coverage
100%
Covered lines: 92
Uncovered lines: 0
Coverable lines: 92
Total lines: 262
Line coverage: 100%
Branch coverage
94%
Covered branches: 36
Total branches: 38
Branch coverage: 94.7%
Method coverage
100%
Covered methods: 18
Total methods: 18
Method coverage: 100%

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
get_Protocol()100%11100%
get_Host()100%11100%
set_Host(...)100%22100%
get_Port()100%11100%
set_Port(...)100%11100%
get_Transport()100%11100%
set_Transport(...)66.66%66100%
get_Params()100%11100%
set_Params(...)100%11100%
get_OriginalUri()100%11100%
.ctor(...)100%11100%
.ctor()100%11100%
.ctor(...)100%1616100%
Equals(...)100%88100%
GetHashCode()100%11100%
ToString()100%44100%
ToUri()100%22100%
.ctor(...)100%11100%

File(s)

/home/runner/work/icerpc-csharp/icerpc-csharp/src/IceRpc/ServerAddress.cs

#LineLine coverage
 1// Copyright (c) ZeroC, Inc.
 2
 3using IceRpc.Internal;
 4using System.Collections.Immutable;
 5using System.ComponentModel;
 6using System.Globalization;
 7using System.Net;
 8using System.Text;
 9
 10namespace IceRpc;
 11
 12/// <summary>A server address specifies the address of the server-end of an ice or icerpc connection: a server listens
 13/// on a server address and a client establishes a connection to a server address.</summary>
 14// The properties of this struct are sorted in URI order.
 15[TypeConverter(typeof(ServerAddressTypeConverter))]
 16public readonly record struct ServerAddress
 17{
 18    /// <summary>Gets the protocol of this server address.</summary>
 19    /// <value>Either <see cref="Protocol.IceRpc" /> or <see cref="Protocol.Ice" />.</value>
 938520    public Protocol Protocol { get; }
 21
 22    /// <summary>Gets or initializes the host.</summary>
 23    /// <value>The host of this server address. Defaults to <c>::0</c> meaning that the server will listen on all the
 24    /// network interfaces. This default value is parsed into <see cref="IPAddress.IPv6Any" />.</value>
 25    public string Host
 26    {
 738727        get => _host;
 28
 29        init
 10230        {
 10231            if (Uri.CheckHostName(value) == UriHostNameType.Unknown)
 432            {
 433                throw new ArgumentException($"Cannot set {nameof(Host)} to '{value}'.", nameof(value));
 34            }
 9835            _host = value;
 9836            OriginalUri = null; // new host invalidates OriginalUri
 9837        }
 38    }
 39
 40    /// <summary>Gets or initializes the port number.</summary>
 41    /// <value>The port number of this server address. Defaults to <see cref="Protocol.DefaultPort" />.</value>
 42    public ushort Port
 43    {
 676244        get => _port;
 45
 46        init
 50247        {
 50248            _port = value;
 50249            OriginalUri = null; // new port invalidates OriginalUri
 50250        }
 51    }
 52
 53    /// <summary>Gets or initializes the transport.</summary>
 54    /// <value>The name of the transport, or <see langword="null"/> if the transport is unspecified. Defaults to
 55    /// <see langword="null"/>.</value>
 56    public string? Transport
 57    {
 1008758        get => _transport;
 59
 60        init
 265261        {
 265262            _transport = value is null || (ServiceAddress.IsValidParamValue(value) && value.Length > 0) ? value :
 265263                throw new ArgumentException($"The value '{value}' is not valid transport name", nameof(value));
 265264            OriginalUri = null; // new transport invalidates OriginalUri
 265265        }
 66    }
 67
 68    /// <summary>Gets or initializes transport-specific parameters.</summary>
 69    /// <value>The server address parameters. Defaults to <see cref="ImmutableDictionary{TKey, TValue}.Empty" />.
 70    /// </value>
 71    public ImmutableDictionary<string, string> Params
 72    {
 874973        get => _params;
 74
 75        init
 1976        {
 77            try
 1978            {
 1979                ServiceAddress.CheckParams(value);
 980            }
 1081            catch (FormatException exception)
 1082            {
 1083                throw new ArgumentException("Invalid parameters.", nameof(value), exception);
 84            }
 985            _params = value;
 986            OriginalUri = null; // new params invalidates OriginalUri
 987        }
 88    }
 89
 90    /// <summary>Gets the URI used to create this server address.</summary>
 91    /// <value>The <see cref="Uri" /> of this server address if it was constructed from a URI; otherwise,
 92    /// <see langword="null"/>.</value>
 632793    public Uri? OriginalUri { get; private init; }
 94
 269295    private readonly string _host = "::0";
 269296    private readonly ImmutableDictionary<string, string> _params = ImmutableDictionary<string, string>.Empty;
 97    private readonly ushort _port;
 98    private readonly string? _transport;
 99
 100    /// <summary>Constructs a server address with default values.</summary>
 101    public ServerAddress()
 14102        : this(Protocol.IceRpc)
 14103    {
 14104    }
 105
 106    /// <summary>Constructs a server address from a supported protocol.</summary>
 107    /// <param name="protocol">The protocol.</param>
 108    public ServerAddress(Protocol protocol)
 246109    {
 246110        Protocol = protocol;
 246111        _port = Protocol.DefaultPort;
 246112        _transport = null;
 246113        OriginalUri = null;
 246114    }
 115
 116    /// <summary>Constructs a server address from a <see cref="Uri" />.</summary>
 117    /// <param name="uri">An absolute URI.</param>
 118    /// <exception cref="ArgumentException">Thrown if <paramref name="uri" /> is not an absolute URI, or if its scheme
 119    /// is not a supported protocol, or if it has a non-empty path or fragment, or if it has an empty host, or if its
 120    /// query can't be parsed or if it has an alt-server query parameter.</exception>
 121    public ServerAddress(Uri uri)
 1827122    {
 1827123        if (!uri.IsAbsoluteUri)
 2124        {
 2125            throw new ArgumentException("Cannot create a server address from a relative URI.", nameof(uri));
 126        }
 127
 1825128        Protocol = Protocol.TryParse(uri.Scheme, out Protocol? protocol) ? protocol :
 1825129            throw new ArgumentException($"Cannot create a server address with protocol '{uri.Scheme}'", nameof(uri));
 130
 1821131        _host = uri.IdnHost;
 1821132        if (_host.Length == 0)
 14133        {
 14134            throw new ArgumentException("Cannot create a server address with an empty host.", nameof(uri));
 135        }
 136
 1807137        _port = uri.Port == -1 ? Protocol.DefaultPort : checked((ushort)uri.Port);
 138
 1807139        if (uri.UserInfo.Length > 0)
 2140        {
 2141            throw new ArgumentException("Cannot create a server address with a user info.", nameof(uri));
 142        }
 143
 1805144        if (uri.AbsolutePath.Length > 1)
 4145        {
 4146            throw new ArgumentException("Cannot create a server address with a path.", nameof(uri));
 147        }
 148
 1801149        if (uri.Fragment.Length > 0)
 2150        {
 2151            throw new ArgumentException("Cannot create a server address with a fragment.", nameof(uri));
 152        }
 153
 154        try
 1799155        {
 1799156            (_params, string? altServerValue, _transport) = uri.ParseQuery();
 157
 1797158            if (altServerValue is not null)
 2159            {
 2160                throw new ArgumentException(
 2161                    "Cannot create a server address with an alt-server query parameter.",
 2162                    nameof(uri));
 163            }
 1795164        }
 2165        catch (FormatException exception)
 2166        {
 2167            throw new ArgumentException("Cannot parse query of server address URI.", nameof(uri), exception);
 168        }
 169
 1795170        OriginalUri = uri;
 1795171    }
 172
 173    /// <summary>Checks if this server address is equal to another server address.</summary>
 174    /// <param name="other">The other server address.</param>
 175    /// <returns><see langword="true" /> when the two server addresses have the same properties, including the same
 176    /// parameters; otherwise, <see langword="false" />.</returns>
 177    public bool Equals(ServerAddress other) =>
 1329178        Protocol == other.Protocol &&
 1329179        Host == other.Host &&
 1329180        Port == other.Port &&
 1329181        Transport == other.Transport &&
 1329182        Params.DictionaryEqual(other.Params);
 183
 184    /// <summary>Computes the hash code for this server address.</summary>
 185    /// <returns>The hash code.</returns>
 2169186    public override int GetHashCode() => HashCode.Combine(Protocol, Host, Port, Transport, Params.Count);
 187
 188    /// <summary>Converts this server address into a string.</summary>
 189    /// <returns>The string representation of this server address.</returns>
 190    public override string ToString() =>
 392191        OriginalUri?.ToString() ?? new StringBuilder().AppendServerAddress(this).ToString();
 192
 193    /// <summary>Converts this server address into a URI.</summary>
 194    /// <returns>The URI.</returns>
 4195    public Uri ToUri() => OriginalUri ?? new Uri(ToString(), UriKind.Absolute);
 196
 197    /// <summary>Constructs a server address from a protocol, a host, a port and parsed parameters, without parameter
 198    /// validation.</summary>
 199    /// <remarks>This constructor is used by <see cref="ServiceAddress" /> for its main server address and by the Slice
 200    /// decoder for Slice1 server addresses.</remarks>
 201    internal ServerAddress(
 202        Protocol protocol,
 203        string host,
 204        ushort port,
 205        string? transport,
 206        ImmutableDictionary<string, string> serverAddressParams)
 619207    {
 619208        Protocol = protocol;
 619209        _host = host;
 619210        _port = port;
 619211        _transport = transport;
 619212        _params = serverAddressParams;
 619213        OriginalUri = null;
 619214    }
 215}
 216
 217/// <summary>Equality comparer for <see cref="ServerAddress" />.</summary>
 218public abstract class ServerAddressComparer : EqualityComparer<ServerAddress>
 219{
 220    /// <summary>Gets a server address comparer that compares all server address properties, except a transport mismatch
 221    /// where the transport of one of the server addresses is null results in equality.</summary>
 222    /// <value>A <see cref="ServerAddressComparer" /> instance that compares server address properties with the
 223    /// exception of the <see cref="ServerAddress.Transport" /> properties which are only compared if non-null.</value>
 224    public static ServerAddressComparer OptionalTransport { get; } = new OptionalTransportServerAddressComparer();
 225
 226    private class OptionalTransportServerAddressComparer : ServerAddressComparer
 227    {
 228        public override bool Equals(ServerAddress lhs, ServerAddress rhs) =>
 229            lhs.Protocol == rhs.Protocol &&
 230            lhs.Host == rhs.Host &&
 231            lhs.Port == rhs.Port &&
 232            (lhs.Transport == rhs.Transport || lhs.Transport is null || rhs.Transport is null) &&
 233            lhs.Params.DictionaryEqual(rhs.Params);
 234
 235        public override int GetHashCode(ServerAddress serverAddress) =>
 236            HashCode.Combine(serverAddress.Protocol, serverAddress.Host, serverAddress.Port, serverAddress.Params.Count)
 237    }
 238}
 239
 240/// <summary>The server address type converter specifies how to convert a string to a serverAddress. It's used by
 241/// sub-systems such as the Microsoft ConfigurationBinder to bind string values to ServerAddress properties.</summary>
 242public class ServerAddressTypeConverter : TypeConverter
 243{
 244    /// <summary>Returns whether this converter can convert an object of the given type into a
 245    /// <see cref="ServerAddress"/> value, using the specified context.</summary>
 246    /// <param name="context">An <see cref="ITypeDescriptorContext"/> that provides a format context.</param>
 247    /// <param name="sourceType">A <see cref="Type"/> that represents the type you want to convert from.</param>
 248    /// <returns><see langword="true"/>if this converter can perform the conversion; otherwise, <see langword="false"/>.
 249    /// </returns>
 250    public override bool CanConvertFrom(ITypeDescriptorContext? context, Type sourceType) =>
 251        sourceType == typeof(string) || base.CanConvertFrom(context, sourceType);
 252
 253    /// <summary>Converts the given object into a <see cref="ServerAddress"/> value, using the specified context and
 254    /// culture information.</summary>
 255    /// <param name="context">An <see cref="ITypeDescriptorContext"/> that provides a format context.</param>
 256    /// <param name="culture">The <see cref="CultureInfo"/> to use as the current culture.</param>
 257    /// <param name="value">The <see cref="object "/> to convert.</param>
 258    /// <returns>An <see cref="object "/> that represents the converted <see cref="ServerAddress"/> value.</returns>
 259    /// <remarks><see cref="TypeConverter"/>.</remarks>
 260    public override object? ConvertFrom(ITypeDescriptorContext? context, CultureInfo? culture, object value) =>
 261        value is string valueStr ? new ServerAddress(new Uri(valueStr)) : base.ConvertFrom(context, culture, value);
 262}