summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKevin Smith <git@kismith.co.uk>2011-11-12 16:56:21 (GMT)
committerKevin Smith <git@kismith.co.uk>2011-12-13 08:17:58 (GMT)
commit81c09a0f6a3e87b078340d7f35d0dea4c03f3a6d (patch)
tree4371c5808ee26b2b5ed79ace9ccb439ff2988945 /Swiften/Network/UnitTest/BOSHConnectionTest.cpp
parentfd17fe0d239f97cedebe4ceffa234155bd299b68 (diff)
downloadswift-contrib-81c09a0f6a3e87b078340d7f35d0dea4c03f3a6d.zip
swift-contrib-81c09a0f6a3e87b078340d7f35d0dea4c03f3a6d.tar.bz2
BOSH Support for Swiften
This adds support for BOSH to Swiften. It does not expose it to Swift. Release-Notes: Swiften now allows connects over BOSH, if used appropriately.
Diffstat (limited to 'Swiften/Network/UnitTest/BOSHConnectionTest.cpp')
-rw-r--r--Swiften/Network/UnitTest/BOSHConnectionTest.cpp291
1 files changed, 291 insertions, 0 deletions
diff --git a/Swiften/Network/UnitTest/BOSHConnectionTest.cpp b/Swiften/Network/UnitTest/BOSHConnectionTest.cpp
new file mode 100644
index 0000000..9215725
--- /dev/null
+++ b/Swiften/Network/UnitTest/BOSHConnectionTest.cpp
@@ -0,0 +1,291 @@
+/*
+ * Copyright (c) 2011 Kevin Smith
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#include <QA/Checker/IO.h>
+
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+
+#include <boost/optional.hpp>
+#include <boost/bind.hpp>
+#include <boost/smart_ptr/make_shared.hpp>
+#include <boost/shared_ptr.hpp>
+#include <boost/lexical_cast.hpp>
+
+#include <Swiften/Base/Algorithm.h>
+#include <Swiften/Network/Connection.h>
+#include <Swiften/Network/ConnectionFactory.h>
+#include <Swiften/Network/BOSHConnection.h>
+#include <Swiften/Network/HostAddressPort.h>
+#include <Swiften/EventLoop/DummyEventLoop.h>
+#include <Swiften/Parser/PlatformXMLParserFactory.h>
+
+using namespace Swift;
+
+class BOSHConnectionTest : public CppUnit::TestFixture {
+ CPPUNIT_TEST_SUITE(BOSHConnectionTest);
+ CPPUNIT_TEST(testHeader);
+ CPPUNIT_TEST(testReadiness_ok);
+ CPPUNIT_TEST(testReadiness_pending);
+ CPPUNIT_TEST(testReadiness_disconnect);
+ CPPUNIT_TEST(testReadiness_noSID);
+ CPPUNIT_TEST(testWrite_Receive);
+ CPPUNIT_TEST(testWrite_ReceiveTwice);
+ CPPUNIT_TEST(testRead_Fragment);
+ CPPUNIT_TEST(testHTTPRequest);
+ CPPUNIT_TEST(testHTTPRequest_Empty);
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+ void setUp() {
+ eventLoop = new DummyEventLoop();
+ connectionFactory = new MockConnectionFactory(eventLoop);
+ connectFinished = false;
+ disconnected = false;
+ dataRead.clear();
+ }
+
+ void tearDown() {
+ eventLoop->processEvents();
+ delete connectionFactory;
+ delete eventLoop;
+ }
+
+ void testHeader() {
+ BOSHConnection::ref testling = createTestling();
+ testling->connect(HostAddressPort(HostAddress("127.0.0.1"), 5280));
+ eventLoop->processEvents();
+ testling->startStream("wonderland.lit", 1);
+ std::string initial("<body wait='60' "
+ "inactivity='30' "
+ "polling='5' "
+ "requests='2' "
+ "hold='1' "
+ "maxpause='120' "
+ "sid='MyShinySID' "
+ "ver='1.6' "
+ "from='wonderland.lit' "
+ "xmlns='http://jabber.org/protocol/httpbind'/>");
+ readResponse(initial, connectionFactory->connections[0]);
+ CPPUNIT_ASSERT_EQUAL(std::string("MyShinySID"), sid);
+ CPPUNIT_ASSERT(testling->isReadyToSend());
+ }
+
+ void testReadiness_ok() {
+ BOSHConnection::ref testling = createTestling();
+ testling->connect(HostAddressPort(HostAddress("127.0.0.1"), 5280));
+ eventLoop->processEvents();
+ testling->setSID("blahhhhh");
+ CPPUNIT_ASSERT(testling->isReadyToSend());
+ }
+
+ void testReadiness_pending() {
+ BOSHConnection::ref testling = createTestling();
+ testling->connect(HostAddressPort(HostAddress("127.0.0.1"), 5280));
+ eventLoop->processEvents();
+ testling->setSID("mySID");
+ CPPUNIT_ASSERT(testling->isReadyToSend());
+ testling->write(createSafeByteArray("<mypayload/>"));
+ CPPUNIT_ASSERT(!testling->isReadyToSend());
+ readResponse("<body><blah/></body>", connectionFactory->connections[0]);
+ CPPUNIT_ASSERT(testling->isReadyToSend());
+ }
+
+ void testReadiness_disconnect() {
+ BOSHConnection::ref testling = createTestling();
+ testling->connect(HostAddressPort(HostAddress("127.0.0.1"), 5280));
+ eventLoop->processEvents();
+ testling->setSID("mySID");
+ CPPUNIT_ASSERT(testling->isReadyToSend());
+ connectionFactory->connections[0]->onDisconnected(false);
+ CPPUNIT_ASSERT(!testling->isReadyToSend());
+ }
+
+
+ void testReadiness_noSID() {
+ BOSHConnection::ref testling = createTestling();
+ testling->connect(HostAddressPort(HostAddress("127.0.0.1"), 5280));
+ eventLoop->processEvents();
+ CPPUNIT_ASSERT(!testling->isReadyToSend());
+ }
+
+ void testWrite_Receive() {
+ BOSHConnection::ref testling = createTestling();
+ testling->connect(HostAddressPort(HostAddress("127.0.0.1"), 5280));
+ eventLoop->processEvents();
+ testling->setSID("mySID");
+ testling->write(createSafeByteArray("<mypayload/>"));
+ readResponse("<body><blah/></body>", connectionFactory->connections[0]);
+ CPPUNIT_ASSERT_EQUAL(std::string("<blah/>"), byteArrayToString(dataRead));
+
+ }
+
+ void testWrite_ReceiveTwice() {
+ BOSHConnection::ref testling = createTestling();
+ testling->connect(HostAddressPort(HostAddress("127.0.0.1"), 5280));
+ eventLoop->processEvents();
+ testling->setSID("mySID");
+ testling->write(createSafeByteArray("<mypayload/>"));
+ readResponse("<body><blah/></body>", connectionFactory->connections[0]);
+ CPPUNIT_ASSERT_EQUAL(std::string("<blah/>"), byteArrayToString(dataRead));
+ dataRead.clear();
+ testling->write(createSafeByteArray("<mypayload2/>"));
+ readResponse("<body><bleh/></body>", connectionFactory->connections[0]);
+ CPPUNIT_ASSERT_EQUAL(std::string("<bleh/>"), byteArrayToString(dataRead));
+ }
+
+ void testRead_Fragment() {
+ BOSHConnection::ref testling = createTestling();
+ testling->connect(HostAddressPort(HostAddress("127.0.0.1"), 5280));
+ eventLoop->processEvents();
+ CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), connectionFactory->connections.size());
+ boost::shared_ptr<MockConnection> connection = connectionFactory->connections[0];
+ boost::shared_ptr<SafeByteArray> data1 = boost::make_shared<SafeByteArray>(createSafeByteArray(
+ "HTTP/1.1 200 OK\r\n"
+ "Content-Type: text/xml; charset=utf-8\r\n"
+ "Access-Control-Allow-Origin: *\r\n"
+ "Access-Control-Allow-Headers: Content-Type\r\n"
+ "Content-Length: 64\r\n"));
+ boost::shared_ptr<SafeByteArray> data2 = boost::make_shared<SafeByteArray>(createSafeByteArray(
+ "\r\n<body xmlns='http://jabber.org/protocol/httpbind'>"
+ "<bl"));
+ boost::shared_ptr<SafeByteArray> data3 = boost::make_shared<SafeByteArray>(createSafeByteArray(
+ "ah/>"
+ "</body>"));
+ connection->onDataRead(data1);
+ connection->onDataRead(data2);
+ CPPUNIT_ASSERT(dataRead.empty());
+ connection->onDataRead(data3);
+ CPPUNIT_ASSERT_EQUAL(std::string("<blah/>"), byteArrayToString(dataRead));
+ }
+
+ void testHTTPRequest() {
+ std::string data = "<blah/>";
+ std::string sid = "wigglebloom";
+ std::string fullBody = "<body xmlns='http://jabber.org/protocol/httpbind' sid='" + sid + "' rid='20'>" + data + "</body>";
+ std::pair<SafeByteArray, size_t> http = BOSHConnection::createHTTPRequest(createSafeByteArray(data), false, false, 20, sid, URL());
+ CPPUNIT_ASSERT_EQUAL(fullBody.size(), http.second);
+ }
+
+ void testHTTPRequest_Empty() {
+ std::string data = "";
+ std::string sid = "wigglebloomsickle";
+ std::string fullBody = "<body rid='42' sid='" + sid + "' xmlns='http://jabber.org/protocol/httpbind'>" + data + "</body>";
+ std::pair<SafeByteArray, size_t> http = BOSHConnection::createHTTPRequest(createSafeByteArray(data), false, false, 42, sid, URL());
+ CPPUNIT_ASSERT_EQUAL(fullBody.size(), http.second);
+ std::string response = safeByteArrayToString(http.first);
+ size_t bodyPosition = response.find("\r\n\r\n");
+ CPPUNIT_ASSERT_EQUAL(fullBody, response.substr(bodyPosition+4));
+ }
+
+ private:
+
+ BOSHConnection::ref createTestling() {
+ BOSHConnection::ref c = BOSHConnection::create(URL("http", "wonderland.lit", 5280, "http-bind"), connectionFactory, &parserFactory, static_cast<TLSContextFactory*>(NULL));
+ c->onConnectFinished.connect(boost::bind(&BOSHConnectionTest::handleConnectFinished, this, _1));
+ c->onDisconnected.connect(boost::bind(&BOSHConnectionTest::handleDisconnected, this, _1));
+ c->onXMPPDataRead.connect(boost::bind(&BOSHConnectionTest::handleDataRead, this, _1));
+ c->onSessionStarted.connect(boost::bind(&BOSHConnectionTest::handleSID, this, _1));
+ c->setRID(42);
+ return c;
+ }
+
+ void handleConnectFinished(bool error) {
+ connectFinished = true;
+ connectFinishedWithError = error;
+ }
+
+ void handleDisconnected(const boost::optional<Connection::Error>& e) {
+ disconnected = true;
+ disconnectedError = e;
+ }
+
+ void handleDataRead(const SafeByteArray& d) {
+ append(dataRead, d);
+ }
+
+ void handleSID(const std::string& s) {
+ sid = s;
+ }
+
+ struct MockConnection : public Connection {
+ public:
+ MockConnection(const std::vector<HostAddressPort>& failingPorts, EventLoop* eventLoop) : eventLoop(eventLoop), failingPorts(failingPorts), disconnected(false) {
+ }
+
+ void listen() { assert(false); }
+
+ void connect(const HostAddressPort& address) {
+ hostAddressPort = address;
+ bool fail = std::find(failingPorts.begin(), failingPorts.end(), address) != failingPorts.end();
+ eventLoop->postEvent(boost::bind(boost::ref(onConnectFinished), fail));
+ }
+
+ HostAddressPort getLocalAddress() const { return HostAddressPort(); }
+
+ void disconnect() {
+ disconnected = true;
+ onDisconnected(boost::optional<Connection::Error>());
+ }
+
+ void write(const SafeByteArray& d) {
+ append(dataWritten, d);
+ }
+
+ EventLoop* eventLoop;
+ boost::optional<HostAddressPort> hostAddressPort;
+ std::vector<HostAddressPort> failingPorts;
+ ByteArray dataWritten;
+ bool disconnected;
+ };
+
+ struct MockConnectionFactory : public ConnectionFactory {
+ MockConnectionFactory(EventLoop* eventLoop) : eventLoop(eventLoop) {
+ }
+
+ boost::shared_ptr<Connection> createConnection() {
+ boost::shared_ptr<MockConnection> connection = boost::make_shared<MockConnection>(failingPorts, eventLoop);
+ connections.push_back(connection);
+ return connection;
+ }
+
+ EventLoop* eventLoop;
+ std::vector< boost::shared_ptr<MockConnection> > connections;
+ std::vector<HostAddressPort> failingPorts;
+ };
+
+ void readResponse(const std::string& response, boost::shared_ptr<MockConnection> connection) {
+ boost::shared_ptr<SafeByteArray> data1 = boost::make_shared<SafeByteArray>(createSafeByteArray(
+ "HTTP/1.1 200 OK\r\n"
+ "Content-Type: text/xml; charset=utf-8\r\n"
+ "Access-Control-Allow-Origin: *\r\n"
+ "Access-Control-Allow-Headers: Content-Type\r\n"
+ "Content-Length: "));
+ connection->onDataRead(data1);
+ boost::shared_ptr<SafeByteArray> data2 = boost::make_shared<SafeByteArray>(createSafeByteArray(boost::lexical_cast<std::string>(response.size())));
+ connection->onDataRead(data2);
+ boost::shared_ptr<SafeByteArray> data3 = boost::make_shared<SafeByteArray>(createSafeByteArray("\r\n\r\n"));
+ connection->onDataRead(data3);
+ boost::shared_ptr<SafeByteArray> data4 = boost::make_shared<SafeByteArray>(createSafeByteArray(response));
+ connection->onDataRead(data4);
+ }
+
+
+ private:
+ DummyEventLoop* eventLoop;
+ MockConnectionFactory* connectionFactory;
+ bool connectFinished;
+ bool connectFinishedWithError;
+ bool disconnected;
+ boost::optional<Connection::Error> disconnectedError;
+ ByteArray dataRead;
+ PlatformXMLParserFactory parserFactory;
+ std::string sid;
+
+};
+
+
+CPPUNIT_TEST_SUITE_REGISTRATION(BOSHConnectionTest);