summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'Swiften/Component')
-rw-r--r--Swiften/Component/ComponentConnector.cpp107
-rw-r--r--Swiften/Component/ComponentConnector.h64
-rw-r--r--Swiften/Component/ComponentHandshakeGenerator.cpp23
-rw-r--r--Swiften/Component/ComponentHandshakeGenerator.h17
-rw-r--r--Swiften/Component/SConscript8
-rw-r--r--Swiften/Component/UnitTest/ComponentConnectorTest.cpp208
-rw-r--r--Swiften/Component/UnitTest/ComponentHandshakeGeneratorTest.cpp33
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('&', "&amp;");
+ concatenatedString.replaceAll('<', "&lt;");
+ concatenatedString.replaceAll('>', "&gt;");
+ concatenatedString.replaceAll('\'', "&apos;");
+ concatenatedString.replaceAll('"', "&quot;");
+ 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);