Factorize common code for handling requests
authorJosé Bollo <jose.bollo@iot.bzh>
Fri, 31 Mar 2017 14:11:07 +0000 (16:11 +0200)
committerJosé Bollo <jose.bollo@iot.bzh>
Fri, 31 Mar 2017 14:14:02 +0000 (16:14 +0200)
The common code for session handling is
shared using struct afb_xreq. At the moment
only hreq leverages the new feature.

The objective is double: make the work of
writing new internal requests more easy and
prepare to check permissions.

Change-Id: If3ca311d68c2d8c427d1125f31a2704b150c2c94
Signed-off-by: José Bollo <jose.bollo@iot.bzh>
16 files changed:
bindings/samples/HelloWorld.c
bindings/samples/export.map
src/CMakeLists.txt
src/afb-api-so-v1.c
src/afb-api-so-v2.c
src/afb-apis.c
src/afb-apis.h
src/afb-evt.h
src/afb-hreq.c
src/afb-hreq.h
src/afb-hsrv.c
src/afb-hswitch.c
src/afb-websock.c
src/afb-xreq.c [new file with mode: 0644]
src/afb-xreq.h [new file with mode: 0644]
test/hello-world.html

index 9e14bef..cb4a5a4 100644 (file)
@@ -245,6 +245,11 @@ static void eventpush (struct afb_req request)
                afb_req_success(request, NULL, NULL);
 }
 
