2
2
// Licensed under the MIT license.
3
3
4
4
using System ;
5
+ using System . Collections . Generic ;
5
6
using System . Diagnostics . CodeAnalysis ;
7
+ using System . Linq ;
6
8
using System . Net ;
7
9
using System . Net . Sockets ;
8
10
using System . Threading . Tasks ;
@@ -31,116 +33,109 @@ internal static bool IsNullOrWhiteSpace([NotNullWhen(false)] this string s) =>
31
33
#pragma warning disable format
32
34
public static class Format
33
35
{
36
+ /// <summary>
37
+ /// Parse address list string containing address separated by whitespace
38
+ /// </summary>
39
+ /// <param name="addressList">Space separated string of IP addresses</param>
40
+ /// <param name="port">Endpoint Port</param>
41
+ /// <param name="endpoints">List of endpoints generated from the input IPs</param>
42
+ /// <param name="errorHostnameOrAddress">Output error if any</param>
43
+ /// <param name="logger">Logger</param>
44
+ /// <returns>True if parse and address validation was successful, otherwise false</returns>
45
+ public static bool TryParseAddressList ( string addressList , int port , out EndPoint [ ] endpoints , out string errorHostnameOrAddress , ILogger logger = null )
46
+ {
47
+ endpoints = null ;
48
+ errorHostnameOrAddress = null ;
49
+ // Check if input null or empty
50
+ if ( string . IsNullOrEmpty ( addressList ) || string . IsNullOrWhiteSpace ( addressList ) )
51
+ {
52
+ endpoints = [ new IPEndPoint ( IPAddress . Any , port ) ] ;
53
+ return true ;
54
+ }
55
+
56
+ var addresses = addressList . Split ( [ ',' , ' ' ] , StringSplitOptions . TrimEntries | StringSplitOptions . RemoveEmptyEntries ) ;
57
+ var endpointList = new List < EndPoint > ( ) ;
58
+ // Validate addresses and create endpoints
59
+ foreach ( var singleAddressOrHostname in addresses )
60
+ {
61
+ var e = TryCreateEndpoint ( singleAddressOrHostname , port , tryConnect : false , logger ) . Result ;
62
+ if ( e == null )
63
+ {
64
+ endpoints = null ;
65
+ errorHostnameOrAddress = singleAddressOrHostname ;
66
+ return false ;
67
+ }
68
+ endpointList . AddRange ( e ) ;
69
+ }
70
+ endpoints = [ .. endpointList ] ;
71
+
72
+ return true ;
73
+ }
74
+
34
75
/// <summary>
35
76
/// Try to create an endpoint from address and port
36
77
/// </summary>
37
- /// <param name="addressOrHostname ">This could be an address or a hostname that the method tries to resolve</param>
38
- /// <param name="port"></param>
39
- /// <param name="useForBind">Binding does not poll connection because is supposed to be called from the server side </param>
40
- /// <param name="logger"></param>
78
+ /// <param name="singleAddressOrHostname ">This could be an address or a hostname that the method tries to resolve</param>
79
+ /// <param name="port">Port number to use for the endpoints </param>
80
+ /// <param name="tryConnect">Whether to try to connect to the created endpoints to ensure that it is reachable </param>
81
+ /// <param name="logger">Logger </param>
41
82
/// <returns></returns>
42
- public static async Task < EndPoint > TryCreateEndpoint ( string addressOrHostname , int port , bool useForBind = false , ILogger logger = null )
83
+ public static async Task < EndPoint [ ] > TryCreateEndpoint ( string singleAddressOrHostname , int port , bool tryConnect = false , ILogger logger = null )
43
84
{
44
- IPEndPoint endpoint = null ;
45
- if ( string . IsNullOrEmpty ( addressOrHostname ) || string . IsNullOrWhiteSpace ( addressOrHostname ) )
46
- return new IPEndPoint ( IPAddress . Any , port ) ;
85
+ if ( string . IsNullOrEmpty ( singleAddressOrHostname ) || string . IsNullOrWhiteSpace ( singleAddressOrHostname ) )
86
+ return [ new IPEndPoint ( IPAddress . Any , port ) ] ;
87
+
88
+ if ( singleAddressOrHostname [ 0 ] == '-' )
89
+ singleAddressOrHostname = singleAddressOrHostname . Substring ( 1 ) ;
47
90
48
- if ( IPAddress . TryParse ( addressOrHostname , out var ipAddress ) )
49
- return new IPEndPoint ( ipAddress , port ) ;
91
+ if ( singleAddressOrHostname . Equals ( "localhost" , StringComparison . CurrentCultureIgnoreCase ) )
92
+ return [ new IPEndPoint ( IPAddress . Loopback , port ) ] ;
93
+
94
+ if ( IPAddress . TryParse ( singleAddressOrHostname , out var ipAddress ) )
95
+ return [ new IPEndPoint ( ipAddress , port ) ] ;
50
96
51
97
// Sanity check, there should be at least one ip address available
52
98
try
53
99
{
54
- var ipAddresses = Dns . GetHostAddresses ( addressOrHostname ) ;
100
+ var ipAddresses = Dns . GetHostAddresses ( singleAddressOrHostname ) ;
55
101
if ( ipAddresses . Length == 0 )
56
102
{
57
- logger ? . LogError ( "No IP address found for hostname:{hostname}" , addressOrHostname ) ;
103
+ logger ? . LogError ( "No IP address found for hostname:{hostname}" , singleAddressOrHostname ) ;
58
104
return null ;
59
105
}
60
106
61
- if ( useForBind )
107
+ if ( tryConnect )
62
108
{
63
109
foreach ( var entry in ipAddresses )
64
110
{
65
- endpoint = new IPEndPoint ( entry , port ) ;
66
- var IsListening = await IsReachable ( endpoint ) ;
67
- if ( IsListening ) break ;
111
+ var endpoint = new IPEndPoint ( entry , port ) ;
112
+ var IsListening = await TryConnect ( endpoint ) ;
113
+ if ( IsListening ) return [ endpoint ] ;
68
114
}
69
115
}
70
116
else
71
117
{
72
118
var machineHostname = GetHostName ( ) ;
73
119
74
- // Hostname does match the one acquired from machine name
75
- if ( ! addressOrHostname . Equals ( machineHostname , StringComparison . OrdinalIgnoreCase ) )
120
+ // User-provided hostname does not match the machine hostname
121
+ if ( ! singleAddressOrHostname . Equals ( machineHostname , StringComparison . OrdinalIgnoreCase ) )
76
122
{
77
- logger ? . LogError ( "Provided hostname does not much acquired machine name {addressOrHostname} {machineHostname}!" , addressOrHostname , machineHostname ) ;
78
- return null ;
79
- }
80
-
81
- if ( ipAddresses . Length > 1 ) {
82
- logger ? . LogError ( "Error hostname resolved to multiple endpoints. Garnet does not support multiple endpoints!" ) ;
123
+ logger ? . LogError ( "Provided hostname does not much acquired machine name {addressOrHostname} {machineHostname}!" , singleAddressOrHostname , machineHostname ) ;
83
124
return null ;
84
125
}
85
126
86
- return new IPEndPoint ( ipAddresses [ 0 ] , port ) ;
127
+ return ipAddresses . Select ( ip => new IPEndPoint ( ip , port ) ) . ToArray ( ) ;
87
128
}
88
- logger ? . LogError ( "No reachable IP address found for hostname:{hostname}" , addressOrHostname ) ;
129
+ logger ? . LogError ( "No reachable IP address found for hostname:{hostname}" , singleAddressOrHostname ) ;
89
130
}
90
131
catch ( Exception ex )
91
132
{
92
- logger ? . LogError ( ex , "Error while trying to resolve hostname:{hostname}" , addressOrHostname ) ;
93
- }
94
-
95
- return endpoint ;
96
-
97
- async Task < bool > IsReachable ( IPEndPoint endpoint )
98
- {
99
- using ( var tcpClient = new TcpClient ( ) )
100
- {
101
- try
102
- {
103
- await tcpClient . ConnectAsync ( endpoint . Address , endpoint . Port ) ;
104
- logger ? . LogTrace ( "Reachable {ip} {port}" , endpoint . Address , endpoint . Port ) ;
105
- return true ;
106
- }
107
- catch
108
- {
109
- logger ? . LogTrace ( "Unreachable {ip} {port}" , endpoint . Address , endpoint . Port ) ;
110
- return false ;
111
- }
112
- }
133
+ logger ? . LogError ( "Error while trying to resolve hostname: {exMessage} [{hostname}]" , ex . Message , singleAddressOrHostname ) ;
113
134
}
114
- }
115
135
116
- /// <summary>
117
- /// Try to
118
- /// </summary>
119
- /// <param name="address"></param>
120
- /// <param name="port"></param>
121
- /// <param name="logger"></param>
122
- /// <returns></returns>
123
- public static async Task < IPEndPoint > TryValidateAndConnectAddress2 ( string address , int port , ILogger logger = null )
124
- {
125
- IPEndPoint endpoint = null ;
126
- if ( ! IPAddress . TryParse ( address , out var ipAddress ) )
127
- {
128
- // Try to identify reachable IP address from hostname
129
- var hostEntry = Dns . GetHostEntry ( address ) ;
130
- foreach ( var entry in hostEntry . AddressList )
131
- {
132
- endpoint = new IPEndPoint ( entry , port ) ;
133
- var IsListening = await IsReachable ( endpoint ) ;
134
- if ( IsListening ) break ;
135
- }
136
- }
137
- else
138
- {
139
- // If address is valid create endpoint
140
- endpoint = new IPEndPoint ( ipAddress , port ) ;
141
- }
136
+ return null ;
142
137
143
- async Task < bool > IsReachable ( IPEndPoint endpoint )
138
+ async Task < bool > TryConnect ( IPEndPoint endpoint )
144
139
{
145
140
using ( var tcpClient = new TcpClient ( ) )
146
141
{
@@ -157,8 +152,6 @@ async Task<bool> IsReachable(IPEndPoint endpoint)
157
152
}
158
153
}
159
154
}
160
-
161
- return endpoint ;
162
155
}
163
156
164
157
/// <summary>
0 commit comments