X-Git-Url: https://gerrit.automotivelinux.org/gerrit/gitweb?a=blobdiff_plain;f=src%2Frest-api.c;h=83bb2d22e1a4c4fbcbdd91a9bf0200130215a98d;hb=d00571d3c5365f40e7ec2ec3ab0f636afa0db480;hp=5e32d318559c2bddbad8aaf86b1decb50c2a1fcd;hpb=4db4634b28378379bce04f4073c2a511b92b1ee6;p=src%2Fapp-framework-binder.git diff --git a/src/rest-api.c b/src/rest-api.c index 5e32d318..83bb2d22 100644 --- a/src/rest-api.c +++ b/src/rest-api.c @@ -23,6 +23,8 @@ #include #include +#define AFB_MSG_JTYPE "AJB_reply" + // handle to hold queryAll values typedef struct { @@ -31,6 +33,28 @@ typedef struct { size_t len; } queryHandleT; +static json_object *afbJsonType; + + +// Sample Generic Ping Debug API +PUBLIC json_object* apiPingTest(AFB_request *request, void *pluginHandle) { + static pingcount = 0; + json_object *response; + char query [512]; + int len; + + // request all query key/value + len = getQueryAll (request, query, sizeof(query)); + if (len == 0) strcpy (query,"NoSearchQueryList"); + + // 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 count=%d CtxtId=%d Loa=%d query={%s} Handle=0x%x PostData: \'%s\' " + , pingcount++, request->client->cid, request->loa, query, request->post, pluginHandle); + return (response); +} // Helper to retrieve argument from connection PUBLIC const char* getQueryValue(AFB_request * request, char *name) { @@ -47,38 +71,18 @@ STATIC int getQueryCB (void*handle, enum MHD_ValueKind kind, const char *key, co } // Helper to retrieve argument from connection -PUBLIC const char* getQueryAll(AFB_request * request, char *buffer, size_t len) { +PUBLIC int getQueryAll(AFB_request * request, char *buffer, size_t len) { queryHandleT query; - + buffer[0] = '\0'; // start with an empty string query.msg= buffer; query.len= len; query.idx= 0; MHD_get_connection_values (request->connection, MHD_GET_ARGUMENT_KIND, getQueryCB, &query); - return (query.msg); + return (len); } - -// Sample Generic Ping Debug API -PUBLIC json_object* apiPingTest(AFB_session *session, AFB_request *request, void* handle) { - 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} PostData: \'%s\' ", pingcount++, query, request->post); - return (response); -} - - // Because of POST call multiple time requestApi we need to free POST handle here - STATIC void endRequest(void *cls, struct MHD_Connection *connection, void **con_cls, enum MHD_RequestTerminationCode toe) { AFB_HttpPost *posthandle = *con_cls; @@ -91,8 +95,8 @@ STATIC void endRequest(void *cls, struct MHD_Connection *connection, void **con_ } // Check of apiurl is declare in this plugin and call it -STATIC json_object * callPluginApi(AFB_plugin *plugin, AFB_session *session, AFB_request *request) { - json_object *response; +STATIC AFB_error callPluginApi(AFB_plugin *plugin, AFB_request *request) { + json_object *jresp, *jcall; int idx, status, sig; int signals[]= {SIGALRM, SIGSEGV, SIGFPE, 0}; @@ -100,83 +104,110 @@ STATIC json_object * callPluginApi(AFB_plugin *plugin, AFB_session *session, AFB | Signal handler defined inside CallPluginApi to access Request +---------------------------------------------------------------- */ void pluginError (int signum) { - sigset_t sigset; - - // unlock timeout signal to allow a new signal to come + AFB_clientCtx *context; + + // unlock signal to allow a new signal to come sigemptyset (&sigset); - sigaddset (&sigset, SIGALRM); + sigaddset (&sigset, signum); sigprocmask (SIG_UNBLOCK, &sigset, 0); fprintf (stderr, "Oops:%s Plugin Api Timeout timeout\n", configTime()); longjmp (request->checkPluginCall, signum); } + // If a plugin hold this urlpath call its callback for (idx = 0; plugin->apis[idx].callback != NULL; idx++) { if (!strcmp(plugin->apis[idx].name, request->api)) { + // prepare an object to store calling values + jcall=json_object_new_object(); + json_object_object_add(jcall, "prefix", json_object_new_string (plugin->prefix)); + json_object_object_add(jcall, "api" , json_object_new_string (plugin->apis[idx].name)); + // save context before calling the API status = setjmp (request->checkPluginCall); - if (status != 0) { - response = jsonNewMessage(AFB_FATAL, "Plugin Call Fail prefix=%s api=%s info=%s", plugin->prefix, request->api, plugin->info); + if (status != 0) { + + // Plugin aborted somewhere during its execution + json_object_object_add(jcall, "status", json_object_new_string ("abort")); + json_object_object_add(jcall, "info" , json_object_new_string ("Plugin broke during execution")); + json_object_object_add(request->jresp, "request", jcall); + } else { - if (session->config->apiTimeout > 0) { + // If timeout protection==0 we are in debug and we do not apply signal protection + if (request->config->apiTimeout > 0) { for (sig=0; signals[sig] != 0; sig++) { if (signal (signals[sig], pluginError) == SIG_ERR) { - fprintf (stderr, "%s ERR: main no Signal/timeout handler installed.", configTime()); - return NULL; + request->errcode = MHD_HTTP_UNPROCESSABLE_ENTITY; + fprintf (stderr, "%s ERR: main no Signal/timeout handler installed.", configTime()); + return AFB_FAIL; } } - - // Trigger a timer to protect plugin for no return API - alarm (session->config->apiTimeout); + // Trigger a timer to protect from unacceptable long time execution + alarm (request->config->apiTimeout); + } + + // add client context to request + ctxClientGet(request); + + // Effectively call the API with a subset of the context + jresp = plugin->apis[idx].callback(request, plugin->handle); + + // API should return NULL of a valid Json Object + if (jresp == NULL) { + json_object_object_add(jcall, "status", json_object_new_string ("null")); + json_object_object_add(request->jresp, "request", jcall); + request->errcode = MHD_HTTP_NO_RESPONSE; + + } else { + json_object_object_add(jcall, "status", json_object_new_string ("processed")); + json_object_object_add(request->jresp, "request", jcall); + json_object_object_add(request->jresp, "response", jresp); } - - response = plugin->apis[idx].callback(session, request, plugin->apis[idx].handle); - if (response != NULL) json_object_object_add(response, "jtype", plugin->jtype); - // cancel timeout and plugin signal handle before next call - if (session->config->apiTimeout > 0) { + if (request->config->apiTimeout > 0) { alarm (0); for (sig=0; signals[sig] != 0; sig++) { signal (signals[sig], SIG_DFL); } - } - } - return (response); + } + } + return (AFB_DONE); } } - return (NULL); + return (AFB_FAIL); } // process rest API query - PUBLIC int doRestApi(struct MHD_Connection *connection, AFB_session *session, const char* url, const char *method , const char *upload_data, size_t *upload_data_size, void **con_cls) { static int postcount = 0; // static counter to debug POST protocol - char *baseurl, *baseapi, *urlcpy1, *urlcpy2, *query; - json_object *jsonResponse, *errMessage; + char *baseurl, *baseapi, *urlcpy1, *urlcpy2, *query, *token, *uuid; + json_object *errMessage; + AFB_error status; struct MHD_Response *webResponse; const char *serialized, parsedurl; AFB_request request; AFB_HttpPost *posthandle = *con_cls; + AFB_clientCtx clientCtx; int idx, ret; // 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 Plugin/API call url=%s", url); + errMessage = jsonNewMessage(AFB_FATAL, "Invalid API call url=[%s]", url); goto ExitOnError; } baseapi = strsep(&urlcpy2, "/"); if (baseapi == NULL) { - errMessage = jsonNewMessage(AFB_FATAL, "Invalid Plugin/API call url=%s/%s", baseurl, url); + errMessage = jsonNewMessage(AFB_FATAL, "Invalid API call url=[%s]", url); goto ExitOnError; } @@ -243,45 +274,61 @@ PUBLIC int doRestApi(struct MHD_Connection *connection, AFB_session *session, co } else { request.post = NULL; }; - - + // build request structure memset(&request, 0, sizeof (request)); request.connection = connection; - request.url = url; + request.config = session->config; + request.url = url; request.plugin = baseurl; - request.api = baseapi; + request.api = baseapi; + request.jresp = json_object_new_object(); + + // increase reference count and add jtype to response + json_object_get (afbJsonType); + json_object_object_add (request.jresp, "jtype", afbJsonType); // Search for a plugin with this urlpath for (idx = 0; session->plugins[idx] != NULL; idx++) { if (!strcmp(session->plugins[idx]->prefix, baseurl)) { - jsonResponse = callPluginApi(session->plugins[idx], session, &request); - free(urlcpy1); + status =callPluginApi(session->plugins[idx], &request); break; } } // No plugin was found if (session->plugins[idx] == NULL) { - errMessage = jsonNewMessage(AFB_FATAL, "No Plugin for %s", baseurl); - free(urlcpy1); + errMessage = jsonNewMessage(AFB_FATAL, "No Plugin=[%s]", request.plugin); goto ExitOnError; } // plugin callback did not return a valid Json Object - if (jsonResponse == NULL) { - errMessage = jsonNewMessage(AFB_FATAL, "No Plugin/API for %s/%s", baseurl, baseapi); + if (status != AFB_DONE) { + errMessage = jsonNewMessage(AFB_FATAL, "No API=[%s] for Plugin=[%s]", request.api, request.plugin); goto ExitOnError; } - serialized = json_object_to_json_string(jsonResponse); + serialized = json_object_to_json_string(request.jresp); webResponse = MHD_create_response_from_buffer(strlen(serialized), (void*) serialized, MHD_RESPMEM_MUST_COPY); - - ret = MHD_queue_response(connection, MHD_HTTP_OK, webResponse); + free(urlcpy1); + + // client did not pass token on URI let's use cookies + if ((!request.restfull) && (request.client != NULL)) { + char cookie[64]; + snprintf (cookie, sizeof (cookie), "%s=%s", COOKIE_NAME, request.client->uuid); + MHD_add_response_header (webResponse, MHD_HTTP_HEADER_SET_COOKIE, cookie); + // if(verbose) fprintf(stderr,"Cookie: [%s]\n", cookie); + } + + // if requested add an error status + if (request.errcode != 0) ret=MHD_queue_response (connection, request.errcode, webResponse); + else ret = MHD_queue_response(connection, MHD_HTTP_OK, webResponse); + MHD_destroy_response(webResponse); - json_object_put(jsonResponse); // decrease reference rqtcount to free the json object + json_object_put(request.jresp); // decrease reference rqtcount to free the json object return ret; ExitOnError: + free(urlcpy1); serialized = json_object_to_json_string(errMessage); webResponse = MHD_create_response_from_buffer(strlen(serialized), (void*) serialized, MHD_RESPMEM_MUST_COPY); ret = MHD_queue_response(connection, MHD_HTTP_BAD_REQUEST, webResponse); @@ -292,12 +339,12 @@ ExitOnError: // Loop on plugins. Check that they have the right type, prepare a JSON object with prefix -STATIC AFB_plugin ** RegisterPlugins(AFB_plugin **plugins) { - int idx; +STATIC AFB_plugin ** RegisterJsonPlugins(AFB_plugin **plugins) { + int idx, jdx; for (idx = 0; plugins[idx] != NULL; idx++) { - if (plugins[idx]->type != AFB_PLUGIN) { - fprintf(stderr, "ERROR: AFSV plugin[%d] invalid type=%d != %d\n", idx, AFB_PLUGIN, plugins[idx]->type); + if (plugins[idx]->type != AFB_PLUGIN_JSON) { + fprintf(stderr, "ERROR: AFSV plugin[%d] invalid type=%d != %d\n", idx, AFB_PLUGIN_JSON, plugins[idx]->type); } else { // some sanity controls if ((plugins[idx]->prefix == NULL) || (plugins[idx]->info == NULL) || (plugins[idx]->apis == NULL)) { @@ -308,13 +355,25 @@ STATIC AFB_plugin ** RegisterPlugins(AFB_plugin **plugins) { } if (verbose) fprintf(stderr, "Loading plugin[%d] prefix=[%s] info=%s\n", idx, plugins[idx]->prefix, plugins[idx]->info); - - // Prepare Plugin name to be added into each API response + + // Prebuild plugin jtype to boost API response plugins[idx]->jtype = json_object_new_string(plugins[idx]->prefix); json_object_get(plugins[idx]->jtype); // increase reference count to make it permanent - - // compute urlprefix lenght plugins[idx]->prefixlen = strlen(plugins[idx]->prefix); + + + // Prebuild each API jtype to boost API json response + for (jdx = 0; plugins[idx]->apis[jdx].name != NULL; jdx++) { + AFB_privateApi *private = malloc (sizeof (AFB_privateApi)); + if (plugins[idx]->apis[jdx].private != NULL) { + fprintf (stderr, "WARNING: plugin=%s api=%s private handle should be NULL=0x%x\n" + ,plugins[idx]->prefix,plugins[idx]->apis[jdx].name, plugins[idx]->apis[jdx].private); + } + private->len = strlen (plugins[idx]->apis[jdx].name); + private->jtype=json_object_new_string(plugins[idx]->apis[jdx].name); + json_object_get(private->jtype); // increase reference count to make it permanent + plugins[idx]->apis[jdx].private = private; + } } } return (plugins); @@ -322,12 +381,17 @@ STATIC AFB_plugin ** RegisterPlugins(AFB_plugin **plugins) { void initPlugins(AFB_session *session) { static AFB_plugin * plugins[10]; - - plugins[0] = afsvRegister(session), - plugins[1] = dbusRegister(session), - plugins[2] = alsaRegister(session), - plugins[3] = NULL; - + afbJsonType = json_object_new_string (AFB_MSG_JTYPE); + int i = 0; + + plugins[i++] = afsvRegister(session), + plugins[i++] = dbusRegister(session), + plugins[i++] = alsaRegister(session), +#ifdef HAVE_RADIO_PLUGIN + plugins[i++] = radioRegister(session), +#endif + plugins[i++] = NULL; + // complete plugins and save them within current sessions - session->plugins = RegisterPlugins(plugins); + session->plugins = RegisterJsonPlugins(plugins); }