From 06d422d1de5c505366f6a029d8af85548c2b646f Mon Sep 17 00:00:00 2001 From: Fulup Ar Foll Date: Thu, 24 Dec 2015 00:38:41 +0100 Subject: [PATCH] Fixed Client Session Context by Plugin --- include/local-def.h | 2 +- plugins/samples/CMakeLists.txt | 6 ++ plugins/samples/ClientCtx.c | 145 +++++++++++++++++++++++++++++++++++++++++ plugins/samples/HelloWorld.c | 2 +- plugins/samples/SamplePost.c | 2 +- src/rest-api.c | 12 ++-- src/session.c | 4 +- 7 files changed, 162 insertions(+), 11 deletions(-) create mode 100644 plugins/samples/ClientCtx.c diff --git a/include/local-def.h b/include/local-def.h index d278dc47..edaf1bbc 100644 --- a/include/local-def.h +++ b/include/local-def.h @@ -243,12 +243,12 @@ typedef struct { AFB_PostRequest *post; json_object *jresp; void *context; // Hold Client Context when using session + void *handle; // provide callback and easy access to plugin int restfull; // request is resfull [uuid token provided] int errcode; // http error code sigjmp_buf checkPluginCall; // context save for timeout set/longjmp AFB_config *config; // plugin may need access to config struct MHD_Connection *connection; - AFB_plugin *plugin; // provide callback and easy access to plugin AFB_plugin **plugins; } AFB_request; diff --git a/plugins/samples/CMakeLists.txt b/plugins/samples/CMakeLists.txt index aa22cd24..c3c305ca 100644 --- a/plugins/samples/CMakeLists.txt +++ b/plugins/samples/CMakeLists.txt @@ -11,3 +11,9 @@ SET_TARGET_PROPERTIES(samplePost-api PROPERTIES PREFIX "") TARGET_LINK_LIBRARIES(samplePost-api ${link_libraries}) INSTALL(TARGETS samplePost-api LIBRARY DESTINATION ${plugin_install_dir}) + +ADD_LIBRARY(clientCtx-api MODULE ClientCtx.c) +SET_TARGET_PROPERTIES(clientCtx-api PROPERTIES PREFIX "") +TARGET_LINK_LIBRARIES(clientCtx-api ${link_libraries}) +INSTALL(TARGETS clientCtx-api + LIBRARY DESTINATION ${plugin_install_dir}) diff --git a/plugins/samples/ClientCtx.c b/plugins/samples/ClientCtx.c new file mode 100644 index 00000000..b4ac0e13 --- /dev/null +++ b/plugins/samples/ClientCtx.c @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2015 "IoT.bzh" + * Author "Fulup Ar Foll" + * + * 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 . + */ + + +#include "local-def.h" + +typedef struct { + /* + * In case your plugin is implemented on multiple files or used share routines + * with other plugins, it might not be possible to use global static variable. + * In this case you can attach a static handle to your plugin. This handle + * is passed within each API call under request->handle + * + */ + void *anythingYouWant; + + +} MyPluginHandleT; + +typedef struct { + /* + * client context is attached a session but private to a each plugin. + * Context is passed to each API under request->context + * + * Note: + * -client context is free when a session is closed. Developer should not + * forget that even if context is private to each plugin, session is unique + * to a client. When session close, every plugin are notified to free there + * private context. + * -by default standard "free" function from libc is used to free context. + * Developer may define it own under plugin->freeCB. This call received + * FreeCtxCb(void *ClientCtx, void*PluginHandle, char*SessionUUID) if + * FreeCtxCb=(void*)-1 then context wont be free by session manager. + * -when an API use AFB_SESSION_RESET this close the session and each plugin + * will be notified to free ressources. + */ + + int count; + char *abcd; + +} MyClientContextT; + + + +// This function is call at session open time. Any client trying to +// call it with an already open session will be denied. +// Ex: http://localhost:1234/api/context/create?token=123456789 +STATIC json_object* myCreate (AFB_request *request) { + json_object *jresp; + + MyClientContextT *ctx= malloc (sizeof (MyClientContextT)); + MyPluginHandleT *handle = (MyPluginHandleT*) request->handle; + + // store something in our plugin private client context + ctx->count = 0; + ctx->abcd = "SomeThingUseful"; + + request->context = ctx; + jresp = jsonNewMessage(AFB_SUCCESS, "SUCCESS: create client context for plugin [%s]", handle->anythingYouWant); + + return jresp; +} + +// This function can only be called with a valid token. Token should be renew before +// session timeout a standard renew api is avaliable at /api/token/renew this API +// can be called automatically with HTML5 widget. +// ex: http://localhost:1234/api/context/action?token=xxxxxx-xxxxxx-xxxxx-xxxxx-xxxxxx +STATIC json_object* myAction (AFB_request *request) { + json_object* jresp; + MyPluginHandleT *handle = (MyPluginHandleT*) request->handle; + MyClientContextT *ctx= (MyClientContextT*) request->context; + + // store something in our plugin private client context + ctx->count++; + jresp = jsonNewMessage(AFB_SUCCESS, "SUCCESS: plugin [%s] Check=[%d]\n", handle->anythingYouWant, ctx->count); + + return jresp; +} + +// After execution of this function, client session will be close and if they +// created a context [request->context != NULL] every plugins will be notified +// that they should free context resources. +// ex: http://localhost:1234/api/context/close?token=xxxxxx-xxxxxx-xxxxx-xxxxx-xxxxxx +STATIC json_object* myClose (AFB_request *request) { + json_object* jresp; + MyPluginHandleT *handle = (MyPluginHandleT*) request->handle; + MyClientContextT *ctx= (MyClientContextT*) request->context; + + // store something in our plugin private client context + ctx->count++; + jresp = jsonNewMessage(AFB_SUCCESS, "SUCCESS: plugin [%s] Close=[%d]\n", handle->anythingYouWant, ctx->count); + + // Note Context resource should be free in FreeCtxCB and not here in case session timeout. + return jresp; +} + +STATIC void freeCtxCB (MyClientContextT *ctx, MyPluginHandleT *handle, char *uuid) { + fprintf (stderr, "FreeCtxCB uuid=[%s] Plugin=[%s] count=[%d]", uuid, handle->anythingYouWant, ctx->count); + free (ctx); + + // Note: handle should be free it is a static resource attached to plugin and not to session +} + +// 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 AFB_restapi pluginApis[]= { + {"create", AFB_SESSION_CREATE, (AFB_apiCB)myCreate , "Create a new session"}, + {"action", AFB_SESSION_CHECK , (AFB_apiCB)myAction , "Use Session Context"}, + {"close" , AFB_SESSION_CLOSE , (AFB_apiCB)myClose , "Free Context"}, + {NULL} +}; + +PUBLIC AFB_plugin *pluginRegister () { + + // Plugin handle should not be in stack (malloc or static) + STATIC MyPluginHandleT handle; + + AFB_plugin *plugin = malloc (sizeof (AFB_plugin)); + plugin->type = AFB_PLUGIN_JSON; + plugin->info = "Sample of Client Context Usage"; + plugin->prefix = "context"; + plugin->apis = pluginApis; + plugin->handle = &handle; + plugin->freeCtxCB= (AFB_freeCtxCB) freeCtxCB; + + // feed plugin handle before returning from registration + handle.anythingYouWant = "My Plugin Handle"; + + return (plugin); +}; diff --git a/plugins/samples/HelloWorld.c b/plugins/samples/HelloWorld.c index 2f6dc4d1..d9106920 100644 --- a/plugins/samples/HelloWorld.c +++ b/plugins/samples/HelloWorld.c @@ -87,7 +87,7 @@ STATIC AFB_restapi pluginApis[]= { PUBLIC AFB_plugin *pluginRegister () { AFB_plugin *plugin = malloc (sizeof (AFB_plugin)); plugin->type = AFB_PLUGIN_JSON; - plugin->info = "Application Framework Binder Service"; + plugin->info = "Minimal Hello World Sample"; plugin->prefix= "hello"; plugin->apis = pluginApis; return (plugin); diff --git a/plugins/samples/SamplePost.c b/plugins/samples/SamplePost.c index beadb774..13e1ad67 100644 --- a/plugins/samples/SamplePost.c +++ b/plugins/samples/SamplePost.c @@ -108,7 +108,7 @@ STATIC AFB_restapi pluginApis[]= { PUBLIC AFB_plugin *pluginRegister () { AFB_plugin *plugin = malloc (sizeof (AFB_plugin)); plugin->type = AFB_PLUGIN_JSON; - plugin->info = "Application Framework Binder Service"; + plugin->info = "Sample with Post Upload Files"; plugin->prefix= "post"; // url base plugin->apis = pluginApis; plugin->handle= (void*) "What ever you want"; diff --git a/src/rest-api.c b/src/rest-api.c index c08d18c8..e19e1f2b 100644 --- a/src/rest-api.c +++ b/src/rest-api.c @@ -195,9 +195,12 @@ STATIC AFB_error callPluginApi(AFB_request *request, int plugidx, void *context) } } - // Effectively call the API with a subset of the context + // Effectively CALL PLUGIN API with a subset of the context jresp = plugin->apis[idx].callback(request, context); + // Store context in case it was updated by plugins + clientCtx->contexts[plugidx] = request->context; + // handle intermediary Post Iterates out of band if ((jresp == NULL) && (request->errcode == MHD_HTTP_OK)) return (AFB_SUCCESS); @@ -345,12 +348,7 @@ STATIC AFB_request *createRequest (struct MHD_Connection *connection, AFB_sessio request->prefix = strdup (baseurl); request->api = strdup (baseapi); request->plugins= session->plugins; - for (idx = 0; idx < session->config->pluginCount; idx++) { - if (!strcmp(baseurl, session->plugins[idx]->prefix)) { - request->plugin = session->plugins[idx]; - break; - } - } + // note request->handle is fed with request->context in ctxClientGet Done: free(urlcpy1); diff --git a/src/session.c b/src/session.c index f9f24d21..781ecd94 100644 --- a/src/session.c +++ b/src/session.c @@ -330,7 +330,7 @@ STATIC void ctxUuidFreeCB (AFB_clientCtx *client) { if (client->contexts[idx] != NULL) { freeCtxCB = client->plugins[idx]->freeCtxCB; if (freeCtxCB == NULL) free (client->contexts[idx]); - else if (freeCtxCB != (void*)-1) freeCtxCB(client->contexts[idx], client->plugins[idx]->handle, client->uuid); + else if (freeCtxCB != (void*)-1) freeCtxCB(client->contexts[idx], plugins[idx]->handle, client->uuid); } } } @@ -466,6 +466,7 @@ PUBLIC AFB_clientCtx *ctxClientGet (AFB_request *request, int idx) { clientCtx=NULL; } else { request->context=clientCtx->contexts[idx]; + request->handle = clientCtx->plugins[idx]->handle; request->uuid= uuid; return (clientCtx); } @@ -493,6 +494,7 @@ PUBLIC AFB_clientCtx *ctxClientGet (AFB_request *request, int idx) { // if (verbose) fprintf (stderr, "ctxClientGet New uuid=[%s] token=[%s] timestamp=%d\n", clientCtx->uuid, clientCtx->token, clientCtx->timeStamp); request->context = clientCtx->contexts[idx]; + request->handle = clientCtx->plugins[idx]->handle; request->uuid=clientCtx->uuid; return(clientCtx); } -- 2.16.6