provides developper files
[src/app-framework-binder.git] / src / afb-hsrv.c
index abec0ad..46e61a0 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2016 IoT.bzh
+ * Copyright (C) 2016 "IoT.bzh"
  * Author: José Bollo <jose.bollo@iot.bzh>
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
 
 #define _GNU_SOURCE
 
+#include <stdint.h>
 #include <stdio.h>
 #include <string.h>
 #include <assert.h>
 #include <poll.h>
 #include <fcntl.h>
+#include <errno.h>
 #include <sys/stat.h>
 
 #include <microhttpd.h>
+#include <systemd/sd-event.h>
 
 #include "afb-method.h"
+#include "afb-context.h"
 #include "afb-hreq.h"
 #include "afb-hsrv.h"
-#include "afb-req-itf.h"
+#include <afb/afb-req-itf.h>
 #include "verbose.h"
-#include "utils-upoll.h"
+
+#include "afb-common.h"
+
 
 
 #define JSON_CONTENT  "application/json"
@@ -54,21 +60,16 @@ struct hsrv_alias {
        int dirfd;
 };
 
-enum afb_hsrv_state {
-       hsrv_idle = 0,
-       hsrv_run,
-       hsrv_rerun
-};
-
 struct afb_hsrv {
        unsigned refcount;
-       enum afb_hsrv_state state;
        struct hsrv_handler *handlers;
        struct MHD_Daemon *httpd;
-       struct upoll *upoll;
+       sd_event_source *evsrc;
+       int in_run;
        char *cache_to;
 };
 
