From 13549775092afa9215de8468e34f6d194c2fd8db Mon Sep 17 00:00:00 2001
From: José Bollo <jose.bollo@iot.bzh>
Date: Tue, 5 Apr 2016 15:25:28 +0200
Subject: new main loop in place
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Change-Id: If297e0a76e74422d456447be52cca460c9e237b3
Signed-off-by: José Bollo <jose.bollo@iot.bzh>
---
 include/afb-plugin.h   |   2 +-
 include/afb-poll-itf.h |   4 +-
 src/CMakeLists.txt     |   2 +-
 src/afb-apis.c         |   8 +-
 src/afb-hsrv.c         | 499 +++++++++++++++++++++++++++++++++++++++++++++++++
 src/http-svc.c         | 498 ------------------------------------------------
 src/local-def.h        |  23 ---
 src/main.c             | 109 +++++------
 src/proto-def.h        |  30 ---
 src/utils-upoll.c      |  49 ++++-
 src/utils-upoll.h      |   7 +-
 src/verbose.h          |   2 +-
 12 files changed, 600 insertions(+), 633 deletions(-)
 create mode 100644 src/afb-hsrv.c
 delete mode 100644 src/http-svc.c
 delete mode 100644 src/proto-def.h

diff --git a/include/afb-plugin.h b/include/afb-plugin.h
index ba86e181..4064f9e9 100644
--- a/include/afb-plugin.h
+++ b/include/afb-plugin.h
@@ -67,7 +67,7 @@ struct AFB_interface
 {
 	int verbosity;
 	enum AFB_Mode mode;
-	struct afb_poll (*poll_open)(int fd, uint32_t events, void (*process)(void *closure, int fd, uint32_t events), void *closure);
+	struct afb_poll (*poll_open)(int fd, void *closure);
 };
 
 extern const struct AFB_plugin *pluginRegister (const struct AFB_interface *interface);
diff --git a/include/afb-poll-itf.h b/include/afb-poll-itf.h
index 4ce1fa48..2c8889b6 100644
--- a/include/afb-poll-itf.h
+++ b/include/afb-poll-itf.h
@@ -17,7 +17,9 @@
 
 struct afb_poll_itf
 {
-	int (*update)(void *data, uint32_t events);
+	int (*on_readable)(void *, void (*cb)(void *));
+	int (*on_writable)(void *, void (*cb)(void *));
+	int (*on_hangup)(void *, void (*cb)(void *));
 	void (*close)(void *data);
 };
 
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index d8bddcf0..3f8b84c6 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -2,7 +2,7 @@
 ADD_LIBRARY(src OBJECT
 	main.c
 	session.c
-	http-svc.c
+	afb-hsrv.c
 	afb-apis.c
 	afb-method.c
 	afb-hreq.c
diff --git a/src/afb-apis.c b/src/afb-apis.c
index 55e68c93..9dcb1809 100644
--- a/src/afb-apis.c
+++ b/src/afb-apis.c
@@ -63,7 +63,9 @@ static int apis_count = 0;
 static const char plugin_register_function[] = "pluginRegister";
 
 static const struct afb_poll_itf upoll_itf = {
-	.update = (void*)upoll_update,
+	.on_readable = (void*)upoll_on_readable,
+	.on_writable = (void*)upoll_on_writable,
+	.on_hangup = (void*)upoll_on_hangup,
 	.close = (void*)upoll_close
 };
 
@@ -85,10 +87,10 @@ void afb_apis_free_context(int apiidx, void *context)
 		free(context);
 }
 
-static struct afb_poll itf_poll_open(int fd, uint32_t events, void (*process)(void *closure, int fd, uint32_t events), void *closure)
+static struct afb_poll itf_poll_open(int fd, void *closure)
 {
 	struct afb_poll result;
-	result.data = upoll_open(fd, events, process, closure);
+	result.data = upoll_open(fd, closure);
 	result.itf = result.data ? &upoll_itf : NULL;
 	return result;
 }
