diff options
Diffstat (limited to 'Swiften/Component')
-rw-r--r-- | Swiften/Component/ComponentConnector.cpp | 107 | ||||
-rw-r--r-- | Swiften/Component/ComponentConnector.h | 64 | ||||
-rw-r--r-- | Swiften/Component/ComponentHandshakeGenerator.cpp | 23 | ||||
-rw-r--r-- | Swiften/Component/ComponentHandshakeGenerator.h | 17 | ||||
-rw-r--r-- | Swiften/Component/SConscript | 8 | ||||
-rw-r--r-- | Swiften/Component/UnitTest/ComponentConnectorTest.cpp | 208 | ||||
-rw-r--r-- | Swiften/Component/UnitTest/ComponentHandshakeGeneratorTest.cpp | 33 |
7 files changed, 460 insertions, 0 deletions
diff --git a/Swiften/Component/ComponentConnector.cpp b/Swiften/Component/ComponentConnector.cpp new file mode 100644 index 0000000..e764138 --- /dev/null +++ b/Swiften/Component/ComponentConnector.cpp @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2010 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include "Swiften/Component/ComponentConnector.h" + +#include <boost/bind.hpp> +#include <iostream> + +#include "Swiften/Network/ConnectionFactory.h" +#include "Swiften/Network/DomainNameResolver.h" +#include "Swiften/Network/DomainNameAddressQuery.h" +#include "Swiften/Network/TimerFactory.h" + +namespace Swift { + +ComponentConnector::ComponentConnector(const String& hostname, int port, DomainNameResolver* resolver, ConnectionFactory* connectionFactory, TimerFactory* timerFactory) : hostname(hostname), port(port), resolver(resolver), connectionFactory(connectionFactory), timerFactory(timerFactory), timeoutMilliseconds(0) { +} + +void ComponentConnector::setTimeoutMilliseconds(int milliseconds) { + timeoutMilliseconds = milliseconds; +} + +void ComponentConnector::start() { + assert(!currentConnection); + assert(!timer); + assert(!addressQuery); + addressQuery = resolver->createAddressQuery(hostname); + addressQuery->onResult.connect(boost::bind(&ComponentConnector::handleAddressQueryResult, shared_from_this(), _1, _2)); + if (timeoutMilliseconds > 0) { + timer = timerFactory->createTimer(timeoutMilliseconds); + timer->onTick.connect(boost::bind(&ComponentConnector::handleTimeout, shared_from_this())); + timer->start(); + } + addressQuery->run(); +} + +void ComponentConnector::stop() { + finish(boost::shared_ptr<Connection>()); +} + + +void ComponentConnector::handleAddressQueryResult(const std::vector<HostAddress>& addresses, boost::optional<DomainNameResolveError> error) { + addressQuery.reset(); + if (error || addresses.empty()) { + finish(boost::shared_ptr<Connection>()); + } + else { + addressQueryResults = std::deque<HostAddress>(addresses.begin(), addresses.end()); + tryNextAddress(); + } +} + +void ComponentConnector::tryNextAddress() { + assert(!addressQueryResults.empty()); + HostAddress address = addressQueryResults.front(); + addressQueryResults.pop_front(); + tryConnect(HostAddressPort(address, port)); +} + +void ComponentConnector::tryConnect(const HostAddressPort& target) { + assert(!currentConnection); + currentConnection = connectionFactory->createConnection(); + currentConnection->onConnectFinished.connect(boost::bind(&ComponentConnector::handleConnectionConnectFinished, shared_from_this(), _1)); + currentConnection->connect(target); +} + +void ComponentConnector::handleConnectionConnectFinished(bool error) { + currentConnection->onConnectFinished.disconnect(boost::bind(&ComponentConnector::handleConnectionConnectFinished, shared_from_this(), _1)); + if (error) { + currentConnection.reset(); + if (!addressQueryResults.empty()) { + tryNextAddress(); + } + else { + finish(boost::shared_ptr<Connection>()); + } + } + else { + finish(currentConnection); + } +} + +void ComponentConnector::finish(boost::shared_ptr<Connection> connection) { + if (timer) { + timer->stop(); + timer->onTick.disconnect(boost::bind(&ComponentConnector::handleTimeout, shared_from_this())); + timer.reset(); + } + if (addressQuery) { + addressQuery->onResult.disconnect(boost::bind(&ComponentConnector::handleAddressQueryResult, shared_from_this(), _1, _2)); + addressQuery.reset(); + } + if (currentConnection) { + currentConnection->onConnectFinished.disconnect(boost::bind(&ComponentConnector::handleConnectionConnectFinished, shared_from_this(), _1)); + currentConnection.reset(); + } + onConnectFinished(connection); +} + +void ComponentConnector::handleTimeout() { + finish(boost::shared_ptr<Connection>()); +} + +}; diff --git a/Swiften/Component/ComponentConnector.h b/Swiften/Component/ComponentConnector.h new file mode 100644 index 0000000..a84d8ba --- /dev/null +++ b/Swiften/Component/ComponentConnector.h @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2010 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include <deque> +#include "Swiften/Base/boost_bsignals.h" +#include <boost/shared_ptr.hpp> + +#include "Swiften/Network/Connection.h" +#include "Swiften/Network/Timer.h" +#include "Swiften/Network/HostAddressPort.h" +#include "Swiften/Base/String.h" +#include "Swiften/Network/DomainNameResolveError.h" + +namespace Swift { + class DomainNameAddressQuery; + class DomainNameResolver; + class ConnectionFactory; + class TimerFactory; + + class ComponentConnector : public boost::bsignals::trackable, public boost::enable_shared_from_this<ComponentConnector> { + public: + typedef boost::shared_ptr<ComponentConnector> ref; + + static ComponentConnector::ref create(const String& hostname, int port, DomainNameResolver* resolver, ConnectionFactory* connectionFactory, TimerFactory* timerFactory) { + return ComponentConnector::ref(new ComponentConnector(hostname, port, resolver, connectionFactory, timerFactory)); + } + + void setTimeoutMilliseconds(int milliseconds); + + void start(); + void stop(); + + boost::signal<void (boost::shared_ptr<Connection>)> onConnectFinished; + + private: + ComponentConnector(const String& hostname, int port, DomainNameResolver*, ConnectionFactory*, TimerFactory*); + + void handleAddressQueryResult(const std::vector<HostAddress>& address, boost::optional<DomainNameResolveError> error); + void tryNextAddress(); + void tryConnect(const HostAddressPort& target); + + void handleConnectionConnectFinished(bool error); + void finish(boost::shared_ptr<Connection>); + void handleTimeout(); + + + private: + String hostname; + int port; + DomainNameResolver* resolver; + ConnectionFactory* connectionFactory; + TimerFactory* timerFactory; + int timeoutMilliseconds; + boost::shared_ptr<Timer> timer; + boost::shared_ptr<DomainNameAddressQuery> addressQuery; + std::deque<HostAddress> addressQueryResults; + boost::shared_ptr<Connection> currentConnection; + }; +}; diff --git a/Swiften/Component/ComponentHandshakeGenerator.cpp b/Swiften/Component/ComponentHandshakeGenerator.cpp new file mode 100644 index 0000000..422f986 --- /dev/null +++ b/Swiften/Component/ComponentHandshakeGenerator.cpp @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2010 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include "Swiften/Component/ComponentHandshakeGenerator.h" +#include "Swiften/StringCodecs/Hexify.h" +#include "Swiften/StringCodecs/SHA1.h" + +namespace Swift { + +String ComponentHandshakeGenerator::getHandshake(const String& streamID, const String& secret) { + String concatenatedString = streamID + secret; + concatenatedString.replaceAll('&', "&"); + concatenatedString.replaceAll('<', "<"); + concatenatedString.replaceAll('>', ">"); + concatenatedString.replaceAll('\'', "'"); + concatenatedString.replaceAll('"', """); + return Hexify::hexify(SHA1::getHash(ByteArray(concatenatedString))); +} + +} diff --git a/Swiften/Component/ComponentHandshakeGenerator.h b/Swiften/Component/ComponentHandshakeGenerator.h new file mode 100644 index 0000000..d71a664 --- /dev/null +++ b/Swiften/Component/ComponentHandshakeGenerator.h @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2010 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include "Swiften/Base/String.h" + +namespace Swift { + class ComponentHandshakeGenerator { + public: + static String getHandshake(const String& streamID, const String& secret); + }; + +} diff --git a/Swiften/Component/SConscript b/Swiften/Component/SConscript new file mode 100644 index 0000000..6d86575 --- /dev/null +++ b/Swiften/Component/SConscript @@ -0,0 +1,8 @@ +Import("swiften_env") + +sources = [ + "ComponentHandshakeGenerator.cpp", + "ComponentConnector.cpp", + ] + +swiften_env.Append(SWIFTEN_OBJECTS = swiften_env.StaticObject(sources))
\ No newline at end of file diff --git a/Swiften/Component/UnitTest/ComponentConnectorTest.cpp b/Swiften/Component/UnitTest/ComponentConnectorTest.cpp new file mode 100644 index 0000000..7b8a4f8 --- /dev/null +++ b/Swiften/Component/UnitTest/ComponentConnectorTest.cpp @@ -0,0 +1,208 @@ +/* + * 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/optional.hpp> +#include <boost/bind.hpp> + +#include "Swiften/Component/ComponentConnector.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/MainEventLoop.h" +#include "Swiften/EventLoop/DummyEventLoop.h" + +using namespace Swift; + +class ComponentConnectorTest : public CppUnit::TestFixture { + CPPUNIT_TEST_SUITE(ComponentConnectorTest); + CPPUNIT_TEST(testConnect); + CPPUNIT_TEST(testConnect_FirstAddressHostFails); + CPPUNIT_TEST(testConnect_NoHosts); + CPPUNIT_TEST(testConnect_TimeoutDuringResolve); + CPPUNIT_TEST(testConnect_TimeoutDuringConnect); + CPPUNIT_TEST(testConnect_NoTimeout); + CPPUNIT_TEST(testStop_Timeout); + CPPUNIT_TEST_SUITE_END(); + + public: + ComponentConnectorTest() : host1("1.1.1.1"), host2("2.2.2.2") { + } + + void setUp() { + eventLoop = new DummyEventLoop(); + resolver = new StaticDomainNameResolver(); + connectionFactory = new MockConnectionFactory(); + timerFactory = new DummyTimerFactory(); + } + + void tearDown() { + delete timerFactory; + delete connectionFactory; + delete resolver; + delete eventLoop; + } + + void testConnect() { + ComponentConnector::ref testling(createConnector("foo.com", 1234)); + resolver->addAddress("foo.com", host1); + + testling->start(); + eventLoop->processEvents(); + + CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(connections.size())); + CPPUNIT_ASSERT(connections[0]); + CPPUNIT_ASSERT(HostAddressPort(host1, 1234) == *(connections[0]->hostAddressPort)); + } + + void testConnect_FirstAddressHostFails() { + ComponentConnector::ref testling(createConnector("foo.com", 1234)); + resolver->addAddress("foo.com", host1); + resolver->addAddress("foo.com", host2); + connectionFactory->failingPorts.push_back(HostAddressPort(host1, 1234)); + + testling->start(); + eventLoop->processEvents(); + + CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(connections.size())); + CPPUNIT_ASSERT(connections[0]); + CPPUNIT_ASSERT(HostAddressPort(host2, 1234) == *(connections[0]->hostAddressPort)); + } + + void testConnect_NoHosts() { + ComponentConnector::ref testling(createConnector("foo.com", 1234)); + + testling->start(); + eventLoop->processEvents(); + + CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(connections.size())); + CPPUNIT_ASSERT(!connections[0]); + } + + + void testConnect_TimeoutDuringResolve() { + ComponentConnector::ref testling(createConnector("foo.com", 1234)); + + testling->setTimeoutMilliseconds(10); + resolver->setIsResponsive(false); + + testling->start(); + eventLoop->processEvents(); + timerFactory->setTime(10); + eventLoop->processEvents(); + + CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(connections.size())); + CPPUNIT_ASSERT(!connections[0]); + } + + void testConnect_TimeoutDuringConnect() { + ComponentConnector::ref testling(createConnector("foo.com", 1234)); + testling->setTimeoutMilliseconds(10); + resolver->addAddress("foo.com", host1); + connectionFactory->isResponsive = false; + + testling->start(); + eventLoop->processEvents(); + timerFactory->setTime(10); + eventLoop->processEvents(); + + CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(connections.size())); + CPPUNIT_ASSERT(!connections[0]); + } + + void testConnect_NoTimeout() { + ComponentConnector::ref testling(createConnector("foo.com", 1234)); + testling->setTimeoutMilliseconds(10); + resolver->addAddress("foo.com", host1); + + testling->start(); + eventLoop->processEvents(); + timerFactory->setTime(10); + eventLoop->processEvents(); + + CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(connections.size())); + CPPUNIT_ASSERT(connections[0]); + } + + void testStop_Timeout() { + ComponentConnector::ref testling(createConnector("foo.com", 1234)); + testling->setTimeoutMilliseconds(10); + resolver->addAddress("foo.com", host1); + + testling->start(); + testling->stop(); + + eventLoop->processEvents(); + timerFactory->setTime(10); + eventLoop->processEvents(); + + CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(connections.size())); + CPPUNIT_ASSERT(!connections[0]); + } + + private: + ComponentConnector::ref createConnector(const String& hostname, int port) { + ComponentConnector::ref connector = ComponentConnector::create(hostname, port, resolver, connectionFactory, timerFactory); + connector->onConnectFinished.connect(boost::bind(&ComponentConnectorTest::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(const std::vector<HostAddressPort>& failingPorts, bool isResponsive) : failingPorts(failingPorts), isResponsive(isResponsive) {} + + void listen() { assert(false); } + void connect(const HostAddressPort& address) { + hostAddressPort = address; + if (isResponsive) { + bool fail = std::find(failingPorts.begin(), failingPorts.end(), address) != failingPorts.end(); + MainEventLoop::postEvent(boost::bind(boost::ref(onConnectFinished), fail)); + } + } + + void disconnect() { assert(false); } + void write(const ByteArray&) { assert(false); } + + boost::optional<HostAddressPort> hostAddressPort; + std::vector<HostAddressPort> failingPorts; + bool isResponsive; + }; + + struct MockConnectionFactory : public ConnectionFactory { + MockConnectionFactory() : isResponsive(true) { + } + + boost::shared_ptr<Connection> createConnection() { + return boost::shared_ptr<Connection>(new MockConnection(failingPorts, isResponsive)); + } + + bool isResponsive; + std::vector<HostAddressPort> failingPorts; + }; + + private: + HostAddress host1; + HostAddress host2; + DummyEventLoop* eventLoop; + StaticDomainNameResolver* resolver; + MockConnectionFactory* connectionFactory; + DummyTimerFactory* timerFactory; + std::vector< boost::shared_ptr<MockConnection> > connections; +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(ComponentConnectorTest); diff --git a/Swiften/Component/UnitTest/ComponentHandshakeGeneratorTest.cpp b/Swiften/Component/UnitTest/ComponentHandshakeGeneratorTest.cpp new file mode 100644 index 0000000..e72dbea --- /dev/null +++ b/Swiften/Component/UnitTest/ComponentHandshakeGeneratorTest.cpp @@ -0,0 +1,33 @@ +/* + * 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 "Swiften/Component/ComponentHandshakeGenerator.h" + +using namespace Swift; + +class ComponentHandshakeGeneratorTest : public CppUnit::TestFixture { + CPPUNIT_TEST_SUITE(ComponentHandshakeGeneratorTest); + CPPUNIT_TEST(testGetHandshake); + CPPUNIT_TEST(testGetHandshake_SpecialChars); + CPPUNIT_TEST_SUITE_END(); + + public: + void testGetHandshake() { + String result = ComponentHandshakeGenerator::getHandshake("myid", "mysecret"); + CPPUNIT_ASSERT_EQUAL(String("4011cd31f9b99ac089a0cd7ce297da7323fa2525"), result); + } + + void testGetHandshake_SpecialChars() { + String result = ComponentHandshakeGenerator::getHandshake("&<", ">'\""); + CPPUNIT_ASSERT_EQUAL(String("33631b3e0aaeb2a11c4994c917919324028873fe"), result); + } + +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(ComponentHandshakeGeneratorTest); |