From aa3b4c09352cdfb2f999fc6c12faed6339405d03 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Remko=20Tron=C3=A7on?= <git@el-tramo.be>
Date: Sun, 2 Aug 2009 00:56:13 +0200
Subject: Added BoostConnectionServer error signaling.


diff --git a/Slimber/Server.cpp b/Slimber/Server.cpp
index 36595b1..44a5861 100644
--- a/Slimber/Server.cpp
+++ b/Slimber/Server.cpp
@@ -37,33 +37,82 @@ Server::Server(
 			clientConnectionPort(clientConnectionPort), 
 			linkLocalConnectionPort(linkLocalConnectionPort),
 			linkLocalServiceBrowser(linkLocalServiceBrowser),
-			vCardCollection(vCardCollection) {
+			vCardCollection(vCardCollection),
+			presenceManager(NULL),
+			stopping(false) {
+	linkLocalServiceBrowser->onServiceRegistered.connect(
+			boost::bind(&Server::handleServiceRegistered, this, _1));
+}
+
+Server::~Server() {
+	stop();
+}
+
+void Server::start() {
+	assert(!serverFromClientConnectionServer);
 	serverFromClientConnectionServer = 
 			boost::shared_ptr<BoostConnectionServer>(new BoostConnectionServer(
 					clientConnectionPort, &boostIOServiceThread.getIOService()));
 	serverFromClientConnectionServer->onNewConnection.connect(
 			boost::bind(&Server::handleNewClientConnection, this, _1));
+	serverFromClientConnectionServer->onStopped.connect(
+			boost::bind(&Server::handleClientConnectionServerStopped, this, _1));
 	serverFromClientConnectionServer->start();
 
-	presenceManager = new LinkLocalPresenceManager(linkLocalServiceBrowser);
-	presenceManager->onRosterChanged.connect(
-			boost::bind(&Server::handleRosterChanged, this, _1));
-	presenceManager->onPresenceChanged.connect(
-			boost::bind(&Server::handlePresenceChanged, this, _1));
-
-	linkLocalServiceBrowser->onServiceRegistered.connect(
-			boost::bind(&Server::handleServiceRegistered, this, _1));
-
+	assert(!serverFromNetworkConnectionServer);
 	serverFromNetworkConnectionServer = 
 		boost::shared_ptr<BoostConnectionServer>(new BoostConnectionServer(
 			linkLocalConnectionPort, &boostIOServiceThread.getIOService()));
 	serverFromNetworkConnectionServer->onNewConnection.connect(
 			boost::bind(&Server::handleNewLinkLocalConnection, this, _1));
+	serverFromClientConnectionServer->onStopped.connect(
+			boost::bind(&Server::handleLinkLocalConnectionServerStopped, this, _1));
 	serverFromNetworkConnectionServer->start();
+
+	assert(!presenceManager);
+	presenceManager = new LinkLocalPresenceManager(linkLocalServiceBrowser);
+	presenceManager->onRosterChanged.connect(
+			boost::bind(&Server::handleRosterChanged, this, _1));
+	presenceManager->onPresenceChanged.connect(
+			boost::bind(&Server::handlePresenceChanged, this, _1));
 }
 
-Server::~Server() {
+void Server::stop() {
+	stop(boost::optional<ServerError>());
+}
+
+void Server::stop(boost::optional<ServerError> e) {
+	if (stopping) {
+		return;
+	}
+
+	stopping = true;
+
 	delete presenceManager;
+
+	if (serverFromClientSession) {
+		serverFromClientSession->finishSession();
+	}
+	serverFromClientSession.reset();
+	foreach(boost::shared_ptr<Session> session, linkLocalSessions) {
+		session->finishSession();
+	}
+	linkLocalSessions.clear();
+	foreach(boost::shared_ptr<LinkLocalConnector> connector, connectors) {
+		connector->cancel();
+	}
+	connectors.clear();
+	tracers.clear();
+
+	if (serverFromNetworkConnectionServer) {
+		serverFromNetworkConnectionServer->stop();
+	}
+	if (serverFromClientConnectionServer) {
+		serverFromClientConnectionServer->stop();
+	}
+
+	stopping = false;
+	onStopped(e);
 }
 
 void Server::handleNewClientConnection(boost::shared_ptr<Connection> connection) {
@@ -294,6 +343,34 @@ void Server::handlePresenceChanged(boost::shared_ptr<Presence> presence) {
 	}
 }
 
+void Server::handleClientConnectionServerStopped(boost::optional<BoostConnectionServer::Error> e) {
+	if (e) {
+		if (*e == BoostConnectionServer::Conflict) {
+			stop(ServerError(ServerError::C2SPortConflict));
+		}
+		else {
+			stop(ServerError(ServerError::C2SError));
+		}
+	}
+	else {
+		stop();
+	}
+}
+
+void Server::handleLinkLocalConnectionServerStopped(boost::optional<BoostConnectionServer::Error> e) {
+	if (e) {
+		if (*e == BoostConnectionServer::Conflict) {
+			stop(ServerError(ServerError::LinkLocalPortConflict));
+		}
+		else {
+			stop(ServerError(ServerError::LinkLocalError));
+		}
+	}
+	else {
+		stop();
+	}
+}
+
 LinkLocalServiceInfo Server::getLinkLocalServiceInfo(boost::shared_ptr<Presence> presence) {
 	LinkLocalServiceInfo info;
 	boost::shared_ptr<VCard> vcard = vCardCollection->getOwnVCard();
diff --git a/Slimber/Server.h b/Slimber/Server.h
index 38c45de..ff3f70d 100644
--- a/Slimber/Server.h
+++ b/Slimber/Server.h
@@ -1,9 +1,11 @@
 #pragma once
 
 #include <boost/shared_ptr.hpp>
+#include <boost/optional.hpp>
 #include <vector>
 
 #include "Swiften/Network/BoostIOServiceThread.h"
+#include "Swiften/Network/BoostConnectionServer.h"
 #include "Swiften/Server/UserRegistry.h"
 #include "Swiften/Base/IDGenerator.h"
 #include "Swiften/Server/ServerFromClientSession.h"
@@ -11,6 +13,7 @@
 #include "Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.h"
 #include "Swiften/Serializer/PayloadSerializers/FullPayloadSerializerCollection.h"
 #include "Swiften/LinkLocal/LinkLocalServiceInfo.h"
+#include "Slimber/ServerError.h"
 
 namespace Swift {
 	class DNSSDServiceID;
@@ -33,9 +36,15 @@ namespace Swift {
 					VCardCollection* vCardCollection);
 			~Server();
 
+			void start();
+			void stop();
+
 			boost::signal<void (bool)> onSelfConnected;
+			boost::signal<void (boost::optional<ServerError>)> onStopped;
 
 		private:
+			void stop(boost::optional<ServerError>);
+
 			void handleNewClientConnection(boost::shared_ptr<Connection> c);
 			void handleSessionStarted();
 			void handleSessionFinished(boost::shared_ptr<ServerFromClientSession>);
@@ -47,6 +56,10 @@ namespace Swift {
 			void handleLinkLocalSessionFinished(boost::shared_ptr<Session> session);
 			void handleLinkLocalElementReceived(boost::shared_ptr<Element> element, boost::shared_ptr<Session> session);
 			void handleConnectFinished(boost::shared_ptr<LinkLocalConnector> connector, bool error);
+			void handleClientConnectionServerStopped(
+					boost::optional<BoostConnectionServer::Error>);
+			void handleLinkLocalConnectionServerStopped(
+					boost::optional<BoostConnectionServer::Error>);
 			boost::shared_ptr<Session> getLinkLocalSessionForJID(const JID& jid);
 			boost::shared_ptr<LinkLocalConnector> getLinkLocalConnectorForJID(const JID& jid);
 			void registerLinkLocalSession(boost::shared_ptr<Session> session);
@@ -76,6 +89,7 @@ namespace Swift {
 			LinkLocalServiceBrowser* linkLocalServiceBrowser;
 			VCardCollection* vCardCollection;
 			LinkLocalPresenceManager* presenceManager;
+			bool stopping;
 			boost::shared_ptr<BoostConnectionServer> serverFromClientConnectionServer;
 			boost::shared_ptr<ServerFromClientSession> serverFromClientSession;
 			boost::shared_ptr<Presence> lastPresence;
diff --git a/Slimber/ServerError.h b/Slimber/ServerError.h
new file mode 100644
index 0000000..ce293c2
--- /dev/null
+++ b/Slimber/ServerError.h
@@ -0,0 +1,31 @@
+#pragma once
+
+#include "Swiften/Base/String.h"
+
+namespace Swift {
+	class ServerError {
+		public:
+			enum Type {
+				C2SPortConflict,
+				C2SError,
+				LinkLocalPortConflict,
+				LinkLocalError
+			};
+
+			ServerError(Type type, const String& message = String()) :
+					type(type), message(message) {
+			}
+
+			Type getType() const {
+				return type;
+			}
+
+			const String& getMessage() const {
+				return message;
+			}
+
+		private:
+			Type type;
+			String message;
+	};
+}
diff --git a/Swiften/Network/BoostConnectionServer.cpp b/Swiften/Network/BoostConnectionServer.cpp
index 18a3ca4..596761a 100644
--- a/Swiften/Network/BoostConnectionServer.cpp
+++ b/Swiften/Network/BoostConnectionServer.cpp
@@ -1,28 +1,64 @@
 #include "Swiften/Network/BoostConnectionServer.h"
 
 #include <boost/bind.hpp>
+#include <boost/system/system_error.hpp>
 
 #include "Swiften/EventLoop/MainEventLoop.h"
 
 namespace Swift {
 
-BoostConnectionServer::BoostConnectionServer(int port, boost::asio::io_service* ioService) : acceptor_(*ioService, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), port)) {
+BoostConnectionServer::BoostConnectionServer(int port, boost::asio::io_service* ioService) : port_(port), ioService_(ioService), acceptor_(NULL) {
 }
 
 
 void BoostConnectionServer::start() {
-	acceptNextConnection();
+	try {
+		assert(!acceptor_);
+		acceptor_ = new boost::asio::ip::tcp::acceptor(
+				*ioService_, 
+				boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), port_));
+		acceptNextConnection();
+	}
+	catch (const boost::system::system_error& e) {
+		if (e.code() == boost::asio::error::address_in_use) {
+			onStopped(Conflict);
+		}
+		else {
+			onStopped(UnknownError);
+		}
+	}
+}
+
+
+void BoostConnectionServer::stop() {
+	stop(boost::optional<Error>());
+}
+
+void BoostConnectionServer::stop(boost::optional<Error> e) {
+	if (acceptor_) {
+		acceptor_->close();
+		acceptor_ = NULL;
+	}
+	onStopped(e);
 }
 
 void BoostConnectionServer::acceptNextConnection() {
-	boost::shared_ptr<BoostConnection> newConnection(new BoostConnection(&acceptor_.io_service()));
-	acceptor_.async_accept(newConnection->getSocket(), 
+	boost::shared_ptr<BoostConnection> newConnection(new BoostConnection(&acceptor_->io_service()));
+	acceptor_->async_accept(newConnection->getSocket(), 
 		boost::bind(&BoostConnectionServer::handleAccept, shared_from_this(), newConnection, boost::asio::placeholders::error));
 }
 
 void BoostConnectionServer::handleAccept(boost::shared_ptr<BoostConnection> newConnection, const boost::system::error_code& error) {
-	if (!error) {
-		MainEventLoop::postEvent(boost::bind(boost::ref(onNewConnection), newConnection), shared_from_this());
+	if (error) {
+		MainEventLoop::postEvent(
+				boost::bind(
+						&BoostConnectionServer::stop, shared_from_this(), UnknownError), 
+				shared_from_this());
+	}
+	else {
+		MainEventLoop::postEvent(
+				boost::bind(boost::ref(onNewConnection), newConnection), 
+				shared_from_this());
 		newConnection->listen();
 		acceptNextConnection();
 	}
diff --git a/Swiften/Network/BoostConnectionServer.h b/Swiften/Network/BoostConnectionServer.h
index c92318e..d8e5eb4 100644
--- a/Swiften/Network/BoostConnectionServer.h
+++ b/Swiften/Network/BoostConnectionServer.h
@@ -3,6 +3,7 @@
 #include <boost/shared_ptr.hpp>
 #include <boost/enable_shared_from_this.hpp>
 #include <boost/asio.hpp>
+#include <boost/signal.hpp>
 
 #include "Swiften/Network/BoostConnection.h"
 #include "Swiften/Network/ConnectionServer.h"
@@ -11,15 +12,25 @@
 namespace Swift {
 	class BoostConnectionServer : public ConnectionServer, public EventOwner, public boost::enable_shared_from_this<BoostConnectionServer> {
 		public:
+			enum Error {
+				Conflict,
+				UnknownError
+			};
 			BoostConnectionServer(int port, boost::asio::io_service* ioService);
 
 			void start();
+			void stop();
+
+			boost::signal<void (boost::optional<Error>)> onStopped;
 
 		private:
+			void stop(boost::optional<Error> e);
 			void acceptNextConnection();
 			void handleAccept(boost::shared_ptr<BoostConnection> newConnection, const boost::system::error_code& error);
 
 		private:
-			boost::asio::ip::tcp::acceptor acceptor_;
+			int port_;
+			boost::asio::io_service* ioService_;
+			boost::asio::ip::tcp::acceptor* acceptor_;
 	};
 }
diff --git a/Swiften/QA/NetworkTest/BoostConnectionServerTest.cpp b/Swiften/QA/NetworkTest/BoostConnectionServerTest.cpp
new file mode 100644
index 0000000..a5c51aa
--- /dev/null
+++ b/Swiften/QA/NetworkTest/BoostConnectionServerTest.cpp
@@ -0,0 +1,72 @@
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+#include <boost/shared_ptr.hpp>
+
+#include "Swiften/Base/String.h"
+#include "Swiften/Network/BoostConnectionServer.h"
+#include "Swiften/Network/BoostIOServiceThread.h"
+#include "Swiften/EventLoop/DummyEventLoop.h"
+
+using namespace Swift;
+
+class BoostConnectionServerTest : public CppUnit::TestFixture {
+		CPPUNIT_TEST_SUITE(BoostConnectionServerTest);
+		CPPUNIT_TEST(testConstructor_TwoServersOnSamePort);
+		CPPUNIT_TEST(testStart_Conflict);
+		CPPUNIT_TEST(testStop);
+		CPPUNIT_TEST_SUITE_END();
+
+	public:
+		void setUp() {
+			boostIOServiceThread_ = new BoostIOServiceThread();
+			eventLoop_ = new DummyEventLoop();
+			stopped = false;
+			stoppedError.reset();
+		}
+
+		void tearDown() {
+			delete eventLoop_;
+			delete boostIOServiceThread_;
+		}
+
+		void testConstructor_TwoServersOnSamePort() {
+			boost::shared_ptr<BoostConnectionServer> testling(new BoostConnectionServer(9999, &boostIOServiceThread_->getIOService()));
+			boost::shared_ptr<BoostConnectionServer> testling2(new BoostConnectionServer(9999, &boostIOServiceThread_->getIOService()));
+		}
+
+		void testStart_Conflict() {
+			boost::shared_ptr<BoostConnectionServer> testling(new BoostConnectionServer(9999, &boostIOServiceThread_->getIOService()));
+			testling->start();
+
+			boost::shared_ptr<BoostConnectionServer> testling2(new BoostConnectionServer(9999, &boostIOServiceThread_->getIOService()));
+			testling2->onStopped.connect(
+					boost::bind(&BoostConnectionServerTest::handleStopped, this, _1));
+
+			testling->stop();
+		}
+
+		void testStop() {
+			boost::shared_ptr<BoostConnectionServer> testling(new BoostConnectionServer(9999, &boostIOServiceThread_->getIOService()));
+			testling->start();
+
+			testling->stop();
+
+			boost::shared_ptr<BoostConnectionServer> testling2(new BoostConnectionServer(9999, &boostIOServiceThread_->getIOService()));
+			testling2->start();
+
+			testling2->stop();
+		}
+
+		void handleStopped(boost::optional<BoostConnectionServer::Error> e) {
+			stopped = true;
+			stoppedError = e;
+		}
+
+	private:
+		BoostIOServiceThread* boostIOServiceThread_;
+		DummyEventLoop* eventLoop_;
+		bool stopped;
+		boost::optional<BoostConnectionServer::Error> stoppedError;
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(BoostConnectionServerTest);
diff --git a/Swiften/QA/NetworkTest/Makefile.inc b/Swiften/QA/NetworkTest/Makefile.inc
index b263cdb..57c97a1 100644
--- a/Swiften/QA/NetworkTest/Makefile.inc
+++ b/Swiften/QA/NetworkTest/Makefile.inc
@@ -2,6 +2,7 @@ NETWORKTEST_TARGET += Swiften/QA/NetworkTest/checker
 NETWORKTEST_SOURCES += \
 	Swiften/QA/NetworkTest/DomainNameResolverTest.cpp \
 	Swiften/QA/NetworkTest/BoostConnectionTest.cpp \
+	Swiften/QA/NetworkTest/BoostConnectionServerTest.cpp \
 	QA/UnitTest/checker.cpp
 NETWORKTEST_OBJECTS = \
 	$(NETWORKTEST_SOURCES:.cpp=.o)
-- 
cgit v0.10.2-6-g49f6