You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@servicecomb.apache.org by li...@apache.org on 2019/01/14 01:06:15 UTC

[servicecomb-java-chassis] branch master updated: [SCB-1101] support IPv6 address format

This is an automated email from the ASF dual-hosted git repository.

liubao pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/servicecomb-java-chassis.git


The following commit(s) were added to refs/heads/master by this push:
     new a6f9221  [SCB-1101] support IPv6 address format
a6f9221 is described below

commit a6f9221d3e2d43956eac1e9a5e8c8d9a9ead543a
Author: yaohaishi <ya...@huawei.com>
AuthorDate: Thu Jan 10 15:45:56 2019 +0800

    [SCB-1101] support IPv6 address format
---
 .../core/transport/TestAbstractTransport.java      | 22 +++---
 .../foundation/common/net/NetUtils.java            | 80 ++++++++++++++++------
 .../foundation/common/net/TestNetUtils.java        | 78 +++++++++++++++++++--
 .../servicecomb/serviceregistry/RegistryUtils.java |  7 +-
 .../config/ServiceRegistryConfig.java              |  2 +-
 5 files changed, 146 insertions(+), 43 deletions(-)

diff --git a/core/src/test/java/org/apache/servicecomb/core/transport/TestAbstractTransport.java b/core/src/test/java/org/apache/servicecomb/core/transport/TestAbstractTransport.java
index 7490caa..c420f77 100644
--- a/core/src/test/java/org/apache/servicecomb/core/transport/TestAbstractTransport.java
+++ b/core/src/test/java/org/apache/servicecomb/core/transport/TestAbstractTransport.java
@@ -61,12 +61,12 @@ public class TestAbstractTransport {
     }
 
     @Override