+static int global_reqids = 0;
 
 static void reply_error(struct MHD_Connection *connection, unsigned int status)
 {
@@ -114,25 +115,35 @@ static int access_handler(
        hsrv = 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;
+               if (method == afb_method_none) {
+                       reply_error(connection, MHD_HTTP_BAD_REQUEST);
+                       return MHD_YES;
+               }
+
+               /* create the request */
+               hreq = calloc(1, sizeof *hreq);
+               if (hreq == NULL) {
+                       reply_error(connection, MHD_HTTP_INTERNAL_SERVER_ERROR);
+                       return MHD_YES;
+               }
 
                /* init the request */
+               hreq->refcount = 1;
+               hreq->hsrv = hsrv;
                hreq->cacheTimeout = hsrv->cache_to;
+               hreq->reqid = ++global_reqids;
+               hreq->scanned = 0;
+               hreq->suspended = 0;
+               hreq->replied = 0;
                hreq->connection = connection;
                hreq->method = method;
                hreq->version = version;
                hreq->tail = hreq->url = url;
                hreq->lentail = hreq->lenurl = strlen(url);
+               *recordreq = hreq;
 
                /* init the post processing */
                if (method == afb_method_post) {
@@ -143,10 +154,12 @@ static int access_handler(
                        } else if (strcasestr(type, FORM_CONTENT) != NULL) {
                                hreq->postform = MHD_create_post_processor (connection, 65500, postproc, hreq);
                                if (hreq->postform == NULL)
-                                       goto internal_error;
+                                       afb_hreq_reply_error(hreq, MHD_HTTP_INTERNAL_SERVER_ERROR);
                                return MHD_YES;
-                       } else if (strcasestr(type, JSON_CONTENT) == NULL) {
-                               reply_error(connection, MHD_HTTP_UNSUPPORTED_MEDIA_TYPE);
+                       } else if (strcasestr(type, JSON_CONTENT) != NULL) {
+                               return MHD_YES;
+                        } else {
+                               afb_hreq_reply_error(hreq, MHD_HTTP_UNSUPPORTED_MEDIA_TYPE);
                                return MHD_YES;
                        }
                }
@@ -155,30 +168,50 @@ static int access_handler(
        /* 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;
+                       if (!MHD_post_process (hreq->postform, upload_data, *upload_data_size)) {
+                               afb_hreq_reply_error(hreq, MHD_HTTP_INTERNAL_SERVER_ERROR);
+                               return MHD_YES;
+                       }
                } else {
-                       if (!afb_hreq_post_add(hreq, "", upload_data, *upload_data_size))
-                               goto internal_error;
+                       if (!afb_hreq_post_add(hreq, "", upload_data, *upload_data_size)) {
+                               afb_hreq_reply_error(hreq, MHD_HTTP_INTERNAL_SERVER_ERROR);
+                               return MHD_YES;
+                       }
                }
                *upload_data_size = 0;
-               return MHD_YES;         
+               return MHD_YES;
        }
 
        /* flush the data */
        if (hreq->postform != NULL) {
                rc = MHD_destroy_post_processor(hreq->postform);
                hreq->postform = NULL;
-               if (rc == MHD_NO)
-                       goto bad_request;
+               if (rc == MHD_NO) {
+                       afb_hreq_reply_error(hreq, MHD_HTTP_BAD_REQUEST);
+                       return MHD_YES;
+               }
+       }
+
+       if (hreq->scanned != 0) {
+               if (hreq->replied == 0 && hreq->suspended == 0) {
+                       MHD_suspend_connection (connection);
+                       hreq->suspended = 1;
+               }
+               return MHD_YES;
        }
 
        /* search an handler for the request */
+       hreq->scanned = 1;
        iter = hsrv->handlers;
        while (iter) {
                if (afb_hreq_unprefix(hreq, iter->prefix, iter->length)) {
-                       if (iter->handler(hreq, iter->data))
+                       if (iter->handler(hreq, iter->data)) {
+                               if (hreq->replied == 0 && hreq->suspended == 0) {
+                                       MHD_suspend_connection (connection);
+                                       hreq->suspended = 1;
+                               }
                                return MHD_YES;
+                       }
                        hreq->tail = hreq->url;
                        hreq->lentail = hreq->lenurl;
                }
@@ -188,14 +221,6 @@ static int access_handler(
        /* no handler */
        afb_hreq_reply_error(hreq, MHD_HTTP_NOT_FOUND);
        return MHD_YES;
-
-bad_request:
-       reply_error(connection, MHD_HTTP_BAD_REQUEST);
-       return MHD_YES;
-
-internal_error:
-       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 */
@@ -207,21 +232,29 @@ static void end_handler(void *cls, struct MHD_Connection *connection, void **rec
        hreq = *recordreq;
        if (hreq->upgrade)
                MHD_suspend_connection (connection);
-       afb_hreq_free(hreq);
+       afb_hreq_unref(hreq);
 }
 
-static void handle_epoll_readable(struct afb_hsrv *hsrv)
+void run_micro_httpd(struct afb_hsrv *hsrv)
 {
-       if (hsrv->state != hsrv_idle)
-               hsrv->state = hsrv_rerun;
+       if (hsrv->in_run != 0)
+               hsrv->in_run = 2;
        else {
+               sd_event_source_set_io_events(hsrv->evsrc, 0);
                do {
-                       hsrv->state = hsrv_run;
+                       hsrv->in_run = 1;
                        MHD_run(hsrv->httpd);
-               } while (hsrv->state == hsrv_rerun);
-               hsrv->state = hsrv_idle;
+               } while(hsrv->in_run == 2);
+               hsrv->in_run = 0;
+               sd_event_source_set_io_events(hsrv->evsrc, EPOLLIN);
        }
-};
+}
+
+static int io_event_callback(sd_event_source *src, int fd, uint32_t revents, void *hsrv)
+{
+       run_micro_httpd(hsrv);
+       return 0;
+}
 
 static int new_client_handler(void *cls, const struct sockaddr *addr, socklen_t addrlen)
 {
@@ -342,7 +375,8 @@ int afb_hsrv_set_cache_timeout(struct afb_hsrv *hsrv, int duration)
 
 int afb_hsrv_start(struct afb_hsrv *hsrv, uint16_t port, unsigned int connection_timeout)
 {
-       struct upoll *upoll;
+       sd_event_source *evsrc;
+       int rc;
        struct MHD_Daemon *httpd;
        const union MHD_DaemonInfo *info;
 
@@ -356,35 +390,36 @@ int afb_hsrv_start(struct afb_hsrv *hsrv, uint16_t port, unsigned int connection
                MHD_OPTION_END);        /* options-end */
 
        if (httpd == NULL) {
-               fprintf(stderr, "Error: httpStart invalid httpd port: %d", (int)port);
+               ERROR("httpStart invalid httpd port: %d", (int)port);
                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");
+               ERROR("httpStart no pollfd");
                return 0;
        }
 
-       upoll = upoll_open(info->listen_fd, hsrv);
-       if (upoll == NULL) {
+       rc = sd_event_add_io(afb_common_get_event_loop(), &evsrc, info->listen_fd, EPOLLIN, io_event_callback, hsrv);
+       if (rc < 0) {
                MHD_stop_daemon(httpd);
-               fprintf(stderr, "Error: connection to upoll of httpd failed");
+               errno = -rc;
+               ERROR("connection to events for httpd failed");
                return 0;
        }
-       upoll_on_readable(upoll, (void*)handle_epoll_readable);
 
        hsrv->httpd = httpd;
-       hsrv->upoll = upoll;
+       hsrv->evsrc = evsrc;
        return 1;
 }
 
 void afb_hsrv_stop(struct afb_hsrv *hsrv)
 {
-       if (hsrv->upoll)
-               upoll_close(hsrv->upoll);
-       hsrv->upoll = NULL;
+       if (hsrv->evsrc != NULL) {
+               sd_event_source_unref(hsrv->evsrc);
+               hsrv->evsrc = NULL;
+       }
        if (hsrv->httpd != NULL)
                MHD_stop_daemon(hsrv->httpd);
        hsrv->httpd = NULL;