2222import java .net .InetAddress ;
2323import java .net .InetSocketAddress ;
2424import java .net .Socket ;
25- import java .net .URI ;
26- import java .net .UnknownHostException ;
25+ import java .util .regex .Pattern ;
2726
2827/**
2928 * Utility methods for network, internal to Dapr SDK.
@@ -32,13 +31,56 @@ public final class NetworkUtils {
3231
3332 private static final long RETRY_WAIT_MILLISECONDS = 1000 ;
3433
34+ // Thanks to https://ihateregex.io/expr/ipv6/
35+ private static final String IPV6_REGEX = "(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|"
36+ + "([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|"
37+ + "([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|"
38+ + "([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|"
39+ + "([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|"
40+ + ":((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|"
41+ + "::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\\ .){3,3}(25[0-5]|(2[0-4]|"
42+ + "1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|"
43+ + "(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\\ .){3,3}(25[0-5]|"
44+ + "(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))" ;
45+
46+ private static final Pattern IPV6_PATTERN = Pattern .compile (IPV6_REGEX , Pattern .CASE_INSENSITIVE );
47+
48+ // Don't accept "?" to avoid ambiguity with ?tls=true
49+ private static final String GRPC_ENDPOINT_FILENAME_REGEX_PART = "[^\0 \\ ?]+" ;
50+
51+ private static final String GRPC_ENDPOINT_HOSTNAME_REGEX_PART = "(([A-Za-z0-9_\\ -\\ .]+)|(\\ [" + IPV6_REGEX + "\\ ]))" ;
52+
53+ private static final String GRPC_ENDPOINT_DNS_AUTHORITY_REGEX_PART =
54+ "(?<dnsWithAuthority>dns://)(?<authorityEndpoint>" + GRPC_ENDPOINT_HOSTNAME_REGEX_PART + ":[0-9]+)?/" ;
55+
56+ private static final String GRPC_ENDPOINT_PARAM_REGEX_PART = "(\\ ?(?<param>tls\\ =((true)|(false))))?" ;
57+
58+ private static final String GRPC_ENDPOINT_SOCKET_REGEX_PART =
59+ "(?<socket>((unix:)|(unix://)|(unix-abstract:))" + GRPC_ENDPOINT_FILENAME_REGEX_PART + ")" ;
60+
61+ private static final String GRPC_ENDPOINT_VSOCKET_REGEX_PART =
62+ "(?<vsocket>vsock:" + GRPC_ENDPOINT_HOSTNAME_REGEX_PART + ":[0-9]+)" ;
63+ private static final String GRPC_ENDPOINT_HOST_REGEX_PART =
64+ "((?<http>http://)|(?<https>https://)|(?<dns>dns:)|(" + GRPC_ENDPOINT_DNS_AUTHORITY_REGEX_PART + "))?"
65+ + "(?<hostname>" + GRPC_ENDPOINT_HOSTNAME_REGEX_PART + ")?+"
66+ + "(:(?<port>[0-9]+))?" ;
67+
68+ private static final String GRPC_ENDPOINT_REGEX = "^("
69+ + "(" + GRPC_ENDPOINT_HOST_REGEX_PART + ")|"
70+ + "(" + GRPC_ENDPOINT_SOCKET_REGEX_PART + ")|"
71+ + "(" + GRPC_ENDPOINT_VSOCKET_REGEX_PART + ")"
72+ + ")" + GRPC_ENDPOINT_PARAM_REGEX_PART + "$" ;
73+
74+ private static final Pattern GRPC_ENDPOINT_PATTERN = Pattern .compile (GRPC_ENDPOINT_REGEX , Pattern .CASE_INSENSITIVE );
75+
3576 private NetworkUtils () {
3677 }
3778
3879 /**
3980 * Tries to connect to a socket, retrying every 1 second.
40- * @param host Host to connect to.
41- * @param port Port to connect to.
81+ *
82+ * @param host Host to connect to.
83+ * @param port Port to connect to.
4284 * @param timeoutInMilliseconds Timeout in milliseconds to give up trying.
4385 * @throws InterruptedException If retry is interrupted.
4486 */
@@ -60,26 +102,15 @@ public static void waitForSocket(String host, int port, int timeoutInMillisecond
60102
61103 /**
62104 * Creates a GRPC managed channel.
105+ *
63106 * @param interceptors Optional interceptors to add to the channel.
64107 * @return GRPC managed channel to communicate with the sidecar.
65108 */
66109 public static ManagedChannel buildGrpcManagedChannel (ClientInterceptor ... interceptors ) {
67- String address = Properties .SIDECAR_IP .get ();
68- int port = Properties .GRPC_PORT .get ();
69- boolean insecure = true ;
70- String grpcEndpoint = Properties .GRPC_ENDPOINT .get ();
71- if ((grpcEndpoint != null ) && !grpcEndpoint .isEmpty ()) {
72- URI uri = URI .create (grpcEndpoint );
73- insecure = uri .getScheme ().equalsIgnoreCase ("http" );
74- port = uri .getPort () > 0 ? uri .getPort () : (insecure ? 80 : 443 );
75- address = uri .getHost ();
76- if ((uri .getPath () != null ) && !uri .getPath ().isEmpty ()) {
77- address += uri .getPath ();
78- }
79- }
80- ManagedChannelBuilder <?> builder = ManagedChannelBuilder .forAddress (address , port )
110+ var settings = GrpcEndpointSettings .parse ();
111+ ManagedChannelBuilder <?> builder = ManagedChannelBuilder .forTarget (settings .endpoint )
81112 .userAgent (Version .getSdkVersion ());
82- if (insecure ) {
113+ if (! settings . secure ) {
83114 builder = builder .usePlaintext ();
84115 }
85116 if (interceptors != null && interceptors .length > 0 ) {
@@ -88,6 +119,73 @@ public static ManagedChannel buildGrpcManagedChannel(ClientInterceptor... interc
88119 return builder .build ();
89120 }
90121
122+ // Not private to allow unit testing
123+ static final class GrpcEndpointSettings {
124+ final String endpoint ;
125+ final boolean secure ;
126+
127+ private GrpcEndpointSettings (String endpoint , boolean secure ) {
128+ this .endpoint = endpoint ;
129+ this .secure = secure ;
130+ }
131+
132+ static GrpcEndpointSettings parse () {
133+ String address = Properties .SIDECAR_IP .get ();
134+ int port = Properties .GRPC_PORT .get ();
135+ boolean secure = false ;
136+ String grpcEndpoint = Properties .GRPC_ENDPOINT .get ();
137+ if ((grpcEndpoint != null ) && !grpcEndpoint .isEmpty ()) {
138+ var matcher = GRPC_ENDPOINT_PATTERN .matcher (grpcEndpoint );
139+ if (!matcher .matches ()) {
140+ throw new IllegalArgumentException ("Illegal gRPC endpoint: " + grpcEndpoint );
141+ }
142+ var parsedHost = matcher .group ("hostname" );
143+ if (parsedHost != null ) {
144+ address = parsedHost ;
145+ }
146+
147+ var https = matcher .group ("https" ) != null ;
148+ var http = matcher .group ("http" ) != null ;
149+ secure = https ;
150+
151+ String parsedPort = matcher .group ("port" );
152+ if (parsedPort != null ) {
153+ port = Integer .parseInt (parsedPort );
154+ } else {
155+ // This implements default port as 80 for http for backwards compatibility.
156+ port = http ? 80 : 443 ;
157+ }
158+
159+ String parsedParam = matcher .group ("param" );
160+ if ((http || https ) && (parsedParam != null )) {
161+ throw new IllegalArgumentException ("Query params is not supported in HTTP URI for gRPC endpoint." );
162+ }
163+
164+ if (parsedParam != null ) {
165+ secure = parsedParam .equalsIgnoreCase ("tls=true" );
166+ }
167+
168+ var authorityEndpoint = matcher .group ("authorityEndpoint" );
169+ if (authorityEndpoint != null ) {
170+ return new GrpcEndpointSettings (String .format ("dns://%s/%s:%d" , authorityEndpoint , address , port ), secure );
171+ }
172+
173+ var socket = matcher .group ("socket" );
174+ if (socket != null ) {
175+ return new GrpcEndpointSettings (socket , secure );
176+ }
177+
178+ var vsocket = matcher .group ("vsocket" );
179+ if (vsocket != null ) {
180+ return new GrpcEndpointSettings (vsocket , secure );
181+ }
182+ }
183+
184+ return new GrpcEndpointSettings (String .format ("dns:///%s:%d" , address , port ), secure );
185+ }
186+
187+ }
188+
91189 private static void callWithRetry (Runnable function , long retryTimeoutMilliseconds ) throws InterruptedException {
92190 long started = System .currentTimeMillis ();
93191 while (true ) {
@@ -104,7 +202,7 @@ private static void callWithRetry(Runnable function, long retryTimeoutMillisecon
104202 long elapsed = System .currentTimeMillis () - started ;
105203 if (elapsed >= retryTimeoutMilliseconds ) {
106204 if (exception instanceof RuntimeException ) {
107- throw (RuntimeException )exception ;
205+ throw (RuntimeException ) exception ;
108206 }
109207
110208 throw new RuntimeException (exception );
@@ -117,9 +215,14 @@ private static void callWithRetry(Runnable function, long retryTimeoutMillisecon
117215
118216 /**
119217 * Retrieve loopback address for the host.
218+ *
120219 * @return The loopback address String
121220 */
122221 public static String getHostLoopbackAddress () {
123222 return InetAddress .getLoopbackAddress ().getHostAddress ();
124223 }
224+
225+ static boolean isIPv6 (String ip ) {
226+ return IPV6_PATTERN .matcher (ip ).matches ();
227+ }
125228}
0 commit comments