diff --git a/src/afb-hsrv.c b/src/afb-hsrv.c
new file mode 100644
index 00000000..a358cbd7
--- /dev/null
+++ b/src/afb-hsrv.c
@@ -0,0 +1,499 @@
+/*
+ * Copyright 2016 IoT.bzh
+ * Author: José Bollo <jose.bollo@iot.bzh>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define _GNU_SOURCE
+
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include <poll.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+
+#include <microhttpd.h>
+
+#include "local-def.h"
+#include "afb-method.h"
+#include "afb-hreq.h"
+#include "afb-websock.h"
+#include "afb-apis.h"
+#include "afb-req-itf.h"
+#include "verbose.h"
+#include "utils-upoll.h"
+
+#define JSON_CONTENT  "application/json"
+#define FORM_CONTENT  MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA
+
+
+struct afb_hsrv_handler {
+	struct afb_hsrv_handler *next;
+	const char *prefix;
+	size_t length;
+	int (*handler) (struct afb_hreq *, void *);
+	void *data;
+	int priority;
+};
+
+struct afb_diralias {
+	const char *alias;
+	const char *directory;
+	size_t lendir;
+	int dirfd;
+};
+
+static struct upoll *upoll = NULL;
+
+int afb_hreq_one_page_api_redirect(
+		struct afb_hreq *hreq,
+		void *data)
+{
+	size_t plen;
+	char *url;
+
+	if (hreq->lentail >= 2 && hreq->tail[1] == '#')
+		return 0;
+	/*
+	 * Here we have for example:
+	 *    url  = "/pre/dir/page"   lenurl = 13
+	 *    tail =     "/dir/page"   lentail = 9
+	 *
+	 * We will produce "/pre/#!dir/page"
+	 *
+	 * Let compute plen that include the / at end (for "/pre/")
+	 */
+	plen = hreq->lenurl - hreq->lentail + 1;
+	url = alloca(hreq->lenurl + 3);
+	memcpy(url, hreq->url, plen);
+	url[plen++] = '#';
+	url[plen++] = '!';
+	memcpy(&url[plen], &hreq->tail[1], hreq->lentail);
+	return afb_hreq_redirect_to(hreq, url);
+}
+
+static struct afb_hsrv_handler *new_handler(
+		struct afb_hsrv_handler *head,
+		const char *prefix,
+		int (*handler) (struct afb_hreq *, void *),
+		void *data,
+		int priority)
+{
+	struct afb_hsrv_handler *link, *iter, *previous;
+	size_t length;
+
+	/* get the length of the prefix without its leading / */
+	length = strlen(prefix);
+	while (length && prefix[length - 1] == '/')
+		length--;
+
+	/* allocates the new link */
+	link = malloc(sizeof *link);
+	if (link == NULL)
+		return NULL;
+
+	/* initialize it */
+	link->prefix = prefix;
+	link->length = length;
+	link->handler = handler;
+	link->data = data;
+	link->priority = priority;
+
+	/* adds it */
+	previous = NULL;
+	iter = head;
+	while (iter && (priority < iter->priority || (priority == iter->priority && length <= iter->length))) {
+		previous = iter;
+		iter = iter->next;
+	}
+	link->next = iter;
+	if (previous == NULL)
+		return link;
+	previous->next = link;
+	return head;
+}
+
+int afb_hsrv_add_handler(
+		AFB_session * session,
+		const char *prefix,
+		int (*handler) (struct afb_hreq *, void *),
+		void *data,
+		int priority)
+{
+	struct afb_hsrv_handler *head;
+
+	head = new_handler(session->handlers, prefix, handler, data, priority);
+	if (head == NULL)
+		return 0;
+	session->handlers = head;
+	return 1;
+}
+
+static int afb_hreq_websocket_switch(struct afb_hreq *hreq, void *data)
+{
+	int later;
+
+	afb_hreq_context(hreq);
+	if (hreq->lentail != 0 || !afb_websock_check(hreq, &later))
+		return 0;
+
+	if (!later) {
+		struct afb_websock *ws = afb_websock_create(hreq->connection);
+		if (ws == NULL) {
+			/* TODO */
+		} else {
+			/* TODO */
+		}
+	}
+	return 1;
+}
+
+static int afb_hreq_rest_api(struct afb_hreq *hreq, void *data)
+{
+	const char *api, *verb;
+	size_t lenapi, lenverb;
+	struct AFB_clientCtx *context;
+
+	api = &hreq->tail[strspn(hreq->tail, "/")];
+	lenapi = strcspn(api, "/");
+	verb = &api[lenapi];
+	verb = &verb[strspn(verb, "/")];
+	lenverb = strcspn(verb, "/");
+
+	if (!(*api && *verb && lenapi && lenverb))
+		return 0;
+
+	context = afb_hreq_context(hreq);
+	return afb_apis_handle(afb_hreq_to_req(hreq), context, api, lenapi, verb, lenverb);
+}
+
+static int handle_alias(struct afb_hreq *hreq, void *data)
+{
+	struct afb_diralias *da = data;
+
+	if (hreq->method != afb_method_get) {
+		afb_hreq_reply_error(hreq, MHD_HTTP_METHOD_NOT_ALLOWED);
+		return 1;
+	}
+
+	if (!afb_hreq_valid_tail(hreq)) {
+		afb_hreq_reply_error(hreq, MHD_HTTP_FORBIDDEN);
+		return 1;
+	}
+
+	return afb_hreq_reply_file(hreq, da->dirfd, &hreq->tail[1]);
+}
+
+int afb_hsrv_add_alias(AFB_session * session, const char *prefix, const char *alias, int priority)
+{
+	struct afb_diralias *da;
+	int dirfd;
+
+	dirfd = open(alias, O_PATH|O_DIRECTORY);
+	if (dirfd < 0) {
+		/* TODO message */
+		return 0;
+	}
+	da = malloc(sizeof *da);
+	if (da != NULL) {
+		da->alias = prefix;
+		da->directory = alias;
+		da->lendir = strlen(da->directory);
+		da->dirfd = dirfd;
+		if (afb_hsrv_add_handler(session, prefix, handle_alias, da, priority))
+			return 1;
+		free(da);
+	}
+	close(dirfd);
+	return 0;
+}
+
+void afb_hsrv_reply_error(struct MHD_Connection *connection, unsigned int status)
+{
+	char *buffer;
+	int length;
+	struct MHD_Response *response;
+
+	length = asprintf(&buffer, "<html><body>error %u</body></html>", status);
+	if (length > 0)
+		response = MHD_create_response_from_buffer((unsigned)length, buffer, MHD_RESPMEM_MUST_FREE);
+	else {
+		buffer = "<html><body>error</body></html>";
+		response = MHD_create_response_from_buffer(strlen(buffer), buffer, MHD_RESPMEM_PERSISTENT);
+	}
+	if (!MHD_queue_response(connection, status, response))
+		fprintf(stderr, "Failed to reply error code %u", status);
+	MHD_destroy_response(response);
+}
+
+static int postproc(void *cls,
+                    enum MHD_ValueKind kind,
+                    const char *key,
+                    const char *filename,
+                    const char *content_type,
+                    const char *transfer_encoding,
+                    const char *data,
+		    uint64_t off,
+		    size_t size)
+{
+	struct afb_hreq *hreq = cls;
+	if (filename != NULL)
+		return afb_hreq_post_add_file(hreq, key, filename, data, size);
+	else
+		return afb_hreq_post_add(hreq, key, data, size);
+}
+
+static int access_handler(
+		void *cls,
+		struct MHD_Connection *connection,
+		const char *url,
+		const char *methodstr,
+		const char *version,
+		const char *upload_data,
+		size_t *upload_data_size,
+		void **recordreq)
+{
+	int rc;
+	struct afb_hreq *hreq;
+	enum afb_method method;
+	AFB_session *session;
+	struct afb_hsrv_handler *iter;
+	const char *type;
+
+	session = cls;
+	hreq = *recordreq;
+	if (hreq == NULL) {
+		/* create the request */
+		hreq = calloc(1, sizeof *hreq);
+		if (hreq == NULL)
+			goto internal_error;
+		*recordreq = hreq;
+
+		/* get the method */
+		method = get_method(methodstr);
+		method &= afb_method_get | afb_method_post;
+		if (method == afb_method_none)
+			goto bad_request;
+
+		/* init the request */
+		hreq->session = cls;
+		hreq->connection = connection;
+		hreq->method = method;
+		hreq->version = version;
+		hreq->tail = hreq->url = url;
+		hreq->lentail = hreq->lenurl = strlen(url);
+
+		/* init the post processing */
+		if (method == afb_method_post) {
+			type = afb_hreq_get_header(hreq, MHD_HTTP_HEADER_CONTENT_TYPE);
+			if (type == NULL) {
+				/* an empty post, let's process it as a get */
+				hreq->method = afb_method_get;
+			} else if (strcasestr(type, FORM_CONTENT) != NULL) {
+				hreq->postform = MHD_create_post_processor (connection, 65500, postproc, hreq);
+				if (hreq->postform == NULL)
+					goto internal_error;
+			} else if (strcasestr(type, JSON_CONTENT) == NULL) {
+				afb_hsrv_reply_error(connection, MHD_HTTP_UNSUPPORTED_MEDIA_TYPE);
+				return MHD_YES;
+			}
+		}
+	}
+
+	/* process further data */
+	if (*upload_data_size) {
+		if (hreq->postform != NULL) {
+			if (!MHD_post_process (hreq->postform, upload_data, *upload_data_size))
+				goto internal_error;
+		} else {
+			if (!afb_hreq_post_add(hreq, NULL, upload_data, *upload_data_size))
+				goto internal_error;
+		}
+		*upload_data_size = 0;
+		return MHD_YES;		
+	}
+
+	/* flush the data */
+	afb_hreq_post_end(hreq);
+	if (hreq->postform != NULL) {
+		rc = MHD_destroy_post_processor(hreq->postform);
+		hreq->postform = NULL;
+		if (rc == MHD_NO)
+			goto bad_request;
+	}
+
+	/* search an handler for the request */
+	iter = session->handlers;
+	while (iter) {
+		if (afb_hreq_unprefix(hreq, iter->prefix, iter->length)) {
+			if (iter->handler(hreq, iter->data))
+				return MHD_YES;
+			hreq->tail = hreq->url;
+			hreq->lentail = hreq->lenurl;
+		}
+		iter = iter->next;
+	}
+
+	/* no handler */
+	afb_hreq_reply_error(hreq, MHD_HTTP_NOT_FOUND);
+	return MHD_YES;
+
+bad_request:
+	afb_hsrv_reply_error(connection, MHD_HTTP_BAD_REQUEST);
+	return MHD_YES;
+
+internal_error:
+	afb_hsrv_reply_error(connection, MHD_HTTP_INTERNAL_SERVER_ERROR);
+	return MHD_YES;
+}
+
+/* Because of POST call multiple time requestApi we need to free POST handle here */
+static void end_handler(void *cls, struct MHD_Connection *connection, void **recordreq,
+			enum MHD_RequestTerminationCode toe)
+{
+	struct afb_hreq *hreq;
+
+	hreq = *recordreq;
+
+	afb_hreq_free(hreq);
+}
+
+static int new_client_handler(void *cls, const struct sockaddr *addr, socklen_t addrlen)
+{
+	return MHD_YES;
+}
+
+#if defined(USE_MAGIC_MIME_TYPE)
+
+#if !defined(MAGIC_DB)
+#define MAGIC_DB "/usr/share/misc/magic.mgc"
+#endif
+
+static int init_lib_magic (AFB_session *session)
+{
+	/* MAGIC_MIME tells magic to return a mime of the file, but you can specify different things */
+	if (verbosity)
+		printf("Loading mimetype default magic database\n");
+
+	session->magic = magic_open(MAGIC_MIME_TYPE);
+	if (session->magic == NULL) {
+		fprintf(stderr,"ERROR: unable to initialize magic library\n");
+		return 0;
+	}
+
+	/* Warning: should not use NULL for DB [libmagic bug wont pass efence check] */
+	if (magic_load(session->magic, MAGIC_DB) != 0) {
+		fprintf(stderr,"cannot load magic database - %s\n", magic_error(session->magic));
+		magic_close(session->magic);
+		session->magic = NULL;
+		return 0;
+	}
+
+	return 1;
+}
+#endif
+
+static int my_default_init(AFB_session * session)
+{
+	int idx;
+
+	if (!afb_hsrv_add_handler(session, session->config->rootapi, afb_hreq_websocket_switch, NULL, 20))
+		return 0;
+
+	if (!afb_hsrv_add_handler(session, session->config->rootapi, afb_hreq_rest_api, NULL, 10))
+		return 0;
+
+	for (idx = 0; session->config->aliasdir[idx].url != NULL; idx++)
+		if (!afb_hsrv_add_alias (session, session->config->aliasdir[idx].url, session->config->aliasdir[idx].path, 0))
+			return 0;
+
+	if (!afb_hsrv_add_alias(session, "", session->config->rootdir, -10))
+		return 0;
+
+	if (!afb_hsrv_add_handler(session, session->config->rootbase, afb_hreq_one_page_api_redirect, NULL, -20))
+		return 0;
+
+#if defined(USE_MAGIC_MIME_TYPE)
+	/*TBD open libmagic cache [fail to pass EFENCE check (allocating 0 bytes)] */
+	init_lib_magic (session);
+#endif
+
+	return 1;
+}
+
+/* infinite loop */
+static void hsrv_handle_event(struct MHD_Daemon *httpd)
+{
+	MHD_run(httpd);
+}
+
+int afb_hsrv_start(AFB_session * session)
+{
+	struct MHD_Daemon *httpd;
+	const union MHD_DaemonInfo *info;
+
+	if (!my_default_init(session)) {
+		printf("Error: initialisation of httpd failed");
+		return 0;
+	}
+
+	if (verbosity) {
+		printf("AFB:notice Waiting port=%d rootdir=%s\n", session->config->httpdPort, session->config->rootdir);
+		printf("AFB:notice Browser URL= http:/*localhost:%d\n", session->config->httpdPort);
+	}
+
+	httpd = MHD_start_daemon(
+		MHD_USE_EPOLL_LINUX_ONLY | MHD_USE_TCP_FASTOPEN | MHD_USE_DEBUG | MHD_USE_SUSPEND_RESUME,
+		(uint16_t) session->config->httpdPort,	/* port */
+		new_client_handler, NULL,	/* Tcp Accept call back + extra attribute */
+		access_handler, session,	/* Http Request Call back + extra attribute */
+		MHD_OPTION_NOTIFY_COMPLETED, end_handler, session,
+		MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int)15,	/* 15 seconds */
+		MHD_OPTION_END);	/* options-end */
+
+	if (httpd == NULL) {
+		printf("Error: httpStart invalid httpd port: %d", session->config->httpdPort);
+		return 0;
+	}
+
+	info = MHD_get_daemon_info(httpd, MHD_DAEMON_INFO_EPOLL_FD_LINUX_ONLY);
+	if (info == NULL) {
+		MHD_stop_daemon(httpd);
+		fprintf(stderr, "Error: httpStart no pollfd");
+		return 0;
+	}
+
+	upoll = upoll_open(info->listen_fd, httpd);
+	if (upoll == NULL) {
+		MHD_stop_daemon(httpd);
+		fprintf(stderr, "Error: connection to upoll of httpd failed");
+		return 0;
+	}
+	upoll_on_readable(upoll, (void*)hsrv_handle_event);
+
+	session->httpd = httpd;
+	return 1;
+}
+
+void afb_hsrv_stop(AFB_session * session)
+{
+	if (upoll)
+		upoll_close(upoll);
+	upoll = NULL;
+	if (session->httpd != NULL)
+		MHD_stop_daemon(session->httpd);
+	session->httpd = NULL;
+}
+
diff --git a/src/http-svc.c b/src/http-svc.c
deleted file mode 100644
index b36d5f7e..00000000
--- a/src/http-svc.c
+++ /dev/null
@@ -1,498 +0,0 @@
-/*
- * Copyright 2016 IoT.bzh
- * Author: José Bollo <jose.bollo@iot.bzh>
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define _GNU_SOURCE
-
-#include <stdio.h>
-#include <string.h>
-#include <assert.h>
-#include <poll.h>
-#include <fcntl.h>
-#include <sys/stat.h>
-
-#include <microhttpd.h>
-
-#include "local-def.h"
-#include "afb-method.h"
-#include "afb-hreq.h"
-#include "afb-websock.h"
-#include "afb-apis.h"
-#include "afb-req-itf.h"
-#include "verbose.h"
-
-#define JSON_CONTENT  "application/json"
-#define FORM_CONTENT  MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA
-
-
-struct afb_hsrv_handler {
-	struct afb_hsrv_handler *next;
-	const char *prefix;
-	size_t length;
-	int (*handler) (struct afb_hreq *, void *);
-	void *data;
-	int priority;
-};
-
-struct afb_diralias {
-	const char *alias;
-	const char *directory;
-	size_t lendir;
-	int dirfd;
-};
-
-int afb_hreq_one_page_api_redirect(
-		struct afb_hreq *hreq,
-		void *data)
-{
-	size_t plen;
-	char *url;
-
-	if (hreq->lentail >= 2 && hreq->tail[1] == '#')
-		return 0;
-	/*
-	 * Here we have for example:
-	 *    url  = "/pre/dir/page"   lenurl = 13
-	 *    tail =     "/dir/page"   lentail = 9
-	 *
-	 * We will produce "/pre/#!dir/page"
-	 *
-	 * Let compute plen that include the / at end (for "/pre/")
-	 */
-	plen = hreq->lenurl - hreq->lentail + 1;
-	url = alloca(hreq->lenurl + 3);
-	memcpy(url, hreq->url, plen);
-	url[plen++] = '#';
-	url[plen++] = '!';
-	memcpy(&url[plen], &hreq->tail[1], hreq->lentail);
-	return afb_hreq_redirect_to(hreq, url);
-}
-
-static struct afb_hsrv_handler *new_handler(
-		struct afb_hsrv_handler *head,
-		const char *prefix,
-		int (*handler) (struct afb_hreq *, void *),
-		void *data,
-		int priority)
-{
-	struct afb_hsrv_handler *link, *iter, *previous;
-	size_t length;
-
-	/* get the length of the prefix without its leading / */
-	length = strlen(prefix);
-	while (length && prefix[length - 1] == '/')
-		length--;
-
-	/* allocates the new link */
-	link = malloc(sizeof *link);
-	if (link == NULL)
-		return NULL;
-
-	/* initialize it */
-	link->prefix = prefix;
-	link->length = length;
-	link->handler = handler;
-	link->data = data;
-	link->priority = priority;
-
-	/* adds it */
-	previous = NULL;
-	iter = head;
-	while (iter && (priority < iter->priority || (priority == iter->priority && length <= iter->length))) {
-		previous = iter;
-		iter = iter->next;
-	}
-	link->next = iter;
-	if (previous == NULL)
-		return link;
-	previous->next = link;
-	return head;
-}
-
-int afb_hsrv_add_handler(
-		AFB_session * session,
-		const char *prefix,
-		int (*handler) (struct afb_hreq *, void *),
-		void *data,
-		int priority)
-{
-	struct afb_hsrv_handler *head;
-
-	head = new_handler(session->handlers, prefix, handler, data, priority);
-	if (head == NULL)
-		return 0;
-	session->handlers = head;
-	return 1;
-}
-
-static int afb_hreq_websocket_switch(struct afb_hreq *hreq, void *data)
-{
-	int later;
-
-	afb_hreq_context(hreq);
-	if (hreq->lentail != 0 || !afb_websock_check(hreq, &later))
-		return 0;
-
-	if (!later) {
-		struct afb_websock *ws = afb_websock_create(hreq->connection);
-		if (ws == NULL) {
-			/* TODO */
-		} else {
-			/* TODO */
-		}
-	}
-	return 1;
-}
-
-static int afb_hreq_rest_api(struct afb_hreq *hreq, void *data)
-{
-	const char *api, *verb;
-	size_t lenapi, lenverb;
-	struct AFB_clientCtx *context;
-
-	api = &hreq->tail[strspn(hreq->tail, "/")];
-	lenapi = strcspn(api, "/");
-	verb = &api[lenapi];
-	verb = &verb[strspn(verb, "/")];
-	lenverb = strcspn(verb, "/");
-
-	if (!(*api && *verb && lenapi && lenverb))
-		return 0;
-
-	context = afb_hreq_context(hreq);
-	return afb_apis_handle(afb_hreq_to_req(hreq), context, api, lenapi, verb, lenverb);
-}
-
-static int handle_alias(struct afb_hreq *hreq, void *data)
-{
-	struct afb_diralias *da = data;
-
-	if (hreq->method != afb_method_get) {
-		afb_hreq_reply_error(hreq, MHD_HTTP_METHOD_NOT_ALLOWED);
-		return 1;
-	}
-
-	if (!afb_hreq_valid_tail(hreq)) {
-		afb_hreq_reply_error(hreq, MHD_HTTP_FORBIDDEN);
-		return 1;
-	}
-
-	return afb_hreq_reply_file(hreq, da->dirfd, &hreq->tail[1]);
-}
-
-int afb_hsrv_add_alias(AFB_session * session, const char *prefix, const char *alias, int priority)
-{
-	struct afb_diralias *da;
-	int dirfd;
-
-	dirfd = open(alias, O_PATH|O_DIRECTORY);
-	if (dirfd < 0) {
-		/* TODO message */
-		return 0;
-	}
-	da = malloc(sizeof *da);
-	if (da != NULL) {
-		da->alias = prefix;
-		da->directory = alias;
-		da->lendir = strlen(da->directory);
-		da->dirfd = dirfd;
-		if (afb_hsrv_add_handler(session, prefix, handle_alias, da, priority))
-			return 1;
-		free(da);
-	}
-	close(dirfd);
-	return 0;
-}
-
-void afb_hsrv_reply_error(struct MHD_Connection *connection, unsigned int status)
-{
-	char *buffer;
-	int length;
-	struct MHD_Response *response;
-
-	length = asprintf(&buffer, "<html><body>error %u</body></html>", status);
-	if (length > 0)
-		response = MHD_create_response_from_buffer((unsigned)length, buffer, MHD_RESPMEM_MUST_FREE);
-	else {
-		buffer = "<html><body>error</body></html>";
-		response = MHD_create_response_from_buffer(strlen(buffer), buffer, MHD_RESPMEM_PERSISTENT);
-	}
-	if (!MHD_queue_response(connection, status, response))
-		fprintf(stderr, "Failed to reply error code %u", status);
-	MHD_destroy_response(response);
-}
-
-static int postproc(void *cls,
-                    enum MHD_ValueKind kind,
-                    const char *key,
-                    const char *filename,
-                    const char *content_type,
-                    const char *transfer_encoding,
-                    const char *data,
-		    uint64_t off,
-		    size_t size)
-{
-	struct afb_hreq *hreq = cls;
-	if (filename != NULL)
-		return afb_hreq_post_add_file(hreq, key, filename, data, size);
-	else
-		return afb_hreq_post_add(hreq, key, data, size);
-}
-
-static int access_handler(
-		void *cls,
-		struct MHD_Connection *connection,
-		const char *url,
-		const char *methodstr,
-		const char *version,
-		const char *upload_data,
-		size_t *upload_data_size,
-		void **recordreq)
-{
-	int rc;
-	struct afb_hreq *hreq;
-	enum afb_method method;
-	AFB_session *session;
-	struct afb_hsrv_handler *iter;
-	const char *type;
-
-	session = cls;
-	hreq = *recordreq;
-	if (hreq == NULL) {
-		/* create the request */
-		hreq = calloc(1, sizeof *hreq);
-		if (hreq == NULL)
-			goto internal_error;
-		*recordreq = hreq;
-
-		/* get the method */
-		method = get_method(methodstr);
-		method &= afb_method_get | afb_method_post;
-		if (method == afb_method_none)
-			goto bad_request;
-
-		/* init the request */
-		hreq->session = cls;
-		hreq->connection = connection;
-		hreq->method = method;
-		hreq->version = version;
-		hreq->tail = hreq->url = url;
-		hreq->lentail = hreq->lenurl = strlen(url);
-
-		/* init the post processing */
-		if (method == afb_method_post) {
-			type = afb_hreq_get_header(hreq, MHD_HTTP_HEADER_CONTENT_TYPE);
-			if (type == NULL) {
-				/* an empty post, let's process it as a get */
-				hreq->method = afb_method_get;
-			} else if (strcasestr(type, FORM_CONTENT) != NULL) {
-				hreq->postform = MHD_create_post_processor (connection, 65500, postproc, hreq);
-				if (hreq->postform == NULL)
-					goto internal_error;
-			} else if (strcasestr(type, JSON_CONTENT) == NULL) {
-				afb_hsrv_reply_error(connection, MHD_HTTP_UNSUPPORTED_MEDIA_TYPE);
-				return MHD_YES;
-			}
-		}
-	}
-
-	/* process further data */
-	if (*upload_data_size) {
-		if (hreq->postform != NULL) {
-			if (!MHD_post_process (hreq->postform, upload_data, *upload_data_size))
-				goto internal_error;
-		} else {
-			if (!afb_hreq_post_add(hreq, NULL, upload_data, *upload_data_size))
-				goto internal_error;
-		}
-		*upload_data_size = 0;
-		return MHD_YES;		
-	}
-
-	/* flush the data */
-	afb_hreq_post_end(hreq);
-	if (hreq->postform != NULL) {
-		rc = MHD_destroy_post_processor(hreq->postform);
-		hreq->postform = NULL;
-		if (rc == MHD_NO)
-			goto bad_request;
-	}
-
-	/* search an handler for the request */
-	iter = session->handlers;
-	while (iter) {
-		if (afb_hreq_unprefix(hreq, iter->prefix, iter->length)) {
-			if (iter->handler(hreq, iter->data))
-				return MHD_YES;
-			hreq->tail = hreq->url;
-			hreq->lentail = hreq->lenurl;
-		}
-		iter = iter->next;
-	}
-
-	/* no handler */
-	afb_hreq_reply_error(hreq, MHD_HTTP_NOT_FOUND);
-	return MHD_YES;
-
-bad_request:
-	afb_hsrv_reply_error(connection, MHD_HTTP_BAD_REQUEST);
-	return MHD_YES;
-
-internal_error:
-	afb_hsrv_reply_error(connection, MHD_HTTP_INTERNAL_SERVER_ERROR);
-	return MHD_YES;
-}
-
-/* Because of POST call multiple time requestApi we need to free POST handle here */
-static void end_handler(void *cls, struct MHD_Connection *connection, void **recordreq,
-			enum MHD_RequestTerminationCode toe)
-{
-	struct afb_hreq *hreq;
-
-	hreq = *recordreq;
-
-	afb_hreq_free(hreq);
-}
-
-static int new_client_handler(void *cls, const struct sockaddr *addr, socklen_t addrlen)
-{
-	return MHD_YES;
-}
-
-#if defined(USE_MAGIC_MIME_TYPE)
-
-#if !defined(MAGIC_DB)
-#define MAGIC_DB "/usr/share/misc/magic.mgc"
-#endif
-
-static int init_lib_magic (AFB_session *session)
-{
-	/* MAGIC_MIME tells magic to return a mime of the file, but you can specify different things */
-	if (verbosity)
-		printf("Loading mimetype default magic database\n");
-
-	session->magic = magic_open(MAGIC_MIME_TYPE);
-	if (session->magic == NULL) {
-		fprintf(stderr,"ERROR: unable to initialize magic library\n");
-		return 0;
-	}
-
-	/* Warning: should not use NULL for DB [libmagic bug wont pass efence check] */
-	if (magic_load(session->magic, MAGIC_DB) != 0) {
-		fprintf(stderr,"cannot load magic database - %s\n", magic_error(session->magic));
-		magic_close(session->magic);
-		session->magic = NULL;
-		return 0;
-	}
-
-	return 1;
-}
-#endif
-
-static int my_default_init(AFB_session * session)
-{
-	int idx;
-
-	if (!afb_hsrv_add_handler(session, session->config->rootapi, afb_hreq_websocket_switch, NULL, 20))
-		return 0;
-
-	if (!afb_hsrv_add_handler(session, session->config->rootapi, afb_hreq_rest_api, NULL, 10))
-		return 0;
-
-	for (idx = 0; session->config->aliasdir[idx].url != NULL; idx++)
-		if (!afb_hsrv_add_alias (session, session->config->aliasdir[idx].url, session->config->aliasdir[idx].path, 0))
-			return 0;
-
-	if (!afb_hsrv_add_alias(session, "", session->config->rootdir, -10))
-		return 0;
-
-	if (!afb_hsrv_add_handler(session, session->config->rootbase, afb_hreq_one_page_api_redirect, NULL, -20))
-		return 0;
-
-	return 1;
-}
-
-AFB_error httpdStart(AFB_session * session)
-{
-	if (!my_default_init(session)) {
-		printf("Error: initialisation of httpd failed");
-		return AFB_FATAL;
-	}
-
-#if defined(USE_MAGIC_MIME_TYPE)
-	/*TBD open libmagic cache [fail to pass EFENCE check (allocating 0 bytes)] */
-	init_lib_magic (session);
-#endif
-
-	if (verbosity) {
-		printf("AFB:notice Waiting port=%d rootdir=%s\n", session->config->httpdPort, session->config->rootdir);
-		printf("AFB:notice Browser URL= http:/*localhost:%d\n", session->config->httpdPort);
-	}
-
-	session->httpd = MHD_start_daemon(
-		MHD_USE_EPOLL_LINUX_ONLY | MHD_USE_TCP_FASTOPEN | MHD_USE_DEBUG | MHD_USE_SUSPEND_RESUME,
-		(uint16_t) session->config->httpdPort,	/* port */
-		new_client_handler, NULL,	/* Tcp Accept call back + extra attribute */
-		access_handler, session,	/* Http Request Call back + extra attribute */
-		MHD_OPTION_NOTIFY_COMPLETED, end_handler, session,
-		MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int)15,	/* 15 seconds */
-		MHD_OPTION_END);	/* options-end */
-
-	if (session->httpd == NULL) {
-		printf("Error: httpStart invalid httpd port: %d", session->config->httpdPort);
-		return AFB_FATAL;
-	}
-	return AFB_SUCCESS;
-}
-
-/* infinite loop */
-AFB_error httpdLoop(AFB_session * session)
-{
-	int count = 0;
-	const union MHD_DaemonInfo *info;
-	struct pollfd pfd;
-
-	info = MHD_get_daemon_info(session->httpd, MHD_DAEMON_INFO_EPOLL_FD_LINUX_ONLY);
-	if (info == NULL) {
-		printf("Error: httpLoop no pollfd");
-		goto error;
-	}
-	pfd.fd = info->listen_fd;
-	pfd.events = POLLIN;
-
-	if (verbosity)
-		fprintf(stderr, "AFB:notice entering httpd waiting loop\n");
-	while (TRUE) {
-		if (verbosity)
-			fprintf(stderr, "AFB:notice httpd alive [%d]\n", count++);
-		poll(&pfd, 1, 15000);	/* 15 seconds (as above timeout when starting) */
-		MHD_run(session->httpd);
-	}
-
- error:
-	/* should never return from here */
-	return AFB_FATAL;
-}
-
-int httpdStatus(AFB_session * session)
-{
-	return MHD_run(session->httpd);
-}
-
-void httpdStop(AFB_session * session)
-{
-	MHD_stop_daemon(session->httpd);
-}
diff --git a/src/local-def.h b/src/local-def.h
index 3298322e..c21918cd 100644
--- a/src/local-def.h
+++ b/src/local-def.h
@@ -40,22 +40,6 @@
 #define DEFLT_HTTP_TIMEOUT  15     // Max MibMicroHttp timeout
 #define AFB_MAX_PLUGINS     20     // Max number of plugins for a given binder
 
