summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--bindings/samples/HelloWorld.c6
-rw-r--r--bindings/samples/export.map2
-rw-r--r--src/CMakeLists.txt1
-rw-r--r--src/afb-api-so-v1.c35
-rw-r--r--src/afb-api-so-v2.c41
-rw-r--r--src/afb-apis.c98
-rw-r--r--src/afb-apis.h3
-rw-r--r--src/afb-evt.h2
-rw-r--r--src/afb-hreq.c111
-rw-r--r--src/afb-hreq.h17
-rw-r--r--src/afb-hsrv.c12
-rw-r--r--src/afb-hswitch.c4
-rw-r--r--src/afb-websock.c2
-rw-r--r--src/afb-xreq.c319
-rw-r--r--src/afb-xreq.h66
-rw-r--r--test/hello-world.html1
16 files changed, 598 insertions, 122 deletions
diff --git a/bindings/samples/HelloWorld.c b/bindings/samples/HelloWorld.c
index 9e14befa..cb4a5a4a 100644
--- a/bindings/samples/HelloWorld.c
+++ b/bindings/samples/HelloWorld.c
@@ -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}
};
diff --git a/bindings/samples/export.map b/bindings/samples/export.map
index 0ef1ac79..ee2f4133 100644
--- a/bindings/samples/export.map
+++ b/bindings/samples/export.map
@@ -1 +1 @@
-{ global: afbBindingV1Register; local: *; };
+{ global: afbBindingV*; local: *; };
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 653cf8c9..919e0d59 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -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
diff --git a/src/afb-api-so-v1.c b/src/afb-api-so-v1.c
index 4fafb138..a09b0f51 100644
--- a/src/afb-api-so-v1.c
+++ b/src/afb-api-so-v1.c
@@ -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;
diff --git a/src/afb-api-so-v2.c b/src/afb-api-so-v2.c
index 0c8079c4..2770f64a 100644
--- a/src/afb-api-so-v2.c
+++ b/src/afb-api-so-v2.c
@@ -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;
diff --git a/src/afb-apis.c b/src/afb-apis.c
index 9ffffdb3..a0af0800 100644
--- a/src/afb-apis.c
+++ b/src/afb-apis.c
@@ -28,11 +28,16 @@
#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);
+ }
+}
+
diff --git a/src/afb-apis.h b/src/afb-apis.h
index 9b060ce8..f211bda8 100644
--- a/src/afb-apis.h
+++ b/src/afb-apis.h
@@ -19,12 +19,14 @@
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);
diff --git a/src/afb-evt.h b/src/afb-evt.h
index 52b40ab1..5b96b93e 100644
--- a/src/afb-evt.h
+++ b/src/afb-evt.h
@@ -19,7 +19,7 @@
struct afb_event;
struct afb_session;
-
+struct json_object;
struct afb_evt_listener;
struct afb_evt_itf
diff --git a/src/afb-hreq.c b/src/afb-hreq.c
index 0fe908cb..8e3230db 100644
--- a/src/afb-hreq.c
+++ b/src/afb-hreq.c
@@ -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;
+}
diff --git a/src/afb-hreq.h b/src/afb-hreq.h
index 5cb86091..47d6aa53 100644
--- a/src/afb-hreq.h
+++ b/src/afb-hreq.h
@@ -17,22 +17,16 @@
#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();
diff --git a/src/afb-hsrv.c b/src/afb-hsrv.c
index 577ef854..f0866f82 100644
--- a/src/afb-hsrv.c
+++ b/src/afb-hsrv.c
@@ -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;
diff --git a/src/afb-hswitch.c b/src/afb-hswitch.c
index 909480af..eab5b9b7 100644
--- a/src/afb-hswitch.c
+++ b/src/afb-hswitch.c
@@ -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;
}
diff --git a/src/afb-websock.c b/src/afb-websock.c
index 8fbf22c2..abdcfa1d 100644
--- a/src/afb-websock.c
+++ b/src/afb-websock.c
@@ -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
index 00000000..5bd2068c
--- /dev/null
+++ b/src/afb-xreq.c
@@ -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
index 00000000..42824d7d
--- /dev/null
+++ b/src/afb-xreq.h
@@ -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);
+
diff --git a/test/hello-world.html b/test/hello-world.html
index f8e0f54a..7d197b2e 100644
--- a/test/hello-world.html
+++ b/test/hello-world.html
@@ -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>