Skip to content

Commit

Permalink
[SCB-1101] support IPv6 address format
Browse files Browse the repository at this point in the history
  • Loading branch information
yhs0092 authored and liubao68 committed Jan 14, 2019
1 parent 0139312 commit a6f9221
Show file tree
Hide file tree
Showing 5 changed files with 146 additions and 43 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
}
}

Expand Down Expand Up @@ -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", "中 国"));
Expand All @@ -120,22 +120,22 @@ 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"));
Assert.assertEquals("my://127.0.0.1:9090?a=aValue&country=chinese", transport.getEndpoint().getEndpoint());
}

@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());
Expand All @@ -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");
Expand All @@ -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"));
Expand All @@ -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"));
Expand All @@ -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"));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}

Expand Down Expand Up @@ -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}.
*
* <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;
}
Expand All @@ -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));
}

/**
Expand All @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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());
}
Expand All @@ -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));
Expand Down Expand Up @@ -158,4 +213,13 @@ public void testGetHostAddress() {
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());
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ public ArrayList<IpPort> 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);
}
Expand Down

0 comments on commit a6f9221

Please sign in to comment.