-    public boolean init() throws Exception {
+    public boolean init() {
       return true;
     }
 
     @Override
-    public void send(Invocation invocation, AsyncResponse asyncResp) throws Exception {
+    public void send(Invocation invocation, AsyncResponse asyncResp) {
     }
   }
 
@@ -104,7 +104,7 @@ public class TestAbstractTransport {
   }
 
   @Test
-  public void testSetListenAddressWithoutSchemaChineseSpaceOldSC() throws UnsupportedEncodingException {
+  public void testSetListenAddressWithoutSchemaChineseSpaceOldSC() {
     MyAbstractTransport transport = new MyAbstractTransport();
     try {
       transport.setListenAddressWithoutSchema("127.0.0.1:9090", Collections.singletonMap("country", "中 国"));
@@ -120,14 +120,14 @@ public class TestAbstractTransport {
   }
 
   @Test
-  public void testSetListenAddressWithoutSchemaNormalNotEncode() throws UnsupportedEncodingException {
+  public void testSetListenAddressWithoutSchemaNormalNotEncode() {
     MyAbstractTransport transport = new MyAbstractTransport();
     transport.setListenAddressWithoutSchema("127.0.0.1:9090", Collections.singletonMap("country", "chinese"));
     Assert.assertEquals("my://127.0.0.1:9090?country=chinese", transport.getEndpoint().getEndpoint());
   }
 
   @Test
-  public void testSetListenAddressWithoutSchemaAlreadyHaveQuery() throws UnsupportedEncodingException {
+  public void testSetListenAddressWithoutSchemaAlreadyHaveQuery() {
     MyAbstractTransport transport = new MyAbstractTransport();
     transport.setListenAddressWithoutSchema("127.0.0.1:9090?a=aValue",
         Collections.singletonMap("country", "chinese"));
@@ -135,7 +135,7 @@ public class TestAbstractTransport {
   }
 
   @Test
-  public void testMyAbstractTransport() throws Exception {
+  public void testMyAbstractTransport() {
     MyAbstractTransport transport = new MyAbstractTransport();
     transport.setListenAddressWithoutSchema("127.0.0.1:9090");
     Assert.assertEquals("my", transport.getName());
@@ -149,8 +149,8 @@ public class TestAbstractTransport {
     Assert.assertEquals(30000, AbstractTransport.getReqTimeout("sayHi", "hello", "test"));
   }
 
-  @Test(expected = NumberFormatException.class)
-  public void testMyAbstractTransportException(@Mocked TransportManager manager) throws Exception {
+  @Test(expected = IllegalArgumentException.class)
+  public void testMyAbstractTransportException(@Mocked TransportManager manager) {
     MyAbstractTransport transport = new MyAbstractTransport();
 
     transport.setListenAddressWithoutSchema(":127.0.0.1:9090");
@@ -160,7 +160,7 @@ public class TestAbstractTransport {
    * Tests the request call timeout for service level timeout value
    */
   @Test
-  public void testRequestCfgService() throws Exception {
+  public void testRequestCfgService() {
     System.setProperty("servicecomb.request.hello1.timeout", "3000");
     //check for service level timeout value
     Assert.assertEquals(3000, AbstractTransport.getReqTimeout("sayHello1", "sayHelloSchema1", "hello1"));
@@ -171,7 +171,7 @@ public class TestAbstractTransport {
    * Tests the request call timeout for schema level timeout value
    */
   @Test
-  public void testRequestCfgSchema() throws Exception {
+  public void testRequestCfgSchema() {
     System.setProperty("servicecomb.request.hello2.sayHelloSchema2.timeout", "2000");
 
     Assert.assertEquals(2000, AbstractTransport.getReqTimeout("sayHello2", "sayHelloSchema2", "hello2"));
@@ -182,7 +182,7 @@ public class TestAbstractTransport {
    * Tests the request call timeout for operatation level timeout value
    */
   @Test
-  public void testRequestCfgOperation() throws Exception {
+  public void testRequestCfgOperation() {
     System.setProperty("servicecomb.request.hello3.sayHelloSchema3.sayHello3.timeout", "1000");
 
     Assert.assertEquals(1000, AbstractTransport.getReqTimeout("sayHello3", "sayHelloSchema3", "hello3"));
diff --git a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/net/NetUtils.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/net/NetUtils.java
index 30f8e29..fa7d52b 100644
--- a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/net/NetUtils.java
+++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/net/NetUtils.java
@@ -70,6 +70,7 @@ public final class NetUtils {
         hostAddress = availabelAddress.getHostAddress();
         LOGGER.warn("cannot find a proper host address, choose {}, may not be correct.", hostAddress);
       } else {
+        LOGGER.info("get localhost address: {}", localHost.getHostAddress());
         hostAddress = localHost.getHostAddress();
       }
 
@@ -124,32 +125,77 @@ public final class NetUtils {
   }
 
   /**
-   * address ip:port格式
+   * The format of address should be {@code IPv4:port} or {@code [IPv6]:port}, or {@code host:port},
+   * or you will not get expected result.
+   *
+   * Note that the IPv6 address should be wrapped by square brackets.
+   * @return IpPort parsed from input param, or {@code null} if the param is null.
    */
   public static IpPort parseIpPort(String address) {
     if (address == null) {
       return null;
     }
 
-    int idx = address.indexOf(':');
-    if (idx == -1) {
-      return null;
+    URI uri = URI.create("http://" + address);
+    return parseIpPort(uri, true);
+  }
+
+  /**
+   * Parse a {@link URI} into an {@link IpPort}.
+   *
+   * <p>
+   *   A uri without port is allowed, in which case the port will be inferred from the scheme. {@code http} is 80, and
+   *   {@code https} is 443.
+   * </p>
+   * <p>
+   *   The host of the {@code uri} should not be null, or it will be treated as an illegal param,
+   *   and an {@link IllegalArgumentException} will be thrown.
+   * </p>
+   */
+  public static IpPort parseIpPort(URI uri) {
+    return parseIpPort(uri, false);
+  }
+
+  /**
+   * Parse a {@link URI} into an {@link IpPort}
+   * @param uri a uri representing {@link IpPort}
+   * @param ignorePortUndefined whether the port should be inferred from scheme, when the port part of {@code uri} is {@code -1}.
+   * If {@code true} the undefined port is ignored;
+   * otherwise a port will be inferred from scheme: {@code http} is 80, and {@code https} is 443.
+   */
+  public static IpPort parseIpPort(URI uri, boolean ignorePortUndefined) {
+    if (null == uri.getHost()) {
+      // if the format of address is legal but the value is out of range, URI#create(String) will not throw exception
+      // but return a URI with null host.
+      throw new IllegalArgumentException("Illegal uri: [" + uri + "]");
     }
-    String hostOrIp = address.substring(0, idx);
-    int port = Integer.parseInt(address.substring(idx + 1));
 
-    return new IpPort(hostOrIp, port);
+    IpPort ipPort = new IpPort(uri.getHost(), uri.getPort());
+    if (-1 != ipPort.getPort() || ignorePortUndefined) {
+      return ipPort;
+    }
+
+    if (uri.getScheme().equals("http")) {
+      ipPort.setPort(80);
+    }
+    if (uri.getScheme().equals("https")) {
+      ipPort.setPort(443);
+    }
+
+    return ipPort;
   }
 
+  /**
+   * @param uriAddress the address containing IP and port info.
+   * @return IpPort parsed from input param, or {@code null} if the param is null.
+   */
   public static IpPort parseIpPortFromURI(String uriAddress) {
     if (uriAddress == null) {
       return null;
     }
 
     try {
-      URI uri = new URI(uriAddress);
-      String authority = uri.getAuthority();
-      return parseIpPort(uri.getScheme(), authority);
+      return parseIpPort(new URI(uriAddress));
     } catch (URISyntaxException e) {
       return null;
     }
@@ -159,17 +205,7 @@ public final class NetUtils {
     if (authority == null) {
       return null;
     }
-    int idx = authority.indexOf(':');
-    if (idx != -1) {
-      return parseIpPort(authority);
-    }
-    if (scheme.equals("http")) {
-      return new IpPort(authority, 80);
-    }
-    if (scheme.equals("https")) {
-      return new IpPort(authority, 443);
-    }
-    return null;
+    return parseIpPort(URI.create(scheme + "://" + authority));
   }
 
   /**
@@ -184,7 +220,7 @@ public final class NetUtils {
     }
     try {
       URI originalURI = new URI(schema + "://" + address);
-      IpPort ipPort = NetUtils.parseIpPort(originalURI.getAuthority());
+      IpPort ipPort = NetUtils.parseIpPort(originalURI);
       if (ipPort == null) {
         LOGGER.error("address {} is not valid.", address);
         return null;
diff --git a/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/net/TestNetUtils.java b/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/net/TestNetUtils.java
index 15e157f..a363747 100644
--- a/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/net/TestNetUtils.java
+++ b/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/net/TestNetUtils.java
@@ -17,11 +17,14 @@
 
 package org.apache.servicecomb.foundation.common.net;
 
+import static org.junit.Assert.fail;
+
 import java.io.IOException;
 import java.net.InetAddress;
 import java.net.ServerSocket;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.function.Consumer;
 
 import org.junit.Assert;
 import org.junit.Test;
@@ -41,18 +44,69 @@ public class TestNetUtils {
   }
 
   @Test
-  public void testNetutils() {
+  public void testNetUtils() {
     Assert.assertEquals("127.0.0.1", NetUtils.parseIpPort("127.0.0.1:8080").getHostOrIp());
     Assert.assertEquals(8080, NetUtils.parseIpPort("127.0.0.1:8080").getPort());
-    Assert.assertEquals(null, NetUtils.parseIpPort(null));
-    Assert.assertEquals(null, NetUtils.parseIpPort("127.0.0.18080"));
+    Assert.assertEquals("127.0.0.1", NetUtils.parseIpPort("127.0.0.1").getHostOrIp());
+    Assert.assertEquals(-1, NetUtils.parseIpPort("127.0.0.1").getPort());
+    Assert.assertEquals(null, NetUtils.parseIpPort((String) null));
     Assert.assertEquals(null, NetUtils.parseIpPortFromURI(null));
-    Assert.assertEquals(null, NetUtils.parseIpPortFromURI("ss"));
     Assert.assertEquals("127.0.0.1", NetUtils.parseIpPortFromURI("rest://127.0.0.1:8080").getHostOrIp());
     Assert.assertEquals(8080, NetUtils.parseIpPortFromURI("http://127.0.0.1:8080").getPort());
     Assert.assertEquals(80, NetUtils.parseIpPortFromURI("http://127.0.0.1").getPort());
     Assert.assertEquals(8080, NetUtils.parseIpPortFromURI("https://127.0.0.1:8080").getPort());
     Assert.assertEquals(443, NetUtils.parseIpPortFromURI("https://127.0.0.1").getPort());
+
+    Assert.assertEquals(30000, NetUtils.parseIpPort("http", "127.0.0.1:30000").getPort());
+    Assert.assertEquals("127.0.0.1", NetUtils.parseIpPort("http", "127.0.0.1:30000").getHostOrIp());
+    Assert.assertEquals(30000, NetUtils.parseIpPort("https", "127.0.0.1:30000").getPort());
+    Assert.assertEquals("127.0.0.1", NetUtils.parseIpPort("https", "127.0.0.1:30000").getHostOrIp());
+    Assert.assertEquals(80, NetUtils.parseIpPort("http", "127.0.0.1").getPort());
+    Assert.assertEquals("127.0.0.1", NetUtils.parseIpPort("http", "127.0.0.1").getHostOrIp());
+    Assert.assertEquals(443, NetUtils.parseIpPort("https", "127.0.0.1").getPort());
+    Assert.assertEquals("127.0.0.1", NetUtils.parseIpPort("https", "127.0.0.1").getHostOrIp());
+    Assert.assertNull(NetUtils.parseIpPort("http", null));
+
+    checkException(v -> NetUtils.parseIpPort("127.0.0.18080"));
+    checkException(v -> NetUtils.parseIpPortFromURI("ss"));
+  }
+
+  @Test
+  public void testNetUtilsIPv6() {
+    Assert.assertEquals("[::1]", NetUtils.parseIpPort("[::1]:8080").getHostOrIp());
+    Assert.assertEquals("[::]", NetUtils.parseIpPort("[::]:8080").getHostOrIp());
+    Assert.assertEquals("[fe80::f816:3eff:feda:38cd%eth0]",
+        NetUtils.parseIpPort("[fe80::f816:3eff:feda:38cd%eth0]:8080").getHostOrIp());
+    Assert.assertEquals("[fe80::38f7:44b8:8ab1:468%16]",
+        NetUtils.parseIpPort("[fe80::38f7:44b8:8ab1:468%16]:8080").getHostOrIp());
+    Assert.assertEquals(8080, NetUtils.parseIpPort("[::1]:8080").getPort());
+    Assert.assertEquals(8080, NetUtils.parseIpPort("[::]:8080").getPort());
+    Assert.assertEquals(8080, NetUtils.parseIpPort("[fe80::f816:3eff:feda:38cd%eth0]:8080").getPort());
+    Assert.assertEquals(8080, NetUtils.parseIpPort("[fe80::38f7:44b8:8ab1:468%16]:8080").getPort());
+
+    Assert.assertEquals("[::1]", NetUtils.parseIpPortFromURI("rest://[::1]:8080").getHostOrIp());
+    Assert.assertEquals("[::]", NetUtils.parseIpPortFromURI("rest://[::]:8080").getHostOrIp());
+    Assert.assertEquals("[fe80::f816:3eff:feda:38cd%eth0]",
+        NetUtils.parseIpPortFromURI("rest://[fe80::f816:3eff:feda:38cd%eth0]:8080").getHostOrIp());
+    Assert.assertEquals("[fe80::38f7:44b8:8ab1:468%16]",
+        NetUtils.parseIpPortFromURI("rest://[fe80::38f7:44b8:8ab1:468%16]:8080").getHostOrIp());
+    Assert.assertEquals(8080, NetUtils.parseIpPortFromURI("rest://[::1]:8080").getPort());
+    Assert.assertEquals(80, NetUtils.parseIpPortFromURI("http://[::1]").getPort());
+    Assert.assertEquals(8080, NetUtils.parseIpPortFromURI("https://[::1]:8080").getPort());
+    Assert.assertEquals(443, NetUtils.parseIpPortFromURI("https://[::1]").getPort());
+
+    Assert.assertEquals(30000, NetUtils.parseIpPort("http", "[fe80::f816:3eff:feda:38cd%eth0]:30000").getPort());
+    Assert.assertEquals("[fe80::f816:3eff:feda:38cd%eth0]",
+        NetUtils.parseIpPort("http", "[fe80::f816:3eff:feda:38cd%eth0]:30000").getHostOrIp());
+    Assert.assertEquals(30000, NetUtils.parseIpPort("https", "[fe80::f816:3eff:feda:38cd%eth0]:30000").getPort());
+    Assert.assertEquals("[fe80::f816:3eff:feda:38cd%eth0]",
+        NetUtils.parseIpPort("https", "[fe80::f816:3eff:feda:38cd%eth0]:30000").getHostOrIp());
+    Assert.assertEquals(80, NetUtils.parseIpPort("http", "[fe80::f816:3eff:feda:38cd%eth0]").getPort());
+    Assert.assertEquals("[fe80::f816:3eff:feda:38cd%eth0]",
+        NetUtils.parseIpPort("http", "[fe80::f816:3eff:feda:38cd%eth0]").getHostOrIp());
+    Assert.assertEquals(443, NetUtils.parseIpPort("https", "[fe80::f816:3eff:feda:38cd%eth0]").getPort());
+    Assert.assertEquals("[fe80::f816:3eff:feda:38cd%eth0]",
+        NetUtils.parseIpPort("https", "[fe80::f816:3eff:feda:38cd%eth0]").getHostOrIp());
   }
 
   @Test
@@ -64,8 +118,9 @@ public class TestNetUtils {
   @Test
   public void testGetRealListenAddress() {
     Assert.assertNull(NetUtils.getRealListenAddress("http", null));
-    Assert.assertNull(NetUtils.getRealListenAddress("http:1", "1.1.1.1:8080"));
     Assert.assertEquals("http://1.1.1.1:8080", NetUtils.getRealListenAddress("http", "1.1.1.1:8080"));
+
+    checkException(v -> NetUtils.getRealListenAddress("http:1", "1.1.1.1:8080"));
   }
 
   @Test
@@ -82,7 +137,7 @@ public class TestNetUtils {
 
     try {
       NetUtils.ensureGetInterfaceAddress("xxx");
-      Assert.fail("must throw exception");
+      fail("must throw exception");
     } catch (IllegalArgumentException e) {
       Assert.assertEquals("Can not find address for interface name: xxx", e.getMessage());
     }
@@ -109,7 +164,7 @@ public class TestNetUtils {
   }
 
   @Test
-  public void humanReadableBytes() throws IOException {
+  public void humanReadableBytes() {
     Assert.assertEquals("0", NetUtils.humanReadableBytes(0L));
     Assert.assertEquals("1", NetUtils.humanReadableBytes(1L));
     Assert.assertEquals("1023", NetUtils.humanReadableBytes(1023L));
@@ -158,4 +213,13 @@ public class TestNetUtils {
     Deencapsulation.setField(NetUtils.class, "hostAddress", null);
     Assert.assertNotEquals(null, NetUtils.getHostAddress());
   }
+
+  public void checkException(Consumer<Void> testedBehavior) {
+    try {
+      testedBehavior.accept(null);
+      fail("IllegalArgumentException is expected!");
+    } catch (Exception e) {
+      Assert.assertEquals(IllegalArgumentException.class, e.getClass());
+    }
+  }
 }
diff --git a/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/RegistryUtils.java b/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/RegistryUtils.java
index 1853578..6787a28 100644
--- a/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/RegistryUtils.java
+++ b/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/RegistryUtils.java
@@ -146,7 +146,10 @@ public final class RegistryUtils {
   }
 
   /**
-   * 对于配置为0.0.0.0的地址,通过查询网卡地址,转换为实际监听的地址。
+   * In the case that listening address configured as 0.0.0.0, the publish address will be determined
+   * by the query result for the net interfaces.
+   *
+   * @return the publish address, or {@code null} if the param {@code address} is null.
    */
   public static String getPublishAddress(String schema, String address) {
     if (address == null) {
@@ -155,7 +158,7 @@ public final class RegistryUtils {
 
     try {
       URI originalURI = new URI(schema + "://" + address);
-      IpPort ipPort = NetUtils.parseIpPort(originalURI.getAuthority());
+      IpPort ipPort = NetUtils.parseIpPort(originalURI);
       if (ipPort == null) {
         LOGGER.warn("address {} not valid.", address);
         return null;
diff --git a/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/config/ServiceRegistryConfig.java b/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/config/ServiceRegistryConfig.java
index c4eccdf..98e126d 100644
--- a/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/config/ServiceRegistryConfig.java
+++ b/service-registry/src/main/java/org/apache/servicecomb/serviceregistry/config/ServiceRegistryConfig.java
@@ -137,7 +137,7 @@ public final class ServiceRegistryConfig {
       try {
         URI uri = new URI(anUriList.trim());
         this.ssl = "https".equals(uri.getScheme());
-        ipPortList.add(NetUtils.parseIpPort(uri.getScheme(), uri.getAuthority()));
+        ipPortList.add(NetUtils.parseIpPort(uri));
       } catch (Exception e) {
         LOGGER.error("servicecomb.service.registry.address invalid : {}", anUriList, e);
       }