From eda3475756f88098de69d5bea05b328b53d7ec04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Remko=20Tron=C3=A7on?= Date: Mon, 11 Apr 2011 20:28:11 +0200 Subject: Added chained connector. This connector will be useful for fallbacks in case of proxies. diff --git a/Swiften/Network/ChainedConnector.cpp b/Swiften/Network/ChainedConnector.cpp new file mode 100644 index 0000000..e08ffc6 --- /dev/null +++ b/Swiften/Network/ChainedConnector.cpp @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2011 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include + +#include + +#include +#include +#include + +using namespace Swift; + +ChainedConnector::ChainedConnector( + const std::string& hostname, + DomainNameResolver* resolver, + const std::vector& connectionFactories, + TimerFactory* timerFactory) : + hostname(hostname), + resolver(resolver), + connectionFactories(connectionFactories), + timerFactory(timerFactory), + timeoutMilliseconds(0) { +} + +void ChainedConnector::setTimeoutMilliseconds(int milliseconds) { + timeoutMilliseconds = milliseconds; +} + +void ChainedConnector::start() { + SWIFT_LOG(debug) << "Starting queued connector for " << hostname << std::endl; + + connectionFactoryQueue = std::deque(connectionFactories.begin(), connectionFactories.end()); + tryNextConnectionFactory(); +} + +void ChainedConnector::stop() { + if (currentConnector) { + currentConnector->onConnectFinished.disconnect(boost::bind(&ChainedConnector::handleConnectorFinished, this, _1)); + currentConnector->stop(); + currentConnector.reset(); + } + finish(boost::shared_ptr()); +} + +void ChainedConnector::tryNextConnectionFactory() { + SWIFT_LOG(debug) << "Trying next connection factory" << std::endl; + assert(!currentConnector); + if (connectionFactoryQueue.empty()) { + SWIFT_LOG(debug) << "No more connection factories" << std::endl; + finish(boost::shared_ptr()); + } + else { + ConnectionFactory* connectionFactory = connectionFactoryQueue.front(); + connectionFactoryQueue.pop_front(); + currentConnector = Connector::create(hostname, resolver, connectionFactory, timerFactory); + currentConnector->setTimeoutMilliseconds(timeoutMilliseconds); + currentConnector->onConnectFinished.connect(boost::bind(&ChainedConnector::handleConnectorFinished, this, _1)); + currentConnector->start(); + } +} + +void ChainedConnector::handleConnectorFinished(boost::shared_ptr connection) { + SWIFT_LOG(debug) << "Connector finished" << std::endl; + currentConnector->onConnectFinished.disconnect(boost::bind(&ChainedConnector::handleConnectorFinished, this, _1)); + currentConnector.reset(); + if (connection) { + finish(connection); + } + else { + tryNextConnectionFactory(); + } +} + +void ChainedConnector::finish(boost::shared_ptr connection) { + onConnectFinished(connection); +} diff --git a/Swiften/Network/ChainedConnector.h b/Swiften/Network/ChainedConnector.h new file mode 100644 index 0000000..15b17f3 --- /dev/null +++ b/Swiften/Network/ChainedConnector.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2011 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include +#include +#include +#include + +#include + +namespace Swift { + class Connection; + class Connector; + class ConnectionFactory; + class TimerFactory; + class DomainNameResolver; + + class ChainedConnector { + public: + ChainedConnector(const std::string& hostname, DomainNameResolver*, const std::vector&, TimerFactory*); + + void setTimeoutMilliseconds(int milliseconds); + void start(); + void stop(); + + boost::signal)> onConnectFinished; + + private: + void finish(boost::shared_ptr connection); + void tryNextConnectionFactory(); + void handleConnectorFinished(boost::shared_ptr); + + private: + std::string hostname; + DomainNameResolver* resolver; + std::vector connectionFactories; + TimerFactory* timerFactory; + int timeoutMilliseconds; + std::deque connectionFactoryQueue; + boost::shared_ptr currentConnector; + }; +}; diff --git a/Swiften/Network/SConscript b/Swiften/Network/SConscript index 420dff5..93732d1 100644 --- a/Swiften/Network/SConscript +++ b/Swiften/Network/SConscript @@ -14,6 +14,7 @@ sourceList = [ "ConnectionServer.cpp", "DummyConnection.cpp", "FakeConnection.cpp", + "ChainedConnector.cpp", "Connector.cpp", "TimerFactory.cpp", "DummyTimerFactory.cpp", diff --git a/Swiften/Network/UnitTest/ChainedConnectorTest.cpp b/Swiften/Network/UnitTest/ChainedConnectorTest.cpp new file mode 100644 index 0000000..9c4c99d --- /dev/null +++ b/Swiften/Network/UnitTest/ChainedConnectorTest.cpp @@ -0,0 +1,161 @@ +/* + * Copyright (c) 2010 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +using namespace Swift; + +class ChainedConnectorTest : public CppUnit::TestFixture { + CPPUNIT_TEST_SUITE(ChainedConnectorTest); + CPPUNIT_TEST(testConnect_FirstConnectorSucceeds); + CPPUNIT_TEST(testConnect_SecondConnectorSucceeds); + CPPUNIT_TEST(testConnect_NoConnectorSucceeds); + CPPUNIT_TEST(testStop); + CPPUNIT_TEST_SUITE_END(); + + public: + void setUp() { + host = HostAddressPort(HostAddress("1.1.1.1"), 1234); + eventLoop = new DummyEventLoop(); + resolver = new StaticDomainNameResolver(eventLoop); + resolver->addXMPPClientService("foo.com", host); + connectionFactory1 = new MockConnectionFactory(eventLoop, 1); + connectionFactory2 = new MockConnectionFactory(eventLoop, 2); + timerFactory = new DummyTimerFactory(); + } + + void tearDown() { + delete timerFactory; + delete connectionFactory2; + delete connectionFactory1; + delete resolver; + delete eventLoop; + } + + void testConnect_FirstConnectorSucceeds() { + boost::shared_ptr testling(createConnector()); + connectionFactory1->connects = true; + connectionFactory2->connects = false; + + testling->start(); + eventLoop->processEvents(); + + CPPUNIT_ASSERT_EQUAL(1, static_cast(connections.size())); + CPPUNIT_ASSERT(connections[0]); + CPPUNIT_ASSERT_EQUAL(1, boost::dynamic_pointer_cast(connections[0])->id); + } + + void testConnect_SecondConnectorSucceeds() { + boost::shared_ptr testling(createConnector()); + connectionFactory1->connects = false; + connectionFactory2->connects = true; + + testling->start(); + eventLoop->processEvents(); + + CPPUNIT_ASSERT_EQUAL(1, static_cast(connections.size())); + CPPUNIT_ASSERT(connections[0]); + CPPUNIT_ASSERT_EQUAL(2, boost::dynamic_pointer_cast(connections[0])->id); + } + + void testConnect_NoConnectorSucceeds() { + boost::shared_ptr testling(createConnector()); + connectionFactory1->connects = false; + connectionFactory2->connects = false; + + testling->start(); + eventLoop->processEvents(); + + CPPUNIT_ASSERT_EQUAL(1, static_cast(connections.size())); + CPPUNIT_ASSERT(!connections[0]); + } + + void testStop() { + boost::shared_ptr testling(createConnector()); + connectionFactory1->connects = true; + connectionFactory2->connects = false; + + testling->start(); + testling->stop(); + eventLoop->processEvents(); + + CPPUNIT_ASSERT_EQUAL(1, static_cast(connections.size())); + CPPUNIT_ASSERT(!connections[0]); + } + + private: + boost::shared_ptr createConnector() { + std::vector factories; + factories.push_back(connectionFactory1); + factories.push_back(connectionFactory2); + boost::shared_ptr connector = boost::make_shared("foo.com", resolver, factories, timerFactory); + connector->onConnectFinished.connect(boost::bind(&ChainedConnectorTest::handleConnectorFinished, this, _1)); + return connector; + } + + void handleConnectorFinished(boost::shared_ptr connection) { + boost::shared_ptr c(boost::dynamic_pointer_cast(connection)); + if (connection) { + assert(c); + } + connections.push_back(c); + } + + struct MockConnection : public Connection { + public: + MockConnection(bool connects, int id, EventLoop* eventLoop) : connects(connects), id(id), eventLoop(eventLoop) { + } + + void listen() { assert(false); } + void connect(const HostAddressPort&) { + eventLoop->postEvent(boost::bind(boost::ref(onConnectFinished), !connects)); + } + + HostAddressPort getLocalAddress() const { return HostAddressPort(); } + void disconnect() { assert(false); } + void write(const ByteArray&) { assert(false); } + + bool connects; + int id; + EventLoop* eventLoop; + }; + + struct MockConnectionFactory : public ConnectionFactory { + MockConnectionFactory(EventLoop* eventLoop, int id) : eventLoop(eventLoop), connects(true), id(id) { + } + + boost::shared_ptr createConnection() { + return boost::make_shared(connects, id, eventLoop); + } + + EventLoop* eventLoop; + bool connects; + int id; + }; + + private: + HostAddressPort host; + DummyEventLoop* eventLoop; + StaticDomainNameResolver* resolver; + MockConnectionFactory* connectionFactory1; + MockConnectionFactory* connectionFactory2; + DummyTimerFactory* timerFactory; + std::vector< boost::shared_ptr > connections; +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(ChainedConnectorTest); diff --git a/Swiften/SConscript b/Swiften/SConscript index 12714ae..d66bfb3 100644 --- a/Swiften/SConscript +++ b/Swiften/SConscript @@ -226,6 +226,7 @@ if env["SCONS_STAGE"] == "build" : File("MUC/UnitTest/MUCTest.cpp"), File("Network/UnitTest/HostAddressTest.cpp"), File("Network/UnitTest/ConnectorTest.cpp"), + File("Network/UnitTest/ChainedConnectorTest.cpp"), File("Parser/PayloadParsers/UnitTest/BodyParserTest.cpp"), File("Parser/PayloadParsers/UnitTest/DiscoInfoParserTest.cpp"), File("Parser/PayloadParsers/UnitTest/ErrorParserTest.cpp"), -- cgit v0.10.2-6-g49f6