From 109e50103d757d880e7ce390482951111dad1e22 Mon Sep 17 00:00:00 2001
From: Kevin Smith <git@kismith.co.uk>
Date: Thu, 27 May 2010 14:24:44 +0100
Subject: Cleaning up code paths for rapid disconnect/reconnect.

This includes a fix in OpensSSLContext that stops assert failures when
more data is received on a connection after a write has failed. It's
worth investigating why this happens, stopping it doing so, and re-instate
the assert.

Resolves: #402

diff --git a/Swift/Controllers/MainController.cpp b/Swift/Controllers/MainController.cpp
index 596edd2..d6a5587 100644
--- a/Swift/Controllers/MainController.cpp
+++ b/Swift/Controllers/MainController.cpp
@@ -333,6 +333,9 @@ void MainController::performLoginFromCachedCredentials() {
 		}
 		client_->onError.connect(boost::bind(&MainController::handleError, this, _1));
 		client_->onConnected.connect(boost::bind(&MainController::handleConnected, this));
+	} else {
+		/* In case we're in the middle of another login, make sure they don't overlap */
+		client_->disconnect();
 	}
 	client_->connect();
 }
@@ -387,7 +390,7 @@ void MainController::signOut() {
 }
 
 void MainController::logout() {
-	if (client_ && client_->isAvailable()) {
+	if (client_ /*&& client_->isAvailable()*/) {
 		client_->disconnect();
 	}
 	if (rosterController_) {
diff --git a/Swiften/Client/Client.cpp b/Swiften/Client/Client.cpp
index 63a93a3..763c83e 100644
--- a/Swiften/Client/Client.cpp
+++ b/Swiften/Client/Client.cpp
@@ -45,15 +45,23 @@ void Client::connect() {
 }
 
 void Client::connect(const String& host) {
-	assert(!connector_);
+	assert(!connector_); // Crash on reconnect is here.
 	connector_ = Connector::create(host, &resolver_, connectionFactory_, timerFactory_);
-	connector_->onConnectFinished.connect(boost::bind(&Client::handleConnectorFinished, this, _1));
+	connector_->onConnectFinished.connect(boost::bind(&Client::handleConnectorFinished, this, _1, connector_));
 	connector_->setTimeoutMilliseconds(60*1000);
 	connector_->start();
 }
 
-void Client::handleConnectorFinished(boost::shared_ptr<Connection> connection) {
+void Client::handleConnectorFinished(boost::shared_ptr<Connection> connection, Connector::ref connector) {
+	bool currentConnection = connector_ && (connector.get() == connector_.get());
 	// TODO: Add domain name resolver error
+	if (!currentConnection) {
+		/* disconnect() was called, this connection should be thrown away*/
+		if (connection) {
+			connection->disconnect();
+		}
+		return;
+	}
 	connector_.reset();
 	if (!connection) {
 		onError(ClientError::ConnectionError);
@@ -81,6 +89,9 @@ void Client::handleConnectorFinished(boost::shared_ptr<Connection> connection) {
 }
 
 void Client::disconnect() {
+	if (connector_) {
+		connector_.reset();
+	}
 	if (session_) {
 		session_->finish();
 	}
diff --git a/Swiften/Client/Client.h b/Swiften/Client/Client.h
index 92e89f1..e0714bb 100644
--- a/Swiften/Client/Client.h
+++ b/Swiften/Client/Client.h
@@ -55,7 +55,7 @@ namespace Swift {
 			boost::signal<void (const String&)> onDataWritten;
 
 		private:
-			void handleConnectorFinished(boost::shared_ptr<Connection>);
+			void handleConnectorFinished(boost::shared_ptr<Connection>, Connector::ref);
 			void send(boost::shared_ptr<Stanza>);
 			virtual String getNewIQID();
 			void handleElement(boost::shared_ptr<Element>);
diff --git a/Swiften/Network/PlatformDomainNameServiceQuery.cpp b/Swiften/Network/PlatformDomainNameServiceQuery.cpp
index 10b9f63..3ee0b18 100644
--- a/Swiften/Network/PlatformDomainNameServiceQuery.cpp
+++ b/Swiften/Network/PlatformDomainNameServiceQuery.cpp
@@ -86,7 +86,6 @@ void PlatformDomainNameServiceQuery::doRun() {
 	ByteArray response;
 	response.resize(NS_PACKETSZ);
 	int responseLength = res_query(const_cast<char*>(service.getUTF8Data()), ns_c_in, ns_t_srv, reinterpret_cast<u_char*>(response.getData()), response.getSize());
-	//std::cout << "res_query done " << (responseLength != -1) << std::endl;
 	if (responseLength == -1) {
 		emitError();
 		return;
diff --git a/Swiften/QA/ReconnectTest/ReconnectTest.cpp b/Swiften/QA/ReconnectTest/ReconnectTest.cpp
new file mode 100644
index 0000000..f630dd8
--- /dev/null
+++ b/Swiften/QA/ReconnectTest/ReconnectTest.cpp
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2010 Kevin Smith
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#include <boost/bind.hpp>
+#include <boost/thread.hpp>
+
+#include "Swiften/Client/Client.h"
+#include "Swiften/Network/BoostTimer.h"
+#include "Swiften/EventLoop/MainEventLoop.h"
+#include "Swiften/EventLoop/SimpleEventLoop.h"
+#include "Swiften/Queries/Requests/GetRosterRequest.h"
+#include "Swiften/Client/ClientXMLTracer.h"
+#include "Swiften/Network/BoostIOServiceThread.h"
+#include "Swiften/Network/MainBoostIOServiceThread.h"
+
+using namespace Swift;
+
+using namespace Swift;
+
+bool connecting_ = false;
+Client* client_;
+SimpleEventLoop eventLoop_;
+int count = 0;
+
+void handleTick(boost::shared_ptr<BoostTimer> timer) {
+	std::cout << "Count " << count++ << std::endl;
+	if (timer) {
+		timer->stop();
+	}
+	if (connecting_) {
+		client_->disconnect();
+	} else {
+		if (count > 60) {
+			eventLoop_.stop();
+			return;
+		}
+		client_->connect();
+	}
+	connecting_ = !connecting_;
+	
+	int delay = 500;
+//	int delay = 0;
+	boost::shared_ptr<BoostTimer> newTimer(new BoostTimer(delay, &MainBoostIOServiceThread::getInstance().getIOService()));
+	newTimer->onTick.connect(boost::bind(&handleTick, timer));
+	newTimer->start();
+}
+
+int main(int, char**) {
+	char* jidChars = getenv("SWIFT_CLIENTTEST_JID");
+	if (!jidChars) {
+		std::cerr << "Please set the SWIFT_CLIENTTEST_JID environment variable" << std::endl;
+		return -1;
+	}
+	char* passChars = getenv("SWIFT_CLIENTTEST_PASS");
+	if (!passChars) {
+		std::cerr << "Please set the SWIFT_CLIENTTEST_PASS environment variable" << std::endl;
+		return -1;
+	}
+	
+	JID jid(jidChars);
+	String pass(passChars);
+
+	client_ = new Swift::Client(jid, pass);
+	handleTick(boost::shared_ptr<BoostTimer>());
+	eventLoop_.run();
+	
+	delete client_;
+	return 0;
+
+}
diff --git a/Swiften/QA/ReconnectTest/SConscript b/Swiften/QA/ReconnectTest/SConscript
new file mode 100644
index 0000000..8e6a0fc
--- /dev/null
+++ b/Swiften/QA/ReconnectTest/SConscript
@@ -0,0 +1,24 @@
+import os
+
+Import("env")
+
+if env["TEST"] :
+	myenv = env.Clone()
+	myenv.MergeFlags(myenv["SWIFTEN_FLAGS"])
+	myenv.MergeFlags(myenv["CPPUNIT_FLAGS"])
+	myenv.MergeFlags(myenv["LIBIDN_FLAGS"])
+	myenv.MergeFlags(myenv["BOOST_FLAGS"])
+	myenv.MergeFlags(myenv.get("SQLITE_FLAGS", ""))
+	myenv.MergeFlags(myenv["ZLIB_FLAGS"])
+	myenv.MergeFlags(myenv["OPENSSL_FLAGS"])
+	myenv.MergeFlags(myenv.get("LIBXML_FLAGS", ""))
+	myenv.MergeFlags(myenv.get("EXPAT_FLAGS", ""))
+#	myenv.Append(LIBPATH = ["/opt/local/lib"])
+#	myenv.Append(LIBS = ["efence"])
+
+	for i in ["SWIFT_CLIENTTEST_JID", "SWIFT_CLIENTTEST_PASS"]:
+		if os.environ.get(i, "") :
+			myenv["ENV"][i] = os.environ[i]
+
+	tester = myenv.Program("ReconnectTest", ["ReconnectTest.cpp"])
+	myenv.Test(tester, "system")
diff --git a/Swiften/QA/SConscript b/Swiften/QA/SConscript
index ede7b39..dda6524 100644
--- a/Swiften/QA/SConscript
+++ b/Swiften/QA/SConscript
@@ -1,4 +1,5 @@
 SConscript([
 		"NetworkTest/SConscript",
+		"ReconnectTest/SConscript",
 		"ClientTest/SConscript",
 	])
diff --git a/Swiften/TLS/OpenSSL/OpenSSLContext.cpp b/Swiften/TLS/OpenSSL/OpenSSLContext.cpp
index ae5ccf5..80575ca 100644
--- a/Swiften/TLS/OpenSSL/OpenSSLContext.cpp
+++ b/Swiften/TLS/OpenSSL/OpenSSLContext.cpp
@@ -93,7 +93,7 @@ void OpenSSLContext::handleDataFromNetwork(const ByteArray& data) {
 			sendPendingDataToApplication();
 			break;
 		case Start: assert(false); break;
-		case Error: assert(false); break;
+		case Error: /*assert(false);*/ break;
 	}
 }
 
-- 
cgit v0.10.2-6-g49f6