diff options
| author | Remko Tronçon <git@el-tramo.be> | 2011-04-11 18:28:11 (GMT) | 
|---|---|---|
| committer | Remko Tronçon <git@el-tramo.be> | 2011-04-18 19:11:42 (GMT) | 
| commit | eda3475756f88098de69d5bea05b328b53d7ec04 (patch) | |
| tree | fb54c7df442d89bc104e21fc4a50fa7ac4a83975 | |
| parent | e1a3e6495c2af6c5bea3e8aa81d83af4f7872e93 (diff) | |
| download | swift-eda3475756f88098de69d5bea05b328b53d7ec04.zip swift-eda3475756f88098de69d5bea05b328b53d7ec04.tar.bz2  | |
Added chained connector.
This connector will be useful for fallbacks in case of proxies.
| -rw-r--r-- | Swiften/Network/ChainedConnector.cpp | 80 | ||||
| -rw-r--r-- | Swiften/Network/ChainedConnector.h | 47 | ||||
| -rw-r--r-- | Swiften/Network/SConscript | 1 | ||||
| -rw-r--r-- | Swiften/Network/UnitTest/ChainedConnectorTest.cpp | 161 | ||||
| -rw-r--r-- | Swiften/SConscript | 1 | 
5 files changed, 290 insertions, 0 deletions
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 <Swiften/Network/ChainedConnector.h> + +#include <boost/bind.hpp> + +#include <Swiften/Base/Log.h> +#include <Swiften/Base/foreach.h> +#include <Swiften/Network/Connector.h> + +using namespace Swift; + +ChainedConnector::ChainedConnector( +		const std::string& hostname,  +		DomainNameResolver* resolver,  +		const std::vector<ConnectionFactory*>& 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<ConnectionFactory*>(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<Connection>()); +} + +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<Connection>()); +	} +	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> 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> 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 <string> +#include <vector> +#include <deque> +#include <boost/shared_ptr.hpp> + +#include <Swiften/Base/boost_bsignals.h> + +namespace Swift { +	class Connection; +	class Connector; +	class ConnectionFactory; +	class TimerFactory; +	class DomainNameResolver; + +	class ChainedConnector { +		public: +			ChainedConnector(const std::string& hostname, DomainNameResolver*, const std::vector<ConnectionFactory*>&, TimerFactory*); + +			void setTimeoutMilliseconds(int milliseconds); +			void start(); +			void stop(); + +			boost::signal<void (boost::shared_ptr<Connection>)> onConnectFinished; + +		private: +			void finish(boost::shared_ptr<Connection> connection); +			void tryNextConnectionFactory(); +			void handleConnectorFinished(boost::shared_ptr<Connection>); + +		private: +			std::string hostname; +			DomainNameResolver* resolver; +			std::vector<ConnectionFactory*> connectionFactories; +			TimerFactory* timerFactory; +			int timeoutMilliseconds; +			std::deque<ConnectionFactory*> connectionFactoryQueue; +			boost::shared_ptr<Connector> 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 <cppunit/extensions/HelperMacros.h> +#include <cppunit/extensions/TestFactoryRegistry.h> + +#include <boost/bind.hpp> +#include <boost/smart_ptr/make_shared.hpp> + +#include <Swiften/Network/ChainedConnector.h> +#include <Swiften/Network/Connection.h> +#include <Swiften/Network/ConnectionFactory.h> +#include <Swiften/Network/HostAddressPort.h> +#include <Swiften/Network/StaticDomainNameResolver.h> +#include <Swiften/Network/DummyTimerFactory.h> +#include <Swiften/EventLoop/DummyEventLoop.h> + +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<ChainedConnector> testling(createConnector()); +			connectionFactory1->connects = true; +			connectionFactory2->connects = false; + +			testling->start(); +			eventLoop->processEvents(); + +			CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(connections.size())); +			CPPUNIT_ASSERT(connections[0]); +			CPPUNIT_ASSERT_EQUAL(1, boost::dynamic_pointer_cast<MockConnection>(connections[0])->id); +		} + +		void testConnect_SecondConnectorSucceeds() { +			boost::shared_ptr<ChainedConnector> testling(createConnector()); +			connectionFactory1->connects = false; +			connectionFactory2->connects = true; + +			testling->start(); +			eventLoop->processEvents(); + +			CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(connections.size())); +			CPPUNIT_ASSERT(connections[0]); +			CPPUNIT_ASSERT_EQUAL(2, boost::dynamic_pointer_cast<MockConnection>(connections[0])->id); +		} + +		void testConnect_NoConnectorSucceeds() { +			boost::shared_ptr<ChainedConnector> testling(createConnector()); +			connectionFactory1->connects = false; +			connectionFactory2->connects = false; + +			testling->start(); +			eventLoop->processEvents(); + +			CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(connections.size())); +			CPPUNIT_ASSERT(!connections[0]); +		} + +		void testStop() { +			boost::shared_ptr<ChainedConnector> testling(createConnector()); +			connectionFactory1->connects = true; +			connectionFactory2->connects = false; + +			testling->start(); +			testling->stop(); +			eventLoop->processEvents(); + +			CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(connections.size())); +			CPPUNIT_ASSERT(!connections[0]); +		} + +	private: +		boost::shared_ptr<ChainedConnector> createConnector() { +			std::vector<ConnectionFactory*> factories; +			factories.push_back(connectionFactory1); +			factories.push_back(connectionFactory2); +			boost::shared_ptr<ChainedConnector> connector = boost::make_shared<ChainedConnector>("foo.com", resolver, factories, timerFactory); +			connector->onConnectFinished.connect(boost::bind(&ChainedConnectorTest::handleConnectorFinished, this, _1)); +			return connector; +		} + +		void handleConnectorFinished(boost::shared_ptr<Connection> connection) { +			boost::shared_ptr<MockConnection> c(boost::dynamic_pointer_cast<MockConnection>(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<Connection> createConnection() { +				return boost::make_shared<MockConnection>(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<MockConnection> > 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"),  | 
 Swift