changeset 454:781494d9c807

Socket: rework connection process
author David Demelier <markand@malikania.fr>
date Tue, 03 Nov 2015 09:37:19 +0100
parents 2d95f0c8fd1d
children 03778e85f455
files C++/modules/Socket/Sockets.h
diffstat 1 files changed, 58 insertions(+), 29 deletions(-) [+]
line wrap: on
line diff
--- a/C++/modules/Socket/Sockets.h	Mon Nov 02 19:16:26 2015 +0100
+++ b/C++/modules/Socket/Sockets.h	Tue Nov 03 09:37:19 2015 +0100
@@ -472,6 +472,7 @@
 	 */
 	inline Socket(Socket &&other) noexcept
 		: m_handle{other.m_handle}
+		, m_type{std::move(other.m_type)}
 	{
 		/* Invalidate other */
 		other.m_handle = -1;
@@ -803,6 +804,7 @@
 	Socket &operator=(Socket &&other) noexcept
 	{
 		m_handle = other.m_handle;
+		m_type = std::move(other.m_type);
 
 		/* Invalidate other */
 		other.m_handle = Invalid;
@@ -1162,6 +1164,13 @@
  * @brief Clear TCP implementation.
  */
 class Tcp {
+private:
+	enum {
+		Undefined,
+		Connecting,
+		Connected
+	} m_state{Undefined};
+
 protected:
 	/**
 	 * Standard accept.
@@ -1215,17 +1224,21 @@
 			int error = WSAGetLastError();
 
 			if (error == WSAEWOULDBLOCK) {
+				m_state = Connecting;
 				throw Error{Error::WouldBlockWrite, "connect", error};
 			}
 
 			throw Error{Error::System, "connect", error};
 #else
 			if (errno == EINPROGRESS) {
+				m_state = Connecting;
 				throw Error{Error::WouldBlockWrite, "connect"};
 			}
 
 			throw Error{Error::System, "connect"};
 #endif
+		} else {
+			m_state = Connected;
 		}
 	}
 
@@ -1278,7 +1291,15 @@
 	template <typename Address>
 	void connect(Socket<Address, Tcp> &sc, const Address &address)
 	{
-		connect(sc.handle(), address.address(), address.length());
+		if (m_state == Undefined) {
+			connect(sc.handle(), address.address(), address.length());
+		} else if (m_state == Connecting) {
+			int error = sc.template get<int>(SOL_SOCKET, SO_ERROR);
+
+			if (error == Failure) {
+				throw Error{Error::System, "connect", error};
+			}
+		}
 	}
 
 	/**
@@ -1587,7 +1608,7 @@
 	void connect(Socket<Address, Tls> &sc, const Address &address)
 	{
 		/* 1. Standard connect */
-		Tcp::connect(sc.handle(), address.address(), address.length());
+		Tcp::connect(sc, address);
 
 		/* 2. OpenSSL handshake */
 		auto ret = SSL_connect(m_ssl.get());
@@ -2541,6 +2562,7 @@
 	using ReadHandler = Callback<const std::string &>;
 	using WriteHandler = Callback<const std::string &>;
 	using DisconnectionHandler = Callback<>;
+	using ErrorHandler = Callback<const Error &>;
 
 private:
 	/* Signals */
@@ -2548,9 +2570,11 @@
 	ReadHandler m_onRead;
 	WriteHandler m_onWrite;
 	DisconnectionHandler m_onDisconnection;
+	ErrorHandler m_onError;
 
 	/* Socket */
 	Socket<Address, Type> m_socket;
+	Address m_address;
 	Listener<> m_listener;
 
 	/* Connection status and output buffer */
@@ -2559,20 +2583,23 @@
 
 	void processConnect()
 	{
-		/* 1. Remove from listener the write flag */
-		m_listener.unset(m_socket.handle(), FlagWrite);
-
-		/* 2. Check for an error */
-		if (m_socket.template get<int>(SOL_SOCKET, SO_ERROR) == Failure) {
-			// TODO: ????? m_onDisconnection();
+		try {
+			m_socket.connect(m_address);
+			m_connected = true;
+			m_onConnection();
+
+			if (m_output.empty()) {
+				m_listener.unset(m_socket.handle(), FlagWrite);
+			}
+		} catch (const Error &error) {
+			if (error.code() == Error::WouldBlockRead) {
+				m_listener.set(m_socket.handle(), FlagRead);
+			} else if (error.code() == Error::WouldBlockWrite) {
+				m_listener.set(m_socket.handle(), FlagWrite);
+			} else {
+				m_onError(error);
+			}
 		}
-
-		/* 3. Listen for input */
-		m_listener.set(m_socket.handle(), FlagRead);
-
-		/* 4. Notify */
-		m_connected = true;
-		m_onConnection();
 	}
 
 	void processSync(int flags)
@@ -2596,9 +2623,10 @@
 				/* 4. Notify user */
 				m_onWrite(sent);
 			}
-		} catch (const std::exception &ex) {
+		} catch (const Error &error) {
 			m_listener.remove(m_socket.handle());
 			m_connected = false;
+			m_onError(error);
 		}
 	}
 
@@ -2614,6 +2642,7 @@
 		: m_socket{address, std::move(type)}
 	{
 		m_socket.setBlockMode(false);
+		m_listener.set(m_socket.handle(), FlagRead);
 	}
 
 	/**
@@ -2657,6 +2686,16 @@
 	}
 
 	/**
+	 * Set the error handler, called when unexpected error occurs.
+	 *
+	 * @param handler the handler
+	 */
+	inline void setErrorHandler(ErrorHandler handler)
+	{
+		m_onError = std::move(handler);
+	}
+
+	/**
 	 * Connect to a server, this function may connect immediately or not in any case the connection handler
 	 * will be called when the connection completed.
 	 *
@@ -2669,19 +2708,9 @@
 			return;
 		}
 
-		try {
-			m_socket.connect(address);
-			m_connected = true;
-			m_onConnection();
-		} catch (const Error &error) {
-			if (error.code() == Error::WouldBlockRead) {
-				m_listener.set(m_socket.handle(), FlagRead);
-			} else if (error.code() == Error::WouldBlockWrite) {
-				m_listener.set(m_socket.handle(), FlagWrite);
-			} else {
-				throw;
-			}
-		}
+		m_address = address;
+
+		processConnect();
 	}
 
 	/**