Ported FaceUri from ndn-cxx
Change-Id: Ie88eebcce0f27c80ee96d27aac04e152bf5f1ef0
diff --git a/src/main/java/net/named_data/jndn_xx/util/FaceUri.java b/src/main/java/net/named_data/jndn_xx/util/FaceUri.java
new file mode 100644
index 0000000..f05b27d
--- /dev/null
+++ b/src/main/java/net/named_data/jndn_xx/util/FaceUri.java
@@ -0,0 +1,456 @@
+/* -*- Mode:jde; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2015 Alexander Afanasyev.
+ *
+ * This file is part of jndn-xx library.
+ *
+ * jndn-xx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * jndn-xx library is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with jndn-xx, e.g., in COPYING.md file. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of jndn-xx authors and contributors.
+ */
+
+package net.named_data.jndn_xx.util;
+
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import com.google.common.net.InetAddresses;
+
+/**
+ * Represents the underlying protocol and address used by Face
+ */
+public class FaceUri implements Cloneable {
+ public static class Error extends IllegalArgumentException {
+ Error(String error) {
+ super(error);
+ }
+ }
+
+ public static class CanonizeError extends Exception {
+ CanonizeError(String error) {
+ super(error);
+ }
+ }
+
+ //////////////////////////////////////////////////////////////////////////////
+
+ public FaceUri()
+ {
+ }
+
+ /**
+ * Construct FaceUri from string
+ * @param uri scheme://host[:port]/path
+ * @throws Error if URI cannot be parsed
+ */
+ public FaceUri(String uri) throws Error {
+ if (!parse(uri)) {
+ throw new Error("Malformed URI: " + uri);
+ }
+ }
+
+ /**
+ * Exception-safe parsing
+ * @param uri FaceUri to parse
+ * @return true if <pre>uri</pre> is successfully parsed
+ */
+ public boolean
+ parse(String uri) {
+ m_scheme = "";
+ m_host = "";
+ m_isV6 = false;
+ m_port = "";
+ m_path = "";
+
+ Pattern protocolExp = Pattern.compile("(\\w+\\d?)://([^/]*)(\\/[^?]*)?");
+ Matcher protocolMatch = protocolExp.matcher(uri);
+ if (!protocolMatch.matches()) {
+ return false;
+ }
+ m_scheme = protocolMatch.group(1);
+ if (m_scheme == null)
+ m_scheme = "";
+ String authority = protocolMatch.group(2);
+ if (authority == null)
+ authority = "";
+ m_path = protocolMatch.group(3);
+ if (m_path == null)
+ m_path = "";
+
+ // pattern for IPv6 address enclosed in [ ], with optional port number
+ final Pattern v6Exp = Pattern.compile("^\\[([a-fA-F0-9:]+)\\](?:\\:(\\d+))?$");
+ // pattern for Ethernet address in standard hex-digits-and-colons notation
+ final Pattern etherExp = Pattern.compile("^\\[((?:[a-fA-F0-9]{1,2}\\:){5}(?:[a-fA-F0-9]{1,2}))\\]$");
+ // pattern for IPv4-mapped IPv6 address, with optional port number
+ final Pattern v4MappedV6Exp = Pattern.compile("^\\[::ffff:(\\d+(?:\\.\\d+){3})\\](?:\\:(\\d+))?$");
+ // pattern for IPv4/hostname/fd/ifname, with optional port number
+ final Pattern v4HostExp = Pattern.compile("^([^:]+)(?:\\:(\\d+))?$");
+
+ if (authority.equals("")) {
+ // UNIX, internal
+ } else {
+ Matcher match = v6Exp.matcher(authority);
+ m_isV6 = match.matches();
+ if (m_isV6 ||
+ (match = etherExp.matcher(authority)).matches() ||
+ (match = v4MappedV6Exp.matcher(authority)).matches() ||
+ (match = v4HostExp.matcher(authority)).matches()) {
+ m_host = match.group(1);
+ if (m_host == null)
+ m_host = "";
+ m_port = match.group(2);
+ if (m_port == null)
+ m_port = "";
+ } else {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ //////////////////////////////////////////////////////////////////////////////
+ // getters
+
+ /**
+ * Get scheme (protocol)
+ * @return scheme (return "" when empty)
+ */
+ public String
+ getScheme() {
+ return m_scheme;
+ }
+
+ /**
+ * Get host (domain)
+ * @return host (return "" when empty)
+ */
+ public String
+ getHost() {
+ return m_host;
+ }
+
+ /**
+ * Get port
+ * @return port number (return "" when empty)
+ */
+ public String
+ getPort() {
+ return m_port;
+ }
+
+ /**
+ * Get path
+ * @return path (return "" when empty)
+ */
+ public String
+ getPath() {
+ return m_path;
+ }
+
+ /**
+ * Convert FaceUri instance to a string
+ * @return string represenation of FaceUri
+ */
+ public String
+ toString() {
+ String out;
+ out = m_scheme + "://";
+ if (m_isV6) {
+ out += "[" + m_host + "]";
+ } else {
+ out += m_host;
+ }
+ if (!m_port.equals("")) {
+ out += ":" + m_port;
+ }
+ out += m_path;
+ return out;
+ }
+
+ //////////////////////////////////////////////////////////////////////////////
+ // comparator
+
+ /**
+ * Compare FaceUris
+ * @param rhs FaceUri to compare with
+ * @return true if <pre>this</pre> is equal to <pre>rhs</pre>
+ */
+ boolean
+ equals(FaceUri rhs) {
+ return (m_scheme == rhs.m_scheme &&
+ m_host == rhs.m_host &&
+ m_isV6 == rhs.m_isV6 &&
+ m_port == rhs.m_port &&
+ m_path == rhs.m_path);
+ }
+
+ //////////////////////////////////////////////////////////////////////////////
+ // canonical FaceUri
+
+ /**
+ * Check whether FaceUri of the scheme can be canonized
+ * @param scheme scheme to check
+ * @return true if FaceUri supports canonization for the scheme
+ */
+ static public boolean
+ canCanonize(String scheme) {
+ return s_canonizeProviders.containsKey(scheme);
+ }
+
+ /**
+ * Determine whether this FaceUri is in canonical form
+ * <p>
+ * Note that this method can block for DNS resolution process
+ * <p>
+ * @return true if this FaceUri is in canonical form,
+ * false if this FaceUri is not in canonical form or
+ * or it's undetermined whether this FaceUri is in canonical form
+ */
+ public boolean
+ isCanonical() {
+ CanonizeProvider provider = s_canonizeProviders.get(m_scheme);
+ if (provider == null)
+ return false;
+
+ return provider.isCanonical(this);
+ }
+
+ /**
+ * Convert this FaceUri to canonical form
+ * <p>
+ * Note that this method can block for DNS resolution process
+ * <p>
+ * @return A new FaceUri in canonical form; this FaceUri is unchanged
+ * @throws CanonizeError when canonization fails
+ */
+ public FaceUri
+ canonize() throws CanonizeError {
+ CanonizeProvider provider = s_canonizeProviders.get(m_scheme);
+ if (provider == null) {
+ throw new CanonizeError(this.toString() + " does not support canonization");
+ }
+
+ return provider.canonize(this);
+ }
+
+ //////////////////////////////////////////////////////////////////////////////
+
+ /**
+ * Interface that provides FaceUri canonization functionality for a group of schemes
+ */
+ private interface CanonizeProvider {
+ public Set<String>
+ getSchemes();
+
+ public boolean
+ isCanonical(FaceUri faceUri);
+
+ public FaceUri
+ canonize(FaceUri faceUri) throws CanonizeError;
+ }
+
+ /**
+ * Canonizer for IPv4 and IPv6-based schemes
+ */
+ private static class IpHostCanonizeProvider implements CanonizeProvider {
+ public Set<String>
+ getSchemes() {
+ Set<String> schemes = new HashSet<String>();
+ schemes.add(m_baseScheme);
+ schemes.add(m_v4Scheme);
+ schemes.add(m_v6Scheme);
+ return schemes;
+ }
+
+ public boolean
+ isCanonical(FaceUri faceUri) {
+ if (faceUri.getPort().equals("")) {
+ return false;
+ }
+ if (!faceUri.getPath().equals("")) {
+ return false;
+ }
+
+ try {
+ InetAddress addr;
+ if (faceUri.getScheme().equals(m_v4Scheme)) {
+ addr = Inet4Address.getByName(faceUri.getHost());
+ } else if (faceUri.getScheme().equals(m_v6Scheme)) {
+ addr = Inet6Address.getByName(faceUri.getHost());
+ } else {
+ return false;
+ }
+ return InetAddresses.toAddrString(addr).equals(faceUri.getHost()) && this.checkAddress(addr);
+ } catch (UnknownHostException e) {
+ return false;
+ }
+ }
+
+ public FaceUri
+ canonize(FaceUri faceUri) throws CanonizeError {
+
+ if (this.isCanonical(faceUri)) {
+ try {
+ return (FaceUri)faceUri.clone();
+ }
+ catch (CloneNotSupportedException e) {
+ assert false;
+ return null;
+ }
+ }
+
+ InetAddress addr = null;
+ try {
+ if (faceUri.getScheme().equals(m_v4Scheme)) {
+ for (InetAddress a : InetAddress.getAllByName(faceUri.getHost())) {
+ if (a instanceof Inet4Address) {
+ addr = a;
+ break;
+ }
+ }
+ } else if (faceUri.getScheme().equals(m_v6Scheme)) {
+ for (InetAddress a : InetAddress.getAllByName(faceUri.getHost())) {
+ if (a instanceof Inet6Address) {
+ addr = a;
+ break;
+ }
+ }
+ } else {
+ addr = InetAddress.getByName(faceUri.getHost());
+ }
+ } catch (UnknownHostException e) {
+ throw new CanonizeError("Cannot resolve " + faceUri.getHost());
+ }
+
+ if (addr == null) {
+ throw new CanonizeError("Could not resolve " + faceUri.getHost() + " for scheme " + faceUri.getScheme());
+ }
+
+ if (!this.checkAddress(addr)) {
+ throw new CanonizeError("Resolved to " + addr.getHostAddress() + ", which is prohibied by the CanonizeProvider");
+ }
+
+ int port = 0;
+ if (faceUri.getPort().equals("")) {
+ port = addr.isMulticastAddress() ? m_defaultMulticastPort : m_defaultUnicastPort;
+ } else {
+ try {
+ port = Integer.valueOf(faceUri.getPort());
+ } catch (NumberFormatException e) {
+ throw new CanonizeError("Invalid port number");
+ }
+ }
+
+ faceUri = new FaceUri();
+ if (addr instanceof Inet4Address) {
+ faceUri.parse(m_v4Scheme + "://" + InetAddresses.toAddrString(addr) + ":" + String.valueOf(port));
+ } else if (addr instanceof Inet6Address) {
+ faceUri.parse(m_v6Scheme + "://[" + InetAddresses.toAddrString(addr) + "]:" + String.valueOf(port));
+ } else {
+ throw new CanonizeError("Unknown type of address: " + addr.getHostAddress());
+ }
+
+ return faceUri;
+ }
+
+ //////////////////////////////////////////////////////////////////////////////
+
+ protected IpHostCanonizeProvider(String baseScheme, int defaultUnicastPort, int defaultMulticastPort) {
+ m_baseScheme = baseScheme;
+ m_v4Scheme = baseScheme + "4";
+ m_v6Scheme = baseScheme + "6";
+ m_defaultUnicastPort = defaultUnicastPort;
+ m_defaultMulticastPort = defaultMulticastPort;
+ }
+
+ protected IpHostCanonizeProvider(String baseScheme) {
+ this(baseScheme, 6363, 56363);
+ }
+
+ /**
+ * @return (true, ignored) if the address is allowable;
+ * (false,reason) if the address is not allowable.
+ * @brief when overriden in a subclass, check the IP address is allowable
+ */
+ protected boolean
+ checkAddress(InetAddress ipAddress) {
+ return true;
+ }
+
+ //////////////////////////////////////////////////////////////////////////////
+
+ private String m_baseScheme = "";
+ private String m_v4Scheme = "";
+ private String m_v6Scheme = "";
+ private int m_defaultUnicastPort = 6363;
+ private int m_defaultMulticastPort = 56363;
+ }
+
+ private static class UdpCanonizeProvider extends IpHostCanonizeProvider {
+ public UdpCanonizeProvider() {
+ super("udp");
+ }
+ }
+
+ private static class TcpCanonizeProvider extends IpHostCanonizeProvider {
+ public TcpCanonizeProvider() {
+ super("tcp");
+ }
+
+ protected boolean
+ checkAddress(InetAddress ipAddress) {
+ return !ipAddress.isMulticastAddress();
+ }
+ }
+
+ //////////////////////////////////////////////////////////////////////////////
+
+ static private Map<String, CanonizeProvider>
+ initCanonizeProviders()
+ {
+ Map<String, CanonizeProvider> providers = new HashMap<String, CanonizeProvider>();
+ addCanonizeProvider(providers, new TcpCanonizeProvider());
+ addCanonizeProvider(providers, new UdpCanonizeProvider());
+ return providers;
+ }
+
+ private static void
+ addCanonizeProvider(Map<String, CanonizeProvider> providers, CanonizeProvider provider) {
+ Set<String> schemes = provider.getSchemes();
+ assert !schemes.isEmpty();
+
+ for (String scheme : schemes) {
+ assert !providers.containsKey(scheme);
+ providers.put(scheme, provider);
+ }
+ }
+
+ //////////////////////////////////////////////////////////////////////////////
+
+ private String m_scheme = "";
+ private String m_host = "";
+ boolean m_isV6 = false; ///< whether to add [] around host when writing string
+ private String m_port = "";
+ private String m_path = "";
+
+ static private final Map<String, CanonizeProvider> s_canonizeProviders = initCanonizeProviders();
+}
diff --git a/src/test/java/net/named_data/jndn_xx/util/FaceUriTest.java b/src/test/java/net/named_data/jndn_xx/util/FaceUriTest.java
new file mode 100644
index 0000000..b05bfcf
--- /dev/null
+++ b/src/test/java/net/named_data/jndn_xx/util/FaceUriTest.java
@@ -0,0 +1,341 @@
+/* -*- Mode:jde; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2015 Alexander Afanasyev.
+ *
+ * This file is part of jndn-xx library.
+ *
+ * jndn-xx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * jndn-xx library is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with jndn-xx, e.g., in COPYING.md file. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of jndn-xx authors and contributors.
+ */
+
+package net.named_data.jndn_xx.util;
+
+import org.junit.Test;
+
+import java.net.Inet4Address;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+public class FaceUriTest {
+
+ @Test
+ public void ParseInternal() {
+ FaceUri uri = new FaceUri();
+
+ assertTrue(uri.parse("internal://"));
+ assertEquals("internal", uri.getScheme());
+ assertEquals("", uri.getHost());
+ assertEquals("", uri.getPort());
+ assertEquals("", uri.getPath());
+
+ assertEquals(uri.parse("internal:"), false);
+ assertEquals(uri.parse("internal:/"), false);
+ }
+
+ @Test
+ public void ParseUdp()
+ {
+ new FaceUri("udp://hostname:6363");
+
+ try {
+ new FaceUri("udp//hostname:6363");
+ fail("FaceUri.Error exception is expected");
+ }
+ catch (FaceUri.Error e) {
+ }
+
+ try {
+ new FaceUri("udp://hostname:port");
+ fail("FaceUri.Error exception is expected");
+ }
+ catch (FaceUri.Error e) {
+ }
+
+ FaceUri uri = new FaceUri();
+ assertEquals(uri.parse("udp//hostname:6363"), false);
+
+ assertTrue(uri.parse("udp://hostname:80"));
+ assertEquals(uri.getScheme(), "udp");
+ assertEquals(uri.getHost(), "hostname");
+ assertEquals(uri.getPort(), "80");
+ assertEquals(uri.getPath(), "");
+
+ assertTrue(uri.parse("udp4://192.0.2.1:20"));
+ assertEquals(uri.getScheme(), "udp4");
+ assertEquals(uri.getHost(), "192.0.2.1");
+ assertEquals(uri.getPort(), "20");
+ assertEquals(uri.getPath(), "");
+
+ assertTrue(uri.parse("udp6://[2001:db8:3f9:0::1]:6363"));
+ assertEquals(uri.getScheme(), "udp6");
+ assertEquals(uri.getHost(), "2001:db8:3f9:0::1");
+ assertEquals(uri.getPort(), "6363");
+ assertEquals(uri.getPath(), "");
+
+ assertTrue(uri.parse("udp6://[2001:db8:3f9:0:3025:ccc5:eeeb:86d3]:6363"));
+ assertEquals(uri.getScheme(), "udp6");
+ assertEquals(uri.getHost(), "2001:db8:3f9:0:3025:ccc5:eeeb:86d3");
+ assertEquals(uri.getPort(), "6363");
+ assertEquals(uri.getPath(), "");
+
+ assertEquals(uri.parse("udp6://[2001:db8:3f9:0:3025:ccc5:eeeb:86dg]:6363"), false);
+ }
+
+ @Test
+ public void CheckCanonicalUdp() {
+ assertEquals(FaceUri.canCanonize("udp"), true);
+ assertEquals(FaceUri.canCanonize("udp4"), true);
+ assertEquals(FaceUri.canCanonize("udp6"), true);
+
+ assertEquals(new FaceUri("udp4://192.0.2.1:6363").isCanonical(), true);
+ assertEquals(new FaceUri("udp://192.0.2.1:6363").isCanonical(), false);
+ assertEquals(new FaceUri("udp4://192.0.2.1").isCanonical(), false);
+ assertEquals(new FaceUri("udp4://192.0.2.1:6363/").isCanonical(), false);
+ assertEquals(new FaceUri("udp6://[2001:db8::1]:6363").isCanonical(), true);
+ assertEquals(new FaceUri("udp6://[2001:db8::01]:6363").isCanonical(), false);
+ assertEquals(new FaceUri("udp://example.net:6363").isCanonical(), false);
+ assertEquals(new FaceUri("udp4://example.net:6363").isCanonical(), false);
+ assertEquals(new FaceUri("udp6://example.net:6363").isCanonical(), false);
+ assertEquals(new FaceUri("udp4://224.0.23.170:56363").isCanonical(), true);
+ }
+
+ private static void
+ addTest(String testUri, boolean shouldSucceed, String canonicalUri) throws FaceUri.CanonizeError
+ {
+ FaceUri uri = new FaceUri(testUri);
+ if (shouldSucceed) {
+ assertEquals(canonicalUri, uri.canonize().toString());
+ }
+ else {
+ try {
+ uri.canonize();
+ fail("Canonization should have failed");
+ }
+ catch (FaceUri.CanonizeError e) {
+ }
+ }
+ }
+
+ @Test
+ public void CanonizeUdpV4() throws FaceUri.CanonizeError
+ {
+ // IPv4 unicast
+ addTest("udp4://192.0.2.1:6363", true, "udp4://192.0.2.1:6363");
+ addTest("udp://192.0.2.2:6363", true, "udp4://192.0.2.2:6363");
+ addTest("udp4://192.0.2.3", true, "udp4://192.0.2.3:6363");
+ addTest("udp4://192.0.2.4:6363/", true, "udp4://192.0.2.4:6363");
+ addTest("udp4://192.0.2.5:9695", true, "udp4://192.0.2.5:9695");
+ addTest("udp4://192.0.2.666:6363", false, "");
+ addTest("udp4://google-public-dns-a.google.com", true, "udp4://8.8.8.8:6363");
+ addTest("udp4://invalid.invalid", false, "");
+
+ // IPv4 multicast
+ addTest("udp4://224.0.23.170:56363", true, "udp4://224.0.23.170:56363");
+ addTest("udp4://224.0.23.170", true, "udp4://224.0.23.170:56363");
+ addTest("udp4://all-routers.mcast.net:56363", true, "udp4://224.0.0.2:56363");
+ }
+
+ @Test
+ public void CanonizeUdpV6() throws FaceUri.CanonizeError
+ {
+ // IPv6 unicast
+ addTest("udp6://[2001:db8::1]:6363", true, "udp6://[2001:db8::1]:6363");
+ addTest("udp://[2001:db8::1]:6363", true, "udp6://[2001:db8::1]:6363");
+ addTest("udp6://[2001:db8::01]:6363", true, "udp6://[2001:db8::1]:6363");
+ addTest("udp6://google-public-dns-a.google.com", true, "udp6://[2001:4860:4860::8888]:6363");
+ addTest("udp6://invalid.invalid", false, "");
+ addTest("udp://invalid.invalid", false, "");
+
+ // IPv6 multicast
+ addTest("udp6://[ff02::2]:56363", true, "udp6://[ff02::2]:56363");
+ addTest("udp6://[ff02::2]", true, "udp6://[ff02::2]:56363");
+ }
+
+ @Test
+ public void ParseTcp()
+ {
+ FaceUri uri = new FaceUri();
+
+ assertTrue(uri.parse("tcp://random.host.name"));
+ assertEquals(uri.getScheme(), "tcp");
+ assertEquals(uri.getHost(), "random.host.name");
+ assertEquals(uri.getPort(), "");
+ assertEquals(uri.getPath(), "");
+
+ assertEquals(uri.parse("tcp://192.0.2.1:"), false);
+ assertEquals(uri.parse("tcp://[::zzzz]"), false);
+ }
+
+ @Test
+ public void CheckCanonicalTcp()
+ {
+ assertEquals(FaceUri.canCanonize("tcp"), true);
+ assertEquals(FaceUri.canCanonize("tcp4"), true);
+ assertEquals(FaceUri.canCanonize("tcp6"), true);
+
+ assertEquals(new FaceUri("tcp4://192.0.2.1:6363").isCanonical(), true);
+ assertEquals(new FaceUri("tcp://192.0.2.1:6363").isCanonical(), false);
+ assertEquals(new FaceUri("tcp4://192.0.2.1").isCanonical(), false);
+ assertEquals(new FaceUri("tcp4://192.0.2.1:6363/").isCanonical(), false);
+ assertEquals(new FaceUri("tcp6://[2001:db8::1]:6363").isCanonical(), true);
+ assertEquals(new FaceUri("tcp6://[2001:db8::01]:6363").isCanonical(), false);
+ assertEquals(new FaceUri("tcp://example.net:6363").isCanonical(), false);
+ assertEquals(new FaceUri("tcp4://example.net:6363").isCanonical(), false);
+ assertEquals(new FaceUri("tcp6://example.net:6363").isCanonical(), false);
+ assertEquals(new FaceUri("tcp4://224.0.23.170:56363").isCanonical(), false);
+ }
+
+ @Test
+ public void CanonizeTcpV4() throws FaceUri.CanonizeError
+ {
+ // IPv4 unicast
+ addTest("tcp4://192.0.2.1:6363", true, "tcp4://192.0.2.1:6363");
+ addTest("tcp://192.0.2.2:6363", true, "tcp4://192.0.2.2:6363");
+ addTest("tcp4://192.0.2.3", true, "tcp4://192.0.2.3:6363");
+ addTest("tcp4://192.0.2.4:6363/", true, "tcp4://192.0.2.4:6363");
+ addTest("tcp4://192.0.2.5:9695", true, "tcp4://192.0.2.5:9695");
+ addTest("tcp4://192.0.2.666:6363", false, "");
+ addTest("tcp4://google-public-dns-a.google.com", true, "tcp4://8.8.8.8:6363");
+ addTest("tcp4://invalid.invalid", false, "");
+
+ // IPv4 multicast
+ addTest("tcp4://224.0.23.170:56363", false, "");
+ addTest("tcp4://224.0.23.170", false, "");
+ addTest("tcp4://all-routers.mcast.net:56363", false, "");
+ }
+
+ @Test
+ public void CanonizeTcpV6() throws FaceUri.CanonizeError
+ {
+ // IPv6 unicast
+ addTest("tcp6://[2001:db8::1]:6363", true, "tcp6://[2001:db8::1]:6363");
+ addTest("tcp://[2001:db8::1]:6363", true, "tcp6://[2001:db8::1]:6363");
+ addTest("tcp6://[2001:db8::01]:6363", true, "tcp6://[2001:db8::1]:6363");
+ addTest("tcp6://google-public-dns-a.google.com", true, "tcp6://[2001:4860:4860::8888]:6363");
+ addTest("tcp6://invalid.invalid", false, "");
+ addTest("tcp://invalid.invalid", false, "");
+
+ // IPv6 multicast
+ addTest("tcp6://[ff02::2]:56363", false, "");
+ addTest("tcp6://[ff02::2]", false, "");
+ }
+
+ @Test
+ public void ParseUnix()
+ {
+ FaceUri uri = new FaceUri();
+
+ assertTrue(uri.parse("unix:///var/run/example.sock"));
+ assertEquals(uri.getScheme(), "unix");
+ assertEquals(uri.getHost(), "");
+ assertEquals(uri.getPort(), "");
+ assertEquals(uri.getPath(), "/var/run/example.sock");
+
+ //assertEquals(uri.parse("unix://var/run/example.sock"), false);
+ // This is not a valid unix:/ URI, but the parse would treat "var" as host
+ }
+
+ @Test
+ public void ParseFd()
+ {
+ FaceUri uri = new FaceUri();
+
+ assertTrue(uri.parse("fd://6"));
+ assertEquals(uri.getScheme(), "fd");
+ assertEquals(uri.getHost(), "6");
+ assertEquals(uri.getPort(), "");
+ assertEquals(uri.getPath(), "");
+ }
+
+ @Test
+ public void ParseEther()
+ {
+ FaceUri uri = new FaceUri();
+
+ assertTrue(uri.parse("ether://[08:00:27:01:dd:01]"));
+ assertEquals(uri.getScheme(), "ether");
+ assertEquals(uri.getHost(), "08:00:27:01:dd:01");
+ assertEquals(uri.getPort(), "");
+ assertEquals(uri.getPath(), "");
+
+ assertEquals(uri.parse("ether://[08:00:27:zz:dd:01]"), false);
+ }
+
+ // @Test
+ // public void CanonizeEther()
+ // {
+ // // not supported (yet?)
+ // assertEquals(FaceUri.canCanonize("ether"), true);
+
+ // assertEquals(FaceUri("ether://[08:00:27:01:01:01]").isCanonical(), true);
+ // assertEquals(FaceUri("ether://[08:00:27:1:1:1]").isCanonical(), false);
+ // assertEquals(FaceUri("ether://[08:00:27:01:01:01]/").isCanonical(), false);
+ // assertEquals(FaceUri("ether://[33:33:01:01:01:01]").isCanonical(), true);
+
+ // addTest("ether://[08:00:27:01:01:01]", true, "ether://[08:00:27:01:01:01]");
+ // addTest("ether://[08:00:27:1:1:1]", true, "ether://[08:00:27:01:01:01]");
+ // addTest("ether://[08:00:27:01:01:01]/", true, "ether://[08:00:27:01:01:01]");
+ // addTest("ether://[33:33:01:01:01:01]", true, "ether://[33:33:01:01:01:01]");
+ // }
+
+ @Test
+ public void ParseDev()
+ {
+ FaceUri uri = new FaceUri();
+
+ assertTrue(uri.parse("dev://eth0"));
+ assertEquals(uri.getScheme(), "dev");
+ assertEquals(uri.getHost(), "eth0");
+ assertEquals(uri.getPort(), "");
+ assertEquals(uri.getPath(), "");
+ }
+
+ @Test
+ public void CanonizeUnsupported() throws FaceUri.CanonizeError
+ {
+ assertEquals(FaceUri.canCanonize("internal"), false);
+ assertEquals(FaceUri.canCanonize("null"), false);
+ assertEquals(FaceUri.canCanonize("unix"), false);
+ assertEquals(FaceUri.canCanonize("fd"), false);
+ assertEquals(FaceUri.canCanonize("dev"), false);
+
+ assertEquals(new FaceUri("internal://").isCanonical(), false);
+ assertEquals(new FaceUri("null://").isCanonical(), false);
+ assertEquals(new FaceUri("unix:///var/run/nfd.sock").isCanonical(), false);
+ assertEquals(new FaceUri("fd://0").isCanonical(), false);
+ assertEquals(new FaceUri("dev://eth1").isCanonical(), false);
+
+ addTest("internal://", false, "");
+ addTest("null://", false, "");
+ addTest("unix:///var/run/nfd.sock", false, "");
+ addTest("fd://0", false, "");
+ addTest("dev://eth1", false, "");
+ }
+
+ @Test
+ public void Bug1635()
+ {
+ FaceUri uri = new FaceUri();
+
+ assertTrue(uri.parse("wsclient://[::ffff:76.90.11.239]:56366"));
+ assertEquals(uri.getScheme(), "wsclient");
+ assertEquals(uri.getHost(), "76.90.11.239");
+ assertEquals(uri.getPort(), "56366");
+ assertEquals(uri.getPath(), "");
+ assertEquals(uri.toString(), "wsclient://76.90.11.239:56366");
+ }
+
+}