< 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: 1321_24790053727
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
 6127    {
 6128        if (value is not null)
 5329        {
 5330            encoder.EncodeServiceAddress(value.Value.ServiceAddress);
 4431        }
 32        else
 833        {
 834            Identity.Empty.Encode(ref encoder);
 835        }
 5236    }
 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)
 5342    {
 5343        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
 5353        var identity = Identity.Parse(value.Path);
 5354        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        }
 5160        identity.Encode(ref encoder);
 61
 5162        encoder.EncodeFragmentAsFacet(value.Fragment);
 5163        encoder.EncodeInvocationMode(InvocationMode.Twoway);
 5164        encoder.EncodeBool(false);              // Secure
 5165        encoder.EncodeByte(protocol.ByteValue); // Protocol Major
 5166        encoder.EncodeByte(0);                  // Protocol Minor
 5167        encoder.EncodeByte(1);                  // Encoding Major
 5168        encoder.EncodeByte(1);                  // Encoding Minor
 69
 5170        if (value.ServerAddress is ServerAddress serverAddress)
 3971        {
 3972            encoder.EncodeSize(1 + value.AltServerAddresses.Count); // server address count
 3973            encoder.EncodeServerAddress(serverAddress);
 9874            foreach (ServerAddress altServer in value.AltServerAddresses)
 175            {
 176                encoder.EncodeServerAddress(altServer);
 177            }
 3278        }
 79        else
 1280        {
 1281            encoder.EncodeSize(0); // 0 server addresses
 1282            int maxCount = value.Params.TryGetValue("adapter-id", out string? escapedAdapterId) ? 1 : 0;
 83
 1284            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            }
 1289            encoder.EncodeString(escapedAdapterId is null ? "" : Uri.UnescapeDataString(escapedAdapterId));
 1290        }
 4491    }
 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)
 4097    {
 98        // If the server address does not specify a transport, we default to TCP. We can't encode "default".
 4099        string transport = serverAddress.Transport ?? TcpName;
 100
 101        // The Ice encoding of ice server addresses is transport-specific, and hard-coded here.
 102
 40103        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
 29119        {
 29120            TransportCode transportCode = serverAddress.Protocol == Protocol.Ice ?
 29121                transport switch
 29122                {
 2123                    SslName => TransportCode.Ssl,
 9124                    TcpName => TransportCode.Tcp,
 1125                    _ => TransportCode.Uri
 29126                } :
 29127                TransportCode.Uri;
 128
 29129            encoder.EncodeShort((short)transportCode);
 130
 29131            int startPos = encoder.EncodedByteCount; // size includes size-length
 29132            Span<byte> sizePlaceholder = encoder.GetPlaceholderSpan(4); // encapsulation size
 29133            encoder.EncodeByte(1); // encoding version major
 29134            encoder.EncodeByte(1); // encoding version minor
 135
 29136            switch (transportCode)
 137            {
 138                case TransportCode.Tcp:
 139                case TransportCode.Ssl:
 11140                    encoder.EncodeTcpServerAddressBody(serverAddress);
 11141                    break;
 142
 143                default:
 18144                    Debug.Assert(transportCode == TransportCode.Uri);
 18145                    encoder.EncodeString(serverAddress.ToString());
 18146                    break;
 147            }
 148
 29149            IceEncoder.EncodeInt(encoder.EncodedByteCount - startPos, sizePlaceholder);
 29150        }
 33151    }
 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
 68176        foreach ((string name, string value) in serverAddress.Params)
 20177        {
 20178            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
 9192                    {
 9193                        transportCode = short.Parse(value, CultureInfo.InvariantCulture);
 8194                    }
 1195                    catch (FormatException exception)
 1196                    {
 1197                        throw new FormatException(
 1198                            $"Invalid value for parameter 't' in server address: '{serverAddress}'.", exception);
 199                    }
 200
 8201                    if (transportCode < 0)
 1202                    {
 1203                        throw new FormatException(
 1204                            $"The value for parameter 't' is out of range in server address: '{serverAddress}'.");
 205                    }
 7206                    break;
 207
 208                case "v":
 209                    try
 6210                    {
 6211                        bytes = Convert.FromBase64String(value);
 5212                    }
 1213                    catch (FormatException exception)
 1214                    {
 1215                        throw new FormatException(
 1216                            $"Invalid Base64 value in server address: '{serverAddress}'.",
 1217                            exception);
 218                    }
 5219                    break;
 220
 221                default:
 1222                    throw new FormatException($"Unknown parameter '{name}' in server address: '{serverAddress}'.");
 223            }
 15224        }
 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}