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 7490caa998..c420f770b7 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 String getName() { } @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 void testSetListenAddressWithoutSchemaChineseSpaceNewSC() throws Unsuppor } @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 void testSetListenAddressWithoutSchemaChineseSpaceOldSC() throws Unsuppor } @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 void testSetListenAddressWithoutSchemaAlreadyHaveQuery() throws Unsupport } @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 void testMyAbstractTransport() throws Exception { 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 void testMyAbstractTransportException(@Mocked TransportManager manager) t * 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 void testRequestCfgService() throws Exception { * 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 void testRequestCfgSchema() throws Exception { * 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 30f8e29db3..fa7d52b728 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 @@ private static void doGetHostNameAndHostAddress() { 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 @@ private static void doGetIpv4AddressFromNetworkInterface() throws SocketExceptio } /** - * 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}. + * + *

+ * 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. + *

+ *

+ * 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. + *

+ */ + 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 static IpPort parseIpPort(String scheme, String authority) { 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 static String getRealListenAddress(String schema, String address) { } 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 15e157fc8e..a363747bc2 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 void testIpPort() { } @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 void testFullOperation() { @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 void testNetworkInterface() { 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 void testCanTcpListenYes() throws IOException { } @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 void testGetHostAddress() { Deencapsulation.setField(NetUtils.class, "hostAddress", null); Assert.assertNotEquals(null, NetUtils.getHostAddress()); } + + public void checkException(Consumer 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 1853578331..6787a28055 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 static String getPublishHostName() { } /** - * 对于配置为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 static String getPublishAddress(String schema, String address) { 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 c4eccdfe3c..98e126d9b7 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 ArrayList getIpPort() { 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); }