< Summary

Information
Class: IceRpc.Ice.Operations.IceProxyIceEncoderExtensions
Assembly: IceRpc.Ice
File(s): /home/runner/work/icerpc-csharp/icerpc-csharp/src/IceRpc.Ice/Operations/IceProxyIceEncoderExtensions.cs
Tag: 1856_27024993493
Line coverage
96%
Covered lines: 133
Uncovered lines: 5
Coverable lines: 138
Total lines: 237
Line coverage: 96.3%
Branch coverage
96%
Covered branches: 52
Total branches: 54
Branch coverage: 96.2%
Method coverage
100%
Covered methods: 5
Fully covered methods: 4
Total methods: 5
Method coverage: 100%
Full method coverage: 80%

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
EncodeProxy(...)100%22100%
EncodeServiceAddress(...)81.25%191678.37%
EncodeServerAddress(...)100%1414100%
EncodeTcpServerAddressBody(...)100%44100%
ParseOpaqueParams(...)100%1818100%

File(s)

/home/runner/work/icerpc-csharp/icerpc-csharp/src/IceRpc.Ice/Operations/IceProxyIceEncoderExtensions.cs

#LineLine coverage
 1// Copyright (c) ZeroC, Inc.
 2
 3using IceRpc.Ice.Codec;
 4using IceRpc.Ice.Internal;
 5using IceRpc.Ice.Operations.Internal;
 6using IceRpc.Internal;
 7using System.Diagnostics;
 8using System.Globalization;
 9
 10namespace IceRpc.Ice.Operations;
 11
 12/// <summary>Provides extension methods for <see cref="IceEncoder" /> to encode proxies.</summary>
 13public static class IceProxyIceEncoderExtensions
 14{
 15    /// <summary>The default timeout value for tcp/ssl server addresses encoded with the Ice encoding.</summary>
 16    internal const int DefaultTcpTimeout = 60_000; // 60s
 17
 18    internal const string OpaqueName = "opaque";
 19    internal const string SslName = "ssl";
 20    internal const string TcpName = "tcp";
 21
 22    /// <summary>Encodes a proxy.</summary>
 23    /// <typeparam name="T">The type of the proxy to encode.</typeparam>
 24    /// <param name="encoder">The Ice encoder.</param>
 25    /// <param name="value">The proxy to encode.</param>
 26    public static void EncodeProxy<T>(this ref IceEncoder encoder, T? value) where T : struct, IIceProxy
 7027    {
 7028        if (value is not null)
 6229        {
 6230            encoder.EncodeServiceAddress(value.Value.ServiceAddress);
 5331        }
 32        else
 833        {
 834            Identity.Empty.Encode(ref encoder);
 835        }
 6136    }
 37
 38    /// <summary>Encodes a service address.</summary>
 39    /// <param name="encoder">The Ice encoder.</param>
 40    /// <param name="value">The value to encode.</param>
 41    private static void EncodeServiceAddress(this ref IceEncoder encoder, ServiceAddress value)
 6242    {
 6243        if (value.Protocol is not Protocol protocol)
 044        {
 045            throw new NotSupportedException("Cannot encode a relative service address with the Ice encoding.");
 46        }
 47
 48        // With the Ice encoding, a non-null proxy/service address is encoded as:
 49        // - identity, fragment, invocation mode, secure, protocol major and minor, and the encoding major and minor
 50        // - a sequence of server addresses (can be empty)
 51        // - an adapter ID string present only when the sequence of server addresses is empty
 52
 6253        var identity = Identity.Parse(value.Path);
 6254        if (identity.Name.Length == 0)
 255        {
 256            throw new ArgumentException(
 257                "Cannot encode a non-null service address with a null Ice identity.",
 258                nameof(value));
 59        }
 6060        identity.Encode(ref encoder);
 61
 6062        encoder.EncodeFragmentAsFacet(value.Fragment);
 6063        encoder.EncodeInvocationMode(InvocationMode.Twoway);
 6064        encoder.EncodeBool(false);              // Secure
 6065        encoder.EncodeByte(protocol.ByteValue); // Protocol Major
 6066        encoder.EncodeByte(0);                  // Protocol Minor
 6067        encoder.EncodeByte(1);                  // Encoding Major
 6068        encoder.EncodeByte(1);                  // Encoding Minor
 69
 6070        if (value.ServerAddress is ServerAddress serverAddress)
 4371        {
 4372            encoder.EncodeSize(1 + value.AltServerAddresses.Count); // server address count
 4373            encoder.EncodeServerAddress(serverAddress);
 11074            foreach (ServerAddress altServer in value.AltServerAddresses)
 175            {
 176                encoder.EncodeServerAddress(altServer);
 177            }
 3678        }
 79        else
 1780        {
 1781            encoder.EncodeSize(0); // 0 server addresses
 1782            int maxCount = value.Params.TryGetValue("adapter-id", out string? escapedAdapterId) ? 1 : 0;
 83
 1784            if (value.Params.Count > maxCount)
 085            {
 086                throw new NotSupportedException(
 087                    "Cannot encode a service address with a parameter other than adapter-id using the Ice encoding.");
 88            }
 1789            encoder.EncodeString(escapedAdapterId is null ? "" : Uri.UnescapeDataString(escapedAdapterId));
 1790        }
 5391    }
 92
 93    /// <summary>Encodes a server address in a nested encapsulation.</summary>
 94    /// <param name="encoder">The Ice encoder.</param>
 95    /// <param name="serverAddress">The server address to encode.</param>
 96    private static void EncodeServerAddress(this ref IceEncoder encoder, ServerAddress serverAddress)
 4497    {
 98        // If the server address does not specify a transport, we default to TCP. We can't encode "default".
 4499        string transport = serverAddress.Transport ?? TcpName;
 100
 101        // The Ice encoding of ice server addresses is transport-specific, and hard-coded here.
 102
 44103        if (serverAddress.Protocol == Protocol.Ice && transport == OpaqueName)
 11104        {
 105            // Opaque server address encoding
 106
 11107            (short transportCode, byte encodingMajor, byte encodingMinor, ReadOnlyMemory<byte> bytes) =
 11108                serverAddress.ParseOpaqueParams();
 109
 4110            encoder.EncodeShort(transportCode);
 111
 112            // encapsulation size includes size-length and 2 bytes for encoding
 4113            encoder.EncodeInt(4 + 2 + bytes.Length);
 4114            encoder.EncodeByte(encodingMajor);
 4115            encoder.EncodeByte(encodingMinor);
 4116            encoder.WriteByteSpan(bytes.Span);
 4117        }
 118        else
 33119        {
 33120            TransportCode transportCode = serverAddress.Protocol == Protocol.Ice ?
 33121                transport switch
 33122                {
 2123                    SslName => TransportCode.Ssl,
 9124                    TcpName => TransportCode.Tcp,
 3125                    _ => TransportCode.Uri
 33126                } :
 33127                TransportCode.Uri;
 128
 33129            encoder.EncodeShort((short)transportCode);
 130
 33131            int startPos = encoder.EncodedByteCount; // size includes size-length
 33132            Span<byte> sizePlaceholder = encoder.GetPlaceholderSpan(4); // encapsulation size
 33133            encoder.EncodeByte(1); // encoding version major
 33134            encoder.EncodeByte(1); // encoding version minor
 135
 33136            switch (transportCode)
 137            {
 138                case TransportCode.Tcp:
 139                case TransportCode.Ssl:
 11140                    encoder.EncodeTcpServerAddressBody(serverAddress);
 11141                    break;
 142
 143                default:
 22144                    Debug.Assert(transportCode == TransportCode.Uri);
 22145                    encoder.EncodeString(serverAddress.ToString());
 22146                    break;
 147            }
 148
 33149            IceEncoder.EncodeInt(encoder.EncodedByteCount - startPos, sizePlaceholder);
 33150        }
 37151    }
 152
 153    /// <summary>Encodes the body of a tcp or ssl server address.</summary>
 154    private static void EncodeTcpServerAddressBody(this ref IceEncoder encoder, ServerAddress serverAddress)
 11155    {
 11156        Debug.Assert(serverAddress.Protocol == Protocol.Ice);
 157
 11158        new TcpServerAddressBody(
 11159            serverAddress.Host,
 11160            serverAddress.Port,
 11161            timeout: serverAddress.Params.TryGetValue("t", out string? timeoutValue) ?
 11162                (timeoutValue == "infinite" ? -1 : int.Parse(timeoutValue, CultureInfo.InvariantCulture)) :
 11163                DefaultTcpTimeout,
 11164            compress: serverAddress.Params.ContainsKey("z")).Encode(ref encoder);
 11165    }
 166
 167    /// <summary>Parses the params of an opaque server address.</summary>
 168    private static (short TransportCode, byte EncodingMajor, byte EncodingMinor, ReadOnlyMemory<byte> Bytes) ParseOpaque
 169       this ServerAddress serverAddress)
 11170    {
 11171        short transportCode = -1;
 11172        ReadOnlyMemory<byte> bytes = default;
 11173        byte encodingMajor = 1;
 11174        byte encodingMinor = 1;
 175
 66176        foreach ((string name, string value) in serverAddress.Params)
 19177        {
 19178            switch (name)
 179            {
 180                case "e":
 4181                    (encodingMajor, encodingMinor) = value switch
 4182                    {
 2183                        "1.0" => ((byte)1, (byte)0),
 1184                        "1.1" => ((byte)1, (byte)1),
 1185                        _ => throw new FormatException(
 1186                            $"Invalid value for parameter 'e' in server address: '{serverAddress}'.")
 4187                    };
 3188                    break;
 189
 190                case "t":
 191                    try
 7192                    {
 7193                        transportCode = short.Parse(value, CultureInfo.InvariantCulture);
 6194                    }
 1195                    catch (FormatException exception)
 1196                    {
 1197                        throw new FormatException(
 1198                            $"Invalid value for parameter 't' in server address: '{serverAddress}'.", exception);
 199                    }
 200
 6201                    if (transportCode < 0)
 1202                    {
 1203                        throw new FormatException(
 1204                            $"The value for parameter 't' is out of range in server address: '{serverAddress}'.");
 205                    }
 5206                    break;
 207
 208                case "v":
 209                    try
 7210                    {
 7211                        bytes = Convert.FromBase64String(value);
 6212                    }
 1213                    catch (FormatException exception)
 1214                    {
 1215                        throw new FormatException(
 1216                            $"Invalid Base64 value in server address: '{serverAddress}'.",
 1217                            exception);
 218                    }
 6219                    break;
 220
 221                default:
 1222                    throw new FormatException($"Unknown parameter '{name}' in server address: '{serverAddress}'.");
 223            }
 14224        }
 225
 6226        if (transportCode == -1)
 1227        {
 1228            throw new FormatException($"Missing 't' parameter in server address: '{serverAddress}'.");
 229        }
 5230        else if (bytes.Length == 0)
 1231        {
 1232            throw new FormatException($"Missing 'v' parameter in server address: '{serverAddress}'.");
 233        }
 234
 4235        return (transportCode, encodingMajor, encodingMinor, bytes);
 4236    }
 237}