• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

icerpc / icerpc-csharp / 20870171951

10 Jan 2026 01:03AM UTC coverage: 83.304% (-0.1%) from 83.399%
20870171951

Pull #4221

github

web-flow
Merge fe4f556d8 into f39e9819d
Pull Request #4221: Use new C#14 extension block syntax

12030 of 14441 relevant lines covered (83.3%)

2957.05 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

94.63
src/IceRpc.Slice/ServiceAddressSliceEncoderExtensions.cs
1
// Copyright (c) ZeroC, Inc.
2

3
using IceRpc.Internal;
4
using IceRpc.Slice.Internal;
5
using System.Diagnostics;
6
using System.Globalization;
7
using ZeroC.Slice;
8

9
namespace IceRpc.Slice;
10

11
/// <summary>Provides extension methods for <see cref="SliceEncoder" /> to encode service addresses.</summary>
12
public 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>Extension methods for <see cref="SliceEncoder" />.</summary>
22
    /// <param name="encoder">The Slice encoder.</param>
23
    extension(ref SliceEncoder encoder)
24
    {
25
        /// <summary>Encodes a service address.</summary>
26
        /// <param name="value">The value to encode.</param>
27
        public void EncodeServiceAddress(ServiceAddress value)
28
        {
69✔
29
            if (encoder.Encoding == SliceEncoding.Slice1)
69✔
30
            {
46✔
31
                if (value.Protocol is not Protocol protocol)
46✔
32
                {
×
33
                    throw new NotSupportedException("Cannot encode a relative service address with Slice1.");
×
34
                }
35

36
                // With Slice1, a non-null proxy/service address is encoded as:
37
                // - identity, fragment, invocation mode, secure, protocol major and minor, and the encoding
38
                //   major and minor
39
                // - a sequence of server addresses (can be empty)
40
                // - an adapter ID string present only when the sequence of server addresses is empty
41

42
                var identity = Identity.Parse(value.Path);
46✔
43
                if (identity.Name.Length == 0)
46✔
44
                {
2✔
45
                    throw new ArgumentException(
2✔
46
                        "Cannot encode a non-null service address with a null Ice identity.",
2✔
47
                        nameof(value));
2✔
48
                }
49
                identity.Encode(ref encoder);
44✔
50

51
                encoder.EncodeFragment(value.Fragment);
44✔
52
                encoder.EncodeInvocationMode(InvocationMode.Twoway);
44✔
53
                encoder.EncodeBool(false);               // Secure
44✔
54
                encoder.EncodeUInt8(protocol.ByteValue); // Protocol Major
44✔
55
                encoder.EncodeUInt8(0);                  // Protocol Minor
44✔
56
                encoder.EncodeUInt8(1);                  // Encoding Major
44✔
57
                encoder.EncodeUInt8(1);                  // Encoding Minor
44✔
58

59
                if (value.ServerAddress is ServerAddress serverAddress)
44✔
60
                {
42✔
61
                    encoder.EncodeSize(1 + value.AltServerAddresses.Count); // server address count
42✔
62
                    encoder.EncodeServerAddress(serverAddress);
42✔
63
                    foreach (ServerAddress altServer in value.AltServerAddresses)
109✔
64
                    {
2✔
65
                        encoder.EncodeServerAddress(altServer);
2✔
66
                    }
2✔
67
                }
35✔
68
                else
69
                {
2✔
70
                    encoder.EncodeSize(0); // 0 server addresses
2✔
71
                    int maxCount = value.Params.TryGetValue("adapter-id", out string? adapterId) ? 1 : 0;
2✔
72

73
                    if (value.Params.Count > maxCount)
2✔
74
                    {
×
75
                        throw new NotSupportedException(
×
76
                            "Cannot encode a service address with a parameter other than adapter-id using Slice1.");
×
77
                    }
78
                    encoder.EncodeString(adapterId ?? "");
2✔
79
                }
2✔
80
            }
37✔
81
            else
82
            {
23✔
83
                encoder.EncodeString(value.ToString()); // a URI or an absolute path
23✔
84
            }
23✔
85
        }
60✔
86

87
        /// <summary>Encodes a nullable service address (Slice1 only).</summary>
88
        /// <param name="value">The service address to encode, or <see langword="null" />.</param>
89
        public void EncodeNullableServiceAddress(ServiceAddress? value)
90
        {
23✔
91
            if (encoder.Encoding != SliceEncoding.Slice1)
23✔
92
            {
×
93
                throw new InvalidOperationException(
×
94
                    "Encoding a nullable service address without a bit sequence is only supported with Slice1.");
×
95
            }
96

97
            if (value is not null)
23✔
98
            {
14✔
99
                encoder.EncodeServiceAddress(value);
14✔
100
            }
14✔
101
            else
102
            {
9✔
103
                Identity.Empty.Encode(ref encoder);
9✔
104
            }
9✔
105
        }
23✔
106

107
        /// <summary>Encodes a server address in a nested encapsulation (Slice1 only).</summary>
108
        /// <param name="serverAddress">The server address to encode.</param>
109
        private void EncodeServerAddress(ServerAddress serverAddress)
110
        {
44✔
111
            // If the server address does not specify a transport, we default to TCP. We can't encode "default".
112
            string transport = serverAddress.Transport ?? TcpName;
44✔
113

114
            // The Slice1 encoding of ice server addresses is transport-specific, and hard-coded here.
115

116
            if (serverAddress.Protocol == Protocol.Ice && transport == OpaqueName)
44✔
117
            {
11✔
118
                // Opaque server address encoding
119

120
                (short transportCode, byte encodingMajor, byte encodingMinor, ReadOnlyMemory<byte> bytes) =
11✔
121
                    serverAddress.ParseOpaqueParams();
11✔
122

123
                encoder.EncodeInt16(transportCode);
4✔
124

125
                // encapsulation size includes size-length and 2 bytes for encoding
126
                encoder.EncodeInt32(4 + 2 + bytes.Length);
4✔
127
                encoder.EncodeUInt8(encodingMajor);
4✔
128
                encoder.EncodeUInt8(encodingMinor);
4✔
129
                encoder.WriteByteSpan(bytes.Span);
4✔
130
            }
4✔
131
            else
132
            {
33✔
133
                TransportCode transportCode = serverAddress.Protocol == Protocol.Ice ?
33✔
134
                    transport switch
33✔
135
                    {
33✔
136
                        SslName => TransportCode.Ssl,
3✔
137
                        TcpName => TransportCode.Tcp,
11✔
138
                        _ => TransportCode.Uri
1✔
139
                    } :
33✔
140
                    TransportCode.Uri;
33✔
141

142
                encoder.EncodeInt16((short)transportCode);
33✔
143

144
                int startPos = encoder.EncodedByteCount; // size includes size-length
33✔
145
                Span<byte> sizePlaceholder = encoder.GetPlaceholderSpan(4); // encapsulation size
33✔
146
                encoder.EncodeUInt8(1); // encoding version major
33✔
147
                encoder.EncodeUInt8(1); // encoding version minor
33✔
148

149
                switch (transportCode)
33✔
150
                {
151
                    case TransportCode.Tcp:
152
                    case TransportCode.Ssl:
153
                        encoder.EncodeTcpServerAddressBody(serverAddress);
14✔
154
                        break;
14✔
155

156
                    default:
157
                        Debug.Assert(transportCode == TransportCode.Uri);
19✔
158
                        encoder.EncodeString(serverAddress.ToString());
19✔
159
                        break;
19✔
160
                }
161

162
                SliceEncoder.EncodeInt32(encoder.EncodedByteCount - startPos, sizePlaceholder);
33✔
163
            }
33✔
164
        }
37✔
165

166
        /// <summary>Encodes the body of a tcp or ssl server address using Slice1.</summary>
167
        private void EncodeTcpServerAddressBody(ServerAddress serverAddress)
168
        {
14✔
169
            Debug.Assert(encoder.Encoding == SliceEncoding.Slice1);
14✔
170
            Debug.Assert(serverAddress.Protocol == Protocol.Ice);
14✔
171

172
            new TcpServerAddressBody(
14✔
173
                serverAddress.Host,
14✔
174
                serverAddress.Port,
14✔
175
                timeout: serverAddress.Params.TryGetValue("t", out string? timeoutValue) ?
14✔
176
                    (timeoutValue == "infinite" ? -1 : int.Parse(timeoutValue, CultureInfo.InvariantCulture)) :
14✔
177
                    DefaultTcpTimeout,
14✔
178
                compress: serverAddress.Params.ContainsKey("z")).Encode(ref encoder);
14✔
179
        }
14✔
180
    }