-#ifndef FALSE
-  #define FALSE 0
-#endif
-#ifndef TRUE
-  #define TRUE 1
-#endif
-
-#define PUBLIC
-#define STATIC    static
-#define FAILED    -1
-
-#define AUDIO_BUFFER "/tmp/buf"
-
-// prebuild json error are constructed in helper-api.c
-typedef enum  { AFB_FALSE, AFB_TRUE, AFB_FATAL, AFB_FAIL, AFB_WARNING, AFB_EMPTY, AFB_SUCCESS, AFB_DONE, AFB_UNAUTH} AFB_error;
-
 #define MAX_POST_SIZE  4096   // maximum size for POST data
 #define CTX_NBCLIENTS   10   // allow a default of 10 authenticated clients
 
@@ -63,11 +47,6 @@ typedef enum  { AFB_FALSE, AFB_TRUE, AFB_FATAL, AFB_FAIL, AFB_WARNING, AFB_EMPTY
 
 
 
-
-
-
-
-
 enum AFB_Mode;
 
 
@@ -133,6 +112,4 @@ struct AFB_session
 typedef struct AFB_config AFB_config;
 typedef struct AFB_session AFB_session;
 
-#include "proto-def.h"
-
 #endif /* LOCAL_DEF_H */
diff --git a/src/main.c b/src/main.c
index 15f84b18..41296e2c 100644
--- a/src/main.c
+++ b/src/main.c
@@ -32,8 +32,10 @@
 
 #include "local-def.h"
 #include "afb-apis.h"
+#include "afb-hsrv.h"
 #include "session.h"
 #include "verbose.h"
+#include "utils-upoll.h"
 
 #if !defined(PLUGIN_INSTALL_DIR)
 #error "you should define PLUGIN_INSTALL_DIR"
@@ -126,24 +128,29 @@ static void printVersion (void)
 }
 
 // load config from disk and merge with CLI option
-static AFB_error config_set_default (AFB_session * session)
+static void config_set_default (AFB_session * session)
 {
    static char cacheTimeout [10];
    
    // default HTTP port
-   if (session->config->httpdPort == 0) session->config->httpdPort=1234;
+   if (session->config->httpdPort == 0)
+	session->config->httpdPort = 1234;
    
    // default Plugin API timeout
-   if (session->config->apiTimeout == 0) session->config->apiTimeout=DEFLT_API_TIMEOUT;
+   if (session->config->apiTimeout == 0)
+	session->config->apiTimeout = DEFLT_API_TIMEOUT;
    
    // default AUTH_TOKEN
-   if (session->config->token == NULL) session->config->token= DEFLT_AUTH_TOKEN;
+   if (session->config->token == NULL)
+		session->config->token = DEFLT_AUTH_TOKEN;
 
    // cache timeout default one hour
-   if (session->config->cacheTimeout == 0) session->config->cacheTimeout=DEFLT_CACHE_TIMEOUT;
+   if (session->config->cacheTimeout == 0)
+		session->config->cacheTimeout = DEFLT_CACHE_TIMEOUT;
 
    // cache timeout default one hour
-   if (session->config->cntxTimeout == 0) session->config->cntxTimeout=DEFLT_CNTX_TIMEOUT;
+   if (session->config->cntxTimeout == 0)
+		session->config->cntxTimeout = DEFLT_CNTX_TIMEOUT;
 
    if (session->config->rootdir == NULL) {
        session->config->rootdir = getenv("AFBDIR");
@@ -157,17 +164,14 @@ static AFB_error config_set_default (AFB_session * session)
    }
    
    // if no Angular/HTML5 rootbase let's try '/' as default
-   if  (session->config->rootbase == NULL) {
+   if  (session->config->rootbase == NULL)
        session->config->rootbase = "/opa";
-   }
    
-   if  (session->config->rootapi == NULL) {
+   if  (session->config->rootapi == NULL)
        session->config->rootapi = "/api";
-   }
 
-   if  (session->config->ldpaths == NULL) {
+   if  (session->config->ldpaths == NULL)
        session->config->ldpaths = PLUGIN_INSTALL_DIR;
-   }
 
    // if no session dir create a default path from rootdir
    if  (session->config->sessiondir == NULL) {
@@ -186,8 +190,6 @@ static AFB_error config_set_default (AFB_session * session)
    // cacheTimeout is an integer but HTTPd wants it as a string
    snprintf (cacheTimeout, sizeof (cacheTimeout),"%d", session->config->cacheTimeout);
    session->cacheTimeout = cacheTimeout; // httpd uses cacheTimeout string version
-
-   return AFB_SUCCESS;
 }
 
 
@@ -406,20 +408,11 @@ static void closeSession (int status, void *data) {
 
 /*----------------------------------------------------------
  | timeout signalQuit
- |
  +--------------------------------------------------------- */
-void signalQuit (int signum) {
-
-  sigset_t sigset;
-
-  // unlock timeout signal to allow a new signal to come
-  sigemptyset (&sigset);
-  sigaddset   (&sigset, SIGABRT);
-  sigprocmask (SIG_UNBLOCK, &sigset, 0);
-
-  fprintf (stderr, "ERR: Received signal quit\n");
-  syslog (LOG_ERR, "Daemon got kill3 & quit [please report bug]");
-  exit(1);
+void signalQuit (int signum)
+{
+	fprintf(stderr, "Terminating signal received %s\n", strsignal(signum));
+	exit(1);
 }
 
 
@@ -439,6 +432,10 @@ static void signalError(int signum)
 		sigprocmask(SIG_UNBLOCK, &sigset, 0);
 		longjmp(*error_handler, signum);
 	}
+	if (signum == SIGALRM)
+		return;
+	fprintf(stderr, "Unmonitored signal received %s\n", strsignal(signum));
+	exit(2);
 }
 
 static void install_error_handlers()
@@ -453,30 +450,6 @@ static void install_error_handlers()
 	}
 }
 
-/*----------------------------------------------------------
- | listenLoop
- |   Main listening HTTP loop
- +--------------------------------------------------------- */
-static void listenLoop (AFB_session *session) {
-  AFB_error  err;
-
-  // ------ Start httpd server
-
-   err = httpdStart (session);
-   if (err != AFB_SUCCESS) return;
-
-	if (session->readyfd != 0) {
-		static const char readystr[] = "READY=1";
-		write(session->readyfd, readystr, sizeof(readystr) - 1);
-		close(session->readyfd);
-	}
-
-   // infinite loop
-   httpdLoop(session);
-
-   fprintf (stderr, "hoops returned from infinite loop [report bug]\n");
-}
-  
 /*----------------------------------------------------------
  | daemonize
  |   set the process in background
@@ -530,6 +503,7 @@ static void daemonize(AFB_session *session)
  +--------------------------------------------------------- */
 
 int main(int argc, char *argv[])  {
+  int rc;
   AFB_session    *session;
 
   // open syslog if ever needed
@@ -560,7 +534,8 @@ int main(int argc, char *argv[])  {
   install_error_handlers();
 
   // ------------------ Some useful default values -------------------------
-  if  ((session->background == 0) && (session->foreground == 0)) session->foreground=1;
+  if  ((session->background == 0) && (session->foreground == 0))
+	session->foreground = 1;
 
   // ------------------ clean exit on CTR-C signal ------------------------
   if (signal (SIGINT, signalQuit) == SIG_ERR || signal (SIGABRT, signalQuit) == SIG_ERR) {
@@ -568,7 +543,6 @@ int main(int argc, char *argv[])  {
      return 1;
   }
 
-
   // let's run this program with a low priority
   nice (20);
 
@@ -576,13 +550,6 @@ int main(int argc, char *argv[])  {
   // let's not take the risk to run as ROOT
   //if (getuid() == 0)  goto errorNoRoot;
 
-#if defined(ALLOWS_SESSION_FILES)
-  // check session dir and create if it does not exist
-  if (sessionCheckdir (session) != AFB_SUCCESS) {
-  	fprintf (stderr,"\nERR: AFB-daemon cannot read/write session dir\n\n");
-  	exit (1);
-  }
-#endif
   if (verbosity) fprintf (stderr, "AFB: notice Init config done\n");
 
   // ---- run in foreground mode --------------------
@@ -606,10 +573,26 @@ int main(int argc, char *argv[])  {
   } // end background-foreground
 
 
-  listenLoop(session);
-  if (verbosity) printf ("\n---- Application Framework Binder Normal End ------\n");
-  exit(0);
+  // ------ Start httpd server
+
+   rc = afb_hsrv_start (session);
+   if (!rc)
+	exit(1);
+
+   if (session->readyfd != 0) {
+		static const char readystr[] = "READY=1";
+		write(session->readyfd, readystr, sizeof(readystr) - 1);
+		close(session->readyfd);
+  }
+
+   // infinite loop
+  for(;;)
+   upoll_wait(30000); 
+
+   if (verbosity)
+       fprintf (stderr, "hoops returned from infinite loop [report bug]\n");
 
+  return 0;
 }
 
 
diff --git a/src/proto-def.h b/src/proto-def.h
deleted file mode 100644
index b549a62e..00000000
--- a/src/proto-def.h
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
-   proto-def.h -- provide a REST/HTTP interface
-
-   Copyright (C) 2015, Fulup Ar Foll
-
-   This program is free software; you can redistribute it and/or modify
-   it under the terms of the GNU General Public License as published by
-   the Free Software Foundation; either version 2 of the License, or
-   (at your option) any later version.
-
-   This program is distributed in the hope that it will be useful,
-   but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-   GNU General Public License for more details.
-
-   You should have received a copy of the GNU General Public License
-   along with this program; if not, write to the Free Software
-   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
-
-
-// Httpd server
-extern AFB_error httpdStart          (AFB_session *session);
-extern AFB_error httpdLoop           (AFB_session *session);
-extern void  httpdStop               (AFB_session *session);
-
-
-
-
diff --git a/src/utils-upoll.c b/src/utils-upoll.c
index af4a6161..eb0e9674 100644
--- a/src/utils-upoll.c
+++ b/src/utils-upoll.c
@@ -28,7 +28,9 @@
 struct upoll
 {
 	int fd;
-	void (*process)(void *closure, int fd, uint32_t events);
+	void (*read)(void *);
+	void (*write)(void *);
+	void (*hangup)(void *);
 	void *closure;
 	struct upoll *next;
 };
@@ -48,7 +50,7 @@ int upoll_is_valid(struct upoll *upoll)
 	return 0;
 }
 
-struct upoll *upoll_open(int fd, uint32_t events, void (*process)(void *closure, int fd, uint32_t events), void *closure)
+struct upoll *upoll_open(int fd, void *closure)
 {
 	struct epoll_event e;
 	struct upoll *result;
@@ -68,13 +70,12 @@ struct upoll *upoll_open(int fd, uint32_t events, void (*process)(void *closure,
 	}
 
 	/* allocates */
-	result = malloc(sizeof *result);
+	result = calloc(1, sizeof *result);
 	if (result == NULL)
 		return NULL;
 
 	/* init */
 	result->fd = fd;
-	result->process = process;
 	result->closure = closure;
 	pthread_mutex_lock(&mutex);
 	result->next = head;
@@ -82,7 +83,7 @@ struct upoll *upoll_open(int fd, uint32_t events, void (*process)(void *closure,
 	pthread_mutex_unlock(&mutex);
 
 	/* records */
-	e.events = events;
+	e.events = 0;
 	e.data.ptr = result;
 	rc = epoll_ctl(pollfd, EPOLL_CTL_ADD, fd, &e);
 	if (rc == 0)
@@ -95,16 +96,39 @@ struct upoll *upoll_open(int fd, uint32_t events, void (*process)(void *closure,
 	return NULL;
 }
 
-int upoll_update(struct upoll *upoll, uint32_t events)
+static int update(struct upoll *upoll)
 {
 	struct epoll_event e;
+	e.events = (upoll->read != NULL ? EPOLLIN : 0 )
+		 | (upoll->write != NULL ? EPOLLOUT : 0);
+	e.data.ptr = upoll;
+	return epoll_ctl(pollfd, EPOLL_CTL_MOD, upoll->fd, &e);
+}
 
+int upoll_on_readable(struct upoll *upoll, void (*process)(void *))
+{
 	assert(pollfd != 0);
 	assert(upoll_is_valid(upoll));
 
-	e.events = events;
-	e.data.ptr = upoll;
-	return epoll_ctl(pollfd, EPOLL_CTL_MOD, upoll->fd, &e);
+	upoll->read = process;
+	return update(upoll);
+}
+
+int upoll_on_writable(struct upoll *upoll, void (*process)(void *))
+{
+	assert(pollfd != 0);
+	assert(upoll_is_valid(upoll));
+
+	upoll->write = process;
+	return update(upoll);
+}
+
+void upoll_on_hangup(struct upoll *upoll, void (*process)(void *))
+{
+	assert(pollfd != 0);
+	assert(upoll_is_valid(upoll));
+
+	upoll->hangup = process;
 }
 
 void upoll_close(struct upoll *upoll)
@@ -136,7 +160,12 @@ void upoll_wait(int timeout)
 	rc = epoll_wait(pollfd, &e, 1, timeout);
 	if (rc == 1) {
 		upoll = e.data.ptr;
-		upoll->process(upoll->closure, upoll->fd, e.events);
+		if ((e.events & EPOLLIN) && upoll->read)
+			upoll->read(upoll->closure);
+		if ((e.events & EPOLLOUT) && upoll->write)
+			upoll->write(upoll->closure);
+		if ((e.events & EPOLLHUP) && upoll->hangup)
+			upoll->hangup(upoll->closure);
 	}
 }
 
diff --git a/src/utils-upoll.h b/src/utils-upoll.h
index 24aaf41a..705fbc36 100644
--- a/src/utils-upoll.h
+++ b/src/utils-upoll.h
@@ -19,9 +19,12 @@ struct upoll;
 
 extern int upoll_is_valid(struct upoll *upoll);
 
-extern struct upoll *upoll_open(int fd, uint32_t events, void (*process)(void *closure, int fd, uint32_t events), void *closure);
+extern struct upoll *upoll_open(int fd, void *closure);
 
-extern int upoll_update(struct upoll *upoll, uint32_t events);
+extern int upoll_on_readable(struct upoll *upoll, void (*process)(void *closure));
+extern int upoll_on_writable(struct upoll *upoll, void (*process)(void *closure));
+
+extern void upoll_on_hangup(struct upoll *upoll, void (*process)(void *closure));
 
 extern void upoll_close(struct upoll *upoll);
 
diff --git a/src/verbose.h b/src/verbose.h
index 7b32a663..09254b22 100644
--- a/src/verbose.h
+++ b/src/verbose.h
@@ -1,5 +1,5 @@
 /*
- Copyright 2015 IoT.bzh
+ Copyright 2016 IoT.bzh
 
  author: José Bollo <jose.bollo@iot.bzh>
 
-- 
cgit