From 6797f9722dd3e5463e0f7c118397955bb59a40c7 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Jos=C3=A9=20Bollo?= Date: Fri, 31 Mar 2017 16:11:07 +0200 Subject: [PATCH] Factorize common code for handling requests MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit 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 --- bindings/samples/HelloWorld.c | 6 + bindings/samples/export.map | 2 +- src/CMakeLists.txt | 1 + src/afb-api-so-v1.c | 35 ++++- src/afb-api-so-v2.c | 41 +++++- src/afb-apis.c | 98 ++++++++++--- src/afb-apis.h | 3 + src/afb-evt.h | 2 +- src/afb-hreq.c | 111 ++++++--------- src/afb-hreq.h | 17 +-- src/afb-hsrv.c | 12 +- src/afb-hswitch.c | 4 +- src/afb-websock.c | 2 +- src/afb-xreq.c | 319 ++++++++++++++++++++++++++++++++++++++++++ src/afb-xreq.h | 66 +++++++++ test/hello-world.html | 1 + 16 files changed, 598 insertions(+), 122 deletions(-) create mode 100644 src/afb-xreq.c create mode 100644 src/afb-xreq.h 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 +/** + * 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 @@ -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 + * + * 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 +#include +#include + +#include +#include + +#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 + * + * 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 +#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 @@
  • ping event
  • not a verb
  • not an api +
  • exit the binder -- 2.16.6