changeset 512:0002b8da93dc

Net: rework a lot, going to stabilization soon
author David Demelier <markand@malikania.fr>
date Mon, 04 Apr 2016 17:34:01 +0200
parents 5bab839611f2
children ab70c638dc1d
files CMakeLists.txt modules/net/CMakeLists.txt modules/net/examples/blocking-accept.cpp modules/net/examples/blocking-connect.cpp modules/net/examples/elapsed-timer.cpp modules/net/examples/elapsed-timer.h modules/net/examples/non-blocking-accept.cpp modules/net/examples/non-blocking-connect.cpp modules/net/examples/test.crt modules/net/examples/test.key modules/net/net.cpp modules/net/net.h modules/net/test/main.cpp modules/net/test/test.crt modules/net/test/test.key modules/sockets/CMakeLists.txt modules/sockets/examples/blocking-accept.cpp modules/sockets/examples/blocking-connect.cpp modules/sockets/examples/elapsed-timer.cpp modules/sockets/examples/elapsed-timer.h modules/sockets/examples/non-blocking-accept.cpp modules/sockets/examples/non-blocking-connect.cpp modules/sockets/examples/test.crt modules/sockets/examples/test.key modules/sockets/test/test.crt modules/sockets/test/test.key
diffstat 26 files changed, 4566 insertions(+), 489 deletions(-) [+]
line wrap: on
line diff
--- a/CMakeLists.txt	Thu Feb 25 21:01:02 2016 +0100
+++ b/CMakeLists.txt	Mon Apr 04 17:34:01 2016 +0200
@@ -53,5 +53,5 @@
 	)
 endif ()
 
+add_subdirectory(modules/net)
 add_subdirectory(modules/options)
-add_subdirectory(modules/sockets)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/net/CMakeLists.txt	Mon Apr 04 17:34:01 2016 +0200
@@ -0,0 +1,46 @@
+#
+# CMakeLists.txt -- sockets module
+#
+# Copyright (c) 2013-2015 David Demelier <markand@malikania.fr>
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+#
+
+cmake_minimum_required(VERSION 3.0)
+project(net)
+
+set(
+	SOURCES
+	${net_SOURCE_DIR}/net.cpp
+	${net_SOURCE_DIR}/net.h
+)
+
+if (OPENSSL_FOUND)
+	if (WIN32)
+		set(LIBRARIES ws2_32)
+	endif ()
+
+	code_define_module(
+		NAME sockets
+		INCLUDES ${OPENSSL_INCLUDE_DIR}
+		LIBRARIES
+			${LIBRARIES}
+			${OPENSSL_LIBRARIES}
+		SOURCES
+			${net_SOURCE_DIR}/net.cpp
+			${net_SOURCE_DIR}/net.h
+		RESOURCES
+			${net_SOURCE_DIR}/test/test.crt
+			${net_SOURCE_DIR}/test/test.key
+	)
+endif ()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/net/examples/blocking-accept.cpp	Mon Apr 04 17:34:01 2016 +0200
@@ -0,0 +1,52 @@
+/*
+ * blocking-accept.cpp -- example of blocking accept
+ *
+ * Options:
+ *   - WITH_PORT (int), the port to use (default: 16000)
+ *   - WITH_TIMEOUT (int), number of seconds before giving up (default: 60)
+ *   - WITH_SSL (bool), true to test with SSL (default: false)
+ */
+
+#include <iostream>
+
+#include "sockets.h"
+
+#if !defined(WITH_PORT)
+#  define WITH_PORT 16000
+#endif
+
+#if !defined(WITH_TIMEOUT)
+#  define WITH_TIMEOUT 60
+#endif
+
+int main()
+{
+#if defined(WITH_SSL)
+	net::SocketTls<net::address::Ip> master;
+	net::SocketTls<net::address::Ip> client{net::Invalid};
+#else
+	net::SocketTcp<net::address::Ip> master;
+	net::SocketTcp<net::address::Ip> client{net::Invalid};
+#endif
+
+	net::Listener<> listener;
+
+	try {
+		master.set(net::option::SockReuseAddress{true});
+		master.bind(net::address::Ip{"*", WITH_PORT});
+		master.listen();
+
+		listener.set(master.handle(), net::Condition::Readable);
+		listener.wait(std::chrono::seconds(WITH_TIMEOUT));
+
+		master.accept(client);
+	} catch (const net::Error &error) {
+		std::cerr << "error: " << error.what() << std::endl;
+		std::exit(1);
+	}
+
+	std::cout << "Client successfully accepted!" << std::endl;
+
+	return 0;	
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/net/examples/blocking-connect.cpp	Mon Apr 04 17:34:01 2016 +0200
@@ -0,0 +1,48 @@
+/*
+ * blocking-connect.cpp -- example of blocking connect
+ *
+ * Options:
+ *   - WITH_HOST (string literal), the host to try (default: "malikania.fr")
+ *   - WITH_PORT (int), the port to use (default: 80)
+ *   - WITH_TIMEOUT (int), number of seconds before giving up (default: 30)
+ *   - WITH_SSL (bool), true to test with SSL (default: false)
+ */
+
+#include <iostream>
+
+#if !defined(WITH_HOST)
+#  define WITH_HOST "malikania.fr"
+#endif
+
+#if !defined(WITH_PORT)
+#  define WITH_PORT 80
+#endif
+
+#if !defined(WITH_TIMEOUT)
+#  define WITH_TIMEOUT 30
+#endif
+
+#include "elapsed-timer.h"
+#include "sockets.h"
+
+int main()
+{
+#if defined(WITH_SSL)
+	net::SocketTls<net::address::Ipv4> socket;
+#else
+	net::SocketTcp<net::address::Ipv4> socket;
+#endif
+
+	try {
+		std::cout << "Trying to connect to " << WITH_HOST << ":" << WITH_PORT << std::endl;
+		socket.connect(net::address::Ip(WITH_HOST, WITH_PORT));
+	} catch (const net::Error &error) {
+		std::cerr << "error: " << error.what() << std::endl;
+		std::exit(1);
+	}
+
+	std::cout << "Successfully connected!" << std::endl;
+
+	return 0;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/net/examples/elapsed-timer.cpp	Mon Apr 04 17:34:01 2016 +0200
@@ -0,0 +1,60 @@
+/*
+ * elapsed-timer.cpp -- measure elapsed time
+ *
+ * Copyright (c) 2013-2015 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "elapsed-timer.h"
+
+using std::chrono::duration_cast;
+using std::chrono::high_resolution_clock;
+using std::chrono::milliseconds;
+
+ElapsedTimer::ElapsedTimer() noexcept
+{
+	m_last = high_resolution_clock::now();
+}
+
+void ElapsedTimer::pause() noexcept
+{
+	/*
+	 * When we put the timer on pause, do not forget to set the already
+	 * elapsed time.
+	 */
+	(void)elapsed();
+	m_paused = true;
+}
+
+void ElapsedTimer::restart() noexcept
+{
+	m_paused = false;
+	m_last = high_resolution_clock::now();
+}
+
+void ElapsedTimer::reset() noexcept
+{
+	m_elapsed = 0;
+	m_last = high_resolution_clock::now();
+}
+
+unsigned ElapsedTimer::elapsed() noexcept
+{
+	if (!m_paused) {
+		m_elapsed += duration_cast<milliseconds>(high_resolution_clock::now() - m_last).count();
+		m_last = high_resolution_clock::now();
+	}
+
+	return m_elapsed;
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/net/examples/elapsed-timer.h	Mon Apr 04 17:34:01 2016 +0200
@@ -0,0 +1,77 @@
+/*
+ * elapsed-timer.h -- measure elapsed time
+ *
+ * Copyright (c) 2013-2015 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _ELAPSED_TIMER_H_
+#define _ELAPSED_TIMER_H_
+
+/**
+ * @file ElapsedTimer.h
+ * @brief Measure elapsed time
+ */
+
+#include <chrono>
+
+/**
+ * @class ElapsedTimer
+ * @brief Measure elapsed time
+ *
+ * This class provides an abstraction to measure elapsed time since the
+ * construction of the object.
+ *
+ * It uses std::chrono::high_resolution_clock for more precision and uses
+ * milliseconds only.
+ */
+class ElapsedTimer {
+public:
+	using TimePoint = std::chrono::time_point<std::chrono::high_resolution_clock>;
+
+private:
+	TimePoint m_last;
+	bool m_paused{false};
+	unsigned m_elapsed{0};
+
+public:
+	/**
+	 * Construct the elapsed timer, start counting.
+	 */
+	ElapsedTimer() noexcept;
+
+	/**
+	 * Put the timer on pause, the already elapsed time is stored.
+	 */
+	void pause() noexcept;
+
+	/**
+	 * Restart the timer, does not reset it.
+	 */
+	void restart() noexcept;
+
+	/**
+	 * Reset the timer to 0.
+	 */
+	void reset() noexcept;
+
+	/**
+	 * Get the number of elapsed milliseconds.
+	 *
+	 * @return the milliseconds
+	 */
+	unsigned elapsed() noexcept;
+};
+
+#endif // !_ELAPSED_TIMER_H_
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/net/examples/non-blocking-accept.cpp	Mon Apr 04 17:34:01 2016 +0200
@@ -0,0 +1,89 @@
+/*
+ * non-blocking-accept.cpp -- example of total non-blocking accept
+ *
+ * Options:
+ *   - WITH_PORT (int), the port to use (default: 16000)
+ *   - WITH_TIMEOUT (int), number of milliseconds before giving up (default: 3000)
+ *   - WITH_SSL (bool), true to test with SSL (default: false)
+ */
+
+#include <iostream>
+
+#include "elapsed-timer.h"
+#include "sockets.h"
+
+#if !defined(WITH_PORT)
+#  define WITH_PORT 16000
+#endif
+
+#if !defined(WITH_TIMEOUT)
+#  define WITH_TIMEOUT 3000
+#endif
+
+int main()
+{
+#if defined(WITH_SSL)
+	net::SocketTls<net::address::Ip> master;
+	net::SocketTls<net::address::Ip> client{net::Invalid};
+#else
+	net::SocketTcp<net::address::Ip> master;
+	net::SocketTcp<net::address::Ip> client{net::Invalid};
+#endif
+
+	net::Listener<> listener;
+	net::Condition cond;
+
+	ElapsedTimer timer;
+
+	// 1. Create the master socket for listening.
+	try {
+		master.set(net::option::SockReuseAddress{true});
+		master.set(net::option::SockBlockMode{false});
+		master.bind(net::address::Ip{"*", WITH_PORT});
+		master.listen();
+
+		listener.set(master.handle(), net::Condition::Readable);
+	} catch (const net::Error &error) {
+		std::cerr << "error: " << error.what() << std::endl;
+		std::exit(1);
+	}
+
+	while (!client.isOpen() && timer.elapsed() < WITH_TIMEOUT) {
+		try {
+			if (!client.isOpen()) {
+				// 2. Wait for a pre-accept process.
+				listener.wait(std::chrono::seconds(WITH_TIMEOUT));
+				master.accept(client, nullptr, &cond);
+				client.set(net::option::SockBlockMode(false));
+				listener.remove(master.handle());
+
+				std::cout << "Accepting new client" << std::endl;
+
+				if (cond != net::Condition::None) {
+					std::cout << "Client accept state not complete" << std::endl;
+					listener.set(client.handle(), cond);
+				}
+			} else {
+				// 3. Wait for the accept process to complete.
+				std::cout << "Continuing accept for the client" << std::endl;
+
+				listener.wait(std::chrono::seconds(WITH_TIMEOUT - timer.elapsed()));
+				client.accept(&cond);
+				listener.remove(client.handle());
+
+				if (cond != net::Condition::None) {
+					std::cout << "Client accept state not complete" << std::endl;
+					listener.set(client.handle(), cond);
+				}
+			}
+		} catch (const net::Error &error) {
+			std::cerr << error.function() << ": " << error.what() << std::endl;
+			std::exit(1);
+		}
+	}
+
+	if (client.isOpen())
+		std::cout << "Client successfully accepted!" << std::endl;
+
+	return 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/net/examples/non-blocking-connect.cpp	Mon Apr 04 17:34:01 2016 +0200
@@ -0,0 +1,65 @@
+/*
+ * non-blocking-connect.cpp -- example of non blocking connect
+ *
+ * Options:
+ *   - WITH_HOST (string literal), the host to try (default: "malikania.fr")
+ *   - WITH_PORT (int), the port to use (default: 80)
+ *   - WITH_TIMEOUT (int), number of milliseconds before giving up (default: 3000)
+ *   - WITH_SSL (bool), true to test with SSL (default: false)
+ */
+
+#include <iostream>
+
+#if !defined(WITH_HOST)
+#  define WITH_HOST "malikania.fr"
+#endif
+
+#if !defined(WITH_PORT)
+#  define WITH_PORT 80
+#endif
+
+#if !defined(WITH_TIMEOUT)
+#  define WITH_TIMEOUT 3000
+#endif
+
+#include "elapsed-timer.h"
+#include "sockets.h"
+
+int main()
+{
+#if defined(WITH_SSL)
+	net::SocketTls<net::address::Ip> socket;
+#else
+	net::SocketTcp<net::address::Ip> socket;
+#endif
+
+	net::Listener<> listener;
+	net::Condition cond;
+	ElapsedTimer timer;
+
+	// 1. Set to non-blocking.
+	socket.set(net::option::SockBlockMode(false));
+
+	try {
+		std::cout << "Trying to connect to " << WITH_HOST << ":" << WITH_PORT << std::endl;
+
+		// 2. Initial connection process.
+		socket.connect(net::address::Ip(WITH_HOST, WITH_PORT), &cond);
+
+		while (cond != net::Condition::None && timer.elapsed() < WITH_TIMEOUT) {
+			listener.remove(socket.handle());
+
+			// 2. Now complete by waiting for the appropriate condition.
+			listener.set(socket.handle(), cond);
+			listener.wait(std::chrono::milliseconds(WITH_TIMEOUT - timer.elapsed()));
+			socket.connect(&cond);
+		}
+	} catch (const net::Error &error) {
+		std::cerr << "error: " << error.what() << std::endl;
+		std::exit(1);
+	}
+
+	std::cout << "Successfully connected!" << std::endl;
+
+	return 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/net/examples/test.crt	Mon Apr 04 17:34:01 2016 +0200
@@ -0,0 +1,14 @@
+-----BEGIN CERTIFICATE-----
+MIICITCCAYoCCQCGm4grkVCohjANBgkqhkiG9w0BAQsFADBVMQswCQYDVQQGEwJG
+UjEPMA0GA1UECAwGRnJhbmNlMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0
+eSBMdGQxEjAQBgNVBAMMCWxvY2FsaG9zdDAeFw0xNTEwMjYyMDM0NThaFw0yNTEw
+MjMyMDM0NThaMFUxCzAJBgNVBAYTAkZSMQ8wDQYDVQQIDAZGcmFuY2UxITAfBgNV
+BAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDESMBAGA1UEAwwJbG9jYWxob3N0
+MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDp13OqVyOWyv5QWD4xr+Duw6SZ
+gU7D5huzsAOcneSI6JUhf+7Ecu6BQ2JGkFn4srIVkMWGQuImETJ8JCpSQH7rk+xO
+L9fTTK+TwhP2hW/Rf/b2gWedhJAS+gilqt4JNT7v2wFv+aTtRt/lpTXVSdtpLa/m
+Pdy219f6MAPgODJ/7QIDAQABMA0GCSqGSIb3DQEBCwUAA4GBAJSnn/IBn1ZblfzP
+rJO/lE1Jwpmx3B7+oR/e4fkZd6JR3s06umGYWr2H+TPl/5dj9x0gPokhoIL9zCGq
+SxCPnOeaxjBkw7yh3Ks6m3xKxmK4aMpAtBHtwmbfQyIcgz71/lfCzbJ3WcKpn1ig
+IZbByt5QSSPcFORRzJJa35eHBdfX
+-----END CERTIFICATE-----
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/net/examples/test.key	Mon Apr 04 17:34:01 2016 +0200
@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICXAIBAAKBgQDp13OqVyOWyv5QWD4xr+Duw6SZgU7D5huzsAOcneSI6JUhf+7E
+cu6BQ2JGkFn4srIVkMWGQuImETJ8JCpSQH7rk+xOL9fTTK+TwhP2hW/Rf/b2gWed
+hJAS+gilqt4JNT7v2wFv+aTtRt/lpTXVSdtpLa/mPdy219f6MAPgODJ/7QIDAQAB
+AoGBANDt4ndQkgi56A1rOm50gVlzTg6lPPXFE/0xB5kYbcdxX0VmI7Q8KCMwTI9V
+jD2rk3e3OPSjr6FpfhzyxylkXMBz2BL5NRNPowCJbiMgZOUIzlcWPKo0tgf1bZJx
+YdB5U003ISGPPBjVOAjyizY7tJnaNvbpLQ0hbIAsvHPEAOnBAkEA9r3g8NQjPrvb
+oIr5SMIxM8HDJ1/q+MEBSFtRFzQpmur6P64Jsu96zCyencUYTxs0L/sottrj6dPC
+vjGCc6PjsQJBAPKdqK1knJv6Y95M2bnEwrymCFVdxCi7AxObStB+bg/+7mMCUqqX
+j2g71bfvhYakHV7CiaYrrORChwj6vTbimv0CQGpd2IZ5LOhyW2+N+YDgFg3Vzac/
+ti+eJEto8kAqgHUELvUctZmpmypBYe9pc91GQO0ePKL3IaE/ZIhRF4d6c0ECQH9A
+XiaD7PiKvjLs0A31u8ZCt4A+7BII9LYl73mntobBSbu4ji9Xyyn6qEAPa1ORZK49
+DwGPSuF2W2lESlYtSOkCQGrtczhx3IyJjk5e2Y1i/UddPKjviAysCSzcW6aVTNr9
+Y2L0sWmva2FKnkl9FDuEqxvmGr6OOkr5Ll7aWLzJri8=
+-----END RSA PRIVATE KEY-----
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/net/net.cpp	Mon Apr 04 17:34:01 2016 +0200
@@ -0,0 +1,181 @@
+/*
+ * net.cpp -- portable C++ socket wrappers
+ *
+ * Copyright (c) 2013-2015 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <cstdlib>
+#include <atomic>
+#include <mutex>
+
+#include "net.h"
+
+namespace net {
+
+/*
+ * Portable constants
+ * ------------------------------------------------------------------
+ */
+
+#if defined(_WIN32)
+
+const Handle Invalid{INVALID_SOCKET};
+const int Failure{SOCKET_ERROR};
+
+#else
+
+const Handle Invalid{-1};
+const int Failure{-1};
+
+#endif
+
+/*
+ * Portable functions
+ * ------------------------------------------------------------------
+ */
+
+#if defined(_WIN32)
+
+namespace {
+
+static std::mutex s_mutex;
+static std::atomic<bool> s_initialized{false};
+
+} // !namespace
+
+#endif // !_WIN32
+
+void init() noexcept
+{
+#if defined(_WIN32)
+	std::lock_guard<std::mutex> lock(s_mutex);
+
+	if (!s_initialized) {
+		s_initialized = true;
+
+		WSADATA wsa;
+		WSAStartup(MAKEWORD(2, 2), &wsa);
+
+		/*
+		 * If NET_WSA_NO_INIT is not set then the user
+		 * must also call finish himself.
+		 */
+#if !defined(NET_NO_AUTO_INIT)
+		atexit(finish);
+#endif
+	}
+#endif
+}
+
+void finish() noexcept
+{
+#if defined(_WIN32)
+	WSACleanup();
+#endif
+}
+
+std::string error(int errn)
+{
+#if defined(_WIN32)
+	LPSTR str = nullptr;
+	std::string errmsg = "Unknown error";
+
+	FormatMessageA(
+		FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
+		NULL,
+		errn,
+		MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+		(LPSTR)&str, 0, NULL);
+
+
+	if (str) {
+		errmsg = std::string(str);
+		LocalFree(str);
+	}
+
+	return errmsg;
+#else
+	return strerror(errn);
+#endif
+}
+
+std::string error()
+{
+#if defined(_WIN32)
+	return error(WSAGetLastError());
+#else
+	return error(errno);
+#endif
+}
+
+#if !defined(NET_NO_SSL)
+
+namespace ssl {
+
+namespace {
+
+std::mutex mutex;
+std::atomic<bool> initialized{false};
+
+} // !namespace
+
+void finish() noexcept
+{
+	ERR_free_strings();
+}
+
+void init() noexcept
+{
+	std::lock_guard<std::mutex> lock{mutex};
+
+	if (!initialized) {
+		initialized = true;
+
+		SSL_library_init();
+		SSL_load_error_strings();
+		OpenSSL_add_all_algorithms();
+
+#if !defined(NET_NO_AUTO_SSL_INIT)
+		atexit(finish);
+#endif
+	}
+}
+
+} // !ssl
+
+#endif // !NET_NO_SSL
+
+Error::Error(Code code, std::string function)
+	: m_code(code)
+	, m_function(std::move(function))
+	, m_error(error())
+{
+}
+
+Error::Error(Code code, std::string function, int n)
+	: m_code(code)
+	, m_function(std::move(function))
+	, m_error(error(n))
+{
+}
+
+Error::Error(Code code, std::string function, std::string error)
+	: m_code(code)
+	, m_function(std::move(function))
+	, m_error(std::move(error))
+{
+}
+
+} // !net
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/net/net.h	Mon Apr 04 17:34:01 2016 +0200
@@ -0,0 +1,3188 @@
+/*
+ * net.h -- portable C++ socket wrappers
+ *
+ * Copyright (c) 2013-2015 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef NET_H
+#define NET_H
+
+/**
+ * @brief Network configuration
+ *
+ * # Portable defines
+ *
+ * This file defines user options and portability stuff.
+ *
+ * ## Options
+ *
+ * The user may set the following variables before compiling these files:
+ *
+ * ### General options
+ *
+ * - **NET_NO_AUTO_INIT**: (bool) Set to 0 if you don't want Socket class to
+ * automatically calls net::init function and net::finish functions.
+ *
+ * - **NET_NO_SSL**: (bool) Set to 0 if you don't have access to OpenSSL library.
+ *
+ * - **NET_NO_AUTO_SSL_INIT**: (bool) Set to 0 if you don't want Socket class with Tls to automatically init
+ *   the OpenSSL library. You will need to call net::ssl::init and net::ssl::finish.
+ *
+ * ### Compatibility options
+ *
+ * The following options are auto detected but you can override them if you want.
+ *
+ * - **NET_HAVE_INET_PTON**: (bool) Set to 1 if you have inet_pton function. True for all platforms and Windows
+ *   if _WIN32_WINNT is greater or equal to 0x0600.
+ *
+ * - **NET_HAVE_INET_NTOP**: (bool) Same as above.
+ *
+ * **Note:** On Windows, it is highly encouraged to set _WIN32_WINNT to at least 0x0600.
+ *
+ * ### Options for Listener class
+ *
+ * Feature detection, multiple implementations may be avaible, for example, Linux has poll, select and epoll.
+ *
+ * We assume that `select(2)` is always available.
+ *
+ * Of course, you can set the variables yourself if you test it with your build system.
+ *
+ * - **NET_HAVE_POLL**: Defined on all BSD, Linux. Also defined on Windows
+ *   if _WIN32_WINNT is set to 0x0600 or greater.
+ *
+ * - **NET_HAVE_KQUEUE**: Defined on all BSD and Apple.
+ * - **NET_HAVE_EPOLL**: Defined on Linux only.
+ * - **NET_DEFAULT_BACKEND**: Which backend to use (e.g. `Select`).
+ *
+ * The preference priority is ordered from left to right.
+ *
+ * | System        | Backend                 | Class name   |
+ * |---------------|-------------------------|--------------|
+ * | Linux         | epoll(7)                | Epoll        |
+ * | *BSD          | kqueue(2)               | Kqueue       |
+ * | Windows       | poll(2), select(2)      | Poll, Select |
+ * | Mac OS X      | kqueue(2)               | Kqueue       |
+ */
+
+/*
+ * Determine which I/O multiplexing implementations are available
+ * ------------------------------------------------------------------
+ *
+ * May define the following:
+ *
+ *   - NET_HAVE_EPOLL
+ *   - NET_HAVE_KQUEUE
+ *   - NET_HAVE_POLL
+ */
+
+#if defined(_WIN32)
+#  if _WIN32_WINNT >= 0x0600 && !defined(NET_HAVE_POLL)
+#    define NET_HAVE_POLL
+#  endif
+#elif defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__)
+#  if !defined(NET_HAVE_KQUEUE)
+#    define NET_HAVE_KQUEUE
+#  endif
+#  if !defined(NET_HAVE_POLL)
+#    define NET_HAVE_POLL
+#  endif
+#elif defined(__linux__)
+#  if !defined(NET_HAVE_EPOLL)
+#    define NET_HAVE_EPOLL
+#  endif
+#  if !defined(NET_HAVE_POLL)
+#    define NET_HAVE_POLL
+#  endif
+#endif
+
+/*
+ * Compatibility functions
+ * ------------------------------------------------------------------
+ */
+
+#if !defined(NET_HAVE_INET_PTON)
+#  if defined(_WIN32)
+#    if _WIN32_WINNT >= 0x0600
+#      define NET_HAVE_INET_PTON
+#    endif
+#  else
+#    define NET_HAVE_INET_PTON
+#  endif
+#endif
+
+#if !defined(NET_HAVE_INET_NTOP)
+#  if defined(_WIN32)
+#    if _WIN32_WINNT >= 0x0600
+#      define NET_HAVE_INET_NTOP
+#    endif
+#  else
+#    define NET_HAVE_INET_NTOP
+#  endif
+#endif
+
+/*
+ * Define NET_DEFAULT_BACKEND
+ * ------------------------------------------------------------------
+ *
+ * Define the default I/O multiplexing implementation to use if not specified.
+ */
+
+#if defined(_WIN32)
+#  if !defined(NET_DEFAULT_BACKEND)
+#    if defined(NET_HAVE_POLL)
+#      define NET_DEFAULT_BACKEND Poll
+#    else
+#      define NET_DEFAULT_BACKEND Select
+#    endif
+#  endif
+#elif defined(__linux__)
+#  include <sys/epoll.h>
+
+#  if !defined(NET_DEFAULT_BACKEND)
+#    define NET_DEFAULT_BACKEND Epoll
+#  endif
+#elif defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__DragonFly__) || defined(__APPLE__)
+#  include <sys/types.h>
+#  include <sys/event.h>
+#  include <sys/time.h>
+
+#  if !defined(NET_DEFAULT_BACKEND)
+#    define NET_DEFAULT_BACKEND Kqueue
+#  endif
+#else
+#  if !defined(NET_DEFAULT_BACKEND)
+#    define NET_DEFAULT_BACKEND Select
+#  endif
+#endif
+
+#if defined(NET_HAVE_POLL) && !defined(_WIN32)
+#  include <poll.h>
+#endif
+
+/*
+ * Headers to include
+ * ------------------------------------------------------------------
+ */
+
+#if defined(_WIN32)
+#  include <cstdlib>
+
+#  include <WinSock2.h>
+#  include <WS2tcpip.h>
+#else
+#  include <cerrno>
+
+#  include <sys/ioctl.h>
+#  include <sys/types.h>
+#  include <sys/socket.h>
+#  include <sys/un.h>
+
+#  include <arpa/inet.h>
+
+#  include <netinet/in.h>
+#  include <netinet/tcp.h>
+
+#  include <fcntl.h>
+#  include <netdb.h>
+#  include <unistd.h>
+#endif
+
+#include <algorithm>
+#include <cassert>
+#include <chrono>
+#include <cstring>
+#include <memory>
+#include <unordered_map>
+#include <vector>
+
+#if !defined(NET_NO_SSL)
+
+#include <openssl/err.h>
+#include <openssl/evp.h>
+#include <openssl/ssl.h>
+
+#endif // !NET_NO_SSL
+
+namespace net {
+
+/*
+ * Portables types
+ * ------------------------------------------------------------------
+ *
+ * The following types are defined differently between Unix and Windows.
+ */
+
+#if defined(_WIN32)
+
+/**
+ * Socket type, SOCKET.
+ */
+using Handle	= SOCKET;
+
+/**
+ * Argument to pass to set.
+ */
+using ConstArg	= const char *;
+
+/**
+ * Argument to pass to get.
+ */
+using Arg	= char *;
+
+#else
+
+/**
+ * Socket type, int.
+ */
+using Handle	= int;
+
+/**
+ * Argument to pass to set.
+ */
+using ConstArg	= const void *;
+
+/**
+ * Argument to pass to get.
+ */
+using Arg	= void *;
+
+#endif
+
+/*
+ * Portable constants
+ * ------------------------------------------------------------------
+ *
+ * These constants are needed to check functions return codes, they are rarely needed in end user code.
+ */
+
+#if defined(_WIN32)
+
+/**
+ * Socket creation failure or invalidation.
+ */
+extern const Handle Invalid;
+
+/**
+ * Socket operation failure.
+ */
+extern const int Failure;
+
+#else
+
+/**
+ * Socket creation failure or invalidation.
+ */
+extern const int Invalid;
+
+/**
+ * Socket operation failure.
+ */
+extern const int Failure;
+
+#endif
+
+/*
+ * Portable functions
+ * ------------------------------------------------------------------
+ *
+ * The following free functions can be used to initialize the library or to get the last system error.
+ */
+
+/**
+ * Initialize the socket library. Except if you defined NET_NO_AUTO_INIT, you don't need to call this
+ * function manually.
+ */
+void init() noexcept;
+
+/**
+ * Close the socket library.
+ */
+void finish() noexcept;
+
+/**
+ * Get the last socket system error. The error is set from errno or from
+ * WSAGetLastError on Windows.
+ *
+ * @return a string message
+ */
+std::string error();
+
+/**
+ * Get the last system error.
+ *
+ * @param errn the error number (errno or WSAGetLastError)
+ * @return the error
+ */
+std::string error(int errn);
+
+#if !defined(NET_NO_SSL)
+
+namespace ssl {
+
+/**
+ * @enum Method
+ * @brief Which OpenSSL method to use.
+ */
+enum Method {
+	Tlsv1,		//!< TLS v1.2 (recommended)
+	Sslv3		//!< SSLv3
+};
+
+/**
+ * Initialize the OpenSSL library. Except if you defined NET_NO_AUTO_SSL_INIT, you don't need to call this function
+ * manually.
+ */
+void init() noexcept;
+
+/**
+ * Close the OpenSSL library.
+ */
+void finish() noexcept;
+
+} // !ssl
+
+#endif // !NET_NO_SSL
+
+/*
+ * Error class
+ * ------------------------------------------------------------------
+ *
+ * This is the main exception thrown on socket operations.
+ */
+
+/**
+ * @class Error
+ * @brief Base class for sockets error
+ */
+class Error : public std::exception {
+public:
+	/**
+	 * @enum Code
+	 * @brief Which kind of error
+	 */
+	enum Code {
+		Timeout,		///!< The action did timeout
+		System,			///!< There is a system error
+		Other			///!< Other custom error
+	};
+
+private:
+	Code m_code;
+	std::string m_function;
+	std::string m_error;
+
+public:
+	/**
+	 * Constructor that use the last system error.
+	 *
+	 * @param code which kind of error
+	 * @param function the function name
+	 */
+	Error(Code code, std::string function);
+
+	/**
+	 * Constructor that use the system error set by the user.
+	 *
+	 * @param code which kind of error
+	 * @param function the function name
+	 * @param error the error
+	 */
+	Error(Code code, std::string function, int error);
+
+	/**
+	 * Constructor that set the error specified by the user.
+	 *
+	 * @param code which kind of error
+	 * @param function the function name
+	 * @param error the error
+	 */
+	Error(Code code, std::string function, std::string error);
+
+	/**
+	 * Get which function has triggered the error.
+	 *
+	 * @return the function name (e.g connect)
+	 */
+	inline const std::string &function() const noexcept
+	{
+		return m_function;
+	}
+
+	/**
+	 * The error code.
+	 *
+	 * @return the code
+	 */
+	inline Code code() const noexcept
+	{
+		return m_code;
+	}
+
+	/**
+	 * Get the error (only the error content).
+	 *
+	 * @return the error
+	 */
+	const char *what() const noexcept
+	{
+		return m_error.c_str();
+	}
+};
+
+/*
+ * Condition enum
+ * ------------------------------------------------------------------
+ *
+ * Defines if we must wait for reading or writing.
+ */
+
+/**
+ * @enum Condition
+ * @brief Define the required condition for the socket.
+ *
+ * As explained in Action enumeration, some operations required to be called several times, before calling these
+ * operations, the user must wait the socket to be readable or writable. This can be checked with Socket::condition.
+ */
+enum class Condition {
+	None,			//!< No condition is required
+	Readable = (1 << 0),	//!< The socket must be readable
+	Writable = (1 << 1)	//!< The socket must be writable
+};
+
+/**
+ * Apply bitwise XOR.
+ *
+ * @param v1 the first value
+ * @param v2 the second value
+ * @return the new value
+ */
+constexpr Condition operator^(Condition v1, Condition v2) noexcept
+{
+	return static_cast<Condition>(static_cast<int>(v1) ^ static_cast<int>(v2));
+}
+
+/**
+ * Apply bitwise AND.
+ *
+ * @param v1 the first value
+ * @param v2 the second value
+ * @return the new value
+ */
+constexpr Condition operator&(Condition v1, Condition v2) noexcept
+{
+	return static_cast<Condition>(static_cast<int>(v1) & static_cast<int>(v2));
+}
+
+/**
+ * Apply bitwise OR.
+ *
+ * @param v1 the first value
+ * @param v2 the second value
+ * @return the new value
+ */
+constexpr Condition operator|(Condition v1, Condition v2) noexcept
+{
+	return static_cast<Condition>(static_cast<int>(v1) | static_cast<int>(v2));
+}
+
+/**
+ * Apply bitwise NOT.
+ *
+ * @param v the value
+ * @return the complement
+ */
+constexpr Condition operator~(Condition v) noexcept
+{
+	return static_cast<Condition>(~static_cast<int>(v));
+}
+
+/**
+ * Assign bitwise OR.
+ *
+ * @param v1 the first value
+ * @param v2 the second value
+ * @return the new value
+ */
+inline Condition &operator|=(Condition &v1, Condition v2) noexcept
+{
+	v1 = static_cast<Condition>(static_cast<int>(v1) | static_cast<int>(v2));
+
+	return v1;
+}
+
+/**
+ * Assign bitwise AND.
+ *
+ * @param v1 the first value
+ * @param v2 the second value
+ * @return the new value
+ */
+inline Condition &operator&=(Condition &v1, Condition v2) noexcept
+{
+	v1 = static_cast<Condition>(static_cast<int>(v1) & static_cast<int>(v2));
+
+	return v1;
+}
+
+/**
+ * Assign bitwise XOR.
+ *
+ * @param v1 the first value
+ * @param v2 the second value
+ * @return the new value
+ */
+inline Condition &operator^=(Condition &v1, Condition v2) noexcept
+{
+	v1 = static_cast<Condition>(static_cast<int>(v1) ^ static_cast<int>(v2));
+
+	return v1;
+}
+
+/**
+ * @class ListenerStatus
+ * @brief Result of polling
+ *
+ * Result of a select call, returns the first ready socket found with its
+ * flags.
+ */
+class ListenerStatus {
+public:
+	Handle socket;		//!< which socket is ready
+	Condition flags;	//!< the flags
+};
+
+/**
+ * Table used in the socket listener to store which sockets have been
+ * set in which directions.
+ */
+using ListenerTable = std::unordered_map<Handle, Condition>;
+
+/*
+ * Base Socket class
+ * ------------------------------------------------------------------
+ *
+ * This base class has operations that are common to all types of sockets but you usually instanciate
+ * a SocketTcp or SocketUdp
+ */
+
+/**
+ * @class Socket
+ * @brief Base socket class for socket operations.
+ *
+ * **Important:** When using non-blocking sockets, some considerations must be taken. See the implementation of the
+ * underlying protocol for more details.
+ *
+ * When using non-blocking sockets, it is important to pass the condition to functions which may block, they indicate
+ * the condition to wait to perform or continue the operation if they would block.
+ *
+ * For example, when trying to connect with non-blocking, user should do the following:
+ *
+ * 1. Call Socket::connect() with the condition,
+ * 2. Loop until condition is not set to Condition::None (or an exception is thrown),
+ * 3. Wait with a listener for the condition to be ready (see Listener::poll),
+ * 4. Call Socket::resumeConnect() with the condition again.
+ *
+ * @see protocol::Tls
+ * @see protocol::Tcp
+ * @see protocol::Udp
+ */
+template <typename Address, typename Protocol>
+class Socket {
+private:
+	Protocol m_proto;
+
+protected:
+	/**
+	 * The native handle.
+	 */
+	Handle m_handle{Invalid};
+
+public:
+	/**
+	 * Create a socket handle.
+	 *
+	 * This is the primary function and the only one that creates the socket handle, all other constructors
+	 * are just overloaded functions.
+	 *
+	 * @param domain the domain AF_*
+	 * @param type the type SOCK_*
+	 * @param protocol the protocol
+	 * @param iface the implementation
+	 * @throw net::Error on errors
+	 */
+	Socket(int domain, int type, int protocol, Protocol iface = {})
+		: m_proto(std::move(iface))
+	{
+#if !defined(NET_NO_AUTO_INIT)
+		init();
+#endif
+		m_handle = ::socket(domain, type, protocol);
+
+		if (m_handle == Invalid)
+			throw Error(Error::System, "socket");
+
+		m_proto.create(*this);
+	}
+
+	/**
+	 * This tries to create a socket.
+	 *
+	 * Domain and type are determined by the Address and Protocol object.
+	 *
+	 * @param protocol the protocol
+	 * @param address which type of address
+	 * @throw net::Error on errors
+	 */
+	explicit inline Socket(Protocol protocol = {}, const Address &address = {})
+		: Socket(address.domain(), protocol.type(), 0, std::move(protocol))
+	{
+	}
+
+	explicit inline Socket(Handle handle, Protocol protocol = {}) noexcept
+		: m_proto(std::move(protocol))
+		, m_handle(handle)
+	{
+	}
+
+	/**
+	 * Create an invalid socket. Can be used when you cannot instanciate the socket immediately.
+	 */
+	explicit inline Socket(std::nullptr_t) noexcept
+		: m_handle(Invalid)
+	{
+	}
+
+	/**
+	 * Copy constructor deleted.
+	 */
+	Socket(const Socket &) = delete;
+
+	/**
+	 * Transfer ownership from other to this.
+	 *
+	 * @param other the other socket
+	 */
+	inline Socket(Socket &&other) noexcept
+		: m_proto(std::move(other.m_proto))
+		, m_handle(other.m_handle)
+	{
+		/* Invalidate other */
+		other.m_handle = Invalid;
+	}
+
+	/**
+	 * Default destructor.
+	 */
+	virtual ~Socket()
+	{
+		close();
+	}
+
+	/**
+	 * Access the implementation.
+	 *
+	 * @return the implementation
+	 * @warning use this function with care
+	 */
+	inline const Protocol &protocol() const noexcept
+	{
+		return m_proto;
+	}
+
+	/**
+	 * Overloaded function.
+	 *
+	 * @return the implementation
+	 */
+	inline Protocol &protocol() noexcept
+	{
+		return m_proto;
+	}
+
+	/**
+	 * Tells if the socket is not invalid.
+	 *
+	 * @return true if not invalid
+	 */
+	inline bool isOpen() const noexcept
+	{
+		return m_handle != Invalid;
+	}
+
+	/**
+	 * Set an option for the socket. Wrapper of setsockopt(2).
+	 *
+	 * @pre isOpen()
+	 * @param level the setting level
+	 * @param name the name
+	 * @param arg the value
+	 * @throw net::Error on errors
+	 */
+	template <typename Argument>
+	inline void set(int level, int name, const Argument &arg)
+	{
+		assert(m_handle != Invalid);
+
+		if (setsockopt(m_handle, level, name, (ConstArg)&arg, sizeof (arg)) == Failure)
+			throw Error(Error::System, "set");
+	}
+
+	/**
+	 * Object-oriented option setter.
+	 *
+	 * The object must have `set(Socket<Address, Protocol> &) const`.
+	 *
+	 * @pre isOpen()
+	 * @param option the option
+	 * @throw net::Error on errors
+	 */
+	template <typename Option>
+	inline void set(const Option &option)
+	{
+		assert(m_handle != Invalid);
+
+		option.set(*this);
+	}
+
+	/**
+	 * Get an option for the socket. Wrapper of getsockopt(2).
+	 *
+	 * @pre isOpen()
+	 * @param level the setting level
+	 * @param name the name
+	 * @throw net::Error on errors
+	 */
+	template <typename Argument>
+	Argument get(int level, int name)
+	{
+		assert(m_handle != Invalid);
+
+		Argument desired, result{};
+		socklen_t size = sizeof (result);
+
+		if (getsockopt(m_handle, level, name, (Arg)&desired, &size) == Failure)
+			throw Error(Error::System, "get");
+
+		std::memcpy(&result, &desired, size);
+
+		return result;
+	}
+
+	/**
+	 * Object-oriented option getter.
+	 *
+	 * The object must have `T get(Socket<Address, Protocol> &) const`, T can be any type and it is the value
+	 * returned from this function.
+	 *
+	 * @pre isOpen()
+	 * @return the same value as get() in the option
+	 * @throw net::Error on errors
+	 */
+	template <typename Option>
+	inline auto get() -> decltype(std::declval<Option>().get(*this))
+	{
+		assert(m_handle != Invalid);
+
+		return Option().get(*this);
+	}
+
+	/**
+	 * Get the native handle.
+	 *
+	 * @return the handle
+	 * @warning Not portable
+	 */
+	inline Handle handle() const noexcept
+	{
+		return m_handle;
+	}
+
+	/**
+	 * Bind using a native address.
+	 *
+	 * @pre isOpen()
+	 * @param address the address
+	 * @param length the size
+	 * @throw net::Error on errors
+	 */
+	inline void bind(const sockaddr *address, socklen_t length)
+	{
+		assert(m_handle != Invalid);
+
+		if (::bind(m_handle, address, length) == Failure)
+			throw Error(Error::System, "bind");
+	}
+
+	/**
+	 * Overload that takes an address.
+	 *
+	 * @pre isOpen()
+	 * @param address the address
+	 * @throw net::Error on errors
+	 */
+	inline void bind(const Address &address)
+	{
+		assert(m_handle != Invalid);
+
+		if (::bind(m_handle, address.address(), address.length()) == Failure)
+			throw Error(Error::System, "bind");
+	}
+
+	/**
+	 * Listen for pending connection.
+	 *
+	 * @pre isOpen()
+	 * @param max the maximum number
+	 * @throw net::Error on errors
+	 */
+	inline void listen(int max = 128)
+	{
+		assert(m_handle != Invalid);
+
+		if (::listen(this->m_handle, max) == Failure)
+			throw Error(Error::System, "listen");
+	}
+
+	/**
+	 * Get the local name. This is a wrapper of getsockname().
+	 *
+	 * @pre isOpen()
+	 * @return the address
+	 * @throw Error on failures
+	 * @pre state() must not be State::Closed
+	 */
+	Address address() const
+	{
+		assert(m_handle != Invalid);
+
+		sockaddr_storage ss;
+		socklen_t length = sizeof (sockaddr_storage);
+
+		if (::getsockname(m_handle, reinterpret_cast<sockaddr *>(&ss), &length) == Failure)
+			throw Error(Error::System, "getsockname");
+
+		return Address(&ss, length);
+	}
+
+	/**
+	 * Initialize connection to the given address.
+	 *
+	 * @pre isOpen()
+	 * @param address the address
+	 * @param length the address length
+	 * @param cond the condition
+	 * @throw net::Error on failures
+	 */
+	void connect(const sockaddr *address, socklen_t length, Condition &cond)
+	{
+		assert(m_handle != Invalid);
+
+		cond = Condition::None;
+
+		m_proto.connect(*this, address, length, cond);
+	}
+
+	/**
+	 * Overloaded function.
+	 *
+	 * @pre isOpen()
+	 * @param address the address
+	 * @param length the address length
+	 * @throw net::Error on failures
+	 */
+	inline void connect(const sockaddr *address, socklen_t length)
+	{
+		Condition dummy;
+
+		connect(address, length, dummy);
+	}
+
+	/**
+	 * Overloaded function.
+	 *
+	 * @pre isOpen()
+	 * @param address the address
+	 * @param cond the condition
+	 * @throw net::Error on failures
+	 */
+	inline void connect(const Address &address, Condition &cond)
+	{
+		connect(address.address(), address.length(), cond);
+	}
+
+	/**
+	 * Overloaded function.
+	 *
+	 * @pre isOpen()
+	 * @param address the address
+	 * @throw net::Error on failures
+	 */
+	inline void connect(const Address &address)
+	{
+		Condition dummy;
+
+		connect(address.address(), address.length(), dummy);
+	}
+
+	void resumeConnect(Condition &cond)
+	{
+		assert(m_handle != Invalid);
+
+		cond = Condition::None;
+
+		m_proto.connect(*this, cond);
+	}
+
+	inline void resumeConnect()
+	{
+		Condition dummy;
+
+		connect(dummy);
+	}
+
+	Socket<Address, Protocol> accept(Address &address, Condition &cond)
+	{
+		assert(m_handle != Invalid);
+
+		sockaddr_storage storage;
+		socklen_t length = sizeof (storage);
+
+		cond = Condition::None;
+
+		Socket<Address, Protocol> client = m_proto.accept(*this, reinterpret_cast<sockaddr *>(&storage), &length, cond);
+
+		address = Address(&storage, length);
+
+		return client;
+	}
+
+	inline Socket<Address, Protocol> accept(Address &address)
+	{
+		Condition dummy;
+
+		return accept(address, dummy);
+	}
+
+	inline Socket<Address, Protocol> accept()
+	{
+		Address da;
+		Condition dc;
+
+		return accept(da, dc);
+	}
+
+	inline void resumeAccept(Condition &condition)
+	{
+		assert(m_handle != Invalid);
+
+		m_proto.resumeAccept(*this, condition);
+	}
+
+	inline void resumeAccept()
+	{
+		Condition dummy;
+
+		resumeAccept(*this, dummy);
+	}
+
+	unsigned recv(void *data, unsigned length, Condition &cond)
+	{
+		assert(m_handle != Invalid);
+
+		cond = Condition::None;
+
+		return m_proto.recv(*this, data, length, cond);
+	}
+
+	inline unsigned recv(void *data, unsigned length)
+	{
+		Condition dummy;
+
+		return recv(data, length, dummy);
+	}
+
+	std::string recv(unsigned count, Condition &cond)
+	{
+		assert(m_handle != Invalid);
+
+		std::string result;
+
+		result.resize(count);
+		auto n = recv(const_cast<char *>(result.data()), count, cond);
+		result.resize(n);
+
+		return result;
+	}
+
+	inline std::string recv(unsigned count)
+	{
+		Condition dummy;
+
+		return recv(count, dummy);
+	}
+
+	unsigned send(const void *data, unsigned length, Condition &cond)
+	{
+		assert(m_handle != Invalid);
+
+		cond = Condition::None;
+
+		return m_proto.send(*this, data, length, cond);
+	}
+
+	inline unsigned send(const void *data, unsigned length)
+	{
+		Condition dummy;
+
+		return send(data, length, dummy);
+	}
+
+	inline unsigned send(const std::string &data, Condition &cond)
+	{
+		return send(data.c_str(), data.length(), cond);
+	}
+
+	inline unsigned send(const std::string &data)
+	{
+		Condition dummy;
+
+		return send(data.c_str(), data.length(), dummy);
+	}
+
+	unsigned sendto(const void *data, unsigned length, const sockaddr *address, socklen_t addrlen, Condition &cond)
+	{
+		assert(m_handle != Invalid);
+
+		cond = Condition::None;
+
+		return m_proto.sendto(*this, data, length, address, addrlen, cond);
+	}
+
+	inline unsigned sendto(const void *data, unsigned length, const sockaddr *address, socklen_t addrlen)
+	{
+		Condition dummy;
+
+		return send(data, length, address, addrlen, dummy);
+	}
+
+	inline unsigned sendto(const void *data, unsigned length, const Address &address, Condition &cond)
+	{
+		return sendto(data, length, address.address(), address.length(), cond);
+	}
+
+	inline unsigned sendto(const void *data, unsigned length, const Address &address)
+	{
+		Condition dummy;
+
+		return sendto(data, length, address.address(), address.length(), dummy);
+	}
+
+	inline unsigned sendto(const std::string &data, const Address &address, Condition &cond)
+	{
+		return sendto(data.c_str(), data.length(), address.address(), address.length(), cond);
+	}
+
+	inline unsigned sendto(const std::string &data, const Address &address)
+	{
+		Condition dummy;
+
+		return sendto(data.c_str(), data.length(), address.address(), address.length(), dummy);
+	}
+
+	unsigned recvfrom(void *data, unsigned length, sockaddr *address, socklen_t *addrlen, Condition &cond)
+	{
+		assert(m_handle != Invalid);
+
+		cond = Condition::None;
+
+		return m_proto.recvfrom(*this, data, length, address, addrlen, cond);
+	}
+
+	inline unsigned recvfrom(void *data, unsigned length, sockaddr *address, socklen_t *addrlen)
+	{
+		Condition dummy;
+
+		return recvfrom(data, length, address, addrlen, dummy);
+	}
+
+	unsigned recvfrom(void *data, unsigned length, Address &address, Condition &cond)
+	{
+		sockaddr_storage storage;
+		socklen_t addrlen = sizeof (sockaddr_storage);
+
+		auto n = recvfrom(data, length, reinterpret_cast<sockaddr *>(&storage), &addrlen, cond);
+
+		if (n != 0)
+			address = Address(&storage, addrlen);
+
+		return n;
+	}
+
+	inline unsigned recvfrom(void *data, unsigned length, Address &address)
+	{
+		Condition dummy;
+
+		return recvfrom(data, length, address, dummy);
+	}
+
+	inline unsigned recvfrom(void *data, unsigned length)
+	{
+		Address da;
+		Condition dc;
+
+		return recvfrom(data, length, da, dc);
+	}
+
+	std::string recvfrom(unsigned count, Address &info, Condition &cond)
+	{
+		std::string result;
+
+		result.resize(count);
+		auto n = recvfrom(const_cast<char *>(result.data()), count, info, cond);
+		result.resize(n);
+
+		return result;
+	}
+
+	inline std::string recvfrom(unsigned count, Address &info)
+	{
+		Condition dummy;
+
+		return recvfrom(count, info, dummy);
+	}
+
+	inline std::string recvfrom(unsigned count)
+	{
+		Address da;
+		Condition dc;
+
+		return recvfrom(count, da, dc);
+	}
+
+	/**
+	 * Close the socket.
+	 *
+	 * Automatically called from the destructor.
+	 */
+	void close()
+	{
+		if (m_handle != Invalid) {
+#if defined(_WIN32)
+			::closesocket(m_handle);
+#else
+			::close(m_handle);
+#endif
+			m_handle = Invalid;
+		}
+	}
+
+	/**
+	 * Assignment operator forbidden.
+	 *
+	 * @return *this
+	 */
+	Socket &operator=(const Socket &) = delete;
+
+	/**
+	 * Transfer ownership from other to this. The other socket is left
+	 * invalid and will not be closed.
+	 *
+	 * @param other the other socket
+	 * @return this
+	 */
+	Socket &operator=(Socket &&other) noexcept
+	{
+		m_handle = other.m_handle;
+		m_proto = std::move(other.m_proto);
+
+		/* Invalidate other */
+		other.m_handle = Invalid;
+
+		return *this;
+	}
+};
+
+/**
+ * Compare two sockets.
+ *
+ * @param s1 the first socket
+ * @param s2 the second socket
+ * @return true if they equals
+ */
+template <typename Address, typename Protocol>
+bool operator==(const Socket<Address, Protocol> &s1, const Socket<Address, Protocol> &s2)
+{
+	return s1.handle() == s2.handle();
+}
+
+/**
+ * Compare two sockets.
+ *
+ * @param s1 the first socket
+ * @param s2 the second socket
+ * @return true if they are different
+ */
+template <typename Address, typename Protocol>
+bool operator!=(const Socket<Address, Protocol> &s1, const Socket<Address, Protocol> &s2)
+{
+	return s1.handle() != s2.handle();
+}
+
+/**
+ * Compare two sockets.
+ *
+ * @param s1 the first socket
+ * @param s2 the second socket
+ * @return true if s1 < s2
+ */
+template <typename Address, typename Protocol>
+bool operator<(const Socket<Address, Protocol> &s1, const Socket<Address, Protocol> &s2)
+{
+	return s1.handle() < s2.handle();
+}
+
+/**
+ * Compare two sockets.
+ *
+ * @param s1 the first socket
+ * @param s2 the second socket
+ * @return true if s1 > s2
+ */
+template <typename Address, typename Protocol>
+bool operator>(const Socket<Address, Protocol> &s1, const Socket<Address, Protocol> &s2)
+{
+	return s1.handle() > s2.handle();
+}
+
+/**
+ * Compare two sockets.
+ *
+ * @param s1 the first socket
+ * @param s2 the second socket
+ * @return true if s1 <= s2
+ */
+template <typename Address, typename Protocol>
+bool operator<=(const Socket<Address, Protocol> &s1, const Socket<Address, Protocol> &s2)
+{
+	return s1.handle() <= s2.handle();
+}
+
+/**
+ * Compare two sockets.
+ *
+ * @param s1 the first socket
+ * @param s2 the second socket
+ * @return true if s1 >= s2
+ */
+template <typename Address, typename Protocol>
+bool operator>=(const Socket<Address, Protocol> &s1, const Socket<Address, Protocol> &s2)
+{
+	return s1.handle() >= s2.handle();
+}
+
+/*
+ * Predefined protocols
+ * ------------------------------------------------------------------
+ */
+
+namespace protocol {
+
+/**
+ * @class Tcp
+ * @brief Clear TCP implementation.
+ *
+ * This is the basic TCP protocol that implements recv, send, connect and accept as wrappers of the usual
+ * C functions.
+ */
+class Tcp {
+public:
+	/**
+	 * Socket type.
+	 *
+	 * @return SOCK_STREAM
+	 */
+	inline int type() const noexcept
+	{
+		return SOCK_STREAM;
+	}
+
+	/**
+	 * Do nothing.
+	 *
+	 * This function is just present for compatibility, it should never be called.
+	 */
+	template <typename Address>
+	inline void create(Socket<Address, Tcp> &) const noexcept
+	{
+		/* No-op */
+	}
+
+	/**
+	 * Initiate connection.
+	 *
+	 * @param sc the socket
+	 * @param address the address
+	 * @param cond the condition
+	 */
+	template <typename Address, typename Protocol>
+	void connect(Socket<Address, Protocol> &sc, const sockaddr *address, socklen_t length, Condition &cond)
+	{
+		if (::connect(sc.handle(), address, length) == Failure) {
+			/*
+			 * Determine if the error comes from a non-blocking connect that cannot be
+			 * accomplished yet.
+			 */
+#if defined(_WIN32)
+			int error = WSAGetLastError();
+
+			if (error == WSAEWOULDBLOCK)
+				cond = Condition::Writable;
+			else
+				throw Error(Error::System, "connect", error);
+#else
+			if (errno == EINPROGRESS)
+				cond = Condition::Writable;
+			else
+				throw Error(Error::System, "connect");
+#endif
+		}
+	}
+
+	/**
+	 * Resume the connection.
+	 *
+	 * Just check for SOL_SOCKET/SO_ERROR.
+	 *
+	 * @param sc the socket
+	 */
+	template <typename Address, typename Protocol>
+	void resumeConnect(Socket<Address, Protocol> &sc, Condition &)
+	{
+		int error = sc.template get<int>(SOL_SOCKET, SO_ERROR);
+
+		if (error != 0)
+			throw Error(Error::System, "connect", error);
+	}
+
+	/**
+	 * Accept a new client.
+	 *
+	 * If there are no pending connection, an invalid socket is returned, condition is left to Condition::None.
+	 *
+	 * @param sc the socket
+	 * @param address the address
+	 * @param length the length
+	 * @return the new socket
+	 */
+	template <typename Address, typename Protocol>
+	Socket<Address, Protocol> accept(Socket<Address, Protocol> &sc, sockaddr *address, socklen_t *length, Condition &)
+	{
+		Handle handle = ::accept(sc.handle(), address, length);
+
+		if (handle == Invalid)
+			return Socket<Address, Protocol>();
+
+		return Socket<Address, Protocol>(handle);
+	}
+
+	/**
+	 * Resume accept process.
+	 *
+	 * No-op for TCP.
+	 */
+	template <typename Address, typename Protocol>
+	inline void accept(Socket<Address, Protocol> &, Condition &) const noexcept
+	{
+		/* no op */
+	}
+
+	/**
+	 * Receive some data.
+	 *
+	 * @param sc the socket
+	 * @param data the destination buffer
+	 * @param length the buffer length
+	 * @param cond the condition
+	 */
+	template <typename Address>
+	unsigned recv(Socket<Address, Tcp> &sc, void *data, unsigned length, Condition &cond)
+	{
+		int nbread = ::recv(sc.handle(), (Arg)data, length, 0);
+
+		if (nbread == Failure) {
+#if defined(_WIN32)
+			int error = WSAGetLastError();
+
+			if (error == WSAEWOULDBLOCK) {
+				nbread = 0;
+				cond = Condition::Readable;
+			} else {
+				throw Error(Error::System, "recv", error);
+			}
+#else
+			if (errno == EAGAIN || errno == EWOULDBLOCK) {
+				nbread = 0;
+				cond = Condition::Readable;
+			} else {
+				throw Error(Error::System, "recv");
+			}
+#endif
+		}
+
+		return static_cast<unsigned>(nbread);
+	}
+
+	/**
+	 * Send some data.
+	 *
+	 * @param sc the socket
+	 * @param data the data to send
+	 * @param length the length
+	 * @param cond the condition
+	 */
+	template <typename Address>
+	unsigned send(Socket<Address, Tcp> &sc, const void *data, unsigned length, Condition &cond)
+	{
+		int nbsent = ::send(sc.handle(), (ConstArg)data, length, 0);
+
+		if (nbsent == Failure) {
+#if defined(_WIN32)
+			int error = WSAGetLastError();
+
+			if (error == WSAEWOULDBLOCK) {
+				nbsent = 0;
+				cond = Condition::Writable;
+			} else {
+				throw Error(Error::System, "send", error);
+			}
+#else
+			if (errno == EAGAIN || errno == EWOULDBLOCK) {
+				nbsent = 0;
+				cond = Condition::Writable;
+			} else {
+				throw Error(Error::System, "send");
+			}
+#endif
+		}
+
+		return static_cast<unsigned>(nbsent);
+	}
+};
+
+/**
+ * @class Udp
+ * @brief Clear UDP type.
+ *
+ * This class is the basic implementation of UDP sockets.
+ */
+class Udp {
+public:
+	/**
+	 * Socket type.
+	 *
+	 * @return SOCK_DGRAM
+	 */
+	inline int type() const noexcept
+	{
+		return SOCK_DGRAM;
+	}
+
+	/**
+	 * Do nothing.
+	 */
+	template <typename Address, typename Protocol>
+	inline void create(Socket<Address, Protocol> &) noexcept
+	{
+		/* No-op */
+	}
+
+	/**
+	 * Receive some data.
+	 *
+	 * @param sc the socket
+	 * @param data the data
+	 * @param length the length
+	 * @param address the source address
+	 * @param addrlen the source address in/out length
+	 * @param cond the condition
+	 * @return the number of bytes received
+	 */
+	template <typename Address, typename Protocol>
+	unsigned recvfrom(Socket<Address, Protocol> &sc, void *data, unsigned length, sockaddr *address, socklen_t *addrlen, Condition &cond)
+	{
+		int nbread;
+
+		nbread = ::recvfrom(sc.handle(), (Arg)data, length, 0, address, addrlen);
+
+		if (nbread == Failure) {
+#if defined(_WIN32)
+			int error = WSAGetLastError();
+
+			if (error == WSAEWOULDBLOCK) {
+				nbread = 0;
+				cond = Condition::Writable;
+			} else {
+				throw Error(Error::System, "recvfrom");
+			}
+#else
+			if (errno == EAGAIN || errno == EWOULDBLOCK) {
+				nbread = 0;
+				cond = Condition::Writable;
+			} else {
+				throw Error(Error::System, "recvfrom");
+			}
+#endif
+		}
+
+		return static_cast<unsigned>(nbread);
+	}
+
+	/**
+	 * Send some data.
+	 *
+	 * @param sc the socket
+	 * @param data the data to send
+	 * @param length the data length
+	 * @param address the destination address
+	 * @param addrlen the destination address length
+	 * @param cond the condition
+	 */
+	template <typename Address, typename Protocol>
+	unsigned sendto(Socket<Address, Protocol> &sc, const void *data, unsigned length, const sockaddr *address, socklen_t addrlen, Condition &cond)
+	{
+		int nbsent;
+
+		nbsent = ::sendto(sc.handle(), (ConstArg)data, length, 0, address, addrlen);
+		if (nbsent == Failure) {
+#if defined(_WIN32)
+			int error = WSAGetLastError();
+
+			if (error == WSAEWOULDBLOCK) {
+				nbsent = 0;
+				cond = Condition::Writable;
+			} else {
+				throw Error(Error::System, "sendto", error);
+			}
+#else
+			if (errno == EAGAIN || errno == EWOULDBLOCK) {
+				nbsent = 0;
+				cond = Condition::Writable;
+			} else {
+				throw Error(Error::System, "sendto");
+			}
+#endif
+		}
+
+		return static_cast<unsigned>(nbsent);
+	}
+};
+
+#if !defined(NET_NO_SSL)
+
+
+class Tls : private Tcp {
+private:
+	using Context = std::shared_ptr<SSL_CTX>;
+	using Ssl = std::unique_ptr<SSL, void (*)(SSL *)>;
+
+	/* OpenSSL objects */
+	Context m_context;
+	Ssl m_ssl{nullptr, nullptr};
+
+	/* Status */
+	bool m_tcpconnected{false};
+
+	/*
+	 * User definable parameters
+	 */
+	ssl::Method m_method{ssl::Tlsv1};
+	std::string m_key;
+	std::string m_certificate;
+	bool m_verify{false};
+
+	/*
+	 * Construct with a context and ssl, for Tls::accept.
+	 */
+	Tls(Context context, Ssl ssl)
+		: m_context(std::move(context))
+		, m_ssl(std::move(ssl))
+	{
+	}
+
+	/*
+	 * Get the OpenSSL error message.
+	 */
+	inline std::string error(int error)
+	{
+		auto msg = ERR_reason_error_string(error);
+
+		return msg == nullptr ? "" : msg;
+	}
+
+	template <typename Function>
+	void wrap(const std::string &func, Condition &cond, Function &&function)
+	{
+		auto ret = function();
+
+		if (ret <= 0) {
+			int no = SSL_get_error(m_ssl.get(), ret);
+
+			switch (no) {
+			case SSL_ERROR_WANT_READ:
+				cond = Condition::Readable;
+				break;
+			case SSL_ERROR_WANT_WRITE:
+				cond = Condition::Writable;
+				break;
+			default:
+				throw Error(Error::System, func, error(no));
+			}
+		}
+	}
+
+	template <typename Address, typename Protocol>
+	void doConnect(Socket<Address, Protocol> &sc, Condition &cond)
+	{
+		wrap("connect", cond, [&] () -> int {
+			return SSL_connect(m_ssl.get());
+		});
+	}
+
+	template <typename Address, typename Protocol>
+	void doAccept(Socket<Address, Protocol> &sc, Condition &cond)
+	{
+		wrap("accept", cond, [&] () -> int {
+			return SSL_accept(m_ssl.get());
+		});
+	}
+
+public:
+	/**
+	 * @copydoc Tcp::type
+	 */
+	inline int type() const noexcept
+	{
+		return SOCK_STREAM;
+	}
+
+	/**
+	 * Empty TLS constructor.
+	 */
+	Tls()
+	{
+#if !defined(NET_NO_SSL_AUTO_INIT)
+		::net::ssl::init();
+#endif
+	}
+
+	/**
+	 * Set the method.
+	 *
+	 * @param method the method
+	 * @pre the socket must not be already created
+	 */
+	inline void setMethod(ssl::Method method) noexcept
+	{
+		assert(!m_context);
+		assert(!m_ssl);
+
+		m_method = method;
+	}
+
+	/**
+	 * Use the specified private key file.
+	 *
+	 * @param file the path to the private key
+	 */
+	inline void setPrivateKey(std::string file) noexcept
+	{
+		m_key = std::move(file);
+	}
+
+	/**
+	 * Use the specified certificate file.
+	 *
+	 * @param file the path to the file
+	 */
+	inline void setCertificate(std::string file) noexcept
+	{
+		m_certificate = std::move(file);
+	}
+
+	/**
+	 * Set to true if we must verify the certificate and private key.
+	 *
+	 * @param verify the mode
+	 */
+	inline void setVerify(bool verify = true) noexcept
+	{
+		m_verify = verify;
+	}
+
+	/**
+	 * Initialize the SSL objects after have created.
+	 *
+	 * @param sc the socket
+	 * @throw net::Error on errors
+	 */
+	template <typename Address>
+	inline void create(Socket<Address, Tls> &sc)
+	{
+		auto method = (m_method == ssl::Tlsv1) ? TLSv1_method() : SSLv3_method();
+
+		m_context = Context(SSL_CTX_new(method), SSL_CTX_free);
+		m_ssl = Ssl(SSL_new(m_context.get()), SSL_free);
+
+		SSL_set_fd(m_ssl.get(), sc.handle());
+
+		/* Load certificates */
+		if (m_certificate.size() > 0)
+			SSL_CTX_use_certificate_file(m_context.get(), m_certificate.c_str(), SSL_FILETYPE_PEM);
+		if (m_key.size() > 0)
+			SSL_CTX_use_PrivateKey_file(m_context.get(), m_key.c_str(), SSL_FILETYPE_PEM);
+		if (m_verify && !SSL_CTX_check_private_key(m_context.get()))
+			throw Error(Error::System, "(openssl)", "unable to verify key");
+	}
+
+	template <typename Address, typename Protocol>
+	void connect(Socket<Address, Protocol> &sc, const sockaddr *address, socklen_t length, Condition &cond)
+	{
+		/* 1. Connect using raw TCP */
+		Tcp::connect(sc, address, length, cond);
+
+		/* 2. If the connection is complete (e.g. non-blocking), try handshake */
+		if (cond == Condition::None) {
+			m_tcpconnected = true;
+			doConnect(sc, cond);
+		}
+	}
+
+	template <typename Address, typename Protocol>
+	void connect(Socket<Address, Protocol> &sc, Condition &cond)
+	{
+		/* 1. Be sure to complete standard connect before */
+		if (!m_tcpconnected) {
+			Tcp::connect(sc, cond);
+			m_tcpconnected = cond = Condition::None;
+		}
+
+		if (m_tcpconnected)
+			doConnect(sc, cond);
+	}
+
+	template <typename Address>
+	void accept(Socket<Address, Tls> &sc, Socket<Address, Tls> &client, sockaddr *address, socklen_t *length, Condition &cond)
+	{
+		/* TCP sets empty client if no pending connection is available */
+		Tcp::accept(sc, client, address, length, cond);
+
+		if (client.isOpen()) {
+			Tls &proto = client.protocol();
+
+			/* 1. Share the context */
+			proto.m_context = m_context;
+
+			/* 2. Create new SSL instance */
+			proto.m_ssl = Ssl(SSL_new(m_context.get()), SSL_free);
+
+			SSL_set_fd(proto.m_ssl.get(), client.handle());
+
+			/* 3. Try accept process on the **new** client */
+			proto.doAccept(client, cond);
+		}
+	}
+
+	template <typename Address, typename Protocol>
+	inline void accept(Socket<Address, Protocol> &sc, Condition &cond)
+	{
+		doAccept(sc, cond);
+	}
+
+
+	template <typename Address>
+	unsigned recv(Socket<Address, Tls> &sc, void *data, unsigned len, Condition &cond)
+	{
+		int nbread = 0;
+
+		wrap("recv", cond, [&] () -> int {
+			return (nbread = SSL_read(m_ssl.get(), data, len));
+		});
+
+		return static_cast<unsigned>(nbread < 0 ? 0 : nbread);
+	}
+
+	template <typename Address>
+	unsigned send(Socket<Address, Tls> &sc, const void *data, unsigned len, Condition &cond)
+	{
+		int nbsent = 0;
+
+		wrap("send", cond, [&] () -> int {
+			return (nbsent = SSL_write(m_ssl.get(), data, len));
+		});
+
+		return static_cast<unsigned>(nbsent < 0 ? 0 : nbsent);
+	}
+};
+
+#endif // !NET_NO_SSL
+
+} // !protocol
+
+/*
+ * Predefined addresses
+ * ------------------------------------------------------------------
+ */
+
+namespace address {
+
+class Ip {
+private:
+	union {
+		sockaddr_in6 m_sin6;
+		sockaddr_in m_sin;
+	};
+
+	int m_domain;
+
+public:
+	inline Ip(int domain = AF_INET) noexcept
+		: m_domain(domain)
+	{
+		assert(domain == AF_INET || domain == AF_INET6);
+
+		std::memset(&m_sin, 0, sizeof (sockaddr_in));
+	}
+
+	inline Ip(const std::string &host, std::uint16_t port, int domain)
+		: Ip(domain)
+	{
+		if (m_domain == AF_INET)
+			make(host, port, m_sin);
+		else
+			make(host, port, m_sin6);
+	}
+
+	inline Ip(const sockaddr_storage *ss, socklen_t length) noexcept
+		: Ip(ss->ss_family)
+	{
+		assert(ss->ss_family == AF_INET || ss->ss_family == AF_INET6);
+
+		if (ss->ss_family == AF_INET)
+			std::memcpy(&m_sin, ss, length);
+		else
+			std::memcpy(&m_sin6, ss, length);
+	}
+
+	inline int domain() const noexcept
+	{
+		return AF_INET;
+	}
+
+	inline const sockaddr *address() const noexcept
+	{
+		return m_domain == AF_INET ? reinterpret_cast<const sockaddr *>(&m_sin) : reinterpret_cast<const sockaddr *>(&m_sin6);
+	}
+
+	inline socklen_t length() const noexcept
+	{
+		return m_domain == AF_INET ? sizeof (sockaddr_in) : sizeof (sockaddr_in6);
+	}
+
+	inline std::uint16_t port() const noexcept
+	{
+		return m_domain == AF_INET ? ntohs(m_sin.sin_port) : ntohs(m_sin6.sin6_port);
+	}
+
+	inline std::string ip() const
+	{
+		return m_domain == AF_INET ? ip(m_sin) : ip(m_sin6);
+	}
+
+	static void make(const std::string &host, std::uint16_t port, sockaddr_in &sin)
+	{
+#if !defined(NET_NO_AUTO_INIT)
+		net::init();
+#endif
+
+		sin.sin_family = AF_INET;
+		sin.sin_port = htons(port);
+
+		if (host == "*")
+			sin.sin_addr.s_addr = INADDR_ANY;
+#if defined(NET_HAVE_INET_PTON)
+		else if (inet_pton(AF_INET, host.c_str(), &sin.sin_addr) <= 0)
+			throw Error(Error::System, "inet_pton");
+#else
+		else
+			throw Error(Error::System, "inet_pton", std::strerror(ENOSYS));
+#endif
+	}
+
+	static void make(const std::string &address, std::uint16_t port, sockaddr_in6 &sin6)
+	{
+#if !defined(NET_NO_AUTO_INIT)
+		net::init();
+#endif
+
+		sin6.sin6_family = AF_INET6;
+		sin6.sin6_port = htons(port);
+
+		if (address == "*")
+			sin6.sin6_addr = in6addr_any;
+#if defined(NET_HAVE_INET_PTON)
+		else if (inet_pton(AF_INET6, address.c_str(), &sin6.sin6_addr) <= 0)
+			throw Error(Error::System, "inet_pton");
+#else
+		else
+			throw Error(Error::System, "inet_pton", std::strerror(ENOSYS));
+#endif
+	}
+
+	static std::string ip(const sockaddr_in &sin)
+	{
+#if !defined(NET_NO_AUTO_INIT)
+		net::init();
+#endif
+
+#if !defined(NET_HAVE_INET_NTOP)
+		throw Error(Error::System, "inet_ntop", std::strerror(ENOSYS));
+#else
+		char result[INET_ADDRSTRLEN + 1];
+
+		std::memset(result, 0, sizeof (result));
+
+		if (!inet_ntop(AF_INET, const_cast<in_addr *>(&sin.sin_addr), result, sizeof (result)))
+			throw Error(Error::System, "inet_ntop");
+
+		return result;
+#endif
+	}
+
+	static std::string ip(const sockaddr_in6 &sin6)
+	{
+#if !defined(NET_NO_AUTO_INIT)
+		net::init();
+#endif
+
+#if !defined(NET_HAVE_INET_NTOP)
+		throw Error(Error::System, "inet_ntop", std::strerror(ENOSYS));
+#else
+		char result[INET6_ADDRSTRLEN];
+
+		std::memset(result, 0, sizeof (result));
+
+		if (!inet_ntop(AF_INET6, const_cast<in6_addr *>(&sin6.sin6_addr), result, sizeof (result)))
+			throw Error(Error::System, "inet_ntop");
+
+		return result;
+#endif
+	}
+};
+
+class Ipv4 {
+private:
+	sockaddr_in m_sin;
+
+public:
+	inline Ipv4() noexcept
+	{
+		std::memset(&m_sin, 0, sizeof (sockaddr_in));
+	}
+
+	inline Ipv4(const std::string &host, std::uint16_t port)
+		: Ipv4()
+	{
+		Ip::make(host, port, m_sin);
+	}
+
+	inline Ipv4(const sockaddr_storage *ss, socklen_t length) noexcept
+	{
+		std::memcpy(&m_sin, ss, length);
+	}
+
+	inline int domain() const noexcept
+	{
+		return AF_INET;
+	}
+
+	inline const sockaddr *address() const noexcept
+	{
+		return reinterpret_cast<const sockaddr *>(&m_sin);
+	}
+
+	inline socklen_t length() const noexcept
+	{
+		return sizeof (sockaddr_in);
+	}
+
+	inline std::uint16_t port() const noexcept
+	{
+		return ntohs(m_sin.sin_port);
+	}
+
+	inline std::string ip() const
+	{
+		return Ip::ip(m_sin);
+	}
+};
+
+class Ipv6 {
+private:
+	sockaddr_in6 m_sin6;
+
+public:
+	inline Ipv6() noexcept
+	{
+		std::memset(&m_sin6, 0, sizeof (sockaddr_in6));
+	}
+
+	inline Ipv6(const std::string &address, std::uint16_t port)
+		: Ipv6()
+	{
+		Ip::make(address, port, m_sin6);
+	}
+
+	inline Ipv6(const sockaddr_storage *ss, socklen_t length) noexcept
+	{
+		std::memcpy(&m_sin6, ss, length);
+	}
+
+	inline int domain() const noexcept
+	{
+		return AF_INET6;
+	}
+
+	inline const sockaddr *address() const noexcept
+	{
+		return reinterpret_cast<const sockaddr *>(&m_sin6);
+	}
+
+	inline socklen_t length() const noexcept
+	{
+		return sizeof (sockaddr_in6);
+	}
+
+	inline std::uint16_t port() const noexcept
+	{
+		return ntohs(m_sin6.sin6_port);
+	}
+
+	inline std::string ip() const
+	{
+		return Ip::ip(m_sin6);
+	}
+};
+
+#if !defined(_WIN32)
+
+/**
+ * @class Local
+ * @brief unix family sockets
+ *
+ * Create an address to a specific path. Only available on Unix.
+ */
+class Local {
+private:
+	sockaddr_un m_sun;
+	std::string m_path;
+
+public:
+	/**
+	 * Get the domain AF_LOCAL.
+	 *
+	 * @return AF_LOCAL
+	 */
+	inline int domain() const noexcept
+	{
+		return AF_LOCAL;
+	}
+
+	/**
+	 * Default constructor.
+	 */
+	inline Local() noexcept
+	{
+		std::memset(&m_sun, 0, sizeof (sockaddr_un));
+	}
+
+	/**
+	 * Construct an address to a path.
+	 *
+	 * @param path the path
+	 * @param rm remove the file before (default: false)
+	 */
+	Local(std::string path, bool rm = false) noexcept
+		: m_path(std::move(path))
+	{
+		/* Silently remove the file even if it fails */
+		if (rm)
+			::remove(m_path.c_str());
+
+		/* Copy the path */
+		std::memset(m_sun.sun_path, 0, sizeof (m_sun.sun_path));
+		std::strncpy(m_sun.sun_path, m_path.c_str(), sizeof (m_sun.sun_path) - 1);
+
+		/* Set the parameters */
+		m_sun.sun_family = AF_LOCAL;
+	}
+
+	/**
+	 * Construct an unix address from a storage address.
+	 *
+	 * @pre storage's domain must be AF_LOCAL
+	 * @param ss the storage
+	 * @param length the length
+	 */
+	Local(const sockaddr_storage *ss, socklen_t length) noexcept
+	{
+		assert(ss->ss_family == AF_LOCAL);
+
+		std::memcpy(&m_sun, ss, length);
+		m_path = reinterpret_cast<const sockaddr_un &>(m_sun).sun_path;
+	}
+
+	/**
+	 * Get the sockaddr_un.
+	 *
+	 * @return the address
+	 */
+	inline const sockaddr *address() const noexcept
+	{
+		return reinterpret_cast<const sockaddr *>(&m_sun);
+	}
+
+	/**
+	 * Get the address length.
+	 *
+	 * @return the length
+	 */
+	inline socklen_t length() const noexcept
+	{
+#if defined(NET_HAVE_SUN_LEN)
+		return SUN_LEN(&m_sun);
+#else
+		return sizeof (m_sun);
+#endif
+	}
+};
+
+#endif // !_WIN32
+
+} // !address
+
+/*
+ * Listener backends
+ * ------------------------------------------------------------------
+ */
+
+namespace backend {
+
+/**
+ * @class Select
+ * @brief Implements select(2)
+ *
+ * This class is the fallback of any other method, it is not preferred at all for many reasons.
+ */
+class Select {
+public:
+	inline void set(const ListenerTable &, Handle, Condition, bool) noexcept
+	{
+	}
+
+	inline void unset(const ListenerTable &, Handle, Condition, bool) noexcept
+	{
+	}
+
+	std::vector<ListenerStatus> wait(const ListenerTable &table, int ms)
+	{
+		timeval maxwait, *towait;
+		fd_set readset;
+		fd_set writeset;
+
+		FD_ZERO(&readset);
+		FD_ZERO(&writeset);
+
+		Handle max = 0;
+
+		for (const auto &pair : table) {
+			if ((pair.second & Condition::Readable) == Condition::Readable) {
+				FD_SET(pair.first, &readset);
+			}
+			if ((pair.second & Condition::Writable) == Condition::Writable) {
+				FD_SET(pair.first, &writeset);
+			}
+
+			if (pair.first > max) {
+				max = pair.first;
+			}
+		}
+
+		maxwait.tv_sec = 0;
+		maxwait.tv_usec = ms * 1000;
+
+		// Set to nullptr for infinite timeout.
+		towait = (ms < 0) ? nullptr : &maxwait;
+
+		auto error = ::select(max + 1, &readset, &writeset, nullptr, towait);
+		if (error == Failure) {
+			throw Error{Error::System, "select"};
+		}
+		if (error == 0) {
+			throw Error{Error::Timeout, "select", std::strerror(ETIMEDOUT)};
+		}
+
+		std::vector<ListenerStatus> sockets;
+
+		for (const auto &pair : table) {
+			if (FD_ISSET(pair.first, &readset)) {
+				sockets.push_back(ListenerStatus{pair.first, Condition::Readable});
+			}
+			if (FD_ISSET(pair.first, &writeset)) {
+				sockets.push_back(ListenerStatus{pair.first, Condition::Writable});
+			}
+		}
+
+		return sockets;
+	}
+
+	inline std::string name() const noexcept
+	{
+		return "select";
+	}
+};
+
+#if defined(NET_HAVE_KQUEUE)
+
+/**
+ * @class Kqueue
+ * @brief Implements kqueue(2).
+ *
+ * This implementation is available on all BSD and Mac OS X. It is better than
+ * poll(2) because it's O(1), however it's a bit more memory consuming.
+ */
+class Kqueue {
+private:
+	std::vector<struct kevent> m_result;
+	int m_handle;
+
+	Kqueue(const Kqueue &) = delete;
+	Kqueue &operator=(const Kqueue &) = delete;
+
+	void update(Handle sc, int filter, int kflags)
+	{
+		struct kevent ev;
+
+		EV_SET(&ev, h, filter, kflags, 0, 0, nullptr);
+
+		if (kevent(m_handle, &ev, 1, nullptr, 0, nullptr) < 0)
+			throw Error(Error::System, "kevent");
+	}
+
+public:
+	inline Kqueue()
+		: m_handle(kqueue())
+	{
+		if (m_handle < 0)
+			throw Error(Error::System, "kqueue");
+	}
+
+	inline Kqueue(Kqueue &&other) noexcept
+		: m_handle(other.m_handle)
+	{
+		other.m_handle = -1;
+	}
+
+	inline ~Kqueue()
+	{
+		if (m_handle != -1)
+			close(m_handle);
+	}
+
+	inline std::string name() const noexcept
+	{
+		return "kqueue";
+	}
+
+	void set(const ListenerTable &, Handle, Condition, bool add)
+	{
+		if ((condition & Condition::Readable) == Condition::Readable)
+			update(h, EVFILT_READ, EV_ADD | EV_ENABLE);
+		if ((condition & Condition::Writable) == Condition::Writable)
+			update(h, EVFILT_WRITE, EV_ADD | EV_ENABLE);
+
+		if (add)
+			m_result.resize(m_result.size() + 1);
+	}
+
+	void unset(const ListenerTable &, Handle, Condition, bool)
+	{
+		if ((condition & Condition::Readable) == Condition::Readable)
+			update(h, EVFILT_READ, EV_DELETE);
+		if ((condition & Condition::Writable) == Condition::Writable)
+			update(h, EVFILT_WRITE, EV_DELETE);
+
+		if (remove)
+			m_result.resize(m_result.size() - 1);
+	}
+
+	std::vector<ListenerStatus> wait(const ListenerTable &, int)
+	{
+		std::vector<ListenerStatus> sockets;
+		timespec ts = { 0, 0 };
+		timespec *pts = (ms <= 0) ? nullptr : &ts;
+
+		ts.tv_sec = ms / 1000;
+		ts.tv_nsec = (ms % 1000) * 1000000;
+
+		int nevents = kevent(m_handle, nullptr, 0, &m_result[0], m_result.capacity(), pts);
+
+		if (nevents == 0)
+			throw Error(Error::Timeout, "kevent", std::strerror(ETIMEDOUT));
+		if (nevents < 0)
+			throw Error(Error::System, "kevent");
+
+		for (int i = 0; i < nevents; ++i) {
+			sockets.push_back(ListenerStatus{
+				static_cast<Handle>(m_result[i].ident),
+				m_result[i].filter == EVFILT_READ ? Condition::Readable : Condition::Writable
+			});
+		}
+
+		return sockets;
+	}
+
+	inline Kqueue &operator=(Kqueue &&other) noexcept
+	{
+		m_handle = other.m_handle;
+		other.m_handle = -1;
+
+		return *this;
+	}
+};
+
+#endif // !NET_HAVE_KQUEUE
+
+#if defined(NET_HAVE_POLL)
+
+#if defined(_WIN32)
+#  define poll WSAPoll
+#endif
+
+/**
+ * @class Poll
+ * @brief Implements poll(2).
+ *
+ * Poll is widely supported and is better than select(2). It is still not the
+ * best option as selecting the sockets is O(n).
+ */
+class Poll {
+private:
+	std::vector<pollfd> m_fds;
+
+	short toPoll(Condition condition) const noexcept
+	{
+		short result = 0;
+
+		if ((condition & Condition::Readable) == Condition::Readable)
+			result |= POLLIN;
+		if ((condition & Condition::Writable) == Condition::Writable)
+			result |= POLLOUT;
+
+		return result;
+	}
+
+	Condition toCondition(short &event) const noexcept
+	{
+		Condition condition = Condition::None;
+
+		/*
+		 * Poll implementations mark the socket differently regarding
+		 * the disconnection of a socket.
+		 *
+		 * At least, even if POLLHUP or POLLIN is set, recv() always
+		 * return 0 so we mark the socket as readable.
+		 */
+		if ((event & POLLIN) || (event & POLLHUP))
+			condition |= Condition::Readable;
+		if (event & POLLOUT)
+			condition |= Condition::Writable;
+
+		/* Reset event for safety */
+		event = 0;
+
+		return condition;
+	}
+
+public:
+	inline std::string name() const noexcept
+	{
+		return "poll";
+	}
+
+	void set(const ListenerTable &, Handle h, Condition condition, bool add)
+	{
+		if (add) {
+			m_fds.push_back(pollfd{h, toPoll(condition), 0});
+		} else {
+			auto it = std::find_if(m_fds.begin(), m_fds.end(), [&] (const pollfd &pfd) {
+				return pfd.fd == h;
+			});
+
+			it->events |= toPoll(condition);
+		}
+	}
+
+	void unset(const ListenerTable &, Handle h, Condition condition, bool remove)
+	{
+		auto it = std::find_if(m_fds.begin(), m_fds.end(), [&] (const pollfd &pfd) {
+			return pfd.fd == h;
+		});
+
+		if (remove)
+			m_fds.erase(it);
+		else
+			it->events &= ~(toPoll(condition));
+	}
+
+	std::vector<ListenerStatus> wait(const ListenerTable &, int ms)
+	{
+		auto result = poll(m_fds.data(), m_fds.size(), ms);
+
+		if (result == 0)
+			throw Error{Error::Timeout, "select", std::strerror(ETIMEDOUT)};
+		if (result < 0)
+			throw Error{Error::System, "poll"};
+
+		std::vector<ListenerStatus> sockets;
+		for (auto &fd : m_fds)
+			if (fd.revents != 0)
+				sockets.push_back(ListenerStatus{fd.fd, toCondition(fd.revents)});
+
+		return sockets;
+	}
+};
+
+#endif // !NET_HAVE_POLL
+
+#if defined(NET_HAVE_EPOLL)
+
+/**
+ * @class Epoll
+ * @brief Linux's epoll.
+ */
+class Epoll {
+private:
+	int m_handle{-1};
+	std::vector<epoll_event> m_events;
+
+	Epoll(const Epoll &) = delete;
+	Epoll &operator=(const Epoll &) = delete;
+
+	std::uint32_t toEpoll(Condition flags) const noexcept
+	{
+		std::uint32_t events = 0;
+
+		if ((condition & Condition::Readable) == Condition::Readable)
+			events |= EPOLLIN;
+		if ((condition & Condition::Writable) == Condition::Writable)
+			events |= EPOLLOUT;
+
+		return events;
+	}
+
+	Condition toCondition(std::uint32_t events) const noexcept
+	{
+		Condition condition = Condition::None;
+
+		if ((events & EPOLLIN) || (events & EPOLLHUP))
+			condition |= Condition::Readable;
+		if (events & EPOLLOUT)
+			condition |= Condition::Writable;
+
+		return condition;
+	}
+
+	void update(Handle sc, int op, int eflags)
+	{
+		epoll_event ev;
+
+		std::memset(&ev, 0, sizeof (epoll_event));
+
+		ev.events = eflags;
+		ev.data.fd = h;
+
+		if (epoll_ctl(m_handle, op, h, &ev) < 0)
+			throw Error(Error::System, "epoll_ctl");
+	}
+
+public:
+	/**
+	 * Construct the epoll instance.
+	 */
+	inline Epoll()
+		: m_handle(epoll_create1(0))
+	{
+		if (m_handle < 0)
+			throw Error{Error::System, "epoll_create"};
+	}
+
+	inline Epoll(Epoll &&other) noexcept
+		: m_handle(other.m_handle)
+	{
+		other.m_handle = -1;
+	}
+
+	inline ~Epoll()
+	{
+		if (m_handle != -1)
+			close(m_handle);
+	}
+
+	inline std::string name() const noexcept
+	{
+		return "epoll";
+	}
+
+	/*
+	 * For set and unset, we need to apply the whole flags required, so if the socket
+	 * was set to Connection::Readable and user add Connection::Writable, we must
+	 * place both.
+	 */
+	void set(const ListenerTable &, Handle, Condition, bool add)
+	{
+		if (add) {
+			update(sc, EPOLL_CTL_ADD, toEpoll(condition));
+			m_events.resize(m_events.size() + 1);
+		} else {
+			update(sc, EPOLL_CTL_MOD, toEpoll(table.at(sc) | condition));
+		}
+	}
+
+	/*
+	 * Unset is a bit complicated case because Listener tells us which
+	 * flag to remove but to update epoll descriptor we need to pass
+	 * the effective flags that we want to be applied.
+	 *
+	 * So we put the same flags that are currently effective and remove the
+	 * requested one.
+	 */
+	void unset(const ListenerTable &, Handle, Condition, bool remove)
+	{
+		if (remove) {
+			update(sc, EPOLL_CTL_DEL, 0);
+			m_events.resize(m_events.size() - 1);
+		} else {
+			update(sc, EPOLL_CTL_MOD, toEpoll(table.at(sc) & ~(condition)));
+		}
+	}
+
+	std::vector<ListenerStatus> wait(const ListenerTable &, int ms)
+	{
+		int ret = epoll_wait(m_handle, m_events.data(), m_events.size(), ms);
+		std::vector<ListenerStatus> result;
+
+		if (ret == 0)
+			throw Error(Error::Timeout, "epoll_wait", std::strerror(ETIMEDOUT));
+		if (ret < 0)
+			throw Error(Error::System, "epoll_wait");
+
+		for (int i = 0; i < ret; ++i)
+			result.push_back(ListenerStatus{m_events[i].data.fd, toCondition(m_events[i].events)});
+
+		return result;
+	}
+
+	inline Epoll &operator=(Epoll &&other)
+	{
+		m_handle = other.m_handle;
+		other.m_handle = -1;
+
+		return *this;
+	}
+};
+
+#endif // !NET_HAVE_EPOLL
+
+} // !backend
+
+/*
+ * Predefined options
+ * ------------------------------------------------------------------
+ */
+
+namespace option {
+
+/**
+ * @class SockBlockMode
+ * @brief Set or get the blocking-mode for a socket.
+ * @warning On Windows, it's not possible to check if the socket is blocking or not.
+ */
+class SockBlockMode {
+private:
+	bool m_value;
+
+public:
+	inline SockBlockMode(bool value = true) noexcept
+		: m_value(value)
+	{
+	}
+
+	/**
+	 * Set the option.
+	 *
+	 * @param sc the socket
+	 * @throw Error on errors
+	 */
+	template <typename Address, typename Protocol>
+	void set(Socket<Address, Protocol> &sc) const
+	{
+#if defined(O_NONBLOCK) && !defined(_WIN32)
+		int flags;
+
+		if ((flags = fcntl(sc.handle(), F_GETFL, 0)) < 0)
+			flags = 0;
+
+		if (m_value)
+			flags &= ~(O_NONBLOCK);
+		else
+			flags |= O_NONBLOCK;
+
+		if (fcntl(sc.handle(), F_SETFL, flags) < 0)
+			throw Error(Error::System, "fcntl");
+#else
+		unsigned long flags = (m_value) ? 0 : 1;
+
+		if (ioctlsocket(sc.handle(), FIONBIO, &flags) == Failure)
+			throw Error(Error::System, "fcntl");
+#endif
+	}
+
+	/**
+	 * Get the option.
+	 *
+	 * @return the value
+	 * @throw Error on errors
+	 */
+	template <typename Address, typename Protocol>
+	bool get(Socket<Address, Protocol> &sc) const
+	{
+#if defined(O_NONBLOCK) && !defined(_WIN32)
+		int flags = fcntl(sc.handle(), F_GETFL, 0);
+
+		if (flags < 0)
+			throw Error(Error::System, "fcntl");
+
+		return !(flags & O_NONBLOCK);
+#else
+		throw Error(Error::Other, "get", std::strerror(ENOSYS));
+#endif
+	}
+};
+
+/**
+ * @class SockReceiveBuffer
+ * @brief Set or get the input buffer.
+ */
+class SockReceiveBuffer {
+public:
+	/**
+	 * Set to the buffer size.
+	 */
+	int value{2048};
+
+	/**
+	 * Set the option.
+	 *
+	 * @param sc the socket
+	 * @throw Error on errors
+	 */
+	template <typename Address, typename Protocol>
+	inline void set(Socket<Address, Protocol> &sc) const
+	{
+		sc.set(SOL_SOCKET, SO_RCVBUF, value);
+	}
+
+	/**
+	 * Get the option.
+	 *
+	 * @return the value
+	 * @throw Error on errors
+	 */
+	template <typename Address, typename Protocol>
+	inline int get(Socket<Address, Protocol> &sc) const
+	{
+		return sc.template get<int>(SOL_SOCKET, SO_RCVBUF);
+	}
+};
+
+/**
+ * @class SockReuseAddress
+ * @brief Reuse address, must be used before calling Socket::bind
+ */
+class SockReuseAddress {
+private:
+	bool m_value;
+
+public:
+	inline SockReuseAddress(bool value = true) noexcept
+		: m_value(value)
+	{
+	}
+
+	/**
+	 * Set the option.
+	 *
+	 * @param sc the socket
+	 * @throw Error on errors
+	 */
+	template <typename Address, typename Protocol>
+	inline void set(Socket<Address, Protocol> &sc) const
+	{
+		sc.set(SOL_SOCKET, SO_REUSEADDR, m_value ? 1 : 0);
+	}
+
+	/**
+	 * Get the option.
+	 *
+	 * @return the value
+	 * @throw Error on errors
+	 */
+	template <typename Address, typename Protocol>
+	inline bool get(Socket<Address, Protocol> &sc) const
+	{
+		return static_cast<bool>(sc.template get<int>(SOL_SOCKET, SO_REUSEADDR));
+	}
+};
+
+/**
+ * @class SockSendBuffer
+ * @brief Set or get the output buffer.
+ */
+class SockSendBuffer {
+public:
+	/**
+	 * Set to the buffer size.
+	 */
+	int value{2048};
+
+	/**
+	 * Set the option.
+	 *
+	 * @param sc the socket
+	 * @throw Error on errors
+	 */
+	template <typename Address, typename Protocol>
+	inline void set(Socket<Address, Protocol> &sc) const
+	{
+		sc.set(SOL_SOCKET, SO_SNDBUF, value);
+	}
+
+	/**
+	 * Get the option.
+	 *
+	 * @return the value
+	 * @throw Error on errors
+	 */
+	template <typename Address, typename Protocol>
+	inline int get(Socket<Address, Protocol> &sc) const
+	{
+		return sc.template get<int>(SOL_SOCKET, SO_SNDBUF);
+	}
+};
+
+/**
+ * @class TcpNoDelay
+ * @brief Set this option if you want to disable nagle's algorithm.
+ */
+class TcpNoDelay {
+private:
+	bool m_value;
+
+public:
+	inline TcpNoDelay(bool value = true) noexcept
+		: m_value(value)
+	{
+	}
+
+	/**
+	 * Set the option.
+	 *
+	 * @param sc the socket
+	 * @throw Error on errors
+	 */
+	template <typename Address, typename Protocol>
+	inline void set(Socket<Address, Protocol> &sc) const
+	{
+		sc.set(IPPROTO_TCP, TCP_NODELAY, m_value ? 1 : 0);
+	}
+
+	/**
+	 * Get the option.
+	 *
+	 * @return the value
+	 * @throw Error on errors
+	 */
+	template <typename Address, typename Protocol>
+	inline bool get(Socket<Address, Protocol> &sc) const
+	{
+		return static_cast<bool>(sc.template get<int>(IPPROTO_TCP, TCP_NODELAY));
+	}
+};
+
+/**
+ * @class Ipv6Only
+ * @brief Control IPPROTO_IPV6/IPV6_V6ONLY
+ *
+ * Note: some systems may or not set this option by default so it's a good idea to set it in any case to either
+ * false or true if portability is a concern.
+ */
+class Ipv6Only {
+private:
+	bool m_value;
+
+public:
+	inline Ipv6Only(bool value = true) noexcept
+		: m_value(value)
+	{
+	}
+
+	/**
+	 * Set the option.
+	 *
+	 * @param sc the socket
+	 * @throw Error on errors
+	 */
+	template <typename Address, typename Protocol>
+	inline void set(Socket<Address, Protocol> &sc) const
+	{
+		sc.set(IPPROTO_IPV6, IPV6_V6ONLY, m_value ? 1 : 0);
+	}
+
+	/**
+	 * Get the option.
+	 *
+	 * @return the value
+	 * @throw Error on errors
+	 */
+	template <typename Address, typename Protocol>
+	inline bool get(Socket<Address, Protocol> &sc) const
+	{
+		return static_cast<bool>(sc.template get<int>(IPPROTO_IPV6, IPV6_V6ONLY));
+	}
+};
+
+} // !option
+
+/**
+ * @class Listener
+ * @brief Synchronous multiplexing
+ *
+ * Convenient wrapper around the select() system call.
+ *
+ * This class is implemented using a bridge pattern to allow different uses
+ * of listener implementation.
+ *
+ * You should not reinstanciate a new Listener at each iteartion of your
+ * main loop as it can be extremely costly. Instead use the same listener that
+ * you can safely modify on the fly.
+ *
+ * Currently, poll, epoll, select and kqueue are available.
+ *
+ * To implement the backend, the following functions must be available:
+ *
+ * ### Set
+ *
+ * @code
+ * void set(const ListenerTable &, Handle sc, Condition condition, bool add);
+ * @endcode
+ *
+ * This function, takes the socket to be added and the flags. The condition is
+ * always guaranteed to be correct and the function will never be called twice
+ * even if the user tries to set the same flag again.
+ *
+ * An optional add argument is added for backends which needs to do different
+ * operation depending if the socket was already set before or if it is the
+ * first time (e.g EPOLL_CTL_ADD vs EPOLL_CTL_MOD for epoll(7).
+ *
+ * ### Unset
+ *
+ * @code
+ * void unset(const ListenerTable &, Handle sc, Condition condition, bool remove);
+ * @endcode
+ *
+ * Like set, this function is only called if the condition is actually set and will
+ * not be called multiple times.
+ *
+ * Also like set, an optional remove argument is set if the socket is being
+ * completely removed (e.g no more flags are set for this socket).
+ *
+ * ### Wait
+ *
+ * @code
+ * std::vector<ListenerStatus> wait(const ListenerTable &, int ms);
+ * @endcode
+ *
+ * Wait for the sockets to be ready with the specified milliseconds. Must return a list of ListenerStatus,
+ * may throw any exceptions.
+ *
+ * ### Name
+ *
+ * @code
+ * inline const char *name() const noexcept
+ * @endcode
+ *
+ * Returns the backend name. Usually the class in lower case.
+ */
+template <typename Backend = backend :: NET_DEFAULT_BACKEND>
+class Listener {
+private:
+	Backend m_backend;
+	ListenerTable m_table;
+
+public:
+	/**
+	 * Construct an empty listener.
+	 */
+	Listener() = default;
+
+	/**
+	 * Get the backend.
+	 *
+	 * @return the backend
+	 */
+	inline const Backend &backend() const noexcept
+	{
+		return m_backend;
+	}
+
+	/**
+	 * Get the non-modifiable table.
+	 *
+	 * @return the table
+	 */
+	inline const ListenerTable &table() const noexcept
+	{
+		return m_table;
+	}
+
+	/**
+	 * Overloaded function.
+	 *
+	 * @return the iterator
+	 */
+	inline ListenerTable::const_iterator begin() const noexcept
+	{
+		return m_table.begin();
+	}
+
+	/**
+	 * Overloaded function.
+	 *
+	 * @return the iterator
+	 */
+	inline ListenerTable::const_iterator cbegin() const noexcept
+	{
+		return m_table.cbegin();
+	}
+
+	/**
+	 * Overloaded function.
+	 *
+	 * @return the iterator
+	 */
+	inline ListenerTable::const_iterator end() const noexcept
+	{
+		return m_table.end();
+	}
+
+	/**
+	 * Overloaded function.
+	 *
+	 * @return the iterator
+	 */
+	inline ListenerTable::const_iterator cend() const noexcept
+	{
+		return m_table.cend();
+	}
+
+	/**
+	 * Add or update a socket to the listener.
+	 *
+	 * If the socket is already placed with the appropriate flags, the
+	 * function is a no-op.
+	 *
+	 * If incorrect flags are passed, the function does nothing.
+	 *
+	 * @param sc the socket
+	 * @param condition the condition (may be OR'ed)
+	 * @throw Error if the backend failed to set
+	 */
+	void set(Handle sc, Condition condition)
+	{
+		/* Invalid or useless flags */
+		if (condition == Condition::None || static_cast<int>(condition) > 0x3)
+			return;
+
+		auto it = m_table.find(sc);
+
+		/*
+		 * Do not update the table if the backend failed to add
+		 * or update.
+		 */
+		if (it == m_table.end()) {
+			m_backend.set(m_table, sc, condition, true);
+			m_table.emplace(sc, condition);
+		} else {
+			/* Remove flag if already present */
+			if ((condition & Condition::Readable) == Condition::Readable &&
+			    (it->second & Condition::Readable) == Condition::Readable)
+				condition &= ~(Condition::Readable);
+			if ((condition & Condition::Writable) == Condition::Writable &&
+			    (it->second & Condition::Writable) == Condition::Writable)
+				condition &= ~(Condition::Writable);
+
+			/* Still need a call? */
+			if (condition != Condition::None) {
+				m_backend.set(m_table, sc, condition, false);
+				it->second |= condition;
+			}
+		}
+	}
+
+	/**
+	 * Unset a socket from the listener, only the flags is removed
+	 * unless the two flagss are requested.
+	 *
+	 * For example, if you added a socket for both reading and writing,
+	 * unsetting the write flags will keep the socket for reading.
+	 *
+	 * @param sc the socket
+	 * @param condition the condition (may be OR'ed)
+	 * @see remove
+	 */
+	void unset(Handle sc, Condition condition)
+	{
+		auto it = m_table.find(sc);
+
+		/* Invalid or useless flags */
+		if (condition == Condition::None || static_cast<int>(condition) > 0x3 || it == m_table.end())
+			return;
+
+		/*
+		 * Like set, do not update if the socket is already at the appropriate
+		 * state.
+		 */
+		if ((condition & Condition::Readable) == Condition::Readable &&
+		    (it->second & Condition::Readable) != Condition::Readable)
+			condition &= ~(Condition::Readable);
+		if ((condition & Condition::Writable) == Condition::Writable &&
+		    (it->second & Condition::Writable) != Condition::Writable)
+			condition &= ~(Condition::Writable);
+
+		if (condition != Condition::None) {
+			/* Determine if it's a complete removal */
+			bool removal = ((it->second) & ~(condition)) == Condition::None;
+
+			m_backend.unset(m_table, sc, condition, removal);
+
+			if (removal)
+				m_table.erase(it);
+			else
+				it->second &= ~(condition);
+		}
+	}
+
+	/**
+	 * Remove completely the socket from the listener.
+	 *
+	 * It is a shorthand for unset(sc, Condition::Readable | Condition::Writable);
+	 *
+	 * @param sc the socket
+	 */
+	inline void remove(Handle sc)
+	{
+		unset(sc, Condition::Readable | Condition::Writable);
+	}
+
+	/**
+	 * Remove all sockets.
+	 */
+	inline void clear()
+	{
+		while (!m_table.empty())
+			remove(m_table.begin()->first);
+	}
+
+	/**
+	 * Get the number of sockets in the listener.
+	 */
+	inline ListenerTable::size_type size() const noexcept
+	{
+		return m_table.size();
+	}
+
+	/**
+	 * Select a socket. Waits for a specific amount of time specified as the duration.
+	 *
+	 * @param duration the duration
+	 * @return the socket ready
+	 */
+	template <typename Rep, typename Ratio>
+	inline ListenerStatus wait(const std::chrono::duration<Rep, Ratio> &duration)
+	{
+		auto cvt = std::chrono::duration_cast<std::chrono::milliseconds>(duration);
+
+		return m_backend.wait(m_table, cvt.count())[0];
+	}
+
+	/**
+	 * Overload with milliseconds.
+	 *
+	 * @param timeout the optional timeout in milliseconds
+	 * @return the socket ready
+	 */
+	inline ListenerStatus wait(int timeout = -1)
+	{
+		return wait(std::chrono::milliseconds(timeout));
+	}
+
+	/**
+	 * Select multiple sockets.
+	 *
+	 * @param duration the duration
+	 * @return the socket ready
+	 */
+	template <typename Rep, typename Ratio>
+	inline std::vector<ListenerStatus> waitMultiple(const std::chrono::duration<Rep, Ratio> &duration)
+	{
+		auto cvt = std::chrono::duration_cast<std::chrono::milliseconds>(duration);
+
+		return m_backend.wait(m_table, cvt.count());
+	}
+
+	/**
+	 * Overload with milliseconds.
+	 *
+	 * @return the socket ready
+	 */
+	inline std::vector<ListenerStatus> waitMultiple(int timeout = -1)
+	{
+		return waitMultiple(std::chrono::milliseconds(timeout));
+	}
+};
+
+} // !net
+
+#endif // !NET_H
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/net/test/main.cpp	Mon Apr 04 17:34:01 2016 +0200
@@ -0,0 +1,701 @@
+/*
+ * main.cpp -- test sockets
+ *
+ * Copyright (c) 2013-2015 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <chrono>
+#include <iostream>
+#include <sstream>
+#include <string>
+#include <thread>
+
+#include <gtest/gtest.h>
+
+#include <net.h>
+
+using namespace net;
+using namespace net::address;
+using namespace net::option;
+using namespace net::protocol;
+
+using namespace std::literals::chrono_literals;
+
+/*
+ * Options
+ * ------------------------------------------------------------------
+ */
+
+TEST(Options, reuse)
+{
+	SocketTcpIp s;
+
+	try {
+		s.set(option::SockReuseAddress(true));
+		ASSERT_TRUE(s.get<option::SockReuseAddress>());
+
+		s.set(option::SockReuseAddress(false));
+		ASSERT_FALSE(s.get<option::SockReuseAddress>());
+	} catch (const std::exception &ex) {
+		FAIL() << ex.what();
+	}
+}
+
+TEST(Options, nodelay)
+{
+	SocketTcpIp s;
+
+	try {
+		s.set(option::TcpNoDelay(true));
+		ASSERT_TRUE(s.get<option::TcpNoDelay>());
+
+		s.set(option::TcpNoDelay(false));
+		ASSERT_FALSE(s.get<option::TcpNoDelay>());
+	} catch (const std::exception &ex) {
+		FAIL() << ex.what();
+	}
+}
+
+TEST(Options, v6only)
+{
+	SocketTcpIpv6 s;
+
+	try {
+		s.set(option::Ipv6Only(true));
+		ASSERT_TRUE(s.get<option::Ipv6Only>());
+
+		s.set(option::Ipv6Only(false));
+		ASSERT_FALSE(s.get<option::Ipv6Only>());
+	} catch (const std::exception &ex) {
+		FAIL() << ex.what();
+	}
+}
+
+/* --------------------------------------------------------
+ * TCP tests
+ * -------------------------------------------------------- */
+
+class TcpServerTest : public testing::Test {
+protected:
+	SocketTcp<Ipv4> m_server;
+	SocketTcp<Ipv4> m_client;
+
+	std::thread m_tserver;
+	std::thread m_tclient;
+
+public:
+	TcpServerTest()
+	{
+		m_server.set(SockReuseAddress());
+	}
+
+	~TcpServerTest()
+	{
+		if (m_tserver.joinable())
+			m_tserver.join();
+		if (m_tclient.joinable())
+			m_tclient.join();
+	}
+};
+
+TEST_F(TcpServerTest, connect)
+{
+	m_tserver = std::thread([this] () {
+		SocketTcp<Ipv4> sc;
+
+		m_server.bind(Ipv4("*", 16000));
+		m_server.listen();
+		m_server.accept(sc);
+		m_server.close();
+	});
+
+	std::this_thread::sleep_for(100ms);
+
+	m_tclient = std::thread([this] () {
+		m_client.connect(Ipv4("127.0.0.1", 16000));
+		m_client.close();
+	});
+}
+
+TEST_F(TcpServerTest, io)
+{
+	m_tserver = std::thread([this] () {
+		m_server.bind(Ipv4("*", 16000));
+		m_server.listen();
+
+		SocketTcp<Ipv4> client;
+
+		m_server.accept(client);
+
+		auto msg = client.recv(512);
+
+		ASSERT_EQ("hello world", msg);
+
+		client.send(msg);
+	});
+
+	std::this_thread::sleep_for(100ms);
+
+	m_tclient = std::thread([this] () {
+		m_client.connect(Ipv4("127.0.0.1", 16000));
+		m_client.send("hello world");
+
+		ASSERT_EQ("hello world", m_client.recv(512));
+	});
+}
+
+/* --------------------------------------------------------
+ * UDP tests
+ * -------------------------------------------------------- */
+
+class UdpServerTest : public testing::Test {
+protected:
+	SocketUdp<Ipv4> m_server;
+	SocketUdp<Ipv4> m_client;
+
+	std::thread m_tserver;
+	std::thread m_tclient;
+
+public:
+	UdpServerTest()
+	{
+		m_server.set(SockBlockMode());
+	}
+
+	~UdpServerTest()
+	{
+		if (m_tserver.joinable())
+			m_tserver.join();
+		if (m_tclient.joinable())
+			m_tclient.join();
+	}
+};
+
+TEST_F(UdpServerTest, io)
+{
+	m_tserver = std::thread([this] () {
+		Ipv4 client;
+		Ipv4 info("*", 16000);
+
+		m_server.bind(info);
+		auto msg = m_server.recvfrom(512, &client);
+
+		ASSERT_EQ("hello world", msg);
+
+		m_server.sendto(msg, client);
+		m_server.close();
+	});
+
+	std::this_thread::sleep_for(100ms);
+
+	m_tclient = std::thread([this] () {
+		Ipv4 info("127.0.0.1", 16000);
+
+		m_client.sendto("hello world", info);
+
+		ASSERT_EQ("hello world", m_client.recvfrom(512, &info));
+
+		m_client.close();
+	});
+}
+
+/* --------------------------------------------------------
+ * Listener: set function
+ * -------------------------------------------------------- */
+
+class TestBackendSet {
+public:
+	int m_callcount{0};
+	bool m_added{false};
+	Condition m_flags{Condition::None};
+
+	inline void set(const ListenerTable &, Handle, Condition flags, bool add) noexcept
+	{
+		m_callcount ++;
+		m_added = add;
+		m_flags |= flags;
+	}
+
+	inline void unset(const ListenerTable &, Handle, Condition, bool) noexcept
+	{
+	}
+
+	std::vector<ListenerStatus> wait(const ListenerTable &, int)
+	{
+		return {};
+	}
+};
+
+class TestBackendSetFail {
+public:
+	inline void set(const ListenerTable &, Handle, Condition, bool)
+	{
+		throw "fail";
+	}
+
+	inline void unset(const ListenerTable &, Handle, Condition, bool) noexcept
+	{
+	}
+
+	std::vector<ListenerStatus> wait(const ListenerTable &, int)
+	{
+		return {};
+	}
+};
+
+TEST(ListenerSet, initialAdd)
+{
+	Listener<TestBackendSet> listener;
+	Handle s = 0;
+
+	listener.set(s, Condition::Readable);
+
+	ASSERT_EQ(1U, listener.size());
+	ASSERT_EQ(1, listener.backend().m_callcount);
+	ASSERT_TRUE(listener.backend().m_added);
+	ASSERT_TRUE(listener.backend().m_flags == Condition::Readable);
+}
+
+TEST(ListenerSet, readThenWrite)
+{
+	Listener<TestBackendSet> listener;
+	Handle s = 0;
+
+	listener.set(s, Condition::Readable);
+	listener.set(s, Condition::Writable);
+
+	ASSERT_EQ(1U, listener.size());
+	ASSERT_EQ(2, listener.backend().m_callcount);
+	ASSERT_FALSE(listener.backend().m_added);
+	ASSERT_TRUE(static_cast<int>(listener.backend().m_flags) == 0x3);
+}
+
+TEST(ListenerSet, allOneShot)
+{
+	Listener<TestBackendSet> listener;
+	Handle s = 0;
+
+	listener.set(s, Condition::Readable | Condition::Writable);
+
+	ASSERT_EQ(1U, listener.size());
+	ASSERT_EQ(1, listener.backend().m_callcount);
+	ASSERT_TRUE(listener.backend().m_added);
+	ASSERT_TRUE(static_cast<int>(listener.backend().m_flags) == 0x3);
+}
+
+TEST(ListenerSet, readTwice)
+{
+	Listener<TestBackendSet> listener;
+	Handle s = 0;
+
+	listener.set(s, Condition::Readable);
+	listener.set(s, Condition::Readable);
+
+	ASSERT_EQ(1U, listener.size());
+	ASSERT_EQ(1, listener.backend().m_callcount);
+	ASSERT_TRUE(listener.backend().m_added);
+	ASSERT_TRUE(listener.backend().m_flags == Condition::Readable);
+}
+
+TEST(ListenerSet, failure)
+{
+	Listener<TestBackendSetFail> listener;
+	Handle s = 0;
+
+	try {
+		listener.set(s, Condition::Readable);
+		FAIL() << "exception expected";
+	} catch (...) {
+	}
+
+	ASSERT_EQ(0U, listener.size());
+}
+
+/* --------------------------------------------------------
+ * Listener: unset / remove functions
+ * -------------------------------------------------------- */
+
+class TestBackendUnset {
+public:
+	bool m_isset{false};
+	bool m_isunset{false};
+	Condition m_flags{Condition::None};
+	bool m_removal{false};
+
+	inline void set(const ListenerTable &, Handle &, Condition flags, bool) noexcept
+	{
+		m_isset = true;
+		m_flags |= flags;
+	}
+
+	inline void unset(const ListenerTable &, Handle &, Condition flags, bool remove) noexcept
+	{
+		m_isunset = true;
+		m_flags &= ~(flags);
+		m_removal = remove;
+	}
+
+	std::vector<ListenerStatus> wait(const ListenerTable &, int) noexcept
+	{
+		return {};
+	}
+};
+
+class TestBackendUnsetFail {
+public:
+	inline void set(const ListenerTable &, Handle &, Condition, bool) noexcept
+	{
+	}
+
+	inline void unset(const ListenerTable &, Handle &, Condition, bool)
+	{
+		throw "fail";
+	}
+
+	std::vector<ListenerStatus> wait(const ListenerTable &, int)
+	{
+		return {};
+	}
+};
+
+TEST(ListenerUnsetRemove, unset)
+{
+	Listener<TestBackendUnset> listener;
+	Handle s = 0;
+
+	listener.set(s, Condition::Readable);
+	listener.unset(s, Condition::Readable);
+
+	ASSERT_EQ(0U, listener.size());
+	ASSERT_TRUE(listener.backend().m_isset);
+	ASSERT_TRUE(listener.backend().m_isunset);
+	ASSERT_TRUE(listener.backend().m_flags == Condition::None);
+	ASSERT_TRUE(listener.backend().m_removal);
+}
+
+TEST(ListenerUnsetRemove, unsetOne)
+{
+	Listener<TestBackendUnset> listener;
+	Handle s = 0;
+
+	listener.set(s, Condition::Readable | Condition::Writable);
+	listener.unset(s, Condition::Readable);
+
+	ASSERT_EQ(1U, listener.size());
+	ASSERT_TRUE(listener.backend().m_isset);
+	ASSERT_TRUE(listener.backend().m_isunset);
+	ASSERT_TRUE(listener.backend().m_flags == Condition::Writable);
+	ASSERT_FALSE(listener.backend().m_removal);
+}
+
+TEST(ListenerUnsetRemove, unsetAll)
+{
+	Listener<TestBackendUnset> listener;
+	Handle s = 0;
+
+	listener.set(s, Condition::Readable | Condition::Writable);
+	listener.unset(s, Condition::Readable);
+	listener.unset(s, Condition::Writable);
+
+	ASSERT_EQ(0U, listener.size());
+	ASSERT_TRUE(listener.backend().m_isset);
+	ASSERT_TRUE(listener.backend().m_isunset);
+	ASSERT_TRUE(listener.backend().m_flags == Condition::None);
+	ASSERT_TRUE(listener.backend().m_removal);
+}
+
+TEST(ListenerUnsetRemove, remove)
+{
+	Listener<TestBackendUnset> listener;
+	Handle s = 0;
+
+	listener.set(s, Condition::Readable | Condition::Writable);
+	listener.remove(s);
+
+	ASSERT_EQ(0U, listener.size());
+	ASSERT_TRUE(listener.backend().m_isset);
+	ASSERT_TRUE(listener.backend().m_isunset);
+	ASSERT_TRUE(listener.backend().m_flags == Condition::None);
+	ASSERT_TRUE(listener.backend().m_removal);
+}
+
+TEST(ListenerUnsetRemove, failure)
+{
+	Listener<TestBackendUnsetFail> listener;
+	Handle s = 0;
+
+	listener.set(s, Condition::Readable | Condition::Writable);
+
+	try {
+		listener.remove(s);
+		FAIL() << "exception expected";
+	} catch (...) {
+	}
+
+	/* If fail, kept into the table */
+	ASSERT_EQ(1U, listener.size());
+}
+
+/* --------------------------------------------------------
+ * Listener: system
+ * -------------------------------------------------------- */
+
+class ListenerTest : public testing::Test {
+protected:
+	Listener<Select> m_listener;
+	SocketTcp<Ipv4> m_masterTcp;
+	SocketTcp<Ipv4> m_clientTcp;
+
+	std::thread m_tserver;
+	std::thread m_tclient;
+
+public:
+	ListenerTest()
+	{
+		m_masterTcp.set(SockReuseAddress());
+		m_masterTcp.bind(Ipv4("*", 16000));
+		m_masterTcp.listen();
+	}
+
+	~ListenerTest()
+	{
+		if (m_tserver.joinable())
+			m_tserver.join();
+		if (m_tclient.joinable())
+			m_tclient.join();
+	}
+};
+
+TEST_F(ListenerTest, accept)
+{
+	m_tserver = std::thread([this] () {
+		try {
+			m_listener.set(m_masterTcp.handle(), Condition::Readable);
+			m_listener.wait();
+			m_masterTcp.accept(nullptr);
+			m_masterTcp.close();
+		} catch (const std::exception &ex) {
+			FAIL() << ex.what();
+		}
+	});
+
+	std::this_thread::sleep_for(100ms);
+
+	m_tclient = std::thread([this] () {
+		m_clientTcp.connect(Ipv4("127.0.0.1", 16000));
+	});
+}
+
+TEST_F(ListenerTest, recv)
+{
+	m_tserver = std::thread([this] () {
+		try {
+			m_listener.set(m_masterTcp.handle(), Condition::Readable);
+			m_listener.wait();
+
+			SocketTcp<Ipv4> sc;
+
+			m_masterTcp.accept(sc);
+
+			ASSERT_EQ("hello", sc.recv(512));
+		} catch (const std::exception &ex) {
+			FAIL() << ex.what();
+		}
+	});
+
+	std::this_thread::sleep_for(100ms);
+
+	m_tclient = std::thread([this] () {
+		m_clientTcp.connect(Ipv4("127.0.0.1", 16000));
+		m_clientTcp.send("hello");
+	});
+}
+
+/* --------------------------------------------------------
+ * Non-blocking connect
+ * -------------------------------------------------------- */
+
+class NonBlockingConnectTest : public testing::Test {
+protected:
+	SocketTcp<Ipv4> m_server;
+	SocketTcp<Ipv4> m_client;
+
+	std::thread m_tserver;
+	std::thread m_tclient;
+
+public:
+	NonBlockingConnectTest()
+	{
+		m_client.set(SockBlockMode(false));
+	}
+
+	~NonBlockingConnectTest()
+	{
+		if (m_tserver.joinable())
+			m_tserver.join();
+		if (m_tclient.joinable())
+			m_tclient.join();
+	}
+};
+
+/* --------------------------------------------------------
+ * TCP accept
+ * -------------------------------------------------------- */
+
+class TcpAcceptTest : public testing::Test {
+protected:
+	SocketTcp<Ipv4> m_server;
+	SocketTcp<Ipv4> m_client;
+
+	std::thread m_tserver;
+	std::thread m_tclient;
+
+public:
+	TcpAcceptTest()
+	{
+		m_server.set(SockReuseAddress());
+		m_server.bind(Ipv4("*", 16000));
+		m_server.listen();
+	}
+
+	~TcpAcceptTest()
+	{
+		if (m_tserver.joinable())
+			m_tserver.join();
+		if (m_tclient.joinable())
+			m_tclient.join();
+	}
+};
+
+/* --------------------------------------------------------
+ * TCP recv
+ * -------------------------------------------------------- */
+
+class TcpRecvTest : public testing::Test {
+protected:
+	SocketTcp<Ipv4> m_server;
+	SocketTcp<Ipv4> m_client;
+
+	std::thread m_tserver;
+	std::thread m_tclient;
+
+public:
+	TcpRecvTest()
+	{
+		m_server.set(SockReuseAddress());
+		m_server.bind(Ipv4("*", 16000));
+		m_server.listen();
+	}
+
+	~TcpRecvTest()
+	{
+		if (m_tserver.joinable())
+			m_tserver.join();
+		if (m_tclient.joinable())
+			m_tclient.join();
+	}
+};
+
+TEST_F(TcpRecvTest, blockingSuccess)
+{
+	m_tserver = std::thread([this] () {
+		SocketTcp<Ipv4> client;
+
+		m_server.accept(client);
+
+		ASSERT_EQ("hello", client.recv(32));
+	});
+
+	std::this_thread::sleep_for(100ms);
+
+	m_tclient = std::thread([this] () {
+		m_client.connect(Ipv4("127.0.0.1", 16000));
+		m_client.send("hello");
+		m_client.close();
+	});
+}
+
+/* --------------------------------------------------------
+ * Socket SSL
+ * -------------------------------------------------------- */
+
+#if !defined(SOCKET_NO_SSL)
+
+class TlsRecvTest : public testing::Test {
+protected:
+	SocketTls<Ipv4> m_server{nullptr};
+	SocketTls<Ipv4> m_client;
+
+	std::thread m_tserver;
+	std::thread m_tclient;
+
+public:
+	TlsRecvTest()
+	{
+		Tls protocol;
+
+		protocol.setCertificate("sockets/test.crt");
+		protocol.setPrivateKey("sockets/test.key");
+		protocol.setVerify(false);
+
+		m_server = SocketTls<Ipv4>(std::move(protocol));
+		m_server.set(SockReuseAddress());
+		m_server.bind(Ipv4("*", 16000));
+		m_server.listen();
+	}
+
+	~TlsRecvTest()
+	{
+		if (m_tserver.joinable())
+			m_tserver.join();
+		if (m_tclient.joinable())
+			m_tclient.join();
+	}
+};
+
+TEST_F(TlsRecvTest, blockingSuccess)
+{
+	m_tserver = std::thread([this] () {
+		try {
+			SocketTls<Ipv4> client;
+
+			m_server.accept(client);
+
+			ASSERT_EQ("hello", client.recv(32));
+		} catch (const net::Error &ex) {
+			FAIL() << ex.function() << ": " << ex.what();
+		}
+	});
+
+	std::this_thread::sleep_for(250ms);
+
+	m_tclient = std::thread([this] () {
+		try {
+			m_client.connect(Ipv4("127.0.0.1", 16000));
+			m_client.send("hello");
+		} catch (const net::Error &ex) {
+			FAIL() << ex.function() << ": " << ex.what();
+		}
+	});
+}
+
+#endif
+
+int main(int argc, char **argv)
+{
+	testing::InitGoogleTest(&argc, argv);
+
+	return RUN_ALL_TESTS();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/net/test/test.crt	Mon Apr 04 17:34:01 2016 +0200
@@ -0,0 +1,14 @@
+-----BEGIN CERTIFICATE-----
+MIICITCCAYoCCQCGm4grkVCohjANBgkqhkiG9w0BAQsFADBVMQswCQYDVQQGEwJG
+UjEPMA0GA1UECAwGRnJhbmNlMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0
+eSBMdGQxEjAQBgNVBAMMCWxvY2FsaG9zdDAeFw0xNTEwMjYyMDM0NThaFw0yNTEw
+MjMyMDM0NThaMFUxCzAJBgNVBAYTAkZSMQ8wDQYDVQQIDAZGcmFuY2UxITAfBgNV
+BAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDESMBAGA1UEAwwJbG9jYWxob3N0
+MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDp13OqVyOWyv5QWD4xr+Duw6SZ
+gU7D5huzsAOcneSI6JUhf+7Ecu6BQ2JGkFn4srIVkMWGQuImETJ8JCpSQH7rk+xO
+L9fTTK+TwhP2hW/Rf/b2gWedhJAS+gilqt4JNT7v2wFv+aTtRt/lpTXVSdtpLa/m
+Pdy219f6MAPgODJ/7QIDAQABMA0GCSqGSIb3DQEBCwUAA4GBAJSnn/IBn1ZblfzP
+rJO/lE1Jwpmx3B7+oR/e4fkZd6JR3s06umGYWr2H+TPl/5dj9x0gPokhoIL9zCGq
+SxCPnOeaxjBkw7yh3Ks6m3xKxmK4aMpAtBHtwmbfQyIcgz71/lfCzbJ3WcKpn1ig
+IZbByt5QSSPcFORRzJJa35eHBdfX
+-----END CERTIFICATE-----
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/net/test/test.key	Mon Apr 04 17:34:01 2016 +0200
@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICXAIBAAKBgQDp13OqVyOWyv5QWD4xr+Duw6SZgU7D5huzsAOcneSI6JUhf+7E
+cu6BQ2JGkFn4srIVkMWGQuImETJ8JCpSQH7rk+xOL9fTTK+TwhP2hW/Rf/b2gWed
+hJAS+gilqt4JNT7v2wFv+aTtRt/lpTXVSdtpLa/mPdy219f6MAPgODJ/7QIDAQAB
+AoGBANDt4ndQkgi56A1rOm50gVlzTg6lPPXFE/0xB5kYbcdxX0VmI7Q8KCMwTI9V
+jD2rk3e3OPSjr6FpfhzyxylkXMBz2BL5NRNPowCJbiMgZOUIzlcWPKo0tgf1bZJx
+YdB5U003ISGPPBjVOAjyizY7tJnaNvbpLQ0hbIAsvHPEAOnBAkEA9r3g8NQjPrvb
+oIr5SMIxM8HDJ1/q+MEBSFtRFzQpmur6P64Jsu96zCyencUYTxs0L/sottrj6dPC
+vjGCc6PjsQJBAPKdqK1knJv6Y95M2bnEwrymCFVdxCi7AxObStB+bg/+7mMCUqqX
+j2g71bfvhYakHV7CiaYrrORChwj6vTbimv0CQGpd2IZ5LOhyW2+N+YDgFg3Vzac/
+ti+eJEto8kAqgHUELvUctZmpmypBYe9pc91GQO0ePKL3IaE/ZIhRF4d6c0ECQH9A
+XiaD7PiKvjLs0A31u8ZCt4A+7BII9LYl73mntobBSbu4ji9Xyyn6qEAPa1ORZK49
+DwGPSuF2W2lESlYtSOkCQGrtczhx3IyJjk5e2Y1i/UddPKjviAysCSzcW6aVTNr9
+Y2L0sWmva2FKnkl9FDuEqxvmGr6OOkr5Ll7aWLzJri8=
+-----END RSA PRIVATE KEY-----
--- a/modules/sockets/CMakeLists.txt	Thu Feb 25 21:01:02 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,39 +0,0 @@
-#
-# CMakeLists.txt -- sockets module
-#
-# Copyright (c) 2013-2015 David Demelier <markand@malikania.fr>
-#
-# Permission to use, copy, modify, and/or distribute this software for any
-# purpose with or without fee is hereby granted, provided that the above
-# copyright notice and this permission notice appear in all copies.
-#
-# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
-# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
-# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
-# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
-# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
-# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
-# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-#
-
-project(sockets)
-
-if (OPENSSL_FOUND)
-	if (WIN32)
-		set(LIBRARIES ws2_32)
-	endif ()
-
-	code_define_module(
-		NAME sockets
-		INCLUDES ${OPENSSL_INCLUDE_DIR}
-		LIBRARIES
-			${LIBRARIES}
-			${OPENSSL_LIBRARIES}
-		SOURCES
-			${sockets_SOURCE_DIR}/sockets.cpp
-			${sockets_SOURCE_DIR}/sockets.h
-		RESOURCES
-			${sockets_SOURCE_DIR}/test/test.crt
-			${sockets_SOURCE_DIR}/test/test.key
-	)
-endif ()
--- a/modules/sockets/examples/blocking-accept.cpp	Thu Feb 25 21:01:02 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,52 +0,0 @@
-/*
- * blocking-accept.cpp -- example of blocking accept
- *
- * Options:
- *   - WITH_PORT (int), the port to use (default: 16000)
- *   - WITH_TIMEOUT (int), number of seconds before giving up (default: 60)
- *   - WITH_SSL (bool), true to test with SSL (default: false)
- */
-
-#include <iostream>
-
-#include "sockets.h"
-
-#if !defined(WITH_PORT)
-#  define WITH_PORT 16000
-#endif
-
-#if !defined(WITH_TIMEOUT)
-#  define WITH_TIMEOUT 60
-#endif
-
-int main()
-{
-#if defined(WITH_SSL)
-	net::SocketTls<net::address::Ip> master;
-	net::SocketTls<net::address::Ip> client{net::Invalid};
-#else
-	net::SocketTcp<net::address::Ip> master;
-	net::SocketTcp<net::address::Ip> client{net::Invalid};
-#endif
-
-	net::Listener<> listener;
-
-	try {
-		master.set(net::option::SockReuseAddress{true});
-		master.bind(net::address::Ip{"*", WITH_PORT});
-		master.listen();
-
-		listener.set(master.handle(), net::Condition::Readable);
-		listener.wait(std::chrono::seconds(WITH_TIMEOUT));
-
-		master.accept(client);
-	} catch (const net::Error &error) {
-		std::cerr << "error: " << error.what() << std::endl;
-		std::exit(1);
-	}
-
-	std::cout << "Client successfully accepted!" << std::endl;
-
-	return 0;	
-}
-
--- a/modules/sockets/examples/blocking-connect.cpp	Thu Feb 25 21:01:02 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,48 +0,0 @@
-/*
- * blocking-connect.cpp -- example of blocking connect
- *
- * Options:
- *   - WITH_HOST (string literal), the host to try (default: "malikania.fr")
- *   - WITH_PORT (int), the port to use (default: 80)
- *   - WITH_TIMEOUT (int), number of seconds before giving up (default: 30)
- *   - WITH_SSL (bool), true to test with SSL (default: false)
- */
-
-#include <iostream>
-
-#if !defined(WITH_HOST)
-#  define WITH_HOST "malikania.fr"
-#endif
-
-#if !defined(WITH_PORT)
-#  define WITH_PORT 80
-#endif
-
-#if !defined(WITH_TIMEOUT)
-#  define WITH_TIMEOUT 30
-#endif
-
-#include "elapsed-timer.h"
-#include "sockets.h"
-
-int main()
-{
-#if defined(WITH_SSL)
-	net::SocketTls<net::address::Ipv4> socket;
-#else
-	net::SocketTcp<net::address::Ipv4> socket;
-#endif
-
-	try {
-		std::cout << "Trying to connect to " << WITH_HOST << ":" << WITH_PORT << std::endl;
-		socket.connect(net::address::Ip(WITH_HOST, WITH_PORT));
-	} catch (const net::Error &error) {
-		std::cerr << "error: " << error.what() << std::endl;
-		std::exit(1);
-	}
-
-	std::cout << "Successfully connected!" << std::endl;
-
-	return 0;
-}
-
--- a/modules/sockets/examples/elapsed-timer.cpp	Thu Feb 25 21:01:02 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,60 +0,0 @@
-/*
- * elapsed-timer.cpp -- measure elapsed time
- *
- * Copyright (c) 2013-2015 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include "elapsed-timer.h"
-
-using std::chrono::duration_cast;
-using std::chrono::high_resolution_clock;
-using std::chrono::milliseconds;
-
-ElapsedTimer::ElapsedTimer() noexcept
-{
-	m_last = high_resolution_clock::now();
-}
-
-void ElapsedTimer::pause() noexcept
-{
-	/*
-	 * When we put the timer on pause, do not forget to set the already
-	 * elapsed time.
-	 */
-	(void)elapsed();
-	m_paused = true;
-}
-
-void ElapsedTimer::restart() noexcept
-{
-	m_paused = false;
-	m_last = high_resolution_clock::now();
-}
-
-void ElapsedTimer::reset() noexcept
-{
-	m_elapsed = 0;
-	m_last = high_resolution_clock::now();
-}
-
-unsigned ElapsedTimer::elapsed() noexcept
-{
-	if (!m_paused) {
-		m_elapsed += duration_cast<milliseconds>(high_resolution_clock::now() - m_last).count();
-		m_last = high_resolution_clock::now();
-	}
-
-	return m_elapsed;
-}
\ No newline at end of file
--- a/modules/sockets/examples/elapsed-timer.h	Thu Feb 25 21:01:02 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,77 +0,0 @@
-/*
- * elapsed-timer.h -- measure elapsed time
- *
- * Copyright (c) 2013-2015 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef _ELAPSED_TIMER_H_
-#define _ELAPSED_TIMER_H_
-
-/**
- * @file ElapsedTimer.h
- * @brief Measure elapsed time
- */
-
-#include <chrono>
-
-/**
- * @class ElapsedTimer
- * @brief Measure elapsed time
- *
- * This class provides an abstraction to measure elapsed time since the
- * construction of the object.
- *
- * It uses std::chrono::high_resolution_clock for more precision and uses
- * milliseconds only.
- */
-class ElapsedTimer {
-public:
-	using TimePoint = std::chrono::time_point<std::chrono::high_resolution_clock>;
-
-private:
-	TimePoint m_last;
-	bool m_paused{false};
-	unsigned m_elapsed{0};
-
-public:
-	/**
-	 * Construct the elapsed timer, start counting.
-	 */
-	ElapsedTimer() noexcept;
-
-	/**
-	 * Put the timer on pause, the already elapsed time is stored.
-	 */
-	void pause() noexcept;
-
-	/**
-	 * Restart the timer, does not reset it.
-	 */
-	void restart() noexcept;
-
-	/**
-	 * Reset the timer to 0.
-	 */
-	void reset() noexcept;
-
-	/**
-	 * Get the number of elapsed milliseconds.
-	 *
-	 * @return the milliseconds
-	 */
-	unsigned elapsed() noexcept;
-};
-
-#endif // !_ELAPSED_TIMER_H_
--- a/modules/sockets/examples/non-blocking-accept.cpp	Thu Feb 25 21:01:02 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,89 +0,0 @@
-/*
- * non-blocking-accept.cpp -- example of total non-blocking accept
- *
- * Options:
- *   - WITH_PORT (int), the port to use (default: 16000)
- *   - WITH_TIMEOUT (int), number of milliseconds before giving up (default: 3000)
- *   - WITH_SSL (bool), true to test with SSL (default: false)
- */
-
-#include <iostream>
-
-#include "elapsed-timer.h"
-#include "sockets.h"
-
-#if !defined(WITH_PORT)
-#  define WITH_PORT 16000
-#endif
-
-#if !defined(WITH_TIMEOUT)
-#  define WITH_TIMEOUT 3000
-#endif
-
-int main()
-{
-#if defined(WITH_SSL)
-	net::SocketTls<net::address::Ip> master;
-	net::SocketTls<net::address::Ip> client{net::Invalid};
-#else
-	net::SocketTcp<net::address::Ip> master;
-	net::SocketTcp<net::address::Ip> client{net::Invalid};
-#endif
-
-	net::Listener<> listener;
-	net::Condition cond;
-
-	ElapsedTimer timer;
-
-	// 1. Create the master socket for listening.
-	try {
-		master.set(net::option::SockReuseAddress{true});
-		master.set(net::option::SockBlockMode{false});
-		master.bind(net::address::Ip{"*", WITH_PORT});
-		master.listen();
-
-		listener.set(master.handle(), net::Condition::Readable);
-	} catch (const net::Error &error) {
-		std::cerr << "error: " << error.what() << std::endl;
-		std::exit(1);
-	}
-
-	while (!client.isOpen() && timer.elapsed() < WITH_TIMEOUT) {
-		try {
-			if (!client.isOpen()) {
-				// 2. Wait for a pre-accept process.
-				listener.wait(std::chrono::seconds(WITH_TIMEOUT));
-				master.accept(client, nullptr, &cond);
-				client.set(net::option::SockBlockMode(false));
-				listener.remove(master.handle());
-
-				std::cout << "Accepting new client" << std::endl;
-
-				if (cond != net::Condition::None) {
-					std::cout << "Client accept state not complete" << std::endl;
-					listener.set(client.handle(), cond);
-				}
-			} else {
-				// 3. Wait for the accept process to complete.
-				std::cout << "Continuing accept for the client" << std::endl;
-
-				listener.wait(std::chrono::seconds(WITH_TIMEOUT - timer.elapsed()));
-				client.accept(&cond);
-				listener.remove(client.handle());
-
-				if (cond != net::Condition::None) {
-					std::cout << "Client accept state not complete" << std::endl;
-					listener.set(client.handle(), cond);
-				}
-			}
-		} catch (const net::Error &error) {
-			std::cerr << error.function() << ": " << error.what() << std::endl;
-			std::exit(1);
-		}
-	}
-
-	if (client.isOpen())
-		std::cout << "Client successfully accepted!" << std::endl;
-
-	return 0;
-}
--- a/modules/sockets/examples/non-blocking-connect.cpp	Thu Feb 25 21:01:02 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,65 +0,0 @@
-/*
- * non-blocking-connect.cpp -- example of non blocking connect
- *
- * Options:
- *   - WITH_HOST (string literal), the host to try (default: "malikania.fr")
- *   - WITH_PORT (int), the port to use (default: 80)
- *   - WITH_TIMEOUT (int), number of milliseconds before giving up (default: 3000)
- *   - WITH_SSL (bool), true to test with SSL (default: false)
- */
-
-#include <iostream>
-
-#if !defined(WITH_HOST)
-#  define WITH_HOST "malikania.fr"
-#endif
-
-#if !defined(WITH_PORT)
-#  define WITH_PORT 80
-#endif
-
-#if !defined(WITH_TIMEOUT)
-#  define WITH_TIMEOUT 3000
-#endif
-
-#include "elapsed-timer.h"
-#include "sockets.h"
-
-int main()
-{
-#if defined(WITH_SSL)
-	net::SocketTls<net::address::Ip> socket;
-#else
-	net::SocketTcp<net::address::Ip> socket;
-#endif
-
-	net::Listener<> listener;
-	net::Condition cond;
-	ElapsedTimer timer;
-
-	// 1. Set to non-blocking.
-	socket.set(net::option::SockBlockMode(false));
-
-	try {
-		std::cout << "Trying to connect to " << WITH_HOST << ":" << WITH_PORT << std::endl;
-
-		// 2. Initial connection process.
-		socket.connect(net::address::Ip(WITH_HOST, WITH_PORT), &cond);
-
-		while (cond != net::Condition::None && timer.elapsed() < WITH_TIMEOUT) {
-			listener.remove(socket.handle());
-
-			// 2. Now complete by waiting for the appropriate condition.
-			listener.set(socket.handle(), cond);
-			listener.wait(std::chrono::milliseconds(WITH_TIMEOUT - timer.elapsed()));
-			socket.connect(&cond);
-		}
-	} catch (const net::Error &error) {
-		std::cerr << "error: " << error.what() << std::endl;
-		std::exit(1);
-	}
-
-	std::cout << "Successfully connected!" << std::endl;
-
-	return 0;
-}
--- a/modules/sockets/examples/test.crt	Thu Feb 25 21:01:02 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,14 +0,0 @@
------BEGIN CERTIFICATE-----
-MIICITCCAYoCCQCGm4grkVCohjANBgkqhkiG9w0BAQsFADBVMQswCQYDVQQGEwJG
-UjEPMA0GA1UECAwGRnJhbmNlMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0
-eSBMdGQxEjAQBgNVBAMMCWxvY2FsaG9zdDAeFw0xNTEwMjYyMDM0NThaFw0yNTEw
-MjMyMDM0NThaMFUxCzAJBgNVBAYTAkZSMQ8wDQYDVQQIDAZGcmFuY2UxITAfBgNV
-BAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDESMBAGA1UEAwwJbG9jYWxob3N0
-MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDp13OqVyOWyv5QWD4xr+Duw6SZ
-gU7D5huzsAOcneSI6JUhf+7Ecu6BQ2JGkFn4srIVkMWGQuImETJ8JCpSQH7rk+xO
-L9fTTK+TwhP2hW/Rf/b2gWedhJAS+gilqt4JNT7v2wFv+aTtRt/lpTXVSdtpLa/m
-Pdy219f6MAPgODJ/7QIDAQABMA0GCSqGSIb3DQEBCwUAA4GBAJSnn/IBn1ZblfzP
-rJO/lE1Jwpmx3B7+oR/e4fkZd6JR3s06umGYWr2H+TPl/5dj9x0gPokhoIL9zCGq
-SxCPnOeaxjBkw7yh3Ks6m3xKxmK4aMpAtBHtwmbfQyIcgz71/lfCzbJ3WcKpn1ig
-IZbByt5QSSPcFORRzJJa35eHBdfX
------END CERTIFICATE-----
--- a/modules/sockets/examples/test.key	Thu Feb 25 21:01:02 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,15 +0,0 @@
------BEGIN RSA PRIVATE KEY-----
-MIICXAIBAAKBgQDp13OqVyOWyv5QWD4xr+Duw6SZgU7D5huzsAOcneSI6JUhf+7E
-cu6BQ2JGkFn4srIVkMWGQuImETJ8JCpSQH7rk+xOL9fTTK+TwhP2hW/Rf/b2gWed
-hJAS+gilqt4JNT7v2wFv+aTtRt/lpTXVSdtpLa/mPdy219f6MAPgODJ/7QIDAQAB
-AoGBANDt4ndQkgi56A1rOm50gVlzTg6lPPXFE/0xB5kYbcdxX0VmI7Q8KCMwTI9V
-jD2rk3e3OPSjr6FpfhzyxylkXMBz2BL5NRNPowCJbiMgZOUIzlcWPKo0tgf1bZJx
-YdB5U003ISGPPBjVOAjyizY7tJnaNvbpLQ0hbIAsvHPEAOnBAkEA9r3g8NQjPrvb
-oIr5SMIxM8HDJ1/q+MEBSFtRFzQpmur6P64Jsu96zCyencUYTxs0L/sottrj6dPC
-vjGCc6PjsQJBAPKdqK1knJv6Y95M2bnEwrymCFVdxCi7AxObStB+bg/+7mMCUqqX
-j2g71bfvhYakHV7CiaYrrORChwj6vTbimv0CQGpd2IZ5LOhyW2+N+YDgFg3Vzac/
-ti+eJEto8kAqgHUELvUctZmpmypBYe9pc91GQO0ePKL3IaE/ZIhRF4d6c0ECQH9A
-XiaD7PiKvjLs0A31u8ZCt4A+7BII9LYl73mntobBSbu4ji9Xyyn6qEAPa1ORZK49
-DwGPSuF2W2lESlYtSOkCQGrtczhx3IyJjk5e2Y1i/UddPKjviAysCSzcW6aVTNr9
-Y2L0sWmva2FKnkl9FDuEqxvmGr6OOkr5Ll7aWLzJri8=
------END RSA PRIVATE KEY-----
--- a/modules/sockets/test/test.crt	Thu Feb 25 21:01:02 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,14 +0,0 @@
------BEGIN CERTIFICATE-----
-MIICITCCAYoCCQCGm4grkVCohjANBgkqhkiG9w0BAQsFADBVMQswCQYDVQQGEwJG
-UjEPMA0GA1UECAwGRnJhbmNlMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0
-eSBMdGQxEjAQBgNVBAMMCWxvY2FsaG9zdDAeFw0xNTEwMjYyMDM0NThaFw0yNTEw
-MjMyMDM0NThaMFUxCzAJBgNVBAYTAkZSMQ8wDQYDVQQIDAZGcmFuY2UxITAfBgNV
-BAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDESMBAGA1UEAwwJbG9jYWxob3N0
-MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDp13OqVyOWyv5QWD4xr+Duw6SZ
-gU7D5huzsAOcneSI6JUhf+7Ecu6BQ2JGkFn4srIVkMWGQuImETJ8JCpSQH7rk+xO
-L9fTTK+TwhP2hW/Rf/b2gWedhJAS+gilqt4JNT7v2wFv+aTtRt/lpTXVSdtpLa/m
-Pdy219f6MAPgODJ/7QIDAQABMA0GCSqGSIb3DQEBCwUAA4GBAJSnn/IBn1ZblfzP
-rJO/lE1Jwpmx3B7+oR/e4fkZd6JR3s06umGYWr2H+TPl/5dj9x0gPokhoIL9zCGq
-SxCPnOeaxjBkw7yh3Ks6m3xKxmK4aMpAtBHtwmbfQyIcgz71/lfCzbJ3WcKpn1ig
-IZbByt5QSSPcFORRzJJa35eHBdfX
------END CERTIFICATE-----
--- a/modules/sockets/test/test.key	Thu Feb 25 21:01:02 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,15 +0,0 @@
------BEGIN RSA PRIVATE KEY-----
-MIICXAIBAAKBgQDp13OqVyOWyv5QWD4xr+Duw6SZgU7D5huzsAOcneSI6JUhf+7E
-cu6BQ2JGkFn4srIVkMWGQuImETJ8JCpSQH7rk+xOL9fTTK+TwhP2hW/Rf/b2gWed
-hJAS+gilqt4JNT7v2wFv+aTtRt/lpTXVSdtpLa/mPdy219f6MAPgODJ/7QIDAQAB
-AoGBANDt4ndQkgi56A1rOm50gVlzTg6lPPXFE/0xB5kYbcdxX0VmI7Q8KCMwTI9V
-jD2rk3e3OPSjr6FpfhzyxylkXMBz2BL5NRNPowCJbiMgZOUIzlcWPKo0tgf1bZJx
-YdB5U003ISGPPBjVOAjyizY7tJnaNvbpLQ0hbIAsvHPEAOnBAkEA9r3g8NQjPrvb
-oIr5SMIxM8HDJ1/q+MEBSFtRFzQpmur6P64Jsu96zCyencUYTxs0L/sottrj6dPC
-vjGCc6PjsQJBAPKdqK1knJv6Y95M2bnEwrymCFVdxCi7AxObStB+bg/+7mMCUqqX
-j2g71bfvhYakHV7CiaYrrORChwj6vTbimv0CQGpd2IZ5LOhyW2+N+YDgFg3Vzac/
-ti+eJEto8kAqgHUELvUctZmpmypBYe9pc91GQO0ePKL3IaE/ZIhRF4d6c0ECQH9A
-XiaD7PiKvjLs0A31u8ZCt4A+7BII9LYl73mntobBSbu4ji9Xyyn6qEAPa1ORZK49
-DwGPSuF2W2lESlYtSOkCQGrtczhx3IyJjk5e2Y1i/UddPKjviAysCSzcW6aVTNr9
-Y2L0sWmva2FKnkl9FDuEqxvmGr6OOkr5Ll7aWLzJri8=
------END RSA PRIVATE KEY-----