From: José Bollo Date: Thu, 7 Apr 2016 15:47:01 +0000 (+0200) Subject: split api handling X-Git-Tag: blowfish_2.0.1~202 X-Git-Url: https://gerrit.automotivelinux.org/gerrit/gitweb?a=commitdiff_plain;h=83ce3b29a598141f8f28afde655beb07ec78c312;p=src%2Fapp-framework-binder.git split api handling Change-Id: If0a8fd7e74dacdf9defb3cc9da4a6ae79c34a190 Signed-off-by: José Bollo --- diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 3f8b84c6..7e25276c 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -3,7 +3,9 @@ ADD_LIBRARY(src OBJECT main.c session.c afb-hsrv.c + afb-hswitch.c afb-apis.c + afb-api-so.c afb-method.c afb-hreq.c afb-websock.c diff --git a/src/afb-api-so.c b/src/afb-api-so.c new file mode 100644 index 00000000..32d097fa --- /dev/null +++ b/src/afb-api-so.c @@ -0,0 +1,360 @@ +/* + * Copyright (C) 2016 "IoT.bzh" + * Author "Fulup Ar Foll" + * Author José Bollo + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * Contain all generic part to handle REST/API + * + * https://www.gnu.org/software/libmicrohttpd/tutorial.html [search 'largepost.c'] + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "afb-plugin.h" +#include "afb-req-itf.h" +#include "afb-poll-itf.h" + +#include "session.h" +#include "afb-apis.h" +#include "afb-api-so.h" +#include "verbose.h" +#include "utils-upoll.h" + +struct api_so_desc { + struct AFB_plugin *plugin; /* descriptor */ + void *handle; /* context of dlopen */ + struct AFB_interface interface; +}; + +static int api_timeout = 15; + +static const char plugin_register_function[] = "pluginRegister"; + +static const struct afb_poll_itf upoll_itf = { + .on_readable = (void*)upoll_on_readable, + .on_writable = (void*)upoll_on_writable, + .on_hangup = (void*)upoll_on_hangup, + .close = (void*)upoll_close +}; + +static struct afb_poll itf_poll_open(int fd, void *closure) +{ + struct afb_poll result; + result.data = upoll_open(fd, closure); + result.itf = result.data ? &upoll_itf : NULL; + return result; +} + +static void free_context(struct api_so_desc *desc, void *context) +{ + void (*cb)(void*); + + cb = desc->plugin->freeCtxCB; + if (cb) + cb(context); + else + free(context); +} + + +// Check of apiurl is declare in this plugin and call it +extern __thread sigjmp_buf *error_handler; +static void trapping_call(struct afb_req req, void(*cb)(struct afb_req)) +{ + volatile int signum, timerset; + timer_t timerid; + sigjmp_buf jmpbuf, *older; + struct sigevent sevp; + struct itimerspec its; + + // save context before calling the API + timerset = 0; + older = error_handler; + signum = setjmp(jmpbuf); + if (signum != 0) { + afb_req_fail_f(req, "aborted", "signal %d caught", signum); + } + else { + error_handler = &jmpbuf; + if (api_timeout > 0) { + timerset = 1; /* TODO: check statuses */ + sevp.sigev_notify = SIGEV_THREAD_ID; + sevp.sigev_signo = SIGALRM; + sevp.sigev_value.sival_ptr = NULL; +#if defined(sigev_notify_thread_id) + sevp.sigev_notify_thread_id = (pid_t)syscall(SYS_gettid); +#else + sevp._sigev_un._tid = (pid_t)syscall(SYS_gettid); +#endif + timer_create(CLOCK_THREAD_CPUTIME_ID, &sevp, &timerid); + its.it_interval.tv_sec = 0; + its.it_interval.tv_nsec = 0; + its.it_value.tv_sec = api_timeout; + its.it_value.tv_nsec = 0; + timer_settime(timerid, 0, &its, NULL); + } + + cb(req); + } + if (timerset) + timer_delete(timerid); + error_handler = older; +} + +static void call_check(struct afb_req req, const struct AFB_restapi *verb) +{ + switch(verb->session) { + case AFB_SESSION_CREATE: + if (!afb_req_session_create(req)) + return; + break; + case AFB_SESSION_RENEW: + if (!afb_req_session_check(req, 1)) + return; + break; + case AFB_SESSION_CLOSE: + case AFB_SESSION_CHECK: + if (!afb_req_session_check(req, 0)) + return; + break; + case AFB_SESSION_NONE: + default: + break; + } + trapping_call(req, verb->callback); + + if (verb->session == AFB_SESSION_CLOSE) + afb_req_session_close(req); +} + +static void call(struct api_so_desc *desc, struct afb_req req, const char *verb, size_t lenverb) +{ + const struct AFB_restapi *v; + + v = desc->plugin->apis; + while (v->name && (strncasecmp(v->name, verb, lenverb) || v->name[lenverb])) + v++; + if (v->name) + call_check(req, v); + else + afb_req_fail_f(req, "unknown-verb", "verb %.*s unknown within api %s", (int)lenverb, verb, desc->plugin->prefix); +} + + + +int afb_api_so_add_plugin(const char *path) +{ + struct api_so_desc *desc; + struct AFB_plugin *(*pluginRegisterFct) (const struct AFB_interface *interface); + + desc = calloc(1, sizeof *desc); + if (desc == NULL) { + fprintf(stderr, "[%s] out of memory\n", path); + goto error; + } + + // This is a loadable library let's check if it's a plugin + desc->handle = dlopen(path, RTLD_NOW | RTLD_LOCAL); + if (desc->handle == NULL) { + fprintf(stderr, "[%s] not loadable, continuing...\n", path); + goto error2; + } + + /* retrieves the register function */ + pluginRegisterFct = dlsym(desc->handle, plugin_register_function); + if (!pluginRegisterFct) { + fprintf(stderr, "[%s] not an AFB plugin, continuing...\n", path); + goto error3; + } + if (verbosity) + fprintf(stderr, "[%s] is a valid AFB plugin\n", path); + + /* init the interface */ + desc->interface.verbosity = 0; + desc->interface.mode = AFB_MODE_LOCAL; + desc->interface.poll_open = itf_poll_open; + + /* init the plugin */ + desc->plugin = pluginRegisterFct(&desc->interface); + if (desc->plugin == NULL) { + fprintf(stderr, "ERROR: plugin [%s] register function failed. continuing...\n", path); + goto error3; + } + + /* check the returned structure */ + if (desc->plugin->type != AFB_PLUGIN_JSON) { + fprintf(stderr, "ERROR: plugin [%s] invalid type %d...\n", path, desc->plugin->type); + goto error3; + } + if (desc->plugin->prefix == NULL || *desc->plugin->prefix == 0) { + fprintf(stderr, "ERROR: plugin [%s] bad prefix...\n", path); + goto error3; + } + if (desc->plugin->info == NULL || *desc->plugin->info == 0) { + fprintf(stderr, "ERROR: plugin [%s] bad description...\n", path); + goto error3; + } + if (desc->plugin->apis == NULL) { + fprintf(stderr, "ERROR: plugin [%s] no APIs...\n", path); + goto error3; + } + + /* records the plugin */ + if (afb_apis_add(desc->plugin->prefix, (struct afb_api){ + .closure = desc, + .call = (void*)call, + .free_context = (void*)free_context}) < 0) { + fprintf(stderr, "ERROR: plugin [%s] can't be registered...\n", path); + goto error3; + } + + return 0; + +error3: + dlclose(desc->handle); +error2: + free(desc); +error: + return -1; +} + +static int adddirs(char path[PATH_MAX], size_t end) +{ + DIR *dir; + struct dirent ent, *result; + size_t len; + + /* open the DIR now */ + dir = opendir(path); + if (dir == NULL) { + fprintf(stderr, "ERROR in scanning plugin directory %s, %m\n", path); + return -1; + } + if (verbosity) + fprintf(stderr, "Scanning dir=[%s] for plugins\n", path); + + /* scan each entry */ + if (end) + path[end++] = '/'; + for (;;) { + readdir_r(dir, &ent, &result); + if (result == NULL) + break; + + len = strlen(ent.d_name); + if (len + end >= PATH_MAX) { + fprintf(stderr, "path too long for %s\n", ent.d_name); + continue; + } + memcpy(&path[end], ent.d_name, len+1); + if (ent.d_type == DT_DIR) { + /* case of directories */ + if (ent.d_name[0] == '.') { + if (len == 1) + continue; + if (ent.d_name[1] == '.' && len == 2) + continue; + } + adddirs(path, end+len);; + } else if (ent.d_type == DT_REG) { + /* case of files */ + if (!strstr(ent.d_name, ".so")) + continue; + afb_api_so_add_plugin(path); + } + } + closedir(dir); + return 0; +} + +int afb_api_so_add_directory(const char *path) +{ + size_t length; + char buffer[PATH_MAX]; + + length = strlen(path); + if (length >= sizeof(buffer)) { + fprintf(stderr, "path too long %lu [%.99s...]\n", (unsigned long)length, path); + return -1; + } + + memcpy(buffer, path, length + 1); + return adddirs(buffer, length); +} + +int afb_api_so_add_path(const char *path) +{ + struct stat st; + int rc; + + rc = stat(path, &st); + if (rc < 0) + fprintf(stderr, "Invalid plugin path [%s]: %m\n", path); + else if (S_ISDIR(st.st_mode)) + rc = afb_api_so_add_directory(path); + else + rc = afb_api_so_add_plugin(path); + return rc; +} + +int afb_api_so_add_pathset(const char *pathset) +{ + static char sep[] = ":"; + char *ps, *p; + + ps = strdupa(pathset); + for (;;) { + p = strsep(&ps, sep); + if (!p) + return 0; + afb_api_so_add_path(p); + }; +} + + + + + + + + + + + + + + + + + + + + + diff --git a/src/afb-api-so.h b/src/afb-api-so.h new file mode 100644 index 00000000..9c2a570b --- /dev/null +++ b/src/afb-api-so.h @@ -0,0 +1,27 @@ +/* + * Copyright 2016 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. + */ + + +extern int afb_api_so_add_plugin(const char *path); + +extern int afb_api_so_add_directory(const char *path); + +extern int afb_api_so_add_path(const char *path); + +extern int afb_api_so_add_pathset(const char *pathset); + + diff --git a/src/afb-apis.c b/src/afb-apis.c index 7d0feb39..561b2752 100644 --- a/src/afb-apis.c +++ b/src/afb-apis.c @@ -47,27 +47,14 @@ #include "utils-upoll.h" struct api_desc { - struct AFB_plugin *plugin; /* descriptor */ - size_t prefixlen; - const char *prefix; - void *handle; /* context of dlopen */ - struct AFB_interface *interface; + struct afb_api api; + const char *name; + size_t namelen; }; -static int api_timeout = 15; static struct api_desc *apis_array = NULL; static int apis_count = 0; -static const char plugin_register_function[] = "pluginRegister"; - -static const struct afb_poll_itf upoll_itf = { - .on_readable = (void*)upoll_on_readable, - .on_writable = (void*)upoll_on_writable, - .on_hangup = (void*)upoll_on_hangup, - .close = (void*)upoll_close -}; - - int afb_apis_count() { return apis_count; @@ -75,308 +62,66 @@ int afb_apis_count() void afb_apis_free_context(int apiidx, void *context) { - void (*cb)(void*); - - assert(0 <= apiidx && apiidx < apis_count); - cb = apis_array[apiidx].plugin->freeCtxCB; - if (cb) - cb(context); - else - free(context); -} - -static struct afb_poll itf_poll_open(int fd, void *closure) -{ - struct afb_poll result; - result.data = upoll_open(fd, closure); - result.itf = result.data ? &upoll_itf : NULL; - return result; + const struct afb_api *api; + api = &apis_array[apiidx].api; + api->free_context(api->closure, context); } - -int afb_apis_add_plugin(const char *path) +int afb_apis_add(const char *name, struct afb_api api) { struct api_desc *apis; - struct AFB_plugin *plugin; - struct AFB_plugin *(*pluginRegisterFct) (const struct AFB_interface *interface); - struct AFB_interface *interface; - void *handle; + size_t len; int i; - // This is a loadable library let's check if it's a plugin - handle = dlopen(path, RTLD_NOW | RTLD_LOCAL); - if (handle == NULL) { - fprintf(stderr, "[%s] not loadable, continuing...\n", path); + /* check existing or not */ + len = strlen(name); + if (len == 0) { + fprintf(stderr, "empty api name forbidden\n"); goto error; } - /* retrieves the register function */ - pluginRegisterFct = dlsym(handle, plugin_register_function); - if (!pluginRegisterFct) { - fprintf(stderr, "[%s] not an AFB plugin, continuing...\n", path); - goto error2; + /* check previously existing plugin */ + for (i = 0 ; i < apis_count ; i++) { + if (!strcasecmp(apis_array[i].name, name)) { + fprintf(stderr, "ERROR: api of name %s already exists\n", name); + goto error; + } } - if (verbosity) - fprintf(stderr, "[%s] is a valid AFB plugin\n", path); /* allocates enough memory */ apis = realloc(apis_array, ((unsigned)apis_count + 1) * sizeof * apis); if (apis == NULL) { - fprintf(stderr, "ERROR: plugin [%s] memory missing. continuing...\n", path); - goto error2; + fprintf(stderr, "out of memory\n"); + goto error; } apis_array = apis; - /* allocates the interface */ - interface = calloc(1, sizeof *interface); - if (interface == NULL) { - fprintf(stderr, "ERROR: plugin [%s] memory missing. continuing...\n", path); - goto error2; - } - interface->verbosity = 0; - interface->mode = AFB_MODE_LOCAL; - interface->poll_open = itf_poll_open; - - /* init the plugin */ - plugin = pluginRegisterFct(interface); - if (plugin == NULL) { - fprintf(stderr, "ERROR: plugin [%s] register function failed. continuing...\n", path); - goto error3; - } - - /* check the returned structure */ - if (plugin->type != AFB_PLUGIN_JSON) { - fprintf(stderr, "ERROR: plugin [%s] invalid type %d...\n", path, plugin->type); - goto error3; - } - if (plugin->prefix == NULL || *plugin->prefix == 0) { - fprintf(stderr, "ERROR: plugin [%s] bad prefix...\n", path); - goto error3; - } - if (plugin->info == NULL || *plugin->info == 0) { - fprintf(stderr, "ERROR: plugin [%s] bad description...\n", path); - goto error3; - } - if (plugin->apis == NULL) { - fprintf(stderr, "ERROR: plugin [%s] no APIs...\n", path); - goto error3; - } - - /* check previously existing plugin */ - for (i = 0 ; i < apis_count ; i++) { - if (!strcasecmp(apis_array[i].prefix, plugin->prefix)) { - fprintf(stderr, "ERROR: plugin [%s] prefix %s duplicated...\n", path, plugin->prefix); - goto error2; - } - } - /* record the plugin */ - if (verbosity) - fprintf(stderr, "Loading plugin[%lu] prefix=[%s] info=%s\n", (unsigned long)apis_count, plugin->prefix, plugin->info); apis = &apis_array[apis_count]; - apis->plugin = plugin; - apis->prefixlen = strlen(plugin->prefix); - apis->prefix = plugin->prefix; - apis->handle = handle; - apis->interface = interface; + apis->api = api; + apis->namelen = len; + apis->name = name; apis_count++; return 0; -error3: - free(interface); -error2: - dlclose(handle); error: return -1; } -static int adddirs(char path[PATH_MAX], size_t end) +void afb_apis_call(struct afb_req req, struct AFB_clientCtx *context, const char *api, size_t lenapi, const char *verb, size_t lenverb) { - int rc; - DIR *dir; - struct dirent ent, *result; - size_t len; - - /* open the DIR now */ - dir = opendir(path); - if (dir == NULL) { - fprintf(stderr, "ERROR in scanning plugin directory %s, %m\n", path); - return -1; - } - if (verbosity) - fprintf(stderr, "Scanning dir=[%s] for plugins\n", path); - - /* scan each entry */ - if (end) - path[end++] = '/'; - for (;;) { - readdir_r(dir, &ent, &result); - if (result == NULL) - break; - - len = strlen(ent.d_name); - if (len + end >= PATH_MAX) { - fprintf(stderr, "path too long for %s\n", ent.d_name); - continue; - } - memcpy(&path[end], ent.d_name, len+1); - if (ent.d_type == DT_DIR) { - /* case of directories */ - if (ent.d_name[0] == '.') { - if (len == 1) - continue; - if (ent.d_name[1] == '.' && len == 2) - continue; - } - rc = adddirs(path, end+len);; - } else if (ent.d_type == DT_REG) { - /* case of files */ - if (!strstr(ent.d_name, ".so")) - continue; - rc = afb_apis_add_plugin(path); - } - } - closedir(dir); - return 0; -} - -int afb_apis_add_directory(const char *path) -{ - size_t length; - char buffer[PATH_MAX]; - - length = strlen(path); - if (length >= sizeof(buffer)) { - fprintf(stderr, "path too long %lu [%.99s...]\n", (unsigned long)length, path); - return -1; - } - - memcpy(buffer, path, length + 1); - return adddirs(buffer, length); -} - -int afb_apis_add_path(const char *path) -{ - struct stat st; - int rc; - - rc = stat(path, &st); - if (rc < 0) - fprintf(stderr, "Invalid plugin path [%s]: %m\n", path); - else if (S_ISDIR(st.st_mode)) - rc = afb_apis_add_directory(path); - else - rc = afb_apis_add_plugin(path); - return rc; -} - -int afb_apis_add_pathset(const char *pathset) -{ - static char sep[] = ":"; - char *ps, *p; - int rc; - - ps = strdupa(pathset); - for (;;) { - p = strsep(&ps, sep); - if (!p) - return 0; - rc = afb_apis_add_path(p); - }; -} - -// Check of apiurl is declare in this plugin and call it -extern __thread sigjmp_buf *error_handler; -static void trapping_handle(struct afb_req req, void(*cb)(struct afb_req)) -{ - volatile int signum, timerset; - timer_t timerid; - sigjmp_buf jmpbuf, *older; - struct sigevent sevp; - struct itimerspec its; - - // save context before calling the API - timerset = 0; - older = error_handler; - signum = setjmp(jmpbuf); - if (signum != 0) { - afb_req_fail_f(req, "aborted", "signal %d caught", signum); - } - else { - error_handler = &jmpbuf; - if (api_timeout > 0) { - timerset = 1; /* TODO: check statuses */ - sevp.sigev_notify = SIGEV_THREAD_ID; - sevp.sigev_signo = SIGALRM; - sevp.sigev_value.sival_ptr = NULL; -#if defined(sigev_notify_thread_id) - sevp.sigev_notify_thread_id = (pid_t)syscall(SYS_gettid); -#else - sevp._sigev_un._tid = (pid_t)syscall(SYS_gettid); -#endif - timer_create(CLOCK_THREAD_CPUTIME_ID, &sevp, &timerid); - its.it_interval.tv_sec = 0; - its.it_interval.tv_nsec = 0; - its.it_value.tv_sec = api_timeout; - its.it_value.tv_nsec = 0; - timer_settime(timerid, 0, &its, NULL); - } - - cb(req); - } - if (timerset) - timer_delete(timerid); - error_handler = older; -} - -static void handle(struct afb_req req, const struct AFB_restapi *verb) -{ - switch(verb->session) { - case AFB_SESSION_CREATE: - if (!afb_req_session_create(req)) - return; - break; - case AFB_SESSION_RENEW: - if (!afb_req_session_check(req, 1)) - return; - break; - case AFB_SESSION_CLOSE: - case AFB_SESSION_CHECK: - if (!afb_req_session_check(req, 0)) - return; - break; - case AFB_SESSION_NONE: - default: - break; - } - trapping_handle(req, verb->callback); - - if (verb->session == AFB_SESSION_CLOSE) - afb_req_session_close(req); -} - -int afb_apis_handle(struct afb_req req, struct AFB_clientCtx *context, const char *api, size_t lenapi, const char *verb, size_t lenverb) -{ - int i, j; + int i; const struct api_desc *a; - const struct AFB_restapi *v; a = apis_array; for (i = 0 ; i < apis_count ; i++, a++) { - if (a->prefixlen == lenapi && !strncasecmp(a->prefix, api, lenapi)) { - v = a->plugin->apis; - for (j = 0 ; v->name ; j++, v++) { - if (!strncasecmp(v->name, verb, lenverb) && !v->name[lenverb]) { - req.context = &context->contexts[i]; - handle(req, v); - return 1; - } - } - afb_req_fail_f(req, "unknown-verb", "verb %.*s unknown within api %s", (int)lenverb, verb, a->prefix); - return 1; + if (a->namelen == lenapi && !strncasecmp(a->name, api, lenapi)) { + req.context = &context->contexts[i]; + a->api.call(a->api.closure, req, verb, lenverb); + return; } } - return 0; + afb_req_fail(req, "fail", "api not found"); } diff --git a/src/afb-apis.h b/src/afb-apis.h index 9127e6d8..97778b64 100644 --- a/src/afb-apis.h +++ b/src/afb-apis.h @@ -15,20 +15,20 @@ * limitations under the License. */ +struct afb_req; +struct AFB_clientCtx; -extern int afb_apis_count(); - -extern void afb_apis_free_context(int apiidx, void *context); - -extern int afb_apis_add_plugin(const char *path); - -extern int afb_apis_add_directory(const char *path); +struct afb_api +{ + void *closure; + void (*call)(void *closure, struct afb_req req, const char *verb, size_t lenverb); + void (*free_context)(void *closure, void *context); +}; -extern int afb_apis_add_path(const char *path); -extern int afb_apis_add_pathset(const char *pathset); +extern int afb_apis_count(); +extern int afb_apis_add(const char *name, struct afb_api api); +extern void afb_apis_call(struct afb_req req, struct AFB_clientCtx *context, const char *api, size_t lenapi, const char *verb, size_t lenverb); -struct afb_req; -struct AFB_clientCtx; -extern int afb_apis_handle(struct afb_req req, struct AFB_clientCtx *context, const char *api, size_t lenapi, const char *verb, size_t lenverb); +extern void afb_apis_free_context(int apiidx, void *context); diff --git a/src/afb-hreq.c b/src/afb-hreq.c index e45b2503..953c80a1 100644 --- a/src/afb-hreq.c +++ b/src/afb-hreq.c @@ -34,8 +34,6 @@ #endif #include "afb-method.h" -#include "afb-websock.h" -#include "afb-apis.h" #include "afb-req-itf.h" #include "afb-hreq.h" #include "session.h" @@ -410,68 +408,6 @@ int afb_hreq_redirect_to(struct afb_hreq *hreq, const char *url) return 1; } -int afb_hreq_one_page_api_redirect( - struct afb_hreq *hreq, - void *data) -{ - size_t plen; - char *url; - - if (hreq->lentail >= 2 && hreq->tail[1] == '#') - return 0; - /* - * Here we have for example: - * url = "/pre/dir/page" lenurl = 13 - * tail = "/dir/page" lentail = 9 - * - * We will produce "/pre/#!dir/page" - * - * Let compute plen that include the / at end (for "/pre/") - */ - plen = hreq->lenurl - hreq->lentail + 1; - url = alloca(hreq->lenurl + 3); - memcpy(url, hreq->url, plen); - url[plen++] = '#'; - url[plen++] = '!'; - memcpy(&url[plen], &hreq->tail[1], hreq->lentail); - return afb_hreq_redirect_to(hreq, url); -} - -int afb_hreq_websocket_switch(struct afb_hreq *hreq, void *data) -{ - int later; - - afb_hreq_context(hreq); - if (hreq->lentail != 0 || !afb_websock_check(hreq, &later)) - return 0; - - if (!later) { - struct afb_websock *ws = afb_websock_create(hreq); - if (ws != NULL) - hreq->upgrade = 1; - } - return 1; -} - -int afb_hreq_rest_api(struct afb_hreq *hreq, void *data) -{ - const char *api, *verb; - size_t lenapi, lenverb; - struct AFB_clientCtx *context; - - api = &hreq->tail[strspn(hreq->tail, "/")]; - lenapi = strcspn(api, "/"); - verb = &api[lenapi]; - verb = &verb[strspn(verb, "/")]; - lenverb = strcspn(verb, "/"); - - if (!(*api && *verb && lenapi && lenverb)) - return 0; - - context = afb_hreq_context(hreq); - return afb_apis_handle(afb_hreq_to_req(hreq), context, api, lenapi, verb, lenverb); -} - const char *afb_hreq_get_cookie(struct afb_hreq *hreq, const char *name) { return MHD_lookup_connection_value(hreq->connection, MHD_COOKIE_KIND, name); diff --git a/src/afb-hswitch.c b/src/afb-hswitch.c new file mode 100644 index 00000000..3fcd77de --- /dev/null +++ b/src/afb-hswitch.c @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2015 "IoT.bzh" + * Author "Fulup Ar Foll" + * 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 + +#include +#include + +#include "afb-req-itf.h" +#include "afb-hreq.h" +#include "afb-apis.h" +#include "session.h" +#include "afb-websock.h" + +int afb_hswitch_apis(struct afb_hreq *hreq, void *data) +{ + const char *api, *verb; + size_t lenapi, lenverb; + struct AFB_clientCtx *context; + + api = &hreq->tail[strspn(hreq->tail, "/")]; + lenapi = strcspn(api, "/"); + verb = &api[lenapi]; + verb = &verb[strspn(verb, "/")]; + lenverb = strcspn(verb, "/"); + + if (!(*api && *verb && lenapi && lenverb)) + return 0; + + context = afb_hreq_context(hreq); + afb_apis_call(afb_hreq_to_req(hreq), context, api, lenapi, verb, lenverb); + return 1; +} + +int afb_hswitch_one_page_api_redirect(struct afb_hreq *hreq, void *data) +{ + size_t plen; + char *url; + + if (hreq->lentail >= 2 && hreq->tail[1] == '#') + return 0; + /* + * Here we have for example: + * url = "/pre/dir/page" lenurl = 13 + * tail = "/dir/page" lentail = 9 + * + * We will produce "/pre/#!dir/page" + * + * Let compute plen that include the / at end (for "/pre/") + */ + plen = hreq->lenurl - hreq->lentail + 1; + url = alloca(hreq->lenurl + 3); + memcpy(url, hreq->url, plen); + url[plen++] = '#'; + url[plen++] = '!'; + memcpy(&url[plen], &hreq->tail[1], hreq->lentail); + return afb_hreq_redirect_to(hreq, url); +} + +int afb_hswitch_websocket_switch(struct afb_hreq *hreq, void *data) +{ + int later; + + afb_hreq_context(hreq); + if (hreq->lentail != 0 || !afb_websock_check(hreq, &later)) + return 0; + + if (!later) { + struct afb_websock *ws = afb_websock_create(hreq); + if (ws != NULL) + hreq->upgrade = 1; + } + return 1; +} + + + diff --git a/src/afb-hswitch.h b/src/afb-hswitch.h new file mode 100644 index 00000000..f18b8b14 --- /dev/null +++ b/src/afb-hswitch.h @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2015 "IoT.bzh" + * Author "Fulup Ar Foll" + * 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. + */ + +struct afb_hreq; +extern int afb_hswitch_apis(struct afb_hreq *hreq, void *data); +extern int afb_hswitch_one_page_api_redirect(struct afb_hreq *hreq, void *data); +extern int afb_hswitch_websocket_switch(struct afb_hreq *hreq, void *data); + + diff --git a/src/afb-websock.c b/src/afb-websock.c index e8f596c7..878df042 100644 --- a/src/afb-websock.c +++ b/src/afb-websock.c @@ -367,9 +367,7 @@ static int aws_handle_json(struct afb_websock *aws, struct json_object *obj) r.data = wsreq; r.itf = &wsreq_itf; - rc = afb_apis_handle(r, aws->context, api, lenapi, verb, lenverb); - if (rc == 0) - wsreq_fail(wsreq, "ail", "api not found"); + afb_apis_call(r, aws->context, api, lenapi, verb, lenverb); return 1; error: @@ -414,11 +412,12 @@ static struct afb_arg wsreq_get(struct afb_wsreq *wsreq, const char *name) if (json_object_object_get_ex(wsreq->request, name, &value)) { arg.name = name; arg.value = json_object_get_string(value); + arg.size = strlen(arg.value); } else { arg.name = NULL; arg.value = NULL; + arg.size = 0; } - arg.size = 0; arg.path = NULL; return arg; } diff --git a/src/local-def.h b/src/local-def.h index 99502058..7055e95a 100644 --- a/src/local-def.h +++ b/src/local-def.h @@ -63,9 +63,6 @@ struct AFB_config AFB_aliasdir *aliasdir; // alias mapping for icons,apps,... }; -struct afb_hsrv; -struct MHD_Daemon; - struct AFB_session { struct AFB_config *config; // pointer to current config diff --git a/src/main.c b/src/main.c index b0261a44..f81bfef8 100644 --- a/src/main.c +++ b/src/main.c @@ -30,9 +30,9 @@ #include "afb-plugin.h" #include "local-def.h" -#include "afb-apis.h" +#include "afb-hswitch.h" +#include "afb-api-so.h" #include "afb-hsrv.h" -#include "afb-hreq.h" #include "session.h" #include "verbose.h" #include "utils-upoll.h" @@ -513,9 +513,9 @@ int main(int argc, char *argv[]) { } if (session->config->ldpaths) - afb_apis_add_pathset(session->config->ldpaths); + afb_api_so_add_pathset(session->config->ldpaths); - ctxStoreInit(CTX_NBCLIENTS, session->config->cntxTimeout, afb_apis_count(), session->config->token); + ctxStoreInit(CTX_NBCLIENTS, session->config->cntxTimeout, session->config->token); install_error_handlers(); @@ -569,10 +569,10 @@ static int init(struct afb_hsrv *hsrv, AFB_config * config) { int idx; - if (!afb_hsrv_add_handler(hsrv, config->rootapi, afb_hreq_websocket_switch, NULL, 20)) + if (!afb_hsrv_add_handler(hsrv, config->rootapi, afb_hswitch_websocket_switch, NULL, 20)) return 0; - if (!afb_hsrv_add_handler(hsrv, config->rootapi, afb_hreq_rest_api, NULL, 10)) + if (!afb_hsrv_add_handler(hsrv, config->rootapi, afb_hswitch_apis, NULL, 10)) return 0; for (idx = 0; config->aliasdir[idx].url != NULL; idx++) @@ -582,7 +582,7 @@ static int init(struct afb_hsrv *hsrv, AFB_config * config) if (!afb_hsrv_add_alias(hsrv, "", config->rootdir, -10)) return 0; - if (!afb_hsrv_add_handler(hsrv, config->rootbase, afb_hreq_one_page_api_redirect, NULL, -20)) + if (!afb_hsrv_add_handler(hsrv, config->rootbase, afb_hswitch_one_page_api_redirect, NULL, -20)) return 0; return 1; diff --git a/src/session.c b/src/session.c index da2294bc..11f535b3 100644 --- a/src/session.c +++ b/src/session.c @@ -63,13 +63,13 @@ static void ctxUuidFreeCB (struct AFB_clientCtx *client) } // Create a new store in RAM, not that is too small it will be automatically extended -void ctxStoreInit (int nbSession, int timeout, int apicount, const char *initok) +void ctxStoreInit (int nbSession, int timeout, const char *initok) { // let's create as store as hashtable does not have any sessions.store = calloc (1 + (unsigned)nbSession, sizeof(struct AFB_clientCtx)); sessions.max = nbSession; sessions.timeout = timeout; - sessions.apicount = apicount; + sessions.apicount = afb_apis_count(); if (strlen(initok) >= 37) { fprintf(stderr, "Error: initial token '%s' too long (max length 36)", initok); exit(1); diff --git a/src/session.h b/src/session.h index 10af74fd..4a1c400b 100644 --- a/src/session.h +++ b/src/session.h @@ -26,7 +26,7 @@ struct AFB_clientCtx char token[37]; // short term authentication of remote client }; -extern void ctxStoreInit (int nbSession, int timeout, int apicount, const char *initok); +extern void ctxStoreInit (int nbSession, int timeout, const char *initok); extern struct AFB_clientCtx *ctxClientGetForUuid (const char *uuid); extern struct AFB_clientCtx *ctxClientGet(struct AFB_clientCtx *clientCtx);