181

182
    /// <summary>Parses the params of an opaque server address (Slice1 only).</summary>
183
    private static (short TransportCode, byte EncodingMajor, byte EncodingMinor, ReadOnlyMemory<byte> Bytes)
184
        ParseOpaqueParams(this ServerAddress serverAddress)
185
    {
11✔
186
        short transportCode = -1;
11✔
187
        ReadOnlyMemory<byte> bytes = default;
11✔
188
        byte encodingMajor = 1;
11✔
189
        byte encodingMinor = 1;
11✔
190

191
        foreach ((string name, string value) in serverAddress.Params)
68✔
192
        {
20✔
193
            switch (name)
20✔
194
            {
195
                case "e":
196
                    (encodingMajor, encodingMinor) = value switch
4✔
197
                    {
4✔
198
                        "1.0" => ((byte)1, (byte)0),
2✔
199
                        "1.1" => ((byte)1, (byte)1),
1✔
200
                        _ => throw new FormatException(
1✔
201
                            $"Invalid value for parameter 'e' in server address: '{serverAddress}'.")
1✔
202
                    };
4✔
203
                    break;
3✔
204

205
                case "t":
206
                    try
207
                    {
9✔
208
                        transportCode = short.Parse(value, CultureInfo.InvariantCulture);
9✔
209
                    }
8✔
210
                    catch (FormatException exception)
1✔
211
                    {
1✔
212
                        throw new FormatException(
1✔
213
                            $"Invalid value for parameter 't' in server address: '{serverAddress}'.", exception);
1✔
214
                    }
215

216
                    if (transportCode < 0)
8✔
217
                    {
1✔
218
                        throw new FormatException(
1✔
219
                            $"The value for parameter 't' is out of range in server address: '{serverAddress}'.");
1✔
220
                    }
221
                    break;
7✔
222

223
                case "v":
224
                    try
225
                    {
6✔
226
                        bytes = Convert.FromBase64String(value);
6✔
227
                    }
5✔
228
                    catch (FormatException exception)
1✔
229
                    {
1✔
230
                        throw new FormatException(
1✔
231
                            $"Invalid Base64 value in server address: '{serverAddress}'.",
1✔
232
                            exception);
1✔
233
                    }
234
                    break;
5✔
235

236
                default:
237
                    throw new FormatException($"Unknown parameter '{name}' in server address: '{serverAddress}'.");
1✔
238
            }
239
        }
15✔
240

241
        if (transportCode == -1)
6✔
242
        {
1✔
243
            throw new FormatException($"Missing 't' parameter in server address: '{serverAddress}'.");
1✔
244
        }
245
        else if (bytes.Length == 0)
5✔
246
        {
1✔
247
            throw new FormatException($"Missing 'v' parameter in server address: '{serverAddress}'.");
1✔
248
        }
249

250
        return (transportCode, encodingMajor, encodingMinor, bytes);
4✔
251
    }
4✔
252
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc