< Summary

Information
Class: IceRpc.Ice.Operations.IceProxyIceDecoderExtensions
Assembly: IceRpc.Ice
File(s): /home/runner/work/icerpc-csharp/icerpc-csharp/src/IceRpc.Ice/Operations/IceProxyIceDecoderExtensions.cs
Tag: 1321_24790053727
Line coverage
79%
Covered lines: 137
Uncovered lines: 36
Coverable lines: 173
Total lines: 288
Line coverage: 79.1%
Branch coverage
79%
Covered branches: 46
Total branches: 58
Branch coverage: 79.3%
Method coverage
100%
Covered methods: 6
Fully covered methods: 3
Total methods: 6
Method coverage: 100%
Full method coverage: 50%

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
DecodeProxy(...)100%22100%
CreateProxy(...)100%22100%
DecodeServiceAddress(...)100%22100%
DecodeServerAddress(...)75%362878.37%
DecodeServiceAddressCore(...)77.77%221877.19%
DecodeTcpServerAddressBody(...)83.33%7668.18%

File(s)

/home/runner/work/icerpc-csharp/icerpc-csharp/src/IceRpc.Ice/Operations/IceProxyIceDecoderExtensions.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.Buffers;
 8using System.Collections.Immutable;
 9using System.Diagnostics;
 10using System.Globalization;
 11using System.Runtime.CompilerServices;
 12
 13namespace IceRpc.Ice.Operations;
 14
 15/// <summary>Provides extension methods for <see cref="IceDecoder" /> to decode proxies.</summary>
 16public static class IceProxyIceDecoderExtensions
 17{
 18    /// <summary>Decodes a proxy struct.</summary>
 19    /// <typeparam name="TProxy">The type of the proxy struct to decode.</typeparam>
 20    /// <param name="decoder">The Ice decoder.</param>
 21    /// <returns>The decoded proxy, or <see langword="null" />.</returns>
 22    public static TProxy? DecodeProxy<TProxy>(this ref IceDecoder decoder) where TProxy : struct, IIceProxy =>
 5223        decoder.DecodeServiceAddress() is ServiceAddress serviceAddress ?
 5224            CreateProxy<TProxy>(serviceAddress, decoder.DecodingContext) : null;
 25
 26    private static TProxy CreateProxy<TProxy>(ServiceAddress serviceAddress, object? decodingContext)
 27        where TProxy : struct, IIceProxy
 4428    {
 4429        Debug.Assert(serviceAddress.Protocol is not null, "The Ice encoding does not support relative proxies.");
 30
 4431        if (decodingContext is null)
 3332        {
 3333            return new TProxy { Invoker = InvalidInvoker.Instance, ServiceAddress = serviceAddress };
 34        }
 35        else
 1136        {
 1137            var baseProxy = (IIceProxy)decodingContext;
 1138            return new TProxy
 1139            {
 1140                EncodeOptions = baseProxy.EncodeOptions,
 1141                Invoker = baseProxy.Invoker,
 1142                ServiceAddress = serviceAddress
 1143            };
 44        }
 4445    }
 46
 47    /// <summary>Decodes a service address.</summary>
 48    /// <param name="decoder">The Ice decoder.</param>
 49    /// <returns>The decoded service address, or <see langword="null" />.</returns>
 50    private static ServiceAddress? DecodeServiceAddress(this ref IceDecoder decoder)
 5251    {
 5252        string path = new Identity(ref decoder).ToPath();
 5253        return path != "/" ? decoder.DecodeServiceAddressCore(path) : null;
 5254    }
 55
 56    /// <summary>Decodes a server address.</summary>
 57    /// <param name="decoder">The Ice decoder.</param>
 58    /// <param name="protocol">The protocol of this server address.</param>
 59    /// <returns>The server address decoded by this decoder.</returns>
 60    private static ServerAddress DecodeServerAddress(this ref IceDecoder decoder, Protocol protocol)
 3361    {
 62        // With the Ice encoding, the ice server addresses are transport-specific, with a transport-specific encoding.
 63
 3364        ServerAddress? serverAddress = null;
 3365        var transportCode = (TransportCode)decoder.DecodeShort();
 66
 3367        int size = decoder.DecodeInt();
 3368        if (size < 6)
 069        {
 070            throw new InvalidDataException($"The Ice encapsulation's size ({size}) is too small.");
 71        }
 72
 73        // Remove 6 bytes from the encapsulation size (4 for encapsulation size, 2 for encoding).
 3374        size -= 6;
 75
 3376        byte encodingMajor = decoder.DecodeByte();
 3377        byte encodingMinor = decoder.DecodeByte();
 78
 3379        if (decoder.Remaining < size)
 080        {
 081            throw new InvalidDataException($"The Ice encapsulation's size ({size}) is too big.");
 82        }
 83
 3384        if (encodingMajor == 1 && encodingMinor <= 1)
 3385        {
 3386            long oldPos = decoder.Consumed;
 87
 3388            if (protocol == Protocol.Ice)
 1689            {
 1690                switch (transportCode)
 91                {
 92                    case TransportCode.Tcp:
 1193                        serverAddress = decoder.DecodeTcpServerAddressBody(IceProxyIceEncoderExtensions.TcpName);
 1194                        break;
 95
 96                    case TransportCode.Ssl:
 297                        serverAddress = decoder.DecodeTcpServerAddressBody(IceProxyIceEncoderExtensions.SslName);
 298                        break;
 99
 100                    case TransportCode.Uri:
 1101                        serverAddress = new ServerAddress(new Uri(decoder.DecodeString()));
 1102                        if (serverAddress.Value.Protocol != protocol)
 0103                        {
 0104                            throw new InvalidDataException(
 0105                                $"Expected {protocol} server address but received '{serverAddress.Value}'.");
 106                        }
 1107                        break;
 108
 109                    default:
 110                        // Create a server address for transport opaque
 2111                        ImmutableDictionary<string, string>.Builder builder =
 2112                            ImmutableDictionary.CreateBuilder<string, string>();
 113
 2114                        if (encodingMinor == 0)
 1115                        {
 1116                            builder.Add("e", "1.0");
 1117                        }
 118                        // else no e
 119
 2120                        builder.Add("t", ((short)transportCode).ToString(CultureInfo.InvariantCulture));
 2121                        {
 2122                            using IMemoryOwner<byte> memoryOwner = MemoryPool<byte>.Shared.Rent(size);
 2123                            Span<byte> span = memoryOwner.Memory.Span[0..size];
 2124                            decoder.CopyTo(span);
 2125                            string value = Convert.ToBase64String(span);
 2126                            builder.Add("v", value);
 2127                            decoder.IncreaseCollectionAllocation(value.Length, Unsafe.SizeOf<char>());
 2128                        }
 129
 2130                        serverAddress = new ServerAddress(
 2131                            Protocol.Ice,
 2132                            host: "opaque", // not a real host obviously
 2133                            port: Protocol.Ice.DefaultPort,
 2134                            transport: IceProxyIceEncoderExtensions.OpaqueName,
 2135                            builder.ToImmutable());
 2136                        break;
 137                }
 16138            }
 17139            else if (transportCode == TransportCode.Uri)
 17140            {
 141                // The server addresses of an Ice-encoded icerpc proxies only use TransportCode.Uri.
 17142                serverAddress = new ServerAddress(new Uri(decoder.DecodeString()));
 17143                if (serverAddress.Value.Protocol != protocol)
 0144                {
 0145                    throw new InvalidDataException(
 0146                        $"Expected {protocol} server address but received '{serverAddress.Value}'.");
 147                }
 17148            }
 149
 33150            if (serverAddress is not null)
 33151            {
 152                // Make sure we read the full encapsulation.
 33153                if (decoder.Consumed != oldPos + size)
 0154                {
 0155                    throw new InvalidDataException(
 0156                        $"There are {oldPos + size - decoder.Consumed} bytes left in server address encapsulation.");
 157                }
 33158            }
 33159        }
 160
 33161        if (serverAddress is null)
 0162        {
 0163            throw new InvalidDataException(
 0164                $"Cannot decode server address for protocol '{protocol}' and transport '{transportCode.ToString().ToLowe
 165        }
 166
 33167        return serverAddress.Value;
 33168    }
 169
 170    /// <summary>Decodes a service address encoded with the Ice encoding.</summary>
 171    /// <param name="decoder">The Ice decoder.</param>
 172    /// <param name="path">The decoded path.</param>
 173    /// <returns>The decoded service address.</returns>
 174    private static ServiceAddress DecodeServiceAddressCore(this ref IceDecoder decoder, string path)
 44175    {
 176        // With the Ice encoding, a service address is encoded as a kind of discriminated union with:
 177        // - Identity
 178        // - If Identity is not the null identity:
 179        //     - the fragment, invocation mode, secure, protocol major and minor, and the encoding major and minor
 180        //     - a sequence of server addresses (can be empty)
 181        //     - an adapter ID string present only when the sequence of server addresses is empty
 182
 44183        string fragment = decoder.DecodeFacet().ToFragment();
 44184        _ = decoder.DecodeInvocationMode();
 44185        _ = decoder.DecodeBool();
 44186        byte protocolMajor = decoder.DecodeByte();
 44187        byte protocolMinor = decoder.DecodeByte();
 44188        decoder.Skip(2); // skip encoding major and minor
 189
 44190        if (protocolMajor == 0)
 0191        {
 0192            throw new InvalidDataException("Received service address with protocol set to 0.");
 193        }
 44194        if (protocolMinor != 0)
 0195        {
 0196            throw new InvalidDataException(
 0197                $"Received service address with invalid protocolMinor value: {protocolMinor}.");
 198        }
 199
 44200        int count = decoder.DecodeSize();
 201
 44202        ServerAddress? serverAddress = null;
 44203        IEnumerable<ServerAddress> altServerAddresses = ImmutableList<ServerAddress>.Empty;
 44204        var protocol = Protocol.FromByteValue(protocolMajor);
 44205        ImmutableDictionary<string, string> serviceAddressParams = ImmutableDictionary<string, string>.Empty;
 206
 44207        if (count == 0)
 12208        {
 12209            if (decoder.DecodeString() is string adapterId && adapterId.Length > 0)
 5210            {
 5211                serviceAddressParams = serviceAddressParams.Add("adapter-id", Uri.EscapeDataString(adapterId));
 5212            }
 12213        }
 214        else
 32215        {
 32216            serverAddress = decoder.DecodeServerAddress(protocol);
 32217            if (count >= 2)
 1218            {
 219                // An Ice-encoded server address consumes at least 8 bytes (2 bytes for the server address type and 6
 220                // bytes for the encapsulation header). SizeOf ServerAddress is large but less than 8 * 8.
 1221                decoder.IncreaseCollectionAllocation(count, Unsafe.SizeOf<ServerAddress>());
 222
 1223                var serverAddressArray = new ServerAddress[count - 1];
 4224                for (int i = 0; i < count - 1; ++i)
 1225                {
 1226                    serverAddressArray[i] = decoder.DecodeServerAddress(protocol);
 1227                }
 1228                altServerAddresses = serverAddressArray;
 1229            }
 32230        }
 231
 232        try
 44233        {
 44234            if (!protocol.HasFragment && fragment.Length > 0)
 0235            {
 0236                throw new InvalidDataException($"Unexpected fragment in {protocol} service address.");
 237            }
 238
 44239            return new ServiceAddress(
 44240                protocol,
 44241                path,
 44242                serverAddress,
 44243                altServerAddresses.ToImmutableList(),
 44244                serviceAddressParams,
 44245                fragment);
 246        }
 0247        catch (InvalidDataException)
 0248        {
 0249            throw;
 250        }
 0251        catch (Exception exception)
 0252        {
 0253            throw new InvalidDataException("Received invalid service address.", exception);
 254        }
 44255    }
 256
 257    /// <summary>Decodes the body of a tcp or ssl server address.</summary>
 258    private static ServerAddress DecodeTcpServerAddressBody(this ref IceDecoder decoder, string transport)
 13259    {
 13260        var body = new TcpServerAddressBody(ref decoder);
 261
 13262        if (Uri.CheckHostName(body.Host) == UriHostNameType.Unknown)
 0263        {
 0264            throw new InvalidDataException($"Received service address with invalid host '{body.Host}'.");
 265        }
 266
 13267        ImmutableDictionary<string, string> parameters = ImmutableDictionary<string, string>.Empty;
 13268        if (body.Timeout != IceProxyIceEncoderExtensions.DefaultTcpTimeout)
 4269        {
 4270            parameters = parameters.Add("t", body.Timeout.ToString(CultureInfo.InvariantCulture));
 4271        }
 13272        if (body.Compress)
 1273        {
 1274            parameters = parameters.Add("z", "");
 1275        }
 276
 277        try
 13278        {
 13279            return new ServerAddress(Protocol.Ice, body.Host, checked((ushort)body.Port), transport, parameters);
 280        }
 0281        catch (OverflowException exception)
 0282        {
 0283            throw new InvalidDataException(
 0284                "Cannot decode a server address with a port number larger than 65,535.",
 0285                exception);
 286        }
 13287    }
 288}