+static void exitnow (struct afb_req request)
+{
+       exit(0);
+}
+
 // NOTE: this sample does not use session to keep test a basic as possible
 //       in real application most APIs should be protected with AFB_SESSION_CHECK
 static const struct afb_verb_desc_v1 verbs[]= {
@@ -260,6 +265,7 @@ static const struct afb_verb_desc_v1 verbs[]= {
   {"eventsub",  AFB_SESSION_NONE, eventsub    , "subscribes to the event of 'tag'"},
   {"eventunsub",AFB_SESSION_NONE, eventunsub  , "unsubscribes to the event of 'tag'"},
   {"eventpush", AFB_SESSION_NONE, eventpush   , "pushs the event of 'tag' with the 'data'"},
+  {"exit",      AFB_SESSION_NONE, exitnow     , "exits from afb-daemon"},
   {NULL}
 };
 
index 0ef1ac7..ee2f413 100644 (file)
@@ -1 +1 @@
-{ global: afbBindingV1Register; local: *; };
+{ global: afbBindingV*; local: *; };
index 653cf8c..919e0d5 100644 (file)
@@ -79,6 +79,7 @@ ADD_LIBRARY(afb-lib STATIC
        afb-ws-json1.c
        afb-ws.c
        afb-wsj1.c
+       afb-xreq.c
        jobs.c
        locale-root.c
        sd-fds.c
index 4fafb13..a09b0f5 100644 (file)
@@ -31,6 +31,7 @@
 #include "afb-context.h"
 #include "afb-api-so.h"
 #include "afb-thread.h"
+#include "afb-xreq.h"
 #include "verbose.h"
 
 /*
@@ -173,21 +174,46 @@ static int call_check(struct afb_req req, struct afb_context *context, const str
        return 1;
 }
 
-static void call_cb(void *closure, struct afb_req req, struct afb_context *context, const char *strverb)
+static const struct afb_verb_desc_v1 *search(struct api_so_v1 *desc, const char *name)
 {
        const struct afb_verb_desc_v1 *verb;
-       struct api_so_v1 *desc = closure;
 
        verb = desc->binding->v1.verbs;
-       while (verb->name && strcasecmp(verb->name, strverb))
+       while (verb->name && strcasecmp(verb->name, name))
                verb++;
-       if (!verb->name)
+       return verb->name ? verb : NULL;
+}
+
+static void call_cb(void *closure, struct afb_req req, struct afb_context *context, const char *strverb)
+{
+       const struct afb_verb_desc_v1 *verb;
+       struct api_so_v1 *desc = closure;
+
+       verb = search(desc, strverb);
+       if (!verb)
                afb_req_fail_f(req, "unknown-verb", "verb %s unknown within api %s", strverb, desc->binding->v1.prefix);
        else if (call_check(req, context, verb)) {
                afb_thread_req_call(req, verb->callback, afb_api_so_timeout, desc);
        }
 }
 
+static void xcall_cb(void *closure, struct afb_xreq *xreq)
+{
+       const struct afb_verb_desc_v1 *verb;
+       struct api_so_v1 *desc = closure;
+
+       verb = search(desc, xreq->verb);
+       if (!verb)
+               afb_xreq_fail_f(xreq, "unknown-verb", "verb %s unknown within api %s", xreq->verb, desc->binding->v1.prefix);
+       else {
+               xreq->timeout = afb_api_so_timeout;
+               xreq->sessionflags = (int)verb->session;
+               xreq->group = desc;
+               xreq->callback = verb->callback;
+               afb_xreq_call(xreq);
+       }
+}
+
 static int service_start_cb(void *closure, int share_session, int onneed)
 {
        int (*init)(struct afb_service service);
@@ -300,6 +326,7 @@ int afb_api_so_v1_add(const char *path, void *handle)
        if (afb_apis_add(desc->binding->v1.prefix, (struct afb_api){
                        .closure = desc,
                        .call = call_cb,
+                       .xcall = xcall_cb,
                        .service_start = service_start_cb }) < 0) {
                ERROR("binding [%s] can't be registered...", path);
                goto error2;
index 0c8079c..2770f64 100644 (file)
@@ -31,6 +31,7 @@
 #include "afb-context.h"
 #include "afb-api-so.h"
 #include "afb-thread.h"
+#include "afb-xreq.h"
 #include "verbose.h"
 
 /*
@@ -171,21 +172,46 @@ static int call_check(struct afb_req req, struct afb_context *context, const str
        return 1;
 }
 
-static void call_cb(void *closure, struct afb_req req, struct afb_context *context, const char *strverb)
+static const struct afb_verb_v2 *search(struct api_so_v2 *desc, const char *verb)
+{
+       const struct afb_verb_v2 *result;
+
+       result = desc->binding->verbs;
+       while (result->verb && strcasecmp(result->verb, verb))
+               result++;
+       return result->verb ? result : NULL;
+}
+
+static void call_cb(void *closure, struct afb_req req, struct afb_context *context, const char *name)
 {
-       const struct afb_verb_v2 *verb;
        struct api_so_v2 *desc = closure;
+       const struct afb_verb_v2 *verb;
 
-       verb = desc->binding->verbs;
-       while (verb->verb && strcasecmp(verb->verb, strverb))
-               verb++;
-       if (!verb->verb)
-               afb_req_fail_f(req, "unknown-verb", "verb %s unknown within api %s", strverb, desc->binding->api);
+       verb = search(desc, name);
+       if (!verb)
+               afb_req_fail_f(req, "unknown-verb", "verb %s unknown within api %s", name, desc->binding->api);
        else if (call_check(req, context, verb)) {
                afb_thread_req_call(req, verb->callback, afb_api_so_timeout, desc);
        }
 }
 
+static void xcall_cb(void *closure, struct afb_xreq *xreq)
+{
+       struct api_so_v2 *desc = closure;
+       const struct afb_verb_v2 *verb;
+
+       verb = search(desc, xreq->verb);
+       if (!verb)
+               afb_xreq_fail_f(xreq, "unknown-verb", "verb %s unknown within api %s", xreq->verb, desc->binding->api);
+       else {
+               xreq->timeout = afb_api_so_timeout;
+               xreq->sessionflags = (int)verb->session;
+               xreq->group = desc;
+               xreq->callback = verb->callback;
+               afb_xreq_call(xreq);
+       }
+}
+
 static int service_start_cb(void *closure, int share_session, int onneed)
 {
        int (*start)(const struct afb_binding_interface *interface, struct afb_service service);
@@ -291,6 +317,7 @@ int afb_api_so_v2_add(const char *path, void *handle)
        if (afb_apis_add(binding->api, (struct afb_api){
                        .closure = desc,
                        .call = call_cb,
+                       .xcall = xcall_cb,
                        .service_start = service_start_cb }) < 0) {
                ERROR("binding [%s] can't be registered...", path);
                goto error2;
index 9ffffdb..a0af080 100644 (file)
 #include "afb-apis.h"
 #include "afb-context.h"
 #include "afb-hook.h"
+#include "afb-xreq.h"
+
 #include <afb/afb-req-itf.h>
 
+/**
+ * Internal description of an api
+ */
 struct api_desc {
-       const char *name;
-       struct afb_api api;
+       const char *name;       /**< name of the api */
+       struct afb_api api;     /**< handler of the api */
 };
 
 static struct api_desc *apis_array = NULL;
@@ -138,21 +143,15 @@ error:
 }
 
 /**
- * Dispatch the request 'req' with the 'context' to the
- * method of 'api' and 'verb'.
- * @param req the request to dispatch
- * @param context the context of the request
+ * Search the 'api'.
  * @param api the api of the verb
- * @param verb the verb within the api
+ * @return the descriptor if found or NULL otherwise
  */
-void afb_apis_call(struct afb_req req, struct afb_context *context, const char *api, const char *verb)
+static const struct api_desc *search(const char *api)
 {
        int i, c, up, lo;
        const struct api_desc *a;
 
-       /* init hooking the request */
-       req = afb_hook_req_call(req, context, api, verb);
-
        /* dichotomic search of the api */
        /* initial slice */
        lo = 0;
@@ -160,19 +159,16 @@ void afb_apis_call(struct afb_req req, struct afb_context *context, const char *
        for (;;) {
                /* check remaining slice */
                if (lo >= up) {
-                       /* empty ?! */
-                       afb_req_fail(req, "fail", "api not found");
-                       break;
+                       /* not found */
+                       return NULL;
                }
                /* check the mid of the slice */
                i = (lo + up) >> 1;
                a = &apis_array[i];
                c = strcasecmp(a->name, api);
                if (c == 0) {
-                       /* api found */
-                       context->api_key = a->api.closure;
-                       a->api.call(a->api.closure, req, context, verb);
-                       break;
+                       /* found */
+                       return a;
                }
                /* update the slice */
                if (c < 0)
@@ -182,6 +178,40 @@ void afb_apis_call(struct afb_req req, struct afb_context *context, const char *
        }
 }
 
+/**
+ * Dispatch the request 'req' with the 'context' to the
+ * method of 'api' and 'verb'.
+ * @param req the request to dispatch
+ * @param context the context of the request
+ * @param api the api of the verb
+ * @param verb the verb within the api
+ */
+void afb_apis_call(struct afb_req req, struct afb_context *context, const char *api, const char *verb)
+{
+       const struct api_desc *a;
+
+       /* init hooking the request */
+       req = afb_hook_req_call(req, context, api, verb);
+
+       /* search the api */
+       a = search(api);
+       if (!a)
+               afb_req_fail(req, "fail", "api not found");
+       else {
+               context->api_key = a->api.closure;
+               a->api.call(a->api.closure, req, context, verb);
+       }
+}
+
+/**
+ * Starts a service by its 'api' name.
+ * @param api name of the service to start
+ * @param share_session if true start the servic"e in a shared session
+ *                      if false start it in its own session
+ * @param onneed if true start the service if possible, if false the api
+ *               must be a service
+ * @return a positive number on success
+ */
 int afb_apis_start_service(const char *api, int share_session, int onneed)
 {
        int i;
@@ -191,9 +221,16 @@ int afb_apis_start_service(const char *api, int share_session, int onneed)
                        return apis_array[i].api.service_start(apis_array[i].api.closure, share_session, onneed);
        }
        ERROR("can't find service %s", api);
+       errno = ENOENT;
        return -1;
 }
 
+/**
+ * Starts all possible services but stops at first error.
+ * @param share_session if true start the servic"e in a shared session
+ *                      if false start it in its own session
+ * @return 0 on success or a negative number when an error is found
+ */
 int afb_apis_start_all_services(int share_session)
 {
        int i, rc;
@@ -206,3 +243,28 @@ int afb_apis_start_all_services(int share_session)
        return 0;
 }
 
+/**
+ * Dispatch the request 'req' with the 'context' to the
+ * method of 'api' and 'verb'.
+ * @param req the request to dispatch
+ * @param context the context of the request
+ * @param api the api of the verb
+ * @param verb the verb within the api
+ */
+void afb_apis_xcall(struct afb_xreq *xreq)
+{
+       const struct api_desc *a;
+
+       /* init hooking the request */
+       // TODO req = afb_hook_req_call(req, context, api, verb);
+
+       /* search the api */
+       a = search(xreq->api);
+       if (!a)
+               afb_xreq_fail_f(xreq, "unknown-api", "api %s not found", xreq->api);
+       else {
+               xreq->context.api_key = a->api.closure;
+               a->api.xcall(a->api.closure, xreq);
+       }
+}
+
index 9b060ce..f211bda 100644 (file)
 
 struct afb_req;
 struct afb_context;
+struct afb_xreq;
 
 struct afb_api
 {
        void *closure;
        void (*call)(void *closure, struct afb_req req, struct afb_context *context, const char *verb);
        int (*service_start)(void *closure, int share_session, int onneed);
+       void (*xcall)(void *closure, struct afb_xreq *xreq);
 };
 
 
@@ -36,5 +38,6 @@ extern int afb_apis_start_all_services(int share_session);
 extern int afb_apis_start_service(const char *name, int share_session, int onneed);
 
 extern void afb_apis_call(struct afb_req req, struct afb_context *context, const char *api, const char *verb);
+extern void afb_apis_xcall(struct afb_xreq *xreq);
 
 
index 52b40ab..5b96b93 100644 (file)
@@ -19,7 +19,7 @@
 
 struct afb_event;
 struct afb_session;
-
+struct json_object;
 struct afb_evt_listener;
 
 struct afb_evt_itf
index 0fe908c..8e3230d 100644 (file)
@@ -44,6 +44,8 @@
 
 #define SIZE_RESPONSE_BUFFER   8192
 
+static int global_reqids = 0;
+
 static char empty_string[] = "";
 
 static const char long_key_for_uuid[] = "x-afb-uuid";
@@ -74,27 +76,15 @@ static struct json_object *req_json(struct afb_hreq *hreq);
 static struct afb_arg req_get(struct afb_hreq *hreq, const char *name);
 static void req_fail(struct afb_hreq *hreq, const char *status, const char *info);
 static void req_success(struct afb_hreq *hreq, json_object *obj, const char *info);
-static const char *req_raw(struct afb_hreq *hreq, size_t *size);
-static void req_send(struct afb_hreq *hreq, const char *buffer, size_t size);
-static int req_subscribe_unsubscribe_error(struct afb_hreq *hreq, struct afb_event event);
-static void req_subcall(struct afb_hreq *hreq, const char *api, const char *verb, struct json_object *args, void (*callback)(void*, int, struct json_object*), void *closure);
 
-const struct afb_req_itf afb_hreq_req_itf = {
+static void afb_hreq_destroy(struct afb_hreq *hreq);
+
+const struct afb_xreq_query_itf afb_hreq_xreq_query_itf = {
        .json = (void*)req_json,
        .get = (void*)req_get,
        .success = (void*)req_success,
        .fail = (void*)req_fail,
-       .raw = (void*)req_raw,
-       .send = (void*)req_send,
-       .context_get = (void*)afb_context_get,
-       .context_set = (void*)afb_context_set,
-       .addref = (void*)afb_hreq_addref,
-       .unref = (void*)afb_hreq_unref,
-       .session_close = (void*)afb_context_close,
-       .session_set_LOA = (void*)afb_context_change_loa,
-       .subscribe = (void*)req_subscribe_unsubscribe_error,
-       .unsubscribe = (void*)req_subscribe_unsubscribe_error,
-       .subcall = (void*)req_subcall
+       .unref = (void*)afb_hreq_destroy
 };
 
 static struct hreq_data *get_data(struct afb_hreq *hreq, const char *key, int create)
@@ -172,7 +162,7 @@ static void afb_hreq_reply_v(struct afb_hreq *hreq, unsigned status, struct MHD_
                MHD_add_response_header(response, k, v);
                k = va_arg(args, const char *);
        }
-       v = afb_context_sent_uuid(&hreq->context);
+       v = afb_context_sent_uuid(&hreq->xreq.context);
        if (v != NULL && asprintf(&cookie, cookie_setter, v) > 0) {
                MHD_add_response_header(response, MHD_HTTP_HEADER_SET_COOKIE, cookie);
                free(cookie);
@@ -309,18 +299,10 @@ static const char *mimetype_fd_name(int fd, const char *filename)
        return result;
 }
 
-void afb_hreq_addref(struct afb_hreq *hreq)
-{
-       hreq->refcount++;
-}
-
-void afb_hreq_unref(struct afb_hreq *hreq)
+static void afb_hreq_destroy(struct afb_hreq *hreq)
 {
        struct hreq_data *data;
 
-       if (hreq == NULL || --hreq->refcount)
-               return;
-
        if (hreq->postform != NULL)
                MHD_destroy_post_processor(hreq->postform);
        for (data = hreq->data; data; data = hreq->data) {
@@ -333,13 +315,23 @@ void afb_hreq_unref(struct afb_hreq *hreq)
                free(data->value);
                free(data);
        }
-       afb_context_disconnect(&hreq->context);
+       afb_context_disconnect(&hreq->xreq.context);
        json_object_put(hreq->json);
-       free(hreq->api);
-       free(hreq->verb);
+       free(hreq->xreq.api);
+       free(hreq->xreq.verb);
        free(hreq);
 }
 
+void afb_hreq_addref(struct afb_hreq *hreq)
+{
+       afb_xreq_addref(&hreq->xreq);
+}
+
+void afb_hreq_unref(struct afb_hreq *hreq)
+{
+       afb_xreq_unref(&hreq->xreq);
+}
+
 /*
  * Removes the 'prefix' of 'length' from the tail of 'hreq'
  * if and only if the prefix exists and is terminated by a leading
@@ -828,11 +820,6 @@ int afb_hreq_post_add_file(struct afb_hreq *hreq, const char *key, const char *f
        return !size;
 }
 
-struct afb_req afb_hreq_to_req(struct afb_hreq *hreq)
-{
-       return (struct afb_req){ .itf = &afb_hreq_req_itf, .closure = hreq };
-}
-
 static struct afb_arg req_get(struct afb_hreq *hreq, const char *name)
 {
        const char *value;
@@ -887,18 +874,6 @@ static struct json_object *req_json(struct afb_hreq *hreq)
        return obj;
 }
 
-static const char *req_raw(struct afb_hreq *hreq, size_t *size)
-{
-       const char *result = json_object_get_string(req_json(hreq));
-       *size = result ? strlen(result) : 0;
-       return result;
-}
-
-static void req_send(struct afb_hreq *hreq, const char *buffer, size_t size)
-{
-       afb_hreq_reply_copy(hreq, MHD_HTTP_OK, size, buffer, NULL);
-}
-
 static ssize_t send_json_cb(json_object *obj, uint64_t pos, char *buf, size_t max)
 {
        ssize_t len = stpncpy(buf, json_object_to_json_string_ext(obj, JSON_C_TO_STRING_PLAIN)+pos, max) - buf;
@@ -915,7 +890,7 @@ static void req_reply(struct afb_hreq *hreq, unsigned retcode, const char *statu
        if (reqid == NULL)
                reqid = afb_hreq_get_argument(hreq, short_key_for_reqid);
 
-       reply = afb_msg_json_reply(status, info, resp, &hreq->context, reqid);
+       reply = afb_msg_json_reply(status, info, resp, &hreq->xreq.context, reqid);
 
        response = MHD_create_response_from_callback((uint64_t)strlen(json_object_to_json_string_ext(reply, JSON_C_TO_STRING_PLAIN)), SIZE_RESPONSE_BUFFER, (void*)send_json_cb, reply, (void*)json_object_put);
        afb_hreq_reply(hreq, retcode, response, NULL);
@@ -931,24 +906,13 @@ static void req_success(struct afb_hreq *hreq, json_object *obj, const char *inf
        req_reply(hreq, MHD_HTTP_OK, "success", info, obj);
 }
 
-static int req_subscribe_unsubscribe_error(struct afb_hreq *hreq, struct afb_event event)
-{
-       errno = EINVAL;
-       return -1;
-}
-
-static void req_subcall(struct afb_hreq *hreq, const char *api, const char *verb, struct json_object *args, void (*callback)(void*, int, struct json_object*), void *closure)
-{
-       afb_subcall(&hreq->context, api, verb, args, callback, closure, (struct afb_req){ .itf = &afb_hreq_req_itf, .closure = hreq });
-}
-
 int afb_hreq_init_req_call(struct afb_hreq *hreq, const char *api, size_t lenapi, const char *verb, size_t lenverb)
 {
-       free(hreq->api);
-       free(hreq->verb);
-       hreq->api = strndup(api, lenapi);
-       hreq->verb = strndup(verb, lenverb);
-       if (hreq->api == NULL || hreq->verb == NULL) {
+       free(hreq->xreq.api);
+       free(hreq->xreq.verb);
+       hreq->xreq.api = strndup(api, lenapi);
+       hreq->xreq.verb = strndup(verb, lenverb);
+       if (hreq->xreq.api == NULL || hreq->xreq.verb == NULL) {
                ERROR("Out of memory");
                errno = ENOMEM;
                return -1;
@@ -961,7 +925,7 @@ int afb_hreq_init_context(struct afb_hreq *hreq)
        const char *uuid;
        const char *token;
 
-       if (hreq->context.session != NULL)
+       if (hreq->xreq.context.session != NULL)
                return 0;
 
        uuid = afb_hreq_get_header(hreq, long_key_for_uuid);
@@ -978,7 +942,7 @@ int afb_hreq_init_context(struct afb_hreq *hreq)
        if (token == NULL)
                token = afb_hreq_get_argument(hreq, short_key_for_token);
 
-       return afb_context_connect(&hreq->context, uuid, token);
+       return afb_context_connect(&hreq->xreq.context, uuid, token);
 }
 
 int afb_hreq_init_cookie(int port, const char *path, int maxage)
@@ -1001,4 +965,21 @@ int afb_hreq_init_cookie(int port, const char *path, int maxage)
        return 1;
 }
 
+struct afb_xreq *afb_hreq_to_xreq(struct afb_hreq *hreq)
+{
+       return &hreq->xreq;
+}
+
+struct afb_hreq *afb_hreq_create()
+{
+       struct afb_hreq *hreq = calloc(1, sizeof *hreq);
+       if (hreq) {
+               /* init the request */
+               hreq->xreq.refcount = 1;
+               hreq->xreq.query = hreq;
+               hreq->xreq.queryitf = &afb_hreq_xreq_query_itf;
+               hreq->reqid = ++global_reqids;
+       }
+       return hreq;
+}
 
index 5cb8609..47d6aa5 100644 (file)
 
 #pragma once
 
+#include "afb-xreq.h"
+
 struct afb_session;
 struct json_object;
 struct hreq_data;
 struct afb_hsrv;
-struct afb_req_itf;
 struct locale_search;
 
-extern const struct afb_req_itf afb_hreq_req_itf;
-
 struct afb_hreq {
-       /*
-        * CAUTION: 'context' field should be the first because there
-        * is an implicit convertion to struct afb_context
-        */
-       struct afb_context context;
-       int refcount;
+       struct afb_xreq xreq;
        struct afb_hsrv *hsrv;
        const char *cacheTimeout;
        struct MHD_Connection *connection;
@@ -51,8 +45,6 @@ struct afb_hreq {
        struct hreq_data *data;
        struct json_object *json;
        int upgrade;
-       char *api;
-       char *verb;
 };
 
 extern int afb_hreq_unprefix(struct afb_hreq *request, const char *prefix, size_t length);
@@ -83,8 +75,6 @@ extern int afb_hreq_post_add_file(struct afb_hreq *hreq, const char *name, const
 
 extern int afb_hreq_post_add(struct afb_hreq *hreq, const char *name, const char *data, size_t size);
 
-extern struct afb_req afb_hreq_to_req(struct afb_hreq *hreq);
-
 extern int afb_hreq_init_req_call(struct afb_hreq *hreq, const char *api, size_t lenapi, const char *verb, size_t lenverb);
 
 extern int afb_hreq_init_context(struct afb_hreq *hreq);
@@ -105,3 +95,4 @@ extern void afb_hreq_addref(struct afb_hreq *hreq);
 
 extern void afb_hreq_unref(struct afb_hreq *hreq);
 
+extern struct afb_hreq *afb_hreq_create();
index 577ef85..f0866f8 100644 (file)
@@ -31,6 +31,7 @@
 
 #include "afb-method.h"
 #include "afb-context.h"
+#include "afb-xreq.h"
 #include "afb-hreq.h"
 #include "afb-hsrv.h"
 #include <afb/afb-req-itf.h>
@@ -39,8 +40,6 @@
 
 #include "afb-common.h"
 
-
-
 #define JSON_CONTENT  "application/json"
 #define FORM_CONTENT  MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA
 
@@ -68,8 +67,6 @@ struct afb_hsrv {
        char *cache_to;
 };
 
-static int global_reqids = 0;
-
 static void reply_error(struct MHD_Connection *connection, unsigned int status)
 {
        struct MHD_Response *response = MHD_create_response_from_buffer(0, NULL, MHD_RESPMEM_PERSISTENT);
@@ -124,7 +121,7 @@ static int access_handler(
                }
 
                /* create the request */
-               hreq = calloc(1, sizeof *hreq);
+               hreq = afb_hreq_create();
                if (hreq == NULL) {
                        ERROR("Can't allocate 'hreq'");
                        reply_error(connection, MHD_HTTP_INTERNAL_SERVER_ERROR);
@@ -132,13 +129,8 @@ static int access_handler(
                }
 
                /* 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;
index 909480a..eab5b9b 100644 (file)
@@ -47,7 +47,7 @@ int afb_hswitch_apis(struct afb_hreq *hreq, void *data)
        if (afb_hreq_init_req_call(hreq, api, lenapi, verb, lenverb) < 0)
                afb_hreq_reply_error(hreq, MHD_HTTP_INTERNAL_SERVER_ERROR);
        else
-               afb_apis_call(afb_hreq_to_req(hreq), &hreq->context, hreq->api, hreq->verb);
+               afb_apis_xcall(&hreq->xreq);
        return 1;
 }
 
@@ -87,7 +87,7 @@ int afb_hswitch_websocket_switch(struct afb_hreq *hreq, void *data)
                return 1;
        }
 
-       if (!hreq->context.validated) {
+       if (!hreq->xreq.context.validated) {
                afb_hreq_reply_error(hreq, MHD_HTTP_UNAUTHORIZED);
                return 1;
        }
index 8fbf22c..abdcfa1 100644 (file)
@@ -212,7 +212,7 @@ int afb_websock_check_upgrade(struct afb_hreq *hreq)
                return 0;
 
        ws = NULL;
-       rc = check_websocket_upgrade(hreq->connection, protodefs, &hreq->context, &ws);
+       rc = check_websocket_upgrade(hreq->connection, protodefs, &hreq->xreq.context, &ws);
        if (rc == 1) {
                hreq->replied = 1;
                if (ws != NULL)
diff --git a/src/afb-xreq.c b/src/afb-xreq.c
new file mode 100644 (file)
index 0000000..5bd2068
--- /dev/null
@@ -0,0 +1,319 @@
+/*
+ * Copyright (C) 2017 "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
+#define NO_BINDING_VERBOSE_MACRO
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <json-c/json.h>
+#include <afb/afb-binding.h>
+
+#include "afb-context.h"
+#include "afb-xreq.h"
+#include "afb-evt.h"
+#include "afb-msg-json.h"
+#include "afb-subcall.h"
+#include "jobs.h"
+#include "verbose.h"
+
+
+static struct json_object *xreq_json_cb(void *closure);
+static struct afb_arg xreq_get_cb(void *closure, const char *name);
+
+static void xreq_success_cb(void *closure, struct json_object *obj, const char *info);
+static void xreq_fail_cb(void *closure, const char *status, const char *info);
+
+static const char *xreq_raw_cb(void *closure, size_t *size);
+static void xreq_send_cb(void *closure, const char *buffer, size_t size);
+
+static void *xreq_context_get_cb(void *closure);
+static void xreq_context_set_cb(void *closure, void *value, void (*free_value)(void*));
+
+static void xreq_addref_cb(void *closure);
+static void xreq_unref_cb(void *closure);
+
+static void xreq_session_close_cb(void *closure);
+static int xreq_session_set_LOA_cb(void *closure, unsigned level);
+
+static int xreq_subscribe_cb(void *closure, struct afb_event event);
+static int xreq_unsubscribe_cb(void *closure, struct afb_event event);
+
+static void xreq_subcall_cb(
+               void *closure,
+               const char *api,
+               const char *verb,
+               struct json_object *args,
+               void (*callback)(void*, int, struct json_object*),
+               void *cb_closure);
+
+const struct afb_req_itf xreq_itf = {
+       .json = xreq_json_cb,
+       .get = xreq_get_cb,
+       .success = xreq_success_cb,
+       .fail = xreq_fail_cb,
+       .raw = xreq_raw_cb,
+       .send = xreq_send_cb,
+       .context_get = xreq_context_get_cb,
+       .context_set = xreq_context_set_cb,
+       .addref = xreq_addref_cb,
+       .unref = xreq_unref_cb,
+       .session_close = xreq_session_close_cb,
+       .session_set_LOA = xreq_session_set_LOA_cb,
+       .subscribe = xreq_subscribe_cb,
+       .unsubscribe = xreq_unsubscribe_cb,
+       .subcall = xreq_subcall_cb
+};
+
+
+static struct json_object *xreq_json_cb(void *closure)
+{
+       struct afb_xreq *xreq = closure;
+       return xreq->queryitf->json(xreq->query);
+}
+
+static struct afb_arg xreq_get_cb(void *closure, const char *name)
+{
+       struct afb_xreq *xreq = closure;
+       if (xreq->queryitf->get)
+               return xreq->queryitf->get(xreq->query, name);
+       else
+               return afb_msg_json_get_arg(xreq_json_cb(closure), name);
+}
+
+static void xreq_success_cb(void *closure, struct json_object *obj, const char *info)
+{
+       struct afb_xreq *xreq = closure;
+       if (xreq->replied) {
+               ERROR("reply called more than one time!!");
+               json_object_put(obj);
+       } else {
+               xreq->replied = 1;
+               if (xreq->queryitf->success)
+                       xreq->queryitf->success(xreq->query, obj, info);
+               else
+                       xreq->queryitf->reply(xreq->query, afb_msg_json_reply_ok(info, obj, &xreq->context, NULL));
+       }
+}
+
+static void xreq_fail_cb(void *closure, const char *status, const char *info)
+{
+       struct afb_xreq *xreq = closure;
+       if (xreq->replied) {
+               ERROR("reply called more than one time!!");
+       } else {
+               xreq->replied = 1;
+               if (xreq->queryitf->fail)
+                       xreq->queryitf->fail(xreq->query, status, info);
+               else
+                       xreq->queryitf->reply(xreq->query, afb_msg_json_reply_error(status, info, &xreq->context, NULL));
+       }
+}
+
+static const char *xreq_raw_cb(void *closure, size_t *size)
+{
+       struct afb_xreq *xreq = closure;
+       const char *result = json_object_to_json_string(xreq->queryitf->json(xreq->query));
+       if (size != NULL)
+               *size = strlen(result);
+       return result;
+}
+
+static void xreq_send_cb(void *closure, const char *buffer, size_t size)
+{
+       struct json_object *obj = json_tokener_parse(buffer);
+       if (!obj == !buffer)
+               xreq_success_cb(closure, obj, "fake send");
+       else
+               xreq_fail_cb(closure, "fake-send-failed", "fake send");
+}
+
+static void *xreq_context_get_cb(void *closure)
+{
+       struct afb_xreq *xreq = closure;
+       return afb_context_get(&xreq->context);
+}
+
+static void xreq_context_set_cb(void *closure, void *value, void (*free_value)(void*))
+{
+       struct afb_xreq *xreq = closure;
+       afb_context_set(&xreq->context, value, free_value);
+}
+
+static void xreq_addref_cb(void *closure)
+{
+       struct afb_xreq *xreq = closure;
+       afb_xreq_addref(xreq);
+}
+
+void afb_xreq_addref(struct afb_xreq *xreq)
+{
+       xreq->refcount++;
+}
+
+static void xreq_unref_cb(void *closure)
+{
+       struct afb_xreq *xreq = closure;
+       afb_xreq_unref(xreq);
+}
+
+void afb_xreq_unref(struct afb_xreq *xreq)
+{
+       if (!--xreq->refcount) {
+               xreq->queryitf->unref(xreq->query);
+       }
+}
+
+static void xreq_session_close_cb(void *closure)
+{
+       struct afb_xreq *xreq = closure;
+       afb_context_close(&xreq->context);
+}
+
+static int xreq_session_set_LOA_cb(void *closure, unsigned level)
+{
+       struct afb_xreq *xreq = closure;
+       return afb_context_change_loa(&xreq->context, level);
+}
+
+static int xreq_subscribe_cb(void *closure, struct afb_event event)
+{
+       struct afb_xreq *xreq = closure;
+       if (xreq->listener)
+               return afb_evt_add_watch(xreq->listener, event);
+       ERROR("no event listener, subscription impossible");
+       errno = EINVAL;
+       return -1;
+}
+
+static int xreq_unsubscribe_cb(void *closure, struct afb_event event)
+{
+       struct afb_xreq *xreq = closure;
+       if (xreq->listener)
+               return afb_evt_remove_watch(xreq->listener, event);
+       ERROR("no event listener, unsubscription impossible");
+       errno = EINVAL;
+       return -1;
+}
+
+static void xreq_subcall_cb(void *closure, const char *api, const char *verb, struct json_object *args, void (*callback)(void*, int, struct json_object*), void *cb_closure)
+{
+       struct afb_xreq *xreq = closure;
+       afb_subcall(&xreq->context, api, verb, args, callback, cb_closure, (struct afb_req){ .itf = &xreq_itf, .closure = xreq });
+}
+
+void afb_xreq_success_f(struct afb_xreq *xreq, struct json_object *obj, const char *info, ...)
+{
+       char *message;
+       va_list args;
+       va_start(args, info);
+       if (info == NULL || vasprintf(&message, info, args) < 0)
+               message = NULL;
+       va_end(args);
+       xreq_success_cb(xreq, obj, message);
+       free(message);
+}
+
+void afb_xreq_fail_f(struct afb_xreq *xreq, const char *status, const char *info, ...)
+{
+       char *message;
+       va_list args;
+       va_start(args, info);
+       if (info == NULL || vasprintf(&message, info, args) < 0)
+               message = NULL;
+       va_end(args);
+       xreq_fail_cb(xreq, status, message);
+       free(message);
+}
+
+static int xcheck(struct afb_xreq *xreq)
+{
+       int stag = xreq->sessionflags;
+
+       if ((stag & (AFB_SESSION_CREATE|AFB_SESSION_CLOSE|AFB_SESSION_RENEW|AFB_SESSION_CHECK|AFB_SESSION_LOA_EQ)) != 0) {
+               if (!afb_context_check(&xreq->context)) {
+                       afb_context_close(&xreq->context);
+                       afb_xreq_fail_f(xreq, "failed", "invalid token's identity");
+                       return 0;
+               }
+       }
+
+       if ((stag & AFB_SESSION_CREATE) != 0) {
+               if (afb_context_check_loa(&xreq->context, 1)) {
+                       afb_xreq_fail_f(xreq, "failed", "invalid creation state");
+                       return 0;
+               }
+               afb_context_change_loa(&xreq->context, 1);
+               afb_context_refresh(&xreq->context);
+       }
+
+       if ((stag & (AFB_SESSION_CREATE | AFB_SESSION_RENEW)) != 0)
+               afb_context_refresh(&xreq->context);
+
+       if ((stag & AFB_SESSION_CLOSE) != 0) {
+               afb_context_change_loa(&xreq->context, 0);
+               afb_context_close(&xreq->context);
+       }
+
+       if ((stag & AFB_SESSION_LOA_GE) != 0) {
+               int loa = (stag >> AFB_SESSION_LOA_SHIFT) & AFB_SESSION_LOA_MASK;
+               if (!afb_context_check_loa(&xreq->context, loa)) {
+                       afb_xreq_fail_f(xreq, "failed", "invalid LOA");
+                       return 0;
+               }
+       }
+
+       if ((stag & AFB_SESSION_LOA_LE) != 0) {
+               int loa = (stag >> AFB_SESSION_LOA_SHIFT) & AFB_SESSION_LOA_MASK;
+               if (afb_context_check_loa(&xreq->context, loa + 1)) {
+                       afb_xreq_fail_f(xreq, "failed", "invalid LOA");
+                       return 0;
+               }
+       }
+       return 1;
+}
+
+static void xreq_run_cb(int signum, void *arg)
+{
+       struct afb_xreq *xreq = arg;
+
+       if (signum == 0)
+               xreq->callback((struct afb_req){ .itf = &xreq_itf, .closure = xreq });
+       else {
+               afb_xreq_fail_f(xreq, "aborted", "signal %s(%d) caught", strsignal(signum), signum);
+               
+       }
+       afb_xreq_unref(xreq);
+}
+
+void afb_xreq_call(struct afb_xreq *xreq)
+{
+       int rc;
+       if (xcheck(xreq)) {
+               afb_xreq_addref(xreq);
+               rc = jobs_queue(xreq->group, xreq->timeout, xreq_run_cb, xreq);
+               if (rc < 0) {
+                       /* TODO: allows or not to proccess it directly as when no threading? (see above) */
+                       ERROR("can't process job with threads: %m");
+                       afb_xreq_fail_f(xreq, "cancelled", "not able to pipe a job for the task");
+                       xreq_unref_cb(xreq);
+               }
+       }
+}
+
diff --git a/src/afb-xreq.h b/src/afb-xreq.h
new file mode 100644 (file)
index 0000000..42824d7
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2017 "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.
+ */
+
+#pragma once
+
+
+#define NO_BINDING_VERBOSE_MACRO
+#include <afb/afb-binding.h>
+#include "afb-context.h"
+#include "afb-evt.h"
+
+struct json_object;
+struct afb_evt_listener;
+
+struct afb_xreq_query_itf {
+       struct json_object *(*json)(void *closure);
+       struct afb_arg (*get)(void *closure, const char *name);
+       void (*success)(void *closure, struct json_object *obj, const char *info);
+       void (*fail)(void *closure, const char *status, const char *info);
+       void (*reply)(void *closure, struct json_object *obj);
+       void (*unref)(void *closure);
+       int (*subscribe)(void *closure, struct afb_event event);
+       int (*unsubscribe)(void *closure, struct afb_event event);
+       void (*subcall)(void *closure, const char *api, const char *verb, struct json_object *args, void (*callback)(void*, int, struct json_object*), void *cb_closure);
+};
+
+
+/**
+ * Internal data for requests
+ */
+struct afb_xreq
+{
+       struct afb_context context; /**< context of the request */
+       char *api;      /**< the requested API */
+       char *verb;     /**< the requested VERB */
+       void *query;    /**< closure for the query */
+       const struct afb_xreq_query_itf *queryitf;
+       int refcount;   /**< current ref count */
+       int replied;    /**< is replied? */
+       int timeout;    /**< timeout */
+       int sessionflags; /**< flags to check */
+       void *group;
+       void (*callback)(struct afb_req req);
+       struct afb_evt_listener *listener;
+};
+
+extern void afb_xreq_addref(struct afb_xreq *xreq);
+extern void afb_xreq_unref(struct afb_xreq *xreq);
+extern void afb_xreq_fail_f(struct afb_xreq *xreq, const char *status, const char *info, ...);
+extern void afb_xreq_success_f(struct afb_xreq *xreq, struct json_object *obj, const char *info, ...);
+extern void afb_xreq_call(struct afb_xreq *xreq);
+
index f8e0f54..7d197b2 100644 (file)
@@ -12,3 +12,4 @@
      <li><a href="api/hello/pingevent?toto&tata&titi=u">ping event</a>
      <li><a href="api/hello/none">not a verb</a>
      <li><a href="api/none/none">not an api</a>
+     <li><a href="api/hello/exit">exit the binder</a>