From 8fc68f601b92dc4d0fa378c1d9fa2114e379f7ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Remko=20Tron=C3=A7on?= Date: Wed, 7 Apr 2010 20:52:52 +0200 Subject: Add support for multiple A entries to StaticDomainNameResolver. diff --git a/Swiften/Network/StaticDomainNameResolver.cpp b/Swiften/Network/StaticDomainNameResolver.cpp index a751fdd..196176f 100644 --- a/Swiften/Network/StaticDomainNameResolver.cpp +++ b/Swiften/Network/StaticDomainNameResolver.cpp @@ -38,10 +38,8 @@ namespace { } StaticDomainNameResolver::AddressesMap::const_iterator i = resolver->getAddresses().find(host); if (i != resolver->getAddresses().end()) { - std::vector result; - result.push_back(i->second); MainEventLoop::postEvent( - boost::bind(boost::ref(onResult), result, boost::optional())); + boost::bind(boost::ref(onResult), i->second, boost::optional())); } else { MainEventLoop::postEvent(boost::bind(boost::ref(onResult), std::vector(), boost::optional(DomainNameResolveError()))); @@ -60,7 +58,7 @@ StaticDomainNameResolver::StaticDomainNameResolver() : isResponsive(true) { } void StaticDomainNameResolver::addAddress(const String& domain, const HostAddress& address) { - addresses[domain] = address; + addresses[domain].push_back(address); } void StaticDomainNameResolver::addService(const String& service, const DomainNameServiceQuery::Result& result) { @@ -76,6 +74,10 @@ void StaticDomainNameResolver::addXMPPClientService(const String& domain, const addAddress(hostname, address.getAddress()); } +void StaticDomainNameResolver::addXMPPClientService(const String& domain, const String& hostname, int port) { + addService("_xmpp-client._tcp." + domain, ServiceQuery::Result(hostname, port, 0, 0)); +} + boost::shared_ptr StaticDomainNameResolver::createServiceQuery(const String& name) { return boost::shared_ptr(new ServiceQuery(name, this)); } diff --git a/Swiften/Network/StaticDomainNameResolver.h b/Swiften/Network/StaticDomainNameResolver.h index d7e7ba4..2428d29 100644 --- a/Swiften/Network/StaticDomainNameResolver.h +++ b/Swiften/Network/StaticDomainNameResolver.h @@ -15,7 +15,7 @@ namespace Swift { class StaticDomainNameResolver : public DomainNameResolver { public: - typedef std::map AddressesMap; + typedef std::map > AddressesMap; typedef std::vector< std::pair > ServicesCollection; public: @@ -24,6 +24,7 @@ namespace Swift { void addAddress(const String& domain, const HostAddress& address); void addService(const String& service, const DomainNameServiceQuery::Result& result); void addXMPPClientService(const String& domain, const HostAddressPort&); + void addXMPPClientService(const String& domain, const String& host, int port); const AddressesMap& getAddresses() const { return addresses; -- cgit v0.10.2-6-g49f6 From 2086abd85c97ee4e03f6d7b266076c6607012243 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Remko=20Tron=C3=A7on?= Date: Wed, 7 Apr 2010 21:20:54 +0200 Subject: Support fallback multiple host addresses when connecting. Resolves: #305 diff --git a/Swiften/Network/Connector.cpp b/Swiften/Network/Connector.cpp index ff45481..d2144d7 100644 --- a/Swiften/Network/Connector.cpp +++ b/Swiften/Network/Connector.cpp @@ -10,7 +10,7 @@ namespace Swift { -Connector::Connector(const String& hostname, DomainNameResolver* resolver, ConnectionFactory* connectionFactory, TimerFactory* timerFactory) : hostname(hostname), resolver(resolver), connectionFactory(connectionFactory), timerFactory(timerFactory), timeoutMilliseconds(0), queriedAllHosts(true) { +Connector::Connector(const String& hostname, DomainNameResolver* resolver, ConnectionFactory* connectionFactory, TimerFactory* timerFactory) : hostname(hostname), resolver(resolver), connectionFactory(connectionFactory), timerFactory(timerFactory), timeoutMilliseconds(0), queriedAllServices(true) { } void Connector::setTimeoutMilliseconds(int milliseconds) { @@ -22,7 +22,7 @@ void Connector::start() { assert(!currentConnection); assert(!serviceQuery); assert(!timer); - queriedAllHosts = false; + queriedAllServices = false; serviceQuery = resolver->createServiceQuery("_xmpp-client._tcp." + hostname); serviceQuery->onResult.connect(boost::bind(&Connector::handleServiceQueryResult, this, _1)); if (timeoutMilliseconds > 0) { @@ -44,18 +44,18 @@ void Connector::handleServiceQueryResult(const std::vector(result.begin(), result.end()); serviceQuery.reset(); - tryNextHostname(); + tryNextServiceOrFallback(); } -void Connector::tryNextHostname() { - if (queriedAllHosts) { - //std::cout << "Connector::tryNextHostName(): Queried all hosts. Error." << std::endl; +void Connector::tryNextServiceOrFallback() { + if (queriedAllServices) { + //std::cout << "Connector::tryNextServiceOrCallback(): Queried all hosts. Error." << std::endl; finish(boost::shared_ptr()); } else if (serviceQueryResults.empty()) { //std::cout << "Connector::tryNextHostName(): Falling back on A resolution" << std::endl; // Fall back on simple address resolving - queriedAllHosts = true; + queriedAllServices = true; queryAddress(hostname); } else { @@ -67,28 +67,38 @@ void Connector::tryNextHostname() { void Connector::handleAddressQueryResult(const std::vector& addresses, boost::optional error) { //std::cout << "Connector::handleAddressQueryResult(): Start" << std::endl; addressQuery.reset(); - if (!serviceQueryResults.empty()) { - DomainNameServiceQuery::Result serviceQueryResult = serviceQueryResults.front(); - serviceQueryResults.pop_front(); - if (error || addresses.empty()) { - //std::cout << "Connector::handleAddressQueryResult(): A lookup for SRV host " << serviceQueryResult.hostname << " failed." << std::endl; - tryNextHostname(); - } - else { - //std::cout << "Connector::handleAddressQueryResult(): A lookup for SRV host " << serviceQueryResult.hostname << " succeeded: " << address.toString() << std::endl; - tryConnect(HostAddressPort(addresses[0], serviceQueryResult.port)); + if (error || addresses.empty()) { + if (!serviceQueryResults.empty()) { + serviceQueryResults.pop_front(); } + tryNextServiceOrFallback(); } - else if (error || addresses.empty()) { - //std::cout << "Connector::handleAddressQueryResult(): Fallback address query failed. Giving up" << std::endl; - // The fallback address query failed - assert(queriedAllHosts); - finish(boost::shared_ptr()); + else { + addressQueryResults = std::deque(addresses.begin(), addresses.end()); + tryNextAddress(); + } +} + +void Connector::tryNextAddress() { + if (addressQueryResults.empty()) { + //std::cout << "Connector::tryNextAddress(): Done trying addresses. Moving on" << std::endl; + // Done trying all addresses. Move on to the next host. + if (!serviceQueryResults.empty()) { + serviceQueryResults.pop_front(); + } + tryNextServiceOrFallback(); } else { - //std::cout << "Connector::handleAddressQueryResult(): Fallback address query succeeded: " << address.toString() << std::endl; - // The fallback query succeeded - tryConnect(HostAddressPort(addresses[0], 5222)); + //std::cout << "Connector::tryNextAddress(): trying next address." << std::endl; + HostAddress address = addressQueryResults.front(); + addressQueryResults.pop_front(); + + int port = 5222; + if (!serviceQueryResults.empty()) { + port = serviceQueryResults.front().port; + } + + tryConnect(HostAddressPort(address, port)); } } @@ -104,7 +114,15 @@ void Connector::handleConnectionConnectFinished(bool error) { //std::cout << "Connector::handleConnectionConnectFinished() " << error << std::endl; if (error) { currentConnection.reset(); - tryNextHostname(); + if (!addressQueryResults.empty()) { + tryNextAddress(); + } + else { + if (!serviceQueryResults.empty()) { + serviceQueryResults.pop_front(); + } + tryNextServiceOrFallback(); + } } else { finish(currentConnection); diff --git a/Swiften/Network/Connector.h b/Swiften/Network/Connector.h index 32dd9ab..59fe708 100644 --- a/Swiften/Network/Connector.h +++ b/Swiften/Network/Connector.h @@ -31,7 +31,8 @@ namespace Swift { void handleAddressQueryResult(const std::vector& address, boost::optional error); void queryAddress(const String& hostname); - void tryNextHostname(); + void tryNextServiceOrFallback(); + void tryNextAddress(); void tryConnect(const HostAddressPort& target); void handleConnectionConnectFinished(bool error); @@ -48,7 +49,8 @@ namespace Swift { boost::shared_ptr serviceQuery; std::deque serviceQueryResults; boost::shared_ptr addressQuery; - bool queriedAllHosts; + std::deque addressQueryResults; + bool queriedAllServices; boost::shared_ptr currentConnection; }; }; diff --git a/Swiften/Network/UnitTest/ConnectorTest.cpp b/Swiften/Network/UnitTest/ConnectorTest.cpp index 663011c..2a2ab41 100644 --- a/Swiften/Network/UnitTest/ConnectorTest.cpp +++ b/Swiften/Network/UnitTest/ConnectorTest.cpp @@ -18,6 +18,7 @@ using namespace Swift; class ConnectorTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(ConnectorTest); CPPUNIT_TEST(testConnect); + CPPUNIT_TEST(testConnect_FirstAddressHostFails); CPPUNIT_TEST(testConnect_NoSRVHost); CPPUNIT_TEST(testConnect_NoHosts); CPPUNIT_TEST(testConnect_FirstSRVHostFails); @@ -73,6 +74,24 @@ class ConnectorTest : public CppUnit::TestFixture { CPPUNIT_ASSERT(host3 == *(connections[0]->hostAddressPort)); } + void testConnect_FirstAddressHostFails() { + std::auto_ptr testling(createConnector()); + + HostAddress address1("1.1.1.1"); + HostAddress address2("2.2.2.2"); + resolver->addXMPPClientService("foo.com", "host-foo.com", 1234); + resolver->addAddress("host-foo.com", address1); + resolver->addAddress("host-foo.com", address2); + connectionFactory->failingPorts.push_back(HostAddressPort(address1, 1234)); + + testling->start(); + eventLoop->processEvents(); + + CPPUNIT_ASSERT_EQUAL(1, static_cast(connections.size())); + CPPUNIT_ASSERT(connections[0]); + CPPUNIT_ASSERT(HostAddressPort(address2, 1234) == *(connections[0]->hostAddressPort)); + } + void testConnect_NoHosts() { std::auto_ptr testling(createConnector()); @@ -207,7 +226,8 @@ class ConnectorTest : public CppUnit::TestFixture { void connect(const HostAddressPort& address) { hostAddressPort = address; if (isResponsive) { - MainEventLoop::postEvent(boost::bind(boost::ref(onConnectFinished), std::find(failingPorts.begin(), failingPorts.end(), address) != failingPorts.end())); + bool fail = std::find(failingPorts.begin(), failingPorts.end(), address) != failingPorts.end(); + MainEventLoop::postEvent(boost::bind(boost::ref(onConnectFinished), fail)); } } -- cgit v0.10.2-6-g49f6