From 4e43e3b53482bff8cb835271eb3207aeaf593812 Mon Sep 17 00:00:00 2001 From: Fulup Ar Foll Date: Wed, 16 Dec 2015 02:30:20 +0100 Subject: [PATCH] Add Middleware and change Hashtable techno --- include/local-def.h | 10 +- include/proto-def.h | 1 + nbproject/configurations.xml | 84 ++++++++++-- nbproject/private/Default.properties | 1 - nbproject/private/configurations.xml | 5 +- src/afbs-api.c | 82 +++--------- src/alsa-api.c | 24 +--- src/dbus-api.c | 14 +- src/http-svc.c | 3 + src/rest-api.c | 241 ++++++++++++++++++++++++----------- src/session.c | 163 +++++++++++------------ 11 files changed, 364 insertions(+), 264 deletions(-) diff --git a/include/local-def.h b/include/local-def.h index 021ef13e..cbbef0f9 100644 --- a/include/local-def.h +++ b/include/local-def.h @@ -77,7 +77,7 @@ typedef int BOOL; extern int verbose; // this is the only global variable // Plugin Type -typedef enum {AFB_PLUGIN_JSON=123456789, AFB_PLUGIN_JSCRIPT=987654321, AFB_PLUGIN_RAW=987123546} AFB_pluginT; +typedef enum {AFB_PLUGIN_JSON=123456789, AFB_PLUGIN_JSCRIPT=987654321, AFB_PLUGIN_RAW=987123546} AFB_pluginE; // prebuild json error are constructed in config.c typedef enum { AFB_FALSE, AFB_TRUE, AFB_FATAL, AFB_FAIL, AFB_WARNING, AFB_EMPTY, AFB_SUCCESS, AFB_DONE, AFB_UNAUTH} AFB_error; @@ -116,7 +116,7 @@ typedef struct { int uid; // post uid for debug AFB_PostType type; // JSON or FORM AFB_apiCB completeCB; // callback when post is completed - void *private; // use internally to keep track or partial buffer + char *private; // use internally to keep track or partial buffer struct MHD_PostProcessor *pp; // iterator handle } AFB_PostHandle; @@ -183,9 +183,13 @@ typedef struct { size_t len; } AFB_redirect_msg; +// Enum for Session/Token/Authentication middleware +typedef enum {AFB_SESSION_NONE, AFB_SESSION_CREATE, AFB_SESSION_CLOSE, AFB_SESSION_RENEW, AFB_SESSION_CHECK} AFB_sessionE; + // API definition typedef struct { char *name; + AFB_sessionE session; AFB_apiCB callback; char *info; AFB_privateApi *private; @@ -193,7 +197,7 @@ typedef struct { // Plugin definition typedef struct { - AFB_pluginT type; + AFB_pluginE type; char *info; char *prefix; size_t prefixlen; diff --git a/include/proto-def.h b/include/proto-def.h index 1a3145f5..d8001142 100644 --- a/include/proto-def.h +++ b/include/proto-def.h @@ -49,6 +49,7 @@ PUBLIC AFB_error ctxTokenCreate (AFB_request *request); PUBLIC AFB_error ctxTokenCheck (AFB_request *request); PUBLIC AFB_error ctxTokenReset (AFB_request *request); PUBLIC AFB_error ctxClientGet (AFB_request *request, AFB_plugin *plugin); +PUBLIC void ctxStoreInit (int); diff --git a/nbproject/configurations.xml b/nbproject/configurations.xml index b1f18a07..6b0679df 100644 --- a/nbproject/configurations.xml +++ b/nbproject/configurations.xml @@ -66,12 +66,22 @@ - + - include + src /usr/include/json-c + include + /usr/include/uuid build/src + + __PIC__=2 + __PIE__=2 + __REGISTER_PREFIX__= + __USER_LABEL_PREFIX__= + __pic__=2 + __pie__=2 + @@ -94,39 +104,79 @@ - + - include + src /usr/include/json-c + include + /usr/include/uuid build/src + + __PIC__=2 + __PIE__=2 + __REGISTER_PREFIX__= + __USER_LABEL_PREFIX__= + __pic__=2 + __pie__=2 + - + - include + src /usr/include/json-c + include + /usr/include/uuid build/src + + __PIC__=2 + __PIE__=2 + __REGISTER_PREFIX__= + __USER_LABEL_PREFIX__= + __pic__=2 + __pie__=2 + - + - include + src /usr/include/json-c + include + /usr/include/uuid build/src + + __PIC__=2 + __PIE__=2 + __REGISTER_PREFIX__= + __USER_LABEL_PREFIX__= + __pic__=2 + __pie__=2 + - + - include + src /usr/include/json-c + include + /usr/include/uuid build/src + + __PIC__=2 + __PIE__=2 + __REGISTER_PREFIX__= + __USER_LABEL_PREFIX__= + __pic__=2 + __pie__=2 + @@ -143,12 +193,22 @@ - + - include + src /usr/include/json-c + include + /usr/include/uuid build/src + + __PIC__=2 + __PIE__=2 + __REGISTER_PREFIX__= + __USER_LABEL_PREFIX__= + __pic__=2 + __pie__=2 + diff --git a/nbproject/private/Default.properties b/nbproject/private/Default.properties index a45c3d27..e63c2370 100644 --- a/nbproject/private/Default.properties +++ b/nbproject/private/Default.properties @@ -1,2 +1 @@ /home/fulup/Workspace/afb-daemon/src/session.c=/home/fulup/Workspace/afb-daemon/build/src#-g3 -gdwarf-2 -fPIE -I/home/fulup/Workspace/afb-daemon/include -I/usr/include/json-c -o CMakeFiles/afb-daemon.dir/session.c.o -c /home/fulup/Workspace/afb-daemon/src/session.c -/home/fulup/Workspace/afb-daemon/src/http-svc.c=/home/fulup/Workspace/afb-daemon/build/src#-g3 -gdwarf-2 -fPIE -I/home/fulup/Workspace/afb-daemon/include -I/usr/include/json-c -o CMakeFiles/afb-daemon.dir/http-svc.c.o -c /home/fulup/Workspace/afb-daemon/src/http-svc.c diff --git a/nbproject/private/configurations.xml b/nbproject/private/configurations.xml index 7306ab22..706acf31 100644 --- a/nbproject/private/configurations.xml +++ b/nbproject/private/configurations.xml @@ -11,8 +11,6 @@ - - feature_tests.c @@ -29,6 +27,7 @@ proto-def.h + SamplePost.c afbs-api.c alsa-api.c config.c @@ -49,8 +48,6 @@ - - diff --git a/src/afbs-api.c b/src/afbs-api.c index 1a3399f9..9e1c7660 100644 --- a/src/afbs-api.c +++ b/src/afbs-api.c @@ -29,87 +29,45 @@ typedef struct { // Request Creation of new context if it does not exist STATIC json_object* clientContextCreate (AFB_request *request) { json_object *jresp; - int res; - char *token; - AFB_clientCtx *client=request->client; // get client context from request - - // check we do not already have a session - if ((client != NULL) && (client->ctx != NULL)) { - request->errcode=MHD_HTTP_FORBIDDEN; - return (jsonNewMessage(AFB_FAIL, "Token exist use refresh")); - } - - // request a new client context token and check result - if (AFB_UNAUTH == ctxTokenCreate (request)) { - request->errcode=MHD_HTTP_UNAUTHORIZED; - jresp= jsonNewMessage(AFB_FAIL, "No/Invalid initial token provided [should match --token=xxxx]"); - return (jresp); - } - - // request a new client context token and check result - if (AFB_SUCCESS != ctxTokenCreate (request)) { - request->errcode=MHD_HTTP_UNAUTHORIZED; - jresp= jsonNewMessage(AFB_FAIL, "Token Session Not Activated [restart with --token=xxxx]"); - return (jresp); - } - - // add a client context to session - client->ctx = malloc (sizeof (MyClientApplicationHandle)); + + // add an application specific client context to session + request->client->ctx = malloc (sizeof (MyClientApplicationHandle)); // Send response to UI jresp = json_object_new_object(); - json_object_object_add(jresp, "token", json_object_new_string (client->token)); + json_object_object_add(jresp, "token", json_object_new_string ("A New Token and Session Context Was Created")); return (jresp); } -// Renew an existing context +// Before entering here token will be check and renew STATIC json_object* clientContextRefresh (AFB_request *request) { json_object *jresp; - // note: we do not need to parse the old token as clientContextRefresh doit for us - if (AFB_SUCCESS != ctxTokenRefresh (request)) { - request->errcode=MHD_HTTP_UNAUTHORIZED; - jresp= jsonNewMessage(AFB_FAIL, "Token Exchange Broken Refresh Refused"); - } else { - jresp = json_object_new_object(); - json_object_object_add(jresp, "token", json_object_new_string (request->client->token)); - } - + + jresp = json_object_new_object(); + json_object_object_add(jresp, "token", json_object_new_string ("Token was refreshed")); + return (jresp); } -// Verify a context is still valid +// Session token will we verified before entering here STATIC json_object* clientContextCheck (AFB_request *request) { - json_object *jresp = json_object_new_object(); - - // add an error code to respond - if (AFB_SUCCESS != ctxTokenCheck (request)) { - request->errcode=MHD_HTTP_UNAUTHORIZED; - json_object_object_add(jresp, "isvalid", json_object_new_boolean (FALSE)); - } else { - json_object_object_add(jresp, "isvalid", json_object_new_boolean (TRUE)); - } + json_object *jresp = json_object_new_object(); + json_object_object_add(jresp, "isvalid", json_object_new_boolean (TRUE)); return (jresp); } - // Close and Free context STATIC json_object* clientContextReset (AFB_request *request) { json_object *jresp; - // note: we do not need to parse the old token as clientContextRefresh doit for us - if (AFB_SUCCESS != ctxTokenReset (request)) { - request->errcode=MHD_HTTP_UNAUTHORIZED; - jresp= jsonNewMessage(AFB_FAIL, "No Token Client Context [use --token=xxx]"); - } else { - jresp = json_object_new_object(); - json_object_object_add(jresp, "uuid", json_object_new_string (request->client->uuid)); - } + jresp = json_object_new_object(); + json_object_object_add(jresp, "uuid", json_object_new_string (request->client->uuid)); return (jresp); } @@ -214,12 +172,12 @@ STATIC void clientContextFree(AFB_clientCtx *client) { } STATIC AFB_restapi pluginApis[]= { - {"ping" , (AFB_apiCB)apiPingTest ,"Ping Rest Test Service"}, - {"token-create" , (AFB_apiCB)clientContextCreate ,"Request Client Context Creation"}, - {"token-refresh" , (AFB_apiCB)clientContextRefresh,"Refresh Client Context Token"}, - {"token-check" , (AFB_apiCB)clientContextCheck ,"Check Client Context Token"}, - {"token-reset" , (AFB_apiCB)clientContextReset ,"Close Client Context and Free resources"}, - {"file-upload" , (AFB_apiCB)ProcessPostForm ,"Demo for file upload"}, + {"ping" , AFB_SESSION_NONE , (AFB_apiCB)apiPingTest ,"Ping Rest Test Service"}, + {"token-create" , AFB_SESSION_CREATE, (AFB_apiCB)clientContextCreate ,"Request Client Context Creation"}, + {"token-refresh" , AFB_SESSION_RENEW , (AFB_apiCB)clientContextRefresh,"Refresh Client Context Token"}, + {"token-check" , AFB_SESSION_CHECK , (AFB_apiCB)clientContextCheck ,"Check Client Context Token"}, + {"token-reset" , AFB_SESSION_CLOSE , (AFB_apiCB)clientContextReset ,"Close Client Context and Free resources"}, + {"file-upload" , AFB_SESSION_NONE , (AFB_apiCB)ProcessPostForm ,"Demo for file upload"}, {NULL} }; diff --git a/src/alsa-api.c b/src/alsa-api.c index 46b971b3..9d4f3cb6 100644 --- a/src/alsa-api.c +++ b/src/alsa-api.c @@ -27,23 +27,6 @@ STATIC json_object* wrongApi (AFB_request *request, void* handle) { impossible=bug/zero; } -STATIC json_object* pingSample (AFB_request *request) { - static pingcount = 0; - json_object *response; - char query [512]; - - // request all query key/value - getQueryAll (request,query, sizeof(query)); - - // check if we have some post data - if (request->post == NULL) request->post="NoData"; - - // return response to caller - response = jsonNewMessage(AFB_SUCCESS, "Ping Binder Daemon %d query={%s} handle=[%s] PostData: \'%s\' " - , pingcount++, query, request->post); - - return (response); -} STATIC struct { @@ -52,10 +35,9 @@ STATIC struct { STATIC AFB_restapi pluginApis[]= { - {"ping" , (AFB_apiCB)pingSample , "Ping Application Framework"}, - {"error" , (AFB_apiCB)wrongApi , "Ping Application Framework"}, - {"ctx-store", (AFB_apiCB)pingSample , "Verbose Mode"}, - {"ctx-load" , (AFB_apiCB)pingSample , "Verbose Mode"}, + {"ping" , AFB_SESSION_NONE, (AFB_apiCB)apiPingTest,"Ping Application Framework"}, + {"error" , AFB_SESSION_NONE, (AFB_apiCB)wrongApi , "Ping Application Framework"}, + {NULL} }; diff --git a/src/dbus-api.c b/src/dbus-api.c index f7b071bd..70de03f0 100644 --- a/src/dbus-api.c +++ b/src/dbus-api.c @@ -30,7 +30,7 @@ STATIC json_object* pingSample (AFB_request *request) { if (len == 0) strcpy (query,"NoSearchQueryList"); // check if we have some post data - if (request->post == NULL) request->post="NoData"; + if (request->post == NULL) request->post->data="NoData"; // return response to caller response = jsonNewMessage(AFB_SUCCESS, "Ping Binder Daemon %d query={%s} PostData: \'%s\' ", pingcount++, query, request->post); @@ -75,12 +75,12 @@ STATIC json_object* pingJson (AFB_session *session, AFB_request *request) { STATIC AFB_restapi pluginApis[]= { - {"ping" , (AFB_apiCB)pingSample , "Ping Application Framework"}, - {"pingnull" , (AFB_apiCB)pingFail , "Return NULL"}, - {"pingbug" , (AFB_apiCB)pingBug , "Do a Memory Violation"}, - {"pingJson" , (AFB_apiCB)pingJson , "Return a JSON object"}, - {"ctx-store", (AFB_apiCB)pingSample , "Verbose Mode"}, - {"ctx-load" , (AFB_apiCB)pingSample , "Verbose Mode"}, + {"ping" , AFB_SESSION_NONE, (AFB_apiCB)pingSample , "Ping Application Framework"}, + {"pingnull" , AFB_SESSION_NONE, (AFB_apiCB)pingFail , "Return NULL"}, + {"pingbug" , AFB_SESSION_NONE, (AFB_apiCB)pingBug , "Do a Memory Violation"}, + {"pingJson" , AFB_SESSION_NONE, (AFB_apiCB)pingJson , "Return a JSON object"}, + {"ctx-store", AFB_SESSION_NONE, (AFB_apiCB)pingSample , "Verbose Mode"}, + {"ctx-load" , AFB_SESSION_NONE, (AFB_apiCB)pingSample , "Verbose Mode"}, {NULL} }; diff --git a/src/http-svc.c b/src/http-svc.c index ac0606eb..b3b9a1f7 100644 --- a/src/http-svc.c +++ b/src/http-svc.c @@ -267,6 +267,9 @@ PUBLIC AFB_error httpdStart(AFB_session *session) { apiUrlLen = strlen (session->config->rootapi); baseUrlLen= strlen (session->config->rootbase); rootUrlLen= strlen (session->config->rootdir); + + // Initialise Client Session Hash Table + ctxStoreInit (CTX_NBCLIENTS); // TBD open libmagic cache [fail to pass EFENCE check] // initLibMagic (session); diff --git a/src/rest-api.c b/src/rest-api.c index 35e81d7e..ad3a4f22 100644 --- a/src/rest-api.c +++ b/src/rest-api.c @@ -107,7 +107,7 @@ PUBLIC void endPostRequest(AFB_PostHandle *postHandle) { if (!postHandle->completeCB) postHandle->completeCB (postHandle->private); } } - freeRequest (postHandle->private); + free(postHandle->private); free(postHandle); } @@ -138,6 +138,11 @@ STATIC AFB_error callPluginApi(AFB_plugin *plugin, AFB_request *request, void *c for (idx = 0; plugin->apis[idx].callback != NULL; idx++) { if (!strcmp(plugin->apis[idx].name, request->api)) { + // Request was found and at least partially executed + request->jresp = json_object_new_object(); + json_object_get (afbJsonType); // increate jsontype reference count + json_object_object_add (request->jresp, "jtype", afbJsonType); + // prepare an object to store calling values jcall=json_object_new_object(); json_object_object_add(jcall, "prefix", json_object_new_string (plugin->prefix)); @@ -158,25 +163,94 @@ STATIC AFB_error callPluginApi(AFB_plugin *plugin, AFB_request *request, void *c if (request->config->apiTimeout > 0) { for (sig=0; signals[sig] != 0; sig++) { if (signal (signals[sig], pluginError) == SIG_ERR) { - request->errcode = MHD_HTTP_UNPROCESSABLE_ENTITY; - request->jresp = jsonNewMessage(AFB_FATAL, "%s ERR: Signal/timeout handler activation fail.", configTime()); - return AFB_FAIL; + request->errcode = MHD_HTTP_UNPROCESSABLE_ENTITY; + json_object_object_add(jcall, "status", json_object_new_string ("fail")); + json_object_object_add(jcall, "info", json_object_new_string ("Setting Timeout Handler Failed")); + json_object_object_add(request->jresp, "request", jcall); + return AFB_DONE; } } // Trigger a timer to protect from unacceptable long time execution alarm (request->config->apiTimeout); } + + // Out of SessionNone every call get a client context session + if (AFB_SESSION_NONE != plugin->apis[idx].session) { + + // add client context to request + ctxClientGet(request, plugin); + + if (verbose) fprintf(stderr, "Plugin=[%s] Api=[%s] Middleware=[%d] Client=[0x%x] Uuid=[%s] Token=[%s]\n" + , request->plugin, request->api, plugin->apis[idx].session, request->client, request->client->uuid, request->client->token); + + switch(plugin->apis[idx].session) { + + case AFB_SESSION_CREATE: + if (request->client->token[0] != '\0') { + request->errcode=MHD_HTTP_UNAUTHORIZED; + json_object_object_add(jcall, "status", json_object_new_string ("exist")); + json_object_object_add(jcall, "info", json_object_new_string ("AFB_SESSION_CREATE Session already exist")); + json_object_object_add(request->jresp, "request", jcall); + return (AFB_DONE); + } + + if (AFB_SUCCESS != ctxTokenCreate (request)) { + request->errcode=MHD_HTTP_UNAUTHORIZED; + json_object_object_add(jcall, "status", json_object_new_string ("fail")); + json_object_object_add(jcall, "info", json_object_new_string ("AFB_SESSION_CREATE Invalid Initial Token")); + json_object_object_add(request->jresp, "request", jcall); + return (AFB_DONE); + } else { + json_object_object_add(jcall, "uuid", json_object_new_string (request->client->uuid)); + json_object_object_add(jcall, "token", json_object_new_string (request->client->token)); + } + break; + + + case AFB_SESSION_RENEW: + if (AFB_SUCCESS != ctxTokenRefresh (request)) { + request->errcode=MHD_HTTP_UNAUTHORIZED; + json_object_object_add(jcall, "status", json_object_new_string ("fail")); + json_object_object_add(jcall, "info", json_object_new_string ("AFB_SESSION_REFRESH Broken Exchange Token Chain")); + json_object_object_add(request->jresp, "request", jcall); + return (AFB_DONE); + } else { + json_object_object_add(jcall, "uuid", json_object_new_string (request->client->uuid)); + json_object_object_add(jcall, "token", json_object_new_string (request->client->token)); + } + break; + + case AFB_SESSION_CLOSE: + if (AFB_SUCCESS != ctxTokenCheck (request)) { + request->errcode=MHD_HTTP_UNAUTHORIZED; + json_object_object_add(jcall, "status", json_object_new_string ("empty")); + json_object_object_add(jcall, "info", json_object_new_string ("AFB_SESSION_CLOSE Not a Valid Access Token")); + json_object_object_add(request->jresp, "request", jcall); + return (AFB_DONE); + } else { + json_object_object_add(jcall, "uuid", json_object_new_string (request->client->uuid)); + } + break; + + case AFB_SESSION_CHECK: + default: + // default action is check + if (AFB_SUCCESS != ctxTokenCheck (request)) { + request->errcode=MHD_HTTP_UNAUTHORIZED; + json_object_object_add(jcall, "status", json_object_new_string ("fail")); + json_object_object_add(jcall, "info", json_object_new_string ("AFB_SESSION_CHECK Invalid Active Token")); + json_object_object_add(request->jresp, "request", jcall); + return (AFB_DONE); + } + break; + } + } - // add client context to request - ctxClientGet(request, plugin); - // Effectively call the API with a subset of the context jresp = plugin->apis[idx].callback(request, context); - // Allocate Json object and build response - request->jresp = json_object_new_object(); - json_object_get (afbJsonType); // increate jsontype reference count - json_object_object_add (request->jresp, "jtype", afbJsonType); + // Session close is done after the API call so API can still use session in closing API + if (AFB_SESSION_CLOSE == plugin->apis[idx].session) ctxTokenReset (request); // API should return NULL of a valid Json Object if (jresp == NULL) { @@ -200,17 +274,18 @@ STATIC AFB_error callPluginApi(AFB_plugin *plugin, AFB_request *request, void *c return (AFB_DONE); } } + return (AFB_FAIL); } STATIC AFB_error findAndCallApi (AFB_request *request, void *context) { int idx; - char *baseurl, *baseapi; AFB_error status; + // Search for a plugin with this urlpath for (idx = 0; request->plugins[idx] != NULL; idx++) { - if (!strcmp(request->plugins[idx]->prefix, baseurl)) { + if (!strcmp(request->plugins[idx]->prefix, request->plugin)) { status =callPluginApi(request->plugins[idx], request, context); break; } @@ -226,7 +301,10 @@ STATIC AFB_error findAndCallApi (AFB_request *request, void *context) { request->jresp = jsonNewMessage(AFB_FATAL, "No API=[%s] for Plugin=[%s]", request->api, request->plugin); goto ExitOnError; } - + + + + // Everything look OK return (status); @@ -262,10 +340,10 @@ doPostIterate (void *cls, enum MHD_ValueKind kind, const char *key, item.offset = offset; // Reformat Request to make it somehow similar to GET/PostJson case - post.data= (char*) postctx; - post.len = size; - post.type= AFB_POST_FORM;; - request->post = &post; + postRequest.data= (char*) postHandle; + postRequest.len = size; + postRequest.type= AFB_POST_FORM;; + request->post = &postRequest; // effectively call plugin API status = findAndCallApi (request, &item); @@ -274,10 +352,11 @@ doPostIterate (void *cls, enum MHD_ValueKind kind, const char *key, if (status != AFB_SUCCESS) return MHD_NO; // let's allow iterator to move to next item - return (MHD_YES;); + return (MHD_YES); } STATIC void freeRequest (AFB_request *request) { + free (request->plugin); free (request->api); free (request); @@ -289,32 +368,32 @@ STATIC AFB_request *createRequest (struct MHD_Connection *connection, AFB_sessio // Start with a clean request request = calloc (1, sizeof (AFB_request)); - char *urlcpy1, urlcpy2; + char *urlcpy1, *urlcpy2; + char *baseapi, *baseurl; // Extract plugin urlpath from request and make two copy because strsep overload copy urlcpy1 = urlcpy2 = strdup(url); baseurl = strsep(&urlcpy2, "/"); if (baseurl == NULL) { - errMessage = jsonNewMessage(AFB_FATAL, "Invalid API call url=[%s]", url); - goto ExitOnError; + request->jresp = jsonNewMessage(AFB_FATAL, "Invalid API call url=[%s]", url); } // let's compute URL and call API baseapi = strsep(&urlcpy2, "/"); if (baseapi == NULL) { - errMessage = jsonNewMessage(AFB_FATAL, "Invalid API call url=[%s]", url); - goto ExitOnError; + request->jresp = jsonNewMessage(AFB_FATAL, "Invalid API call url=[%s]", url); } // build request structure request->connection = connection; - request->config = session.config; + request->config = session->config; request->url = url; request->plugin = strdup (baseurl); request->api = strdup (baseapi); request->plugins= session->plugins; free(urlcpy1); + return (request); } // process rest API query @@ -326,41 +405,45 @@ PUBLIC int doRestApi(struct MHD_Connection *connection, AFB_session *session, co AFB_error status; struct MHD_Response *webResponse; const char *serialized; - AFB_request request; - AFB_PostHandle *posthandle; + AFB_request *request; + AFB_PostHandle *postHandle; + AFB_PostRequest postRequest; int ret; // if post data may come in multiple calls if (0 == strcmp(method, MHD_HTTP_METHOD_POST)) { const char *encoding, *param; int contentlen = -1; - posthandle = *con_cls; + postHandle = *con_cls; // This is the initial post event let's create form post structure POST datas come in multiple events - if (posthandle == NULL) { - fprintf(stderr, "This is the 1st Post Event postuid=%d\n", posthandle->uid); + if (postHandle == NULL) { // allocate application POST processor handle to zero - posthandle = cmalloc(1, sizeof (AFB_PostHandle)); - posthandle->uid = postcount++; // build a UID for DEBUG - *con_cls = posthandle; // attache POST handle to current HTTP request + postHandle = calloc(1, sizeof (AFB_PostHandle)); + postHandle->uid = postcount++; // build a UID for DEBUG + *con_cls = postHandle; // attache POST handle to current HTTP request // Let make sure we have the right encoding and a valid length encoding = MHD_lookup_connection_value(connection, MHD_HEADER_KIND, MHD_HTTP_HEADER_CONTENT_TYPE); - param = MHD_lookup_connection_value(connection, MHD_HEADER_KIND, MHD_HTTP_HEADER_CONTENT_LENGTH); - if (param) sscanf(param, "%i", &contentlen); // Form post is handle through a PostProcessor and call API once per form key if (strcasestr(encoding, FORM_CONTENT) != NULL) { - - posthandle = malloc(sizeof (AFB_PostHandle)); // allocate application POST processor handle - posthandle->type = AFB_POST_FORM; - posthandle->private= (void*)createRequest (connection, session, url); - posthandle->pp = MHD_create_post_processor (connection, MAX_POST_SIZE, doPostIterate, posthandle); - - if (NULL == posthandle->pp) { + if (verbose) fprintf(stderr, "Create PostForm[uid=%d]\n", postHandle->uid); + + request = createRequest (connection, session, url); + if (request->jresp != NULL) { + errMessage = request->jresp; + goto ExitOnError; + } + postHandle = malloc(sizeof (AFB_PostHandle)); // allocate application POST processor handle + postHandle->type = AFB_POST_FORM; + postHandle->pp = MHD_create_post_processor (connection, MAX_POST_SIZE, doPostIterate, postHandle); + postHandle->private= (void*)request; + + if (NULL == postHandle->pp) { fprintf(stderr,"OOPS: Internal error fail to allocate MHD_create_post_processor\n"); - free (posthandle); + free (postHandle); return MHD_NO; } return MHD_YES; @@ -369,40 +452,46 @@ PUBLIC int doRestApi(struct MHD_Connection *connection, AFB_session *session, co // POST json is store into a buffer and present in one piece to API if (strcasestr(encoding, JSON_CONTENT) != NULL) { + param = MHD_lookup_connection_value(connection, MHD_HEADER_KIND, MHD_HTTP_HEADER_CONTENT_LENGTH); + if (param) sscanf(param, "%i", &contentlen); + + // Because PostJson are build in RAM size is constrained if (contentlen > MAX_POST_SIZE) { errMessage = jsonNewMessage(AFB_FATAL, "Post Date to big %d > %d", contentlen, MAX_POST_SIZE); goto ExitOnError; } - if (posthandle == NULL) { - posthandle->type = AFB_POST_JSON; - posthandle->private = malloc(contentlen + 1); // allocate memory for full POST data + 1 for '\0' enf of string + // Size is OK, let's allocate a buffer to hold post data + postHandle->type = AFB_POST_JSON; + postHandle->private = malloc(contentlen + 1); // allocate memory for full POST data + 1 for '\0' enf of string - if (verbose) fprintf(stderr, "Create PostJson[%d] Size=%d\n", posthandle->uid, contentlen); - return MHD_YES; - } + if (verbose) fprintf(stderr, "Create PostJson[uid=%d] Size=%d\n", postHandle->uid, contentlen); + return MHD_YES; + } else { // We only support Json and Form Post format errMessage = jsonNewMessage(AFB_FATAL, "Post Date wrong type encoding=%s != %s", encoding, JSON_CONTENT); goto ExitOnError; - } + + } } // This time we receive partial/all Post data. Note that even if we get all POST data. We should nevertheless // return MHD_YES and not process the request directly. Otherwise Libmicrohttpd is unhappy and fails with // 'Internal application error, closing connection'. if (*upload_data_size) { - if (verbose) fprintf(stderr, "Update Post[%d]\n", posthandle->uid); - if (posthandle->type == AFB_POST_FORM) { - MHD_post_process (con_info->postprocessor, upload_data, *upload_data_size); + if (postHandle->type == AFB_POST_FORM) { + if (verbose) fprintf(stderr, "Processing PostForm[uid=%d]\n", postHandle->uid); + MHD_post_process (postHandle->pp, upload_data, *upload_data_size); } // Process JsonPost request when buffer is completed let's call API - if (posthandle->type == AFB_POST_JSON) { + if (postHandle->type == AFB_POST_JSON) { + if (verbose) fprintf(stderr, "Updating PostJson[uid=%d]\n", postHandle->uid); - memcpy(&posthandle->private[posthandle->len], upload_data, *upload_data_size); - posthandle->len = posthandle->len + *upload_data_size; + memcpy(&postHandle->private[postHandle->len], upload_data, *upload_data_size); + postHandle->len = postHandle->len + *upload_data_size; *upload_data_size = 0; } return MHD_YES; @@ -411,25 +500,31 @@ PUBLIC int doRestApi(struct MHD_Connection *connection, AFB_session *session, co // Create a request structure to finalise the request request= createRequest (connection, session, url); - - // We should only start to process DATA after Libmicrohttpd call or application handler with *upload_data_size==0 - if (posthandle->type == AFB_POST_FORM) { - MHD_post_process (posthandle->pp, upload_data, *upload_data_size); + if (request->jresp != NULL) { + errMessage = request->jresp; + goto ExitOnError; } + - if (posthandle->type == AFB_POST_JSON) { + if (postHandle->type == AFB_POST_JSON) { + if (verbose) fprintf(stderr, "Processing PostJson[uid=%d]\n", postHandle->uid); + + param = MHD_lookup_connection_value(connection, MHD_HEADER_KIND, MHD_HTTP_HEADER_CONTENT_LENGTH); + if (param) sscanf(param, "%i", &contentlen); + // At this level we're may verify that we got everything and process DATA - if (posthandle->len != contentlen) { - errMessage = jsonNewMessage(AFB_FATAL, "Post Data Incomplete UID=%d Len %d != %s", posthandle->uid, contentlen, posthandle->len); + if (postHandle->len != contentlen) { + errMessage = jsonNewMessage(AFB_FATAL, "Post Data Incomplete UID=%d Len %d != %d", postHandle->uid, contentlen, postHandle->len); goto ExitOnError; } // Before processing data, make sure buffer string is properly ended - posthandle->private[posthandle->len] = '\0'; - request->post.data = posthandle->private; - request->post.type = posthandle->type; + postHandle->private[postHandle->len] = '\0'; + postRequest.data = postHandle->private; + postRequest.type = postHandle->type; + request->post = &postRequest; - if (verbose) fprintf(stderr, "Close Post[%d] Buffer=%s\n", posthandle->uid, request.post); + if (verbose) fprintf(stderr, "Close Post[%d] Buffer=%s\n", postHandle->uid, request->post->data); } } } else { @@ -441,23 +536,23 @@ PUBLIC int doRestApi(struct MHD_Connection *connection, AFB_session *session, co status = findAndCallApi (request, NULL); ExitOnResponse: - freeRequest (request); - serialized = json_object_to_json_string(request.jresp); + serialized = json_object_to_json_string(request->jresp); webResponse = MHD_create_response_from_buffer(strlen(serialized), (void*) serialized, MHD_RESPMEM_MUST_COPY); // client did not pass token on URI let's use cookies - if ((!request.restfull) && (request.client != NULL)) { + if ((!request->restfull) && (request->client != NULL)) { char cookie[64]; - snprintf (cookie, sizeof (cookie), "%s=%s", COOKIE_NAME, request.client->uuid); + snprintf (cookie, sizeof (cookie), "%s=%s", COOKIE_NAME, request->client->uuid); MHD_add_response_header (webResponse, MHD_HTTP_HEADER_SET_COOKIE, cookie); } // if requested add an error status - if (request.errcode != 0) ret=MHD_queue_response (connection, request.errcode, webResponse); + if (request->errcode != 0) ret=MHD_queue_response (connection, request->errcode, webResponse); else MHD_queue_response(connection, MHD_HTTP_OK, webResponse); MHD_destroy_response(webResponse); - json_object_put(request.jresp); // decrease reference rqtcount to free the json object + json_object_put(request->jresp); // decrease reference rqtcount to free the json object + freeRequest (request); return MHD_YES; ExitOnError: diff --git a/src/session.c b/src/session.c index 3494dadb..55098b41 100644 --- a/src/session.c +++ b/src/session.c @@ -27,6 +27,9 @@ #include #include #include +#include +#include + #define AFB_SESSION_JTYPE "AFB_session" #define AFB_SESSION_JLIST "AFB_sessions" @@ -36,9 +39,8 @@ #define AFB_CURRENT_SESSION "active-session" // file link name within sndcard dir #define AFB_DEFAULT_SESSION "current-session" // should be in sync with UI - -static struct lh_table *clientCtxs=NULL; // let's use JsonObject Hashtable to Store Sessions - +static pthread_mutex_t mutexHash; // declare a mutex to protect hash table +static struct hsearch_data sessions = {0}; // Create an empty hash table for sessions // verify we can read/write in session dir PUBLIC AFB_error sessionCheckdir (AFB_session *session) { @@ -308,32 +310,11 @@ OnErrorExit: } -// Function to handle Cookies and Client session context it relies on json low level -// linked list functionalities https://github.com/json-c/json-c/blob/master/linkhash.c - -// Hash client UUID before storing in table -STATIC unsigned long ctxUuidHashCB (const void *k1) { - unsigned long hash; - - AFB_clientCtx *ctx = (AFB_clientCtx*) k1; - hash = lh_char_hash(ctx->uuid); - return (hash); -} -// Compare client UUIDs within table -STATIC int ctxUuidCompCB (const void *k1, const void *k2) { - int res; - AFB_clientCtx *ctx1 = (AFB_clientCtx*) k1; - AFB_clientCtx *ctx2 = (AFB_clientCtx*) k2; - - res = lh_char_equal(ctx1->uuid, ctx2->uuid); - return (res); -} // Free context [XXXX Should be protected again memory abort XXXX] -STATIC void ctxUuidFreeCB (struct lh_entry *entry) { - AFB_clientCtx *client = (AFB_clientCtx*) entry->v; - +STATIC void ctxUuidFreeCB (AFB_clientCtx *client) { + // If application add a handle let's free it now if (client->ctx != NULL) { @@ -341,41 +322,68 @@ STATIC void ctxUuidFreeCB (struct lh_entry *entry) { if (client->plugin->freeCtxCB == NULL) free (client->ctx); else if (client->plugin->freeCtxCB != (void*)-1) client->plugin->freeCtxCB(client); } - free ((void*)entry->v); } // Create a new store in RAM, not that is too small it will be automatically extended -STATIC struct lh_table *ctxStoreCreate (int nbSession) { - lh_table *table; +PUBLIC void ctxStoreInit (int nbSession) { + int res; + // let's create session hash table + res = hcreate_r(nbSession, &sessions); +} + +STATIC AFB_clientCtx *ctxStoreSearch (const char* uuid) { + ENTRY item = {(char*) uuid}; + ENTRY *pitem = &item; + // printf ("searching uuid=%s\n", uuid); - // function will exit process in case of error !!! - table=lh_table_new (nbSession, "CtxClient", ctxUuidFreeCB, ctxUuidHashCB, ctxUuidCompCB); - return (table); + pthread_mutex_lock(&mutexHash); + if (hsearch_r(item, FIND, &pitem, &sessions)) { + pthread_mutex_unlock(&mutexHash); + return (AFB_clientCtx*) pitem->data; + } + pthread_mutex_unlock(&mutexHash); + return NULL; +} + +// Reference http://stackoverflow.com/questions/25971505/how-to-delete-element-from-hsearch +void ctxStoreAdd (AFB_clientCtx *client) { + ENTRY item = {client->uuid, (void*)client}; + ENTRY *pitem = &item; + + pthread_mutex_lock(&mutexHash); + if (hsearch_r(item, ENTER, &pitem, &sessions)) { + // printf ("storing uuid=%s\n", client->uuid); + pitem->data = (void *)client; + } + pthread_mutex_unlock(&mutexHash); +} + +void ctxStoreDel (AFB_clientCtx *client) { + ENTRY item = {client->uuid}; + ENTRY *pitem = &item; + + pthread_mutex_lock(&mutexHash); + if (hsearch_r(item, FIND, &pitem, &sessions)) { + pitem->data = NULL; + } + pthread_mutex_unlock(&mutexHash); } // Check if context timeout or not STATIC int ctxStoreToOld (const void *k1, int timeout) { - int res; + int res; AFB_clientCtx *ctx = (AFB_clientCtx*) k1; - - res = ((ctx->timeStamp + timeout) < time(NULL)); + time_t now = time(NULL); + res = ((ctx->timeStamp + timeout) <= now); return (res); } // Loop on every entry and remove old context sessions PUBLIC int ctxStoreGarbage (struct lh_table *lht, const int timeout) { - struct lh_entry *c; - - // Loop on every entry within table - for(c = lht->head; c != NULL; c = c->next) { - if(lht->free_fn) { - if(c->k == LH_EMPTY) return lht->count; - if(c->k != LH_FREED && ctxStoreToOld(c->v, timeout)) lh_table_delete_entry (lht, c); - } - } + + if (verbose) fprintf (stderr, "****** Garbage Count=%d timeout=%d\n", lht->count, timeout); + - // return current size after cleanup - return (lht->count); } // This function will return exiting client context or newly created client context @@ -387,11 +395,6 @@ PUBLIC AFB_error ctxClientGet (AFB_request *request, AFB_plugin *plugin) { int ret; if (request->config->token == NULL) return AFB_EMPTY; - - // if client session store is null create it - if (clientCtxs == NULL) { - clientCtxs= ctxStoreCreate(CTX_NBCLIENTS); - } // Check if client as a context or not inside the URL uuid = MHD_lookup_connection_value(request->connection, MHD_GET_ARGUMENT_KIND, "uuid"); @@ -403,32 +406,34 @@ PUBLIC AFB_error ctxClientGet (AFB_request *request, AFB_plugin *plugin) { uuid = MHD_lookup_connection_value (request->connection, MHD_COOKIE_KIND, COOKIE_NAME); }; - - if (uuid != NULL) { + // Warning when no cookie defined MHD_lookup_connection_value may return something !!! + if ((uuid != NULL) && (strnlen (uuid, 10) >= 10)) { + int search; // search if client context exist and it not timeout let's use it - if ((lh_table_lookup_ex (clientCtxs, uuid, (void**) &clientCtx)) - && ! ctxStoreToOld (clientCtx, request->config->cntxTimeout)) { - request->client=clientCtx; - if (verbose) fprintf (stderr, "ctxClientGet Old uuid=[%s] token=[%s] timestamp=%d\n" - ,request->client->uuid, request->client->token, request->client->timeStamp); - return; + printf ("search old UID=%s\n", uuid); + clientCtx = ctxStoreSearch (uuid); + + if (clientCtx && ! ctxStoreToOld (clientCtx, request->config->cntxTimeout)) { + request->client=clientCtx; + return; } } - - + // we have no session let's create one otherwise let's clean any exiting values if (clientCtx == NULL) clientCtx = calloc(1, sizeof(AFB_clientCtx)); // init NULL clientContext uuid_generate(newuuid); // create a new UUID uuid_unparse_lower(newuuid, clientCtx->uuid); clientCtx->cid=cid++; // simple application uniqueID clientCtx->plugin = plugin; // provide plugin callbacks a hook to plugin + clientCtx->plugin; // provide plugin callbacks a hook to plugin // if table is full at 50% let's clean it up - if(clientCtxs->count > (clientCtxs->size*0.5)) ctxStoreGarbage(clientCtxs, request->config->cntxTimeout); + // if(clientCtxs->count > (clientCtxs->size / 2)) ctxStoreGarbage(clientCtxs, request->config->cntxTimeout); // finally add uuid into hashtable - ret=lh_table_insert (clientCtxs, (void*)clientCtx->uuid, clientCtx); - if (ret < 0) return (AFB_FAIL); + ctxStoreAdd (clientCtx); + + // if (ret < 0) return (AFB_FAIL); if (verbose) fprintf (stderr, "ctxClientGet New uuid=[%s] token=[%s] timestamp=%d\n", clientCtx->uuid, clientCtx->token, clientCtx->timeStamp); request->client = clientCtx; @@ -459,16 +464,18 @@ PUBLIC AFB_error ctxTokenCheck (AFB_request *request) { // Free Client Session Context PUBLIC AFB_error ctxTokenReset (AFB_request *request) { - struct lh_entry* entry; int ret; + AFB_clientCtx *clientCtx; if (request->client == NULL) return AFB_EMPTY; + + // Search for an existing client with the same UUID + clientCtx = ctxStoreSearch (request->client->uuid); + if (clientCtx == NULL) return AFB_FALSE; - entry = lh_table_lookup_entry (clientCtxs, request->client->uuid); - if (entry == NULL) return AFB_FALSE; + // Remove client from table + ctxStoreDel (clientCtx); - lh_table_delete_entry (clientCtxs, entry); - return (AFB_SUCCESS); } @@ -492,7 +499,6 @@ PUBLIC AFB_error ctxTokenCreate (AFB_request *request) { if (strcmp(request->config->token, token)) return AFB_UNAUTH; } - // create a UUID as token value uuid_generate(newuuid); uuid_unparse_lower(newuuid, request->client->token); @@ -514,17 +520,12 @@ PUBLIC AFB_error ctxTokenRefresh (AFB_request *request) { if (request->client == NULL) return AFB_EMPTY; // Check if the old token is valid - oldTnkValid= ctxTokenCheck (request); + if (ctxTokenCheck (request) != AFB_SUCCESS) return (AFB_FAIL); + + // Old token was valid let's regenerate a new one + uuid_generate(newuuid); // create a new UUID + uuid_unparse_lower(newuuid, request->client->token); + return (AFB_SUCCESS); - // if token is not valid let check for query argument "oldornew" - if (!oldTnkValid) { - oldornew = MHD_lookup_connection_value(request->connection, MHD_GET_ARGUMENT_KIND, "oldornew"); - if (oldornew != NULL) oldTnkValid= TRUE; - } - - // No existing token and no request to create one - if (oldTnkValid != TRUE) return AFB_WARNING; - - return (ctxTokenCreate (request)); } -- 2.16.6