util: FaceUri canonization for udp,tcp,ether
refs #1994
Change-Id: I5349d999a3dd52a1fe533e17766d70df5e67091f
diff --git a/src/util/face-uri.cpp b/src/util/face-uri.cpp
index e5f9611..ff5669b 100644
--- a/src/util/face-uri.cpp
+++ b/src/util/face-uri.cpp
@@ -26,10 +26,14 @@
*/
#include "face-uri.hpp"
+#include "dns.hpp"
+#include <set>
#include <boost/concept_check.hpp>
#include <boost/regex.hpp>
#include <boost/lexical_cast.hpp>
+#include <boost/mpl/vector.hpp>
+#include <boost/mpl/for_each.hpp>
namespace ndn {
namespace util {
@@ -202,5 +206,340 @@
return os;
}
+/** \brief a CanonizeProvider provides FaceUri canonization functionality for a group of schemes
+ */
+class CanonizeProvider : noncopyable
+{
+public:
+ virtual
+ ~CanonizeProvider()
+ {
+ }
+
+ virtual std::set<std::string>
+ getSchemes() const = 0;
+
+ virtual bool
+ isCanonical(const FaceUri& faceUri) const = 0;
+
+ virtual void
+ canonize(const FaceUri& faceUri,
+ const FaceUri::CanonizeSuccessCallback& onSuccess,
+ const FaceUri::CanonizeFailureCallback& onFailure,
+ boost::asio::io_service& io, const time::nanoseconds& timeout) const = 0;
+};
+
+template<typename Protocol>
+class IpHostCanonizeProvider : public CanonizeProvider
+{
+public:
+ virtual std::set<std::string>
+ getSchemes() const
+ {
+ std::set<std::string> schemes;
+ schemes.insert(m_baseScheme);
+ schemes.insert(m_v4Scheme);
+ schemes.insert(m_v6Scheme);
+ return schemes;
+ }
+
+ virtual bool
+ isCanonical(const FaceUri& faceUri) const
+ {
+ if (faceUri.getPort().empty()) {
+ return false;
+ }
+ if (!faceUri.getPath().empty()) {
+ return false;
+ }
+
+ boost::system::error_code ec;
+ boost::asio::ip::address addr;
+ if (faceUri.getScheme() == m_v4Scheme) {
+ addr = boost::asio::ip::address_v4::from_string(faceUri.getHost(), ec);
+ }
+ else if (faceUri.getScheme() == m_v6Scheme) {
+ addr = boost::asio::ip::address_v6::from_string(faceUri.getHost(), ec);
+ }
+ else {
+ return false;
+ }
+ return !static_cast<bool>(ec) && addr.to_string() == faceUri.getHost() &&
+ this->checkAddress(addr).first;
+ }
+
+ virtual void
+ canonize(const FaceUri& faceUri,
+ const FaceUri::CanonizeSuccessCallback& onSuccess,
+ const FaceUri::CanonizeFailureCallback& onFailure,
+ boost::asio::io_service& io, const time::nanoseconds& timeout) const
+ {
+ if (this->isCanonical(faceUri)) {
+ onSuccess(faceUri);
+ return;
+ }
+
+ dns::AddressSelector addressSelector;
+ if (faceUri.getScheme() == m_v4Scheme) {
+ addressSelector = dns::Ipv4Only();
+ }
+ else if (faceUri.getScheme() == m_v6Scheme) {
+ addressSelector = dns::Ipv6Only();
+ }
+ else {
+ BOOST_ASSERT(faceUri.getScheme() == m_baseScheme);
+ addressSelector = dns::AnyAddress();
+ }
+
+ // make a copy because caller may modify faceUri
+ shared_ptr<FaceUri> uri = make_shared<FaceUri>(faceUri);
+ dns::asyncResolve(faceUri.getHost(),
+ bind(&IpHostCanonizeProvider<Protocol>::onDnsSuccess, this, uri, onSuccess, onFailure, _1),
+ bind(&IpHostCanonizeProvider<Protocol>::onDnsFailure, this, uri, onFailure, _1),
+ io, addressSelector, timeout);
+ }
+
+protected:
+ IpHostCanonizeProvider(const std::string& baseScheme,
+ uint32_t defaultUnicastPort = 6363,
+ uint32_t defaultMulticastPort = 56363)
+ : m_baseScheme(baseScheme)
+ , m_v4Scheme(baseScheme + "4")
+ , m_v6Scheme(baseScheme + "6")
+ , m_defaultUnicastPort(defaultUnicastPort)
+ , m_defaultMulticastPort(defaultMulticastPort)
+ {
+ }
+
+private:
+ // faceUri is a shared_ptr passed by value because this function can take ownership
+ void
+ onDnsSuccess(shared_ptr<FaceUri> faceUri,
+ const FaceUri::CanonizeSuccessCallback& onSuccess,
+ const FaceUri::CanonizeFailureCallback& onFailure,
+ const dns::IpAddress& ipAddress) const
+ {
+ std::pair<bool, std::string> checkAddressRes = this->checkAddress(ipAddress);
+ if (!checkAddressRes.first) {
+ onFailure(checkAddressRes.second);
+ return;
+ }
+
+ uint32_t port = 0;
+ if (faceUri->getPort().empty()) {
+ port = ipAddress.is_multicast() ? m_defaultMulticastPort : m_defaultUnicastPort;
+ }
+ else {
+ try {
+ port = boost::lexical_cast<uint32_t>(faceUri->getPort());
+ }
+ catch (boost::bad_lexical_cast&) {
+ onFailure("invalid port number");
+ return;
+ }
+ }
+
+ FaceUri canonicalUri(typename Protocol::endpoint(ipAddress, port));
+ BOOST_ASSERT(canonicalUri.isCanonical());
+ onSuccess(canonicalUri);
+ }
+
+ // faceUri is a shared_ptr passed by value because this function can take ownership
+ void
+ onDnsFailure(shared_ptr<FaceUri> faceUri, const FaceUri::CanonizeFailureCallback& onFailure,
+ const std::string& reason) const
+ {
+ onFailure(reason);
+ }
+
+ /** \brief when overriden in a subclass, check the IP address is allowable
+ * \return (true,ignored) if the address is allowable;
+ * (false,reason) if the address is not allowable.
+ */
+ virtual std::pair<bool, std::string>
+ checkAddress(const dns::IpAddress& ipAddress) const
+ {
+ return std::make_pair(true, "");
+ }
+
+private:
+ std::string m_baseScheme;
+ std::string m_v4Scheme;
+ std::string m_v6Scheme;
+ uint32_t m_defaultUnicastPort;
+ uint32_t m_defaultMulticastPort;
+};
+
+class UdpCanonizeProvider : public IpHostCanonizeProvider<boost::asio::ip::udp>
+{
+public:
+ UdpCanonizeProvider()
+ : IpHostCanonizeProvider("udp")
+ {
+ }
+
+protected:
+ // checkAddress is not overriden:
+ // Although NFD doesn't support IPv6 multicast, it's an implementation limitation.
+ // FaceMgmt protocol allows IPv6 multicast address in UDP.
+};
+
+class TcpCanonizeProvider : public IpHostCanonizeProvider<boost::asio::ip::tcp>
+{
+public:
+public:
+ TcpCanonizeProvider()
+ : IpHostCanonizeProvider("tcp")
+ {
+ }
+
+protected:
+ virtual std::pair<bool, std::string>
+ checkAddress(const dns::IpAddress& ipAddress) const
+ {
+ if (ipAddress.is_multicast()) {
+ return std::make_pair(false, "cannot use multicast address");
+ }
+ return std::make_pair(true, "");
+ }
+};
+
+class EtherCanonizeProvider : public CanonizeProvider
+{
+public:
+ virtual std::set<std::string>
+ getSchemes() const
+ {
+ std::set<std::string> schemes;
+ schemes.insert("ether");
+ return schemes;
+ }
+
+ virtual bool
+ isCanonical(const FaceUri& faceUri) const
+ {
+ if (!faceUri.getPort().empty()) {
+ return false;
+ }
+ if (!faceUri.getPath().empty()) {
+ return false;
+ }
+
+ ethernet::Address addr = ethernet::Address::fromString(faceUri.getHost());
+ return addr.toString() == faceUri.getHost();
+ }
+
+ virtual void
+ canonize(const FaceUri& faceUri,
+ const FaceUri::CanonizeSuccessCallback& onSuccess,
+ const FaceUri::CanonizeFailureCallback& onFailure,
+ boost::asio::io_service& io, const time::nanoseconds& timeout) const
+ {
+ ethernet::Address addr = ethernet::Address::fromString(faceUri.getHost());
+ if (addr.isNull()) {
+ onFailure("cannot parse address");
+ return;
+ }
+
+ FaceUri canonicalUri(addr);
+ BOOST_ASSERT(canonicalUri.isCanonical());
+ onSuccess(canonicalUri);
+ }
+};
+
+typedef boost::mpl::vector<
+ UdpCanonizeProvider*,
+ TcpCanonizeProvider*,
+ EtherCanonizeProvider*
+ > CanonizeProviders;
+typedef std::map<std::string, shared_ptr<CanonizeProvider> > CanonizeProviderTable;
+
+class CanonizeProviderTableInitializer
+{
+public:
+ explicit
+ CanonizeProviderTableInitializer(CanonizeProviderTable& providerTable)
+ : m_providerTable(providerTable)
+ {
+ }
+
+ template<typename CP> void
+ operator()(CP*)
+ {
+ shared_ptr<CanonizeProvider> cp = make_shared<CP>();
+
+ std::set<std::string> schemes = cp->getSchemes();
+ BOOST_ASSERT(!schemes.empty());
+ for (std::set<std::string>::iterator it = schemes.begin();
+ it != schemes.end(); ++it) {
+ BOOST_ASSERT(m_providerTable.count(*it) == 0);
+ m_providerTable[*it] = cp;
+ }
+ }
+
+private:
+ CanonizeProviderTable& m_providerTable;
+};
+
+static const CanonizeProvider*
+getCanonizeProvider(const std::string& scheme)
+{
+ static CanonizeProviderTable providerTable;
+ if (providerTable.empty()) {
+ boost::mpl::for_each<CanonizeProviders>(CanonizeProviderTableInitializer(providerTable));
+ BOOST_ASSERT(!providerTable.empty());
+ }
+
+ CanonizeProviderTable::const_iterator it = providerTable.find(scheme);
+ if (it == providerTable.end()) {
+ return 0;
+ }
+ return it->second.get();
+}
+
+bool
+FaceUri::canCanonize(const std::string& scheme)
+{
+ return getCanonizeProvider(scheme) != 0;
+}
+
+bool
+FaceUri::isCanonical() const
+{
+ const CanonizeProvider* cp = getCanonizeProvider(this->getScheme());
+ if (cp == 0) {
+ return false;
+ }
+
+ return cp->isCanonical(*this);
+}
+
+static inline void
+nop()
+{
+}
+
+void
+FaceUri::canonize(const CanonizeSuccessCallback& onSuccess,
+ const CanonizeFailureCallback& onFailure,
+ boost::asio::io_service& io, const time::nanoseconds& timeout) const
+{
+ const CanonizeProvider* cp = getCanonizeProvider(this->getScheme());
+ if (cp == 0) {
+ if (static_cast<bool>(onFailure)) {
+ onFailure("scheme not supported");
+ }
+ return;
+ }
+
+ static CanonizeSuccessCallback successNop = bind(&nop);
+ static CanonizeFailureCallback failureNop = bind(&nop);
+
+ cp->canonize(*this,
+ static_cast<bool>(onSuccess) ? onSuccess : successNop,
+ static_cast<bool>(onFailure) ? onFailure : failureNop,
+ io, timeout);
+}
+
} // namespace util
} // namespace ndn
diff --git a/src/util/face-uri.hpp b/src/util/face-uri.hpp
index f2786e1..95d2c87 100644
--- a/src/util/face-uri.hpp
+++ b/src/util/face-uri.hpp
@@ -33,6 +33,7 @@
#include <boost/asio/ip/tcp.hpp>
#include <boost/asio/local/stream_protocol.hpp>
#include "ethernet.hpp"
+#include "time.hpp"
namespace ndn {
namespace util {
@@ -142,6 +143,36 @@
bool
operator!=(const FaceUri& rhs) const;
+public: // canonical FaceUri
+ /** \return whether a FaceUri of the scheme can be canonized
+ */
+ static bool
+ canCanonize(const std::string& scheme);
+
+ /** \brief determine whether this FaceUri is in canonical form
+ * \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
+ */
+ bool
+ isCanonical() const;
+
+ typedef function<void(const FaceUri&)> CanonizeSuccessCallback;
+ typedef function<void(const std::string& reason)> CanonizeFailureCallback;
+
+ /** \brief asynchronously convert this FaceUri to canonical form
+ * \param onSuccess function to call after this FaceUri is converted to canonical form
+ * \note A new FaceUri in canonical form will be created; this FaceUri is unchanged.
+ * \param onFailure function to call if this FaceUri cannot be converted to canonical form
+ * \param timeout maximum allowable duration of the operations.
+ * It's intentional not to provide a default value: the caller should set
+ * a reasonable value in balance between network delay and user experience.
+ */
+ void
+ canonize(const CanonizeSuccessCallback& onSuccess,
+ const CanonizeFailureCallback& onFailure,
+ boost::asio::io_service& io, const time::nanoseconds& timeout) const;
+
private:
std::string m_scheme;
std::string m_host;
diff --git a/tests/unit-tests/util/face-uri.cpp b/tests/unit-tests/util/face-uri.cpp
index b228095..3ed42f5 100644
--- a/tests/unit-tests/util/face-uri.cpp
+++ b/tests/unit-tests/util/face-uri.cpp
@@ -34,7 +34,86 @@
BOOST_AUTO_TEST_SUITE(UtilTestFaceUri)
-BOOST_AUTO_TEST_CASE(Internal)
+class CanonizeFixture : noncopyable
+{
+protected:
+ CanonizeFixture()
+ : m_nPending(0)
+ {
+ }
+
+ void
+ addTest(const std::string& request, bool shouldSucceed, const std::string& expectedUri);
+
+ void
+ runTests()
+ {
+ m_io.run();
+ BOOST_CHECK_EQUAL(m_nPending, 0);
+ }
+
+private:
+ struct CanonizeTestCase
+ {
+ public:
+ CanonizeTestCase(const std::string& request,
+ bool shouldSucceed, const std::string& expectedUri)
+ : m_request(request)
+ , m_shouldSucceed(shouldSucceed)
+ , m_expectedUri(expectedUri)
+ , m_isCompleted(false)
+ {
+ }
+
+ public:
+ std::string m_request;
+ bool m_shouldSucceed;
+ std::string m_expectedUri;
+ bool m_isCompleted;
+ };
+
+ // tc is a shared_ptr passed by value, because this function can take ownership
+ void
+ onCanonizeSuccess(shared_ptr<CanonizeTestCase> tc, const FaceUri& canonicalUri)
+ {
+ BOOST_CHECK(!tc->m_isCompleted);
+ tc->m_isCompleted = true;
+ --m_nPending;
+
+ BOOST_CHECK_MESSAGE(tc->m_shouldSucceed, tc->m_request + " should fail");
+ BOOST_CHECK_EQUAL(tc->m_expectedUri, canonicalUri.toString());
+ }
+
+ // tc is a shared_ptr passed by value, because this function can take ownership
+ void
+ onCanonizeFailure(shared_ptr<CanonizeTestCase> tc, const std::string& reason)
+ {
+ BOOST_CHECK(!tc->m_isCompleted);
+ tc->m_isCompleted = true;
+ --m_nPending;
+
+ BOOST_CHECK_MESSAGE(!tc->m_shouldSucceed, tc->m_request + " should succeed");
+ }
+
+private:
+ boost::asio::io_service m_io;
+ ssize_t m_nPending;
+};
+
+void
+CanonizeFixture::addTest(const std::string& request,
+ bool shouldSucceed, const std::string& expectedUri)
+{
+ ++m_nPending;
+ shared_ptr<CanonizeTestCase> tc = ndn::make_shared<CanonizeTestCase>(
+ request, shouldSucceed, expectedUri);
+ FaceUri uri(request);
+ uri.canonize(bind(&CanonizeFixture::onCanonizeSuccess, this, tc, _1),
+ bind(&CanonizeFixture::onCanonizeFailure, this, tc, _1),
+ m_io, time::seconds(4));
+}
+
+BOOST_AUTO_TEST_CASE(ParseInternal)
{
FaceUri uri;
@@ -48,7 +127,7 @@
BOOST_CHECK_EQUAL(uri.parse("internal:/"), false);
}
-BOOST_AUTO_TEST_CASE(Udp)
+BOOST_AUTO_TEST_CASE(ParseUdp)
{
BOOST_CHECK_NO_THROW(FaceUri("udp://hostname:6363"));
BOOST_CHECK_THROW(FaceUri("udp//hostname:6363"), FaceUri::Error);
@@ -93,7 +172,54 @@
BOOST_CHECK_EQUAL(FaceUri(endpoint6).toString(), "udp6://[2001:db8::1]:7777");
}
-BOOST_AUTO_TEST_CASE(Tcp)
+BOOST_FIXTURE_TEST_CASE(CanonizeUdp, CanonizeFixture)
+{
+ BOOST_CHECK_EQUAL(FaceUri::canCanonize("udp"), true);
+ BOOST_CHECK_EQUAL(FaceUri::canCanonize("udp4"), true);
+ BOOST_CHECK_EQUAL(FaceUri::canCanonize("udp6"), true);
+
+ BOOST_CHECK_EQUAL(FaceUri("udp4://192.0.2.1:6363").isCanonical(), true);
+ BOOST_CHECK_EQUAL(FaceUri("udp://192.0.2.1:6363").isCanonical(), false);
+ BOOST_CHECK_EQUAL(FaceUri("udp4://192.0.2.1").isCanonical(), false);
+ BOOST_CHECK_EQUAL(FaceUri("udp4://192.0.2.1:6363/").isCanonical(), false);
+ BOOST_CHECK_EQUAL(FaceUri("udp6://[2001:db8::1]:6363").isCanonical(), true);
+ BOOST_CHECK_EQUAL(FaceUri("udp6://[2001:db8::01]:6363").isCanonical(), false);
+ BOOST_CHECK_EQUAL(FaceUri("udp://example.net:6363").isCanonical(), false);
+ BOOST_CHECK_EQUAL(FaceUri("udp4://example.net:6363").isCanonical(), false);
+ BOOST_CHECK_EQUAL(FaceUri("udp6://example.net:6363").isCanonical(), false);
+ BOOST_CHECK_EQUAL(FaceUri("udp4://224.0.23.170:56363").isCanonical(), true);
+
+ // 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");
+
+ // 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");
+
+ runTests();
+}
+
+BOOST_AUTO_TEST_CASE(ParseTcp)
{
FaceUri uri;
@@ -119,7 +245,54 @@
BOOST_CHECK_EQUAL(FaceUri(endpoint6).toString(), "tcp6://[2001:db8::1]:7777");
}
-BOOST_AUTO_TEST_CASE(Unix)
+BOOST_FIXTURE_TEST_CASE(CanonizeTcp, CanonizeFixture)
+{
+ BOOST_CHECK_EQUAL(FaceUri::canCanonize("tcp"), true);
+ BOOST_CHECK_EQUAL(FaceUri::canCanonize("tcp4"), true);
+ BOOST_CHECK_EQUAL(FaceUri::canCanonize("tcp6"), true);
+
+ BOOST_CHECK_EQUAL(FaceUri("tcp4://192.0.2.1:6363").isCanonical(), true);
+ BOOST_CHECK_EQUAL(FaceUri("tcp://192.0.2.1:6363").isCanonical(), false);
+ BOOST_CHECK_EQUAL(FaceUri("tcp4://192.0.2.1").isCanonical(), false);
+ BOOST_CHECK_EQUAL(FaceUri("tcp4://192.0.2.1:6363/").isCanonical(), false);
+ BOOST_CHECK_EQUAL(FaceUri("tcp6://[2001:db8::1]:6363").isCanonical(), true);
+ BOOST_CHECK_EQUAL(FaceUri("tcp6://[2001:db8::01]:6363").isCanonical(), false);
+ BOOST_CHECK_EQUAL(FaceUri("tcp://example.net:6363").isCanonical(), false);
+ BOOST_CHECK_EQUAL(FaceUri("tcp4://example.net:6363").isCanonical(), false);
+ BOOST_CHECK_EQUAL(FaceUri("tcp6://example.net:6363").isCanonical(), false);
+ BOOST_CHECK_EQUAL(FaceUri("tcp4://224.0.23.170:56363").isCanonical(), false);
+
+ // 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, "");
+
+ // 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, "");
+
+ runTests();
+}
+
+BOOST_AUTO_TEST_CASE(ParseUnix)
{
FaceUri uri;
@@ -140,7 +313,7 @@
#endif // HAVE_UNIX_SOCKETS
}
-BOOST_AUTO_TEST_CASE(Fd)
+BOOST_AUTO_TEST_CASE(ParseFd)
{
FaceUri uri;
@@ -155,7 +328,7 @@
BOOST_CHECK_EQUAL(FaceUri::fromFd(fd).toString(), "fd://21");
}
-BOOST_AUTO_TEST_CASE(Ether)
+BOOST_AUTO_TEST_CASE(ParseEther)
{
FaceUri uri;
@@ -165,16 +338,31 @@
BOOST_CHECK_EQUAL(uri.getPort(), "");
BOOST_CHECK_EQUAL(uri.getPath(), "");
- BOOST_CHECK_EQUAL(uri.parse("ether://08:00:27:zz:dd:01"), false);
+ BOOST_CHECK_EQUAL(uri.parse("ether://[08:00:27:zz:dd:01]"), false);
-#ifdef HAVE_LIBPCAP
ethernet::Address address = ethernet::Address::fromString("33:33:01:01:01:01");
BOOST_REQUIRE_NO_THROW(FaceUri(address));
BOOST_CHECK_EQUAL(FaceUri(address).toString(), "ether://[33:33:01:01:01:01]");
-#endif // HAVE_LIBPCAP
}
-BOOST_AUTO_TEST_CASE(Dev)
+BOOST_FIXTURE_TEST_CASE(CanonizeEther, CanonizeFixture)
+{
+ BOOST_CHECK_EQUAL(FaceUri::canCanonize("ether"), true);
+
+ BOOST_CHECK_EQUAL(FaceUri("ether://[08:00:27:01:01:01]").isCanonical(), true);
+ BOOST_CHECK_EQUAL(FaceUri("ether://[08:00:27:1:1:1]").isCanonical(), false);
+ BOOST_CHECK_EQUAL(FaceUri("ether://[08:00:27:01:01:01]/").isCanonical(), false);
+ BOOST_CHECK_EQUAL(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]");
+
+ runTests();
+}
+
+BOOST_AUTO_TEST_CASE(ParseDev)
{
FaceUri uri;
@@ -189,6 +377,56 @@
BOOST_CHECK_EQUAL(FaceUri::fromDev(ifname).toString(), "dev://en1");
}
+BOOST_AUTO_TEST_CASE(CanonizeEmptyCallback)
+{
+ boost::asio::io_service io;
+
+ // unsupported scheme
+ FaceUri("null://").canonize(FaceUri::CanonizeSuccessCallback(),
+ FaceUri::CanonizeFailureCallback(),
+ io, time::milliseconds(1));
+
+ // cannot resolve
+ FaceUri("udp://192.0.2.333").canonize(FaceUri::CanonizeSuccessCallback(),
+ FaceUri::CanonizeFailureCallback(),
+ io, time::milliseconds(1));
+
+ // already canonical
+ FaceUri("udp4://192.0.2.1:6363").canonize(FaceUri::CanonizeSuccessCallback(),
+ FaceUri::CanonizeFailureCallback(),
+ io, time::milliseconds(1));
+
+ // need DNS resolution
+ FaceUri("udp://192.0.2.1:6363").canonize(FaceUri::CanonizeSuccessCallback(),
+ FaceUri::CanonizeFailureCallback(),
+ io, time::milliseconds(1));
+
+ io.run(); // should not crash
+}
+
+BOOST_FIXTURE_TEST_CASE(CanonizeUnsupported, CanonizeFixture)
+{
+ BOOST_CHECK_EQUAL(FaceUri::canCanonize("internal"), false);
+ BOOST_CHECK_EQUAL(FaceUri::canCanonize("null"), false);
+ BOOST_CHECK_EQUAL(FaceUri::canCanonize("unix"), false);
+ BOOST_CHECK_EQUAL(FaceUri::canCanonize("fd"), false);
+ BOOST_CHECK_EQUAL(FaceUri::canCanonize("dev"), false);
+
+ BOOST_CHECK_EQUAL(FaceUri("internal://").isCanonical(), false);
+ BOOST_CHECK_EQUAL(FaceUri("null://").isCanonical(), false);
+ BOOST_CHECK_EQUAL(FaceUri("unix:///var/run/nfd.sock").isCanonical(), false);
+ BOOST_CHECK_EQUAL(FaceUri("fd://0").isCanonical(), false);
+ BOOST_CHECK_EQUAL(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, "");
+
+ runTests();
+}
+
BOOST_AUTO_TEST_CASE(Bug1635)
{
FaceUri uri;