< Summary

Information
Class: IceRpc.Slice.ServiceAddressSliceEncoderExtensions
Assembly: IceRpc.Slice
File(s): /home/runner/work/icerpc-csharp/icerpc-csharp/src/IceRpc.Slice/ServiceAddressSliceEncoderExtensions.cs
Tag: 275_13775359185
Line coverage
94%
Covered lines: 141
Uncovered lines: 8
Coverable lines: 149
Total lines: 249
Line coverage: 94.6%
Branch coverage
94%
Covered branches: 55
Total branches: 58
Branch coverage: 94.8%
Method coverage
100%
Covered methods: 5
Total methods: 5
Method coverage: 100%

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
EncodeServiceAddress(...)88.88%18.511888.37%
EncodeNullableServiceAddress(...)75%4.2476.92%
EncodeServerAddress(...)100%1414100%
EncodeTcpServerAddressBody(...)100%44100%
ParseOpaqueParams(...)100%1818100%

File(s)

/home/runner/work/icerpc-csharp/icerpc-csharp/src/IceRpc.Slice/ServiceAddressSliceEncoderExtensions.cs

#LineLine coverage
 1// Copyright (c) ZeroC, Inc.
 2
 3using IceRpc.Internal;
 4using IceRpc.Slice.Internal;
 5using System.Diagnostics;
 6using System.Globalization;
 7using ZeroC.Slice;
 8
 9namespace IceRpc.Slice;
 10
 11/// <summary>Provides extension methods for <see cref="SliceEncoder" /> to encode service addresses.</summary>
 12public static class ServiceAddressSliceEncoderExtensions
 13{
 14    /// <summary>The default timeout value for tcp/ssl server addresses with Slice1.</summary>
 15    internal const int DefaultTcpTimeout = 60_000; // 60s
 16
 17    internal const string OpaqueName = "opaque";
 18    internal const string SslName = "ssl";
 19    internal const string TcpName = "tcp";
 20
 21    /// <summary>Encodes a service address.</summary>
 22    /// <param name="encoder">The Slice encoder.</param>
 23    /// <param name="value">The value to encode.</param>
 24    public static void EncodeServiceAddress(this ref SliceEncoder encoder, ServiceAddress value)
 6925    {
 6926        if (encoder.Encoding == SliceEncoding.Slice1)
 4627        {
 4628            if (value.Protocol is not Protocol protocol)
 029            {
 030                throw new NotSupportedException("Cannot encode a relative service address with Slice1.");
 31            }
 32
 33            // With Slice1, a non-null proxy/service address is encoded as:
 34            // - identity, fragment, invocation mode, secure, protocol major and minor, and the encoding major and minor
 35            // - a sequence of server addresses (can be empty)
 36            // - an adapter ID string present only when the sequence of server addresses is empty
 37
 4638            var identity = Identity.Parse(value.Path);
 4639            if (identity.Name.Length == 0)
 240            {
 241                throw new ArgumentException(
 242                    "Cannot encode a non-null service address with a null Ice identity.",
 243                    nameof(value));
 44            }
 4445            identity.Encode(ref encoder);
 46
 4447            encoder.EncodeFragment(value.Fragment);
 4448            encoder.EncodeInvocationMode(InvocationMode.Twoway);
 4449            encoder.EncodeBool(false);               // Secure
 4450            encoder.EncodeUInt8(protocol.ByteValue); // Protocol Major
 4451            encoder.EncodeUInt8(0);                  // Protocol Minor
 4452            encoder.EncodeUInt8(1);                  // Encoding Major
 4453            encoder.EncodeUInt8(1);                  // Encoding Minor
 54
 4455            if (value.ServerAddress is ServerAddress serverAddress)
 4256            {
 4257                encoder.EncodeSize(1 + value.AltServerAddresses.Count); // server address count
 4258                encoder.EncodeServerAddress(serverAddress);
 10959                foreach (ServerAddress altServer in value.AltServerAddresses)
 260                {
 261                    encoder.EncodeServerAddress(altServer);
 262                }
 3563            }
 64            else
 265            {
 266                encoder.EncodeSize(0); // 0 server addresses
 267                int maxCount = value.Params.TryGetValue("adapter-id", out string? adapterId) ? 1 : 0;
 68
 269                if (value.Params.Count > maxCount)
 070                {
 071                    throw new NotSupportedException(
 072                        "Cannot encode a service address with a parameter other than adapter-id using Slice1.");
 73                }
 274                encoder.EncodeString(adapterId ?? "");
 275            }
 3776        }
 77        else
 2378        {
 2379            encoder.EncodeString(value.ToString()); // a URI or an absolute path
 2380        }
 6081    }
 82
 83    /// <summary>Encodes a nullable service address (Slice1 only).</summary>
 84    /// <param name="encoder">The Slice encoder.</param>
 85    /// <param name="value">The service address to encode, or <see langword="null" />.</param>
 86    public static void EncodeNullableServiceAddress(this ref SliceEncoder encoder, ServiceAddress? value)
 2387    {
 2388        if (encoder.Encoding != SliceEncoding.Slice1)
 089        {
 090            throw new InvalidOperationException(
 091                "Encoding a nullable service address without a bit sequence is only supported with Slice1.");
 92        }
 93
 2394        if (value is not null)
 1495        {
 1496            encoder.EncodeServiceAddress(value);
 1497        }
 98        else
 999        {
 9100            Identity.Empty.Encode(ref encoder);
 9101        }
 23102    }
 103
 104    /// <summary>Encodes a server address in a nested encapsulation (Slice1 only).</summary>
 105    /// <param name="encoder">The Slice encoder.</param>
 106    /// <param name="serverAddress">The server address to encode.</param>
 107    private static void EncodeServerAddress(this ref SliceEncoder encoder, ServerAddress serverAddress)
 44108    {
 109        // If the server address does not specify a transport, we default to TCP. We can't encode "default".
 44110        string transport = serverAddress.Transport ?? TcpName;
 111
 112        // The Slice1 encoding of ice server addresses is transport-specific, and hard-coded here.
 113
 44114        if (serverAddress.Protocol == Protocol.Ice && transport == OpaqueName)
 11115        {
 116            // Opaque server address encoding
 117
 11118            (short transportCode, byte encodingMajor, byte encodingMinor, ReadOnlyMemory<byte> bytes) =
 11119                serverAddress.ParseOpaqueParams();
 120
 4121            encoder.EncodeInt16(transportCode);
 122
 123            // encapsulation size includes size-length and 2 bytes for encoding
 4124            encoder.EncodeInt32(4 + 2 + bytes.Length);
 4125            encoder.EncodeUInt8(encodingMajor);
 4126            encoder.EncodeUInt8(encodingMinor);
 4127            encoder.WriteByteSpan(bytes.Span);
 4128        }
 129        else
 33130        {
 33131            TransportCode transportCode = serverAddress.Protocol == Protocol.Ice ?
 33132                transport switch
 33133                {
 3134                    SslName => TransportCode.Ssl,
 11135                    TcpName => TransportCode.Tcp,
 1136                    _ => TransportCode.Uri
 33137                } :
 33138                TransportCode.Uri;
 139
 33140            encoder.EncodeInt16((short)transportCode);
 141
 33142            int startPos = encoder.EncodedByteCount; // size includes size-length
 33143            Span<byte> sizePlaceholder = encoder.GetPlaceholderSpan(4); // encapsulation size
 33144            encoder.EncodeUInt8(1); // encoding version major
 33145            encoder.EncodeUInt8(1); // encoding version minor
 146
 33147            switch (transportCode)
 148            {
 149                case TransportCode.Tcp:
 150                case TransportCode.Ssl:
 14151                    encoder.EncodeTcpServerAddressBody(serverAddress);
 14152                    break;
 153
 154                default:
 19155                    Debug.Assert(transportCode == TransportCode.Uri);
 19156                    encoder.EncodeString(serverAddress.ToString());
 19157                    break;
 158            }
 159
 33160            SliceEncoder.EncodeInt32(encoder.EncodedByteCount - startPos, sizePlaceholder);
 33161        }
 37162    }
 163
 164    /// <summary>Encodes the body of a tcp or ssl server address using Slice1.</summary>
 165    private static void EncodeTcpServerAddressBody(this ref SliceEncoder encoder, ServerAddress serverAddress)
 14166    {
 14167        Debug.Assert(encoder.Encoding == SliceEncoding.Slice1);
 14168        Debug.Assert(serverAddress.Protocol == Protocol.Ice);
 169
 14170        new TcpServerAddressBody(
 14171            serverAddress.Host,
 14172            serverAddress.Port,
 14173            timeout: serverAddress.Params.TryGetValue("t", out string? timeoutValue) ?
 14174                (timeoutValue == "infinite" ? -1 : int.Parse(timeoutValue, CultureInfo.InvariantCulture)) :
 14175                DefaultTcpTimeout,
 14176            compress: serverAddress.Params.ContainsKey("z")).Encode(ref encoder);
 14177    }
 178
 179    /// <summary>Parses the params of an opaque server address (Slice1 only).</summary>
 180    private static (short TransportCode, byte EncodingMajor, byte EncodingMinor, ReadOnlyMemory<byte> Bytes) ParseOpaque
 181       this ServerAddress serverAddress)
 11182    {
 11183        short transportCode = -1;
 11184        ReadOnlyMemory<byte> bytes = default;
 11185        byte encodingMajor = 1;
 11186        byte encodingMinor = 1;
 187
 70188        foreach ((string name, string value) in serverAddress.Params)
 21189        {
 21190            switch (name)
 191            {
 192                case "e":
 4193                    (encodingMajor, encodingMinor) = value switch
 4194                    {
 2195                        "1.0" => ((byte)1, (byte)0),
 1196                        "1.1" => ((byte)1, (byte)1),
 1197                        _ => throw new FormatException(
 1198                            $"Invalid value for parameter 'e' in server address: '{serverAddress}'.")
 4199                    };
 3200                    break;
 201
 202                case "t":
 203                    try
 8204                    {
 8205                        transportCode = short.Parse(value, CultureInfo.InvariantCulture);
 7206                    }
 1207                    catch (FormatException exception)
 1208                    {
 1209                        throw new FormatException(
 1210                            $"Invalid value for parameter 't' in server address: '{serverAddress}'.", exception);
 211                    }
 212
 7213                    if (transportCode < 0)
 1214                    {
 1215                        throw new FormatException(
 1216                            $"The value for parameter 't' is out of range in server address: '{serverAddress}'.");
 217                    }
 6218                    break;
 219
 220                case "v":
 221                    try
 8222                    {
 8223                        bytes = Convert.FromBase64String(value);
 7224                    }
 1225                    catch (FormatException exception)
 1226                    {
 1227                        throw new FormatException(
 1228                            $"Invalid Base64 value in server address: '{serverAddress}'.",
 1229                            exception);
 230                    }
 7231                    break;
 232
 233                default:
 1234                    throw new FormatException($"Unknown parameter '{name}' in server address: '{serverAddress}'.");
 235            }
 16236        }
 237
 6238        if (transportCode == -1)
 1239        {
 1240            throw new FormatException($"Missing 't' parameter in server address: '{serverAddress}'.");
 241        }
 5242        else if (bytes.Length == 0)
 1243        {
 1244            throw new FormatException($"Missing 'v' parameter in server address: '{serverAddress}'.");
 245        }
 246
 4247        return (transportCode, encodingMajor, encodingMinor, bytes);
 4248    }
 249}