From: José Bollo Date: Tue, 10 May 2016 11:47:58 +0000 (+0200) Subject: Refactoring requests and context handling X-Git-Tag: blowfish_2.0.1~145 X-Git-Url: https://gerrit.automotivelinux.org/gerrit/gitweb?p=src%2Fapp-framework-binder.git;a=commitdiff_plain;h=f1b901ed676b2d45ec8e6ae3d6ef2f94d79f9ee6 Refactoring requests and context handling Also adds a first (untested) implmentation of the afb services over dbus. Change-Id: Id1bdeccf75f3a70d3658bdaf0510d6e7b97f6c32 Signed-off-by: José Bollo --- diff --git a/include/afb-plugin.h b/include/afb-plugin.h index ce78e840..63617cbe 100644 --- a/include/afb-plugin.h +++ b/include/afb-plugin.h @@ -31,11 +31,12 @@ enum AFB_pluginE /* Enum for Session/Token/Authentication middleware */ enum AFB_sessionE { - AFB_SESSION_NONE, - AFB_SESSION_CREATE, - AFB_SESSION_CLOSE, - AFB_SESSION_RENEW, - AFB_SESSION_CHECK + AFB_SESSION_NONE = 0, + AFB_SESSION_CREATE = 1, + AFB_SESSION_CLOSE = 2, + AFB_SESSION_RENEW = 4, + AFB_SESSION_CHECK = 8, + AFB_SESSION_MASK = 15 }; /* API definition */ diff --git a/include/afb-req-itf.h b/include/afb-req-itf.h index 061c1f0d..a975503d 100644 --- a/include/afb-req-itf.h +++ b/include/afb-req-itf.h @@ -26,28 +26,30 @@ struct afb_arg { }; struct afb_req_itf { - struct json_object *(*json)(void *req_closure); - struct afb_arg (*get)(void *req_closure, const char *name); - void (*success)(void *req_closure, struct json_object *obj, const char *info); - void (*fail)(void *req_closure, const char *status, const char *info); - const char *(*raw)(void *req_closure, size_t *size); - void (*send)(void *req_closure, const char *buffer, size_t size); - void *(*context_get)(void *ctx_closure); - void (*context_set)(void *ctx_closure, void *value, void (*free_value)(void*)); - int (*session_create)(void *req_closure); - int (*session_check)(void *req_closure, int refresh); - void (*session_close)(void *req_closure); + struct json_object *(*json)(void *closure); + struct afb_arg (*get)(void *closure, const char *name); + + void (*success)(void *closure, struct json_object *obj, const char *info); + void (*fail)(void *closure, const char *status, const char *info); + + const char *(*raw)(void *closure, size_t *size); + void (*send)(void *closure, const char *buffer, size_t size); + + void *(*context_get)(void *closure); + void (*context_set)(void *closure, void *value, void (*free_value)(void*)); + + void (*addref)(void *closure); + void (*unref)(void *closure); }; struct afb_req { const struct afb_req_itf *itf; - void *req_closure; - void *ctx_closure; + void *closure; }; static inline struct afb_arg afb_req_get(struct afb_req req, const char *name) { - return req.itf->get(req.req_closure, name); + return req.itf->get(req.closure, name); } static inline const char *afb_req_value(struct afb_req req, const char *name) @@ -62,37 +64,37 @@ static inline const char *afb_req_path(struct afb_req req, const char *name) static inline struct json_object *afb_req_json(struct afb_req req) { - return req.itf->json(req.req_closure); + return req.itf->json(req.closure); } static inline void afb_req_success(struct afb_req req, struct json_object *obj, const char *info) { - req.itf->success(req.req_closure, obj, info); + req.itf->success(req.closure, obj, info); } static inline void afb_req_fail(struct afb_req req, const char *status, const char *info) { - req.itf->fail(req.req_closure, status, info); + req.itf->fail(req.closure, status, info); } static inline const char *afb_req_raw(struct afb_req req, size_t *size) { - return req.itf->raw(req.req_closure, size); + return req.itf->raw(req.closure, size); } static inline void afb_req_send(struct afb_req req, const char *buffer, size_t size) { - req.itf->send(req.req_closure, buffer, size); + req.itf->send(req.closure, buffer, size); } static inline void *afb_req_context_get(struct afb_req req) { - return req.itf->context_get(req.ctx_closure); + return req.itf->context_get(req.closure); } static inline void afb_req_context_set(struct afb_req req, void *value, void (*free_value)(void*)) { - return req.itf->context_set(req.ctx_closure, value, free_value); + return req.itf->context_set(req.closure, value, free_value); } static inline void afb_req_context_clear(struct afb_req req) @@ -100,25 +102,14 @@ static inline void afb_req_context_clear(struct afb_req req) afb_req_context_set(req, NULL, NULL); } -static inline int afb_req_session_create(struct afb_req req) +static inline void afb_req_addref(struct afb_req req) { - int result = req.itf->session_create(req.req_closure); - if (!result) - afb_req_fail(req, "fail", "Can't create the session"); - return result; -} - -static inline int afb_req_session_check(struct afb_req req, int refresh) -{ - int result = req.itf->session_check(req.req_closure, refresh); - if (!result) - afb_req_fail(req, "fail", "Token chek failed for the session"); - return result; + return req.itf->addref(req.closure); } -static inline void afb_req_session_close(struct afb_req req) +static inline void afb_req_unref(struct afb_req req) { - req.itf->session_close(req.req_closure); + return req.itf->unref(req.closure); } #include @@ -126,8 +117,10 @@ static inline void afb_req_session_close(struct afb_req req) static inline struct afb_req *afb_req_store(struct afb_req req) { struct afb_req *result = malloc(sizeof *result); - if (result != NULL) + if (result != NULL) { *result = req; + afb_req_addref(req); + } return result; } @@ -135,6 +128,7 @@ static inline struct afb_req afb_req_unstore(struct afb_req *req) { struct afb_req result = *req; free(req); + afb_req_unref(result); return result; } diff --git a/plugins/afm-main-plugin/afm-main-plugin.c b/plugins/afm-main-plugin/afm-main-plugin.c index c6408a51..8b26fe7e 100644 --- a/plugins/afm-main-plugin/afm-main-plugin.c +++ b/plugins/afm-main-plugin/afm-main-plugin.c @@ -62,6 +62,7 @@ static struct memo *make_memo(struct afb_req request, const char *method) if (memo != NULL) { memo->request = request; memo->method = method; + afb_req_addref(request); } return memo; } @@ -109,6 +110,7 @@ static void embed_call_void_callback(int status, struct json_object *obj, struct afb_req_success(memo->request, obj, NULL); } } + afb_req_unref(memo->request); free(memo); } @@ -134,6 +136,7 @@ static void call_appid_callback(int status, struct json_object *obj, struct memo obj = json_object_get(obj); afb_req_success(memo->request, obj, NULL); } + afb_req_unref(memo->request); free(memo); } diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index bc5fd4f0..707b2e09 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,20 +1,22 @@ ADD_LIBRARY(src OBJECT - main.c - session.c + afb-api-dbus.c + afb-api-so.c + afb-apis.c + afb-common.c + afb-context.c + afb-hreq.c afb-hsrv.c afb-hswitch.c - afb-apis.c - afb-api-so.c afb-method.c - afb-hreq.c + afb-msg-json.c afb-websock.c - afb-ws.c afb-ws-json.c - afb-msg-json.c - afb-common.c - websock.c + afb-ws.c + main.c + session.c verbose.c + websock.c ) INCLUDE_DIRECTORIES(${include_dirs}) diff --git a/src/afb-api-dbus.c b/src/afb-api-dbus.c new file mode 100644 index 00000000..818f8d11 --- /dev/null +++ b/src/afb-api-dbus.c @@ -0,0 +1,511 @@ +/* + * Copyright (C) 2015 "IoT.bzh" + * Author José Bollo + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include + +#include +#include + +#include "afb-plugin.h" +#include "afb-req-itf.h" + +#include "afb-common.h" + +#include "session.h" +#include "afb-apis.h" +#include "afb-api-so.h" +#include "afb-context.h" +#include "verbose.h" + +static const char DEFAULT_PATH_PREFIX[] = "/org/agl/afb/api/"; + +/* + * The path given are of the form + * system:/org/agl/afb/api/... + * or + * user:/org/agl/afb/api/... + */ +struct api_dbus +{ + struct sd_bus *sdbus; /* the bus */ + char *path; /* path of the object for the API */ + char *name; /* name/interface of the object */ + char *api; /* api name of the interface */ +}; + +#define RETOK 1 +#define RETERR 2 +#define RETRAW 3 + +/******************* common part **********************************/ + +/* + * create a structure api_dbus connected on either the system + * bus if 'system' is not null or on the user bus. The connection + * is established for either emiting/receiving on 'path' being of length + * 'pathlen'. + */ +static struct api_dbus *make_api_dbus_3(int system, const char *path, size_t pathlen) +{ + struct api_dbus *api; + struct sd_bus *sdbus; + char *ptr; + + /* allocates the structure */ + api = calloc(1, sizeof *api + 1 + pathlen + pathlen); + if (api == NULL) { + errno = ENOMEM; + goto error; + } + + /* init the structure's strings */ + + /* path is copied after the struct */ + api->path = (void*)(api+1); + strcpy(api->path, path); + + /* api name is at the end of the path */ + api->api = strrchr(api->path, '/'); + if (api->api == NULL) { + errno = EINVAL; + goto error2; + } + api->api++; + + /* the name/interface is copied after the path */ + api->name = &api->path[pathlen + 1]; + strcpy(api->name, &path[1]); + ptr = strchr(api->name, '/'); + while(ptr != NULL) { + *ptr = '.'; + ptr = strchr(ptr, '/'); + } + + /* choose the bus */ + sdbus = (system ? afb_common_get_system_bus : afb_common_get_user_bus)(); + if (sdbus == NULL) + goto error2; + + api->sdbus = sdbus; + return api; + +error2: + free(api); +error: + return NULL; +} + +/* + * create a structure api_dbus connected on either the system + * bus if 'system' is not null or on the user bus. The connection + * is established for either emiting/receiving on 'path'. + * If 'path' is not absolute, it is prefixed with DEFAULT_PATH_PREFIX. + */ +static struct api_dbus *make_api_dbus_2(int system, const char *path) +{ + size_t len; + char *ptr; + + /* check the length of the path */ + len = strlen(path); + if (len == 0) { + errno = EINVAL; + return NULL; + } + + /* if the path is absolute, creation now */ + if (path[0] == '/') + return make_api_dbus_3(system, path, len); + + /* compute the path prefixed with DEFAULT_PATH_PREFIX */ + assert(strlen(DEFAULT_PATH_PREFIX) > 0); + assert(DEFAULT_PATH_PREFIX[strlen(DEFAULT_PATH_PREFIX) - 1] == '/'); + len += strlen(DEFAULT_PATH_PREFIX); + ptr = alloca(len + 1); + strcpy(stpcpy(ptr, DEFAULT_PATH_PREFIX), path); + + /* creation for prefixed path */ + return make_api_dbus_3(system, ptr, len); +} + +/* + * create a structure api_dbus connected either emiting/receiving + * on 'path'. + * The path can be prefixed with "system:" or "user:" to select + * either the user or the system D-Bus. If none is set then user's + * bus is selected. + * If remaining 'path' is not absolute, it is prefixed with + * DEFAULT_PATH_PREFIX. + */ +static struct api_dbus *make_api_dbus(const char *path) +{ + const char *ptr; + size_t preflen; + + /* retrieves the prefix "scheme-like" part */ + ptr = strchr(path, ':'); + if (ptr == NULL) + return make_api_dbus_2(0, path); + + /* check the prefix part */ + preflen = (size_t)(ptr - path); + if (strncmp(path, "system", preflen) == 0) + return make_api_dbus_2(1, ptr + 1); + + if (strncmp(path, "user", preflen) == 0) + return make_api_dbus_2(0, ptr + 1); + + /* TODO: connect to a foreign D-Bus? */ + errno = EINVAL; + return NULL; +} + +static void destroy_api_dbus(struct api_dbus *api) +{ + free(api); +} + +/******************* client part **********************************/ + +/* + * structure for recording query data + */ +struct dbus_memo { + struct afb_req req; /* the request handle */ + struct afb_context *context; /* the context of the query */ +}; + +/* allocates and init the memorizing data */ +static struct dbus_memo *api_dbus_client_make_memo(struct afb_req req, struct afb_context *context) +{ + struct dbus_memo *memo; + + memo = malloc(sizeof *memo); + if (memo != NULL) { + afb_req_addref(req); + memo->req = req; + memo->context = context; + } + return memo; +} + +/* free and release the memorizing data */ +static void api_dbus_client_free_memo(struct dbus_memo *memo) +{ + afb_req_unref(memo->req); + free(memo); +} + +/* callback when received answer */ +static int api_dbus_client_on_reply(sd_bus_message *message, void *userdata, sd_bus_error *ret_error) +{ + int rc; + struct dbus_memo *memo; + const char *first, *second; + uint8_t type; + uint32_t flags; + + /* retrieve the recorded data */ + memo = userdata; + + /* get the answer */ + rc = sd_bus_message_read(message, "yssu", &type, &first, &second, &flags); + if (rc < 0) { + /* failing to have the answer */ + afb_req_fail(memo->req, "error", "dbus error"); + } else { + /* report the answer */ + memo->context->flags = (unsigned)flags; + switch(type) { + case RETOK: + afb_req_success(memo->req, json_tokener_parse(first), second); + break; + case RETERR: + afb_req_fail(memo->req, first, second); + break; + case RETRAW: + afb_req_send(memo->req, first, strlen(first)); + break; + default: + afb_req_fail(memo->req, "error", "dbus link broken"); + break; + } + } + api_dbus_client_free_memo(memo); + return 1; +} + +/* on call, propagate it to the dbus service */ +static void api_dbus_client_call(struct api_dbus *api, struct afb_req req, struct afb_context *context, const char *verb, size_t lenverb) +{ + size_t size; + int rc; + char *method = strndupa(verb, lenverb); + struct dbus_memo *memo; + + /* create the recording data */ + memo = api_dbus_client_make_memo(req, context); + if (memo == NULL) { + afb_req_fail(req, "error", "out of memory"); + return; + } + + /* makes the call */ + rc = sd_bus_call_method_async(api->sdbus, NULL, + api->name, api->path, api->name, method, + api_dbus_client_on_reply, memo, + "ssu", + afb_req_raw(req, &size), + ctxClientGetUuid(context->session), + (uint32_t)context->flags); + + /* if there was an error report it directly */ + if (rc < 0) { + errno = -rc; + afb_req_fail(req, "error", "dbus error"); + api_dbus_client_free_memo(memo); + } +} + +/* adds a afb-dbus-service client api */ +int afb_api_dbus_add_client(const char *path) +{ + struct api_dbus *api; + struct afb_api afb_api; + + /* create the dbus client api */ + api = make_api_dbus(path); + if (api == NULL) + goto error; + + /* record it as an API */ + afb_api.closure = api; + afb_api.call = (void*)api_dbus_client_call; + if (afb_apis_add(api->api, afb_api) < 0) + goto error2; + + return 0; + +error2: + destroy_api_dbus(api); +error: + return -1; +} + +/******************* dbus request part for server *****************/ + +/* + * structure for a dbus request + */ +struct dbus_req { + struct afb_context context; /* the context, should be THE FIRST */ + sd_bus_message *message; /* the incoming request message */ + const char *request; /* the readen request as string */ + struct json_object *json; /* the readen request as object */ + int refcount; /* reference count of the request */ +}; + +/* increment the reference count of the request */ +static void dbus_req_addref(struct dbus_req *dreq) +{ + dreq->refcount++; +} + +/* decrement the reference count of the request and free/release it on falling to null */ +static void dbus_req_unref(struct dbus_req *dreq) +{ + if (dreq == NULL || --dreq->refcount) + return; + + afb_context_disconnect(&dreq->context); + json_object_put(dreq->json); + sd_bus_message_unref(dreq->message); + free(dreq); +} + +/* get the object of the request */ +static struct json_object *dbus_req_json(struct dbus_req *dreq) +{ + if (dreq->json == NULL) { + dreq->json = json_tokener_parse(dreq->request); + if (dreq->json == NULL) { + /* lazy error detection of json request. Is it to improve? */ + dreq->json = json_object_new_string(dreq->request); + } + } + return dreq->json; +} + +/* get the argument of the request of 'name' */ +static struct afb_arg dbus_req_get(struct dbus_req *dreq, const char *name) +{ + struct afb_arg arg; + struct json_object *value, *root; + + root = dbus_req_json(dreq); + if (root != NULL && json_object_object_get_ex(root, name, &value)) { + arg.name = name; + arg.value = json_object_get_string(value); + } else { + arg.name = NULL; + arg.value = NULL; + } + arg.path = NULL; + return arg; +} + +static void dbus_req_reply(struct dbus_req *dreq, uint8_t type, const char *first, const char *second) +{ + int rc; + rc = sd_bus_reply_method_return(dreq->message, + "yssu", type, first, second, (uint32_t)dreq->context.flags); +} + +static void dbus_req_success(struct dbus_req *dreq, struct json_object *obj, const char *info) +{ + dbus_req_reply(dreq, RETOK, json_object_to_json_string(obj), info); +} + +static void dbus_req_fail(struct dbus_req *dreq, const char *status, const char *info) +{ + dbus_req_reply(dreq, RETERR, status, info); +} + +static const char *dbus_req_raw(struct dbus_req *dreq, size_t *size) +{ + if (size != NULL) + *size = strlen(dreq->request); + return dreq->request; +} + +static void dbus_req_send(struct dbus_req *dreq, const char *buffer, size_t size) +{ + /* TODO: how to put sized buffer as strings? things aren't clear here!!! */ + dbus_req_reply(dreq, RETRAW, buffer, ""); +} + +struct afb_req_itf dbus_req_itf = { + .json = (void*)dbus_req_json, + .get = (void*)dbus_req_get, + .success = (void*)dbus_req_success, + .fail = (void*)dbus_req_fail, + .raw = (void*)dbus_req_raw, + .send = (void*)dbus_req_send, + .context_get = (void*)afb_context_get, + .context_set = (void*)afb_context_set, + .addref = (void*)dbus_req_addref, + .unref = (void*)dbus_req_unref +}; + +/******************* server part **********************************/ + +/* called when the object for the service is called */ +static int api_dbus_server_on_object_called(sd_bus_message *message, void *userdata, sd_bus_error *ret_error) +{ + int rc; + const char *method; + const char *uuid; + struct dbus_req *dreq; + struct api_dbus *api = userdata; + struct afb_req areq; + uint32_t flags; + + /* check the interface */ + if (strcmp(sd_bus_message_get_interface(message), api->name) != 0) + return 0; + + /* get the method */ + method = sd_bus_message_get_member(message); + + /* create the request */ + dreq = calloc(1 , sizeof *dreq); + if (dreq == NULL) { + sd_bus_reply_method_errorf(message, SD_BUS_ERROR_NO_MEMORY, "out of memory"); + return 1; + } + + /* get the data */ + rc = sd_bus_message_read(message, "ssu", &dreq->request, &uuid, &flags); + if (rc < 0) { + sd_bus_reply_method_errorf(message, SD_BUS_ERROR_INVALID_SIGNATURE, "invalid signature"); + free(dreq); + return 1; + } + + /* connect to the context */ + if (afb_context_connect(&dreq->context, uuid, NULL) < 0) { + sd_bus_reply_method_errorf(message, SD_BUS_ERROR_NO_MEMORY, "out of memory"); + free(dreq); + return 1; + } + + /* fulfill the request and emit it */ + dreq->context.flags = flags; + dreq->message = sd_bus_message_ref(message); + dreq->json = NULL; + dreq->refcount = 1; + areq.itf = &dbus_req_itf; + areq.closure = dreq; + afb_apis_call_(areq, &dreq->context, api->api, method); + dbus_req_unref(dreq); + return 1; +} + +/* create the service */ +int afb_api_dbus_add_server(const char *path) +{ + int rc; + struct api_dbus *api; + sd_bus_slot *slot; + + /* get the dbus api object connected */ + api = make_api_dbus(path); + if (api == NULL) + goto error; + + /* request the service object name */ + rc = sd_bus_request_name(api->sdbus, api->name, 0); + if (rc < 0) { + errno = -rc; + ERROR("can't register name %s", api->name); + goto error2; + } + + /* connect the service to the dbus object */ + rc = sd_bus_add_object(api->sdbus, &slot, api->path, api_dbus_server_on_object_called, api); + if (rc < 0) { + errno = -rc; + ERROR("can't add dbus object %s for %s", api->path, api->name); + goto error3; + } + INFO("afb service over dbus installed, name %s, path %s", api->name, api->path); + + return 0; +error3: + sd_bus_release_name(api->sdbus, api->name); +error2: + destroy_api_dbus(api); +error: + return -1; +} + + diff --git a/src/afb-api-dbus.h b/src/afb-api-dbus.h new file mode 100644 index 00000000..8ea1909b --- /dev/null +++ b/src/afb-api-dbus.h @@ -0,0 +1,25 @@ +/* + * 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. + */ + + +#pragma once + +extern int afb_api_dbus_add_client(const char *path); + +extern int afb_api_dbus_add_server(const char *path); + + diff --git a/src/afb-api-so.c b/src/afb-api-so.c index 89418fb0..38621ecf 100644 --- a/src/afb-api-so.c +++ b/src/afb-api-so.c @@ -37,6 +37,7 @@ #include "session.h" #include "afb-common.h" +#include "afb-context.h" #include "afb-apis.h" #include "afb-api-so.h" #include "verbose.h" @@ -126,32 +127,36 @@ static void trapping_call(struct afb_req req, void(*cb)(struct afb_req)) error_handler = older; } -static void call_check(struct afb_req req, const struct AFB_restapi *verb) +static void call_check(struct afb_req req, struct afb_context *context, 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)) + int stag = (int)(verb->session & AFB_SESSION_MASK); + + if (stag != AFB_SESSION_NONE) { + if (!afb_context_check(context)) { + afb_context_close(context); + afb_req_fail(req, "failed", "invalid token's identity"); return; - break; - case AFB_SESSION_CLOSE: - case AFB_SESSION_CHECK: - if (!afb_req_session_check(req, 0)) + } + } + + if ((stag & AFB_SESSION_CREATE) != 0) { + if (!afb_context_create(context)) { + afb_context_close(context); + afb_req_fail(req, "failed", "invalid creation state"); return; - break; - case AFB_SESSION_NONE: - default: - break; + } } + + if ((stag & (AFB_SESSION_CREATE | AFB_SESSION_RENEW)) != 0) + afb_context_refresh(context); + + if ((stag & AFB_SESSION_CLOSE) != 0) + afb_context_close(context); + 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) +static void call(struct api_so_desc *desc, struct afb_req req, struct afb_context *context, const char *verb, size_t lenverb) { const struct AFB_restapi *v; @@ -159,7 +164,7 @@ static void call(struct api_so_desc *desc, struct afb_req req, const char *verb, while (v->name && (strncasecmp(v->name, verb, lenverb) || v->name[lenverb])) v++; if (v->name) - call_check(req, v); + call_check(req, context, v); else afb_req_fail_f(req, "unknown-verb", "verb %.*s unknown within api %s", (int)lenverb, verb, desc->plugin->prefix); } diff --git a/src/afb-apis.c b/src/afb-apis.c index bcb42429..da0b98f0 100644 --- a/src/afb-apis.c +++ b/src/afb-apis.c @@ -25,6 +25,7 @@ #include "session.h" #include "verbose.h" #include "afb-apis.h" +#include "afb-context.h" #include "afb-req-itf.h" struct api_desc { @@ -83,12 +84,12 @@ error: return -1; } -void afb_apis_call_(struct afb_req req, struct AFB_clientCtx *context, const char *api, const char *verb) +void afb_apis_call_(struct afb_req req, struct afb_context *context, const char *api, const char *verb) { afb_apis_call(req, context, api, strlen(api), verb, strlen(verb)); } -void afb_apis_call(struct afb_req req, struct AFB_clientCtx *context, const char *api, size_t lenapi, const char *verb, size_t lenverb) +void afb_apis_call(struct afb_req req, struct afb_context *context, const char *api, size_t lenapi, const char *verb, size_t lenverb) { int i; const struct api_desc *a; @@ -96,8 +97,8 @@ void afb_apis_call(struct afb_req req, struct AFB_clientCtx *context, const char a = apis_array; for (i = 0 ; i < apis_count ; i++, a++) { if (a->namelen == lenapi && !strncasecmp(a->name, api, lenapi)) { - req.ctx_closure = &context->contexts[i]; - a->api.call(a->api.closure, req, verb, lenverb); + context->api_index = i; + a->api.call(a->api.closure, req, context, verb, lenverb); return; } } diff --git a/src/afb-apis.h b/src/afb-apis.h index 795054aa..3e75130e 100644 --- a/src/afb-apis.h +++ b/src/afb-apis.h @@ -18,18 +18,18 @@ #pragma once struct afb_req; -struct AFB_clientCtx; +struct afb_context; struct afb_api { void *closure; - void (*call)(void *closure, struct afb_req req, const char *verb, size_t lenverb); + void (*call)(void *closure, struct afb_req req, struct afb_context *context, const char *verb, size_t lenverb); }; 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); -extern void afb_apis_call_(struct afb_req req, struct AFB_clientCtx *context, const char *api, const char *verb); +extern void afb_apis_call(struct afb_req req, struct afb_context *context, const char *api, size_t lenapi, const char *verb, size_t lenverb); +extern void afb_apis_call_(struct afb_req req, struct afb_context *context, const char *api, const char *verb); diff --git a/src/afb-context.c b/src/afb-context.c new file mode 100644 index 00000000..2f391dec --- /dev/null +++ b/src/afb-context.c @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2015 "IoT.bzh" + * Author "Fulup Ar Foll" + * + * 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 "session.h" +#include "afb-context.h" + + +void afb_context_init(struct afb_context *context, struct AFB_clientCtx *session, const char *token) +{ + assert(session != NULL); + + /* reset the context for the session */ + context->session = session; + context->flags = 0; + context->api_index = -1; + + /* check the token */ + if (token != NULL) { + if (ctxTokenCheck(session, token)) + context->validated = 1; + else + context->invalidated = 1; + } +} + +int afb_context_connect(struct afb_context *context, const char *uuid, const char *token) +{ + int created; + struct AFB_clientCtx *session; + + session = ctxClientGetSession (uuid, &created); + if (session == NULL) + return -1; + afb_context_init(context, session, token); + if (created) + context->created = 1; + return 0; +} + +void afb_context_disconnect(struct afb_context *context) +{ + if (context->session != NULL) { + if (context->closing && !context->closed) { + context->closed = 1; + ctxClientClose(context->session); + } + ctxClientUnref(context->session); + } +} + +const char *afb_context_sent_token(struct afb_context *context) +{ + if (context->session == NULL || context->closing) + return NULL; + if (!(context->created || context->refreshing)) + return NULL; + if (!context->refreshed) { + ctxTokenNew (context->session); + context->refreshed = 1; + } + return ctxClientGetToken(context->session); +} + +const char *afb_context_sent_uuid(struct afb_context *context) +{ + if (context->session == NULL || context->closing) + return NULL; + if (!context->created) + return NULL; + return ctxClientGetUuid(context->session); +} + +void *afb_context_get(struct afb_context *context) +{ + assert(context->session != NULL); + return ctxClientValueGet(context->session, context->api_index); +} + +void afb_context_set(struct afb_context *context, void *value, void (*free_value)(void*)) +{ + assert(context->session != NULL); + return ctxClientValueSet(context->session, context->api_index, value, free_value); +} + +void afb_context_close(struct afb_context *context) +{ + context->closing = 1; +} + +void afb_context_refresh(struct afb_context *context) +{ + context->refreshing = 1; +} + +int afb_context_check(struct afb_context *context) +{ + return context->validated; +} + +int afb_context_create(struct afb_context *context) +{ + return context->created; +} diff --git a/src/afb-context.h b/src/afb-context.h new file mode 100644 index 00000000..eeb4def3 --- /dev/null +++ b/src/afb-context.h @@ -0,0 +1,53 @@ +/* + * 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. + */ + +#pragma once + +struct AFB_clientCtx; + +struct afb_context +{ + struct AFB_clientCtx *session; + union { + unsigned flags; + struct { + unsigned created: 1; + unsigned validated: 1; + unsigned invalidated: 1; + unsigned refreshing: 1; + unsigned refreshed: 1; + unsigned closing: 1; + unsigned closed: 1; + }; + }; + int api_index; +}; + +extern void afb_context_init(struct afb_context *context, struct AFB_clientCtx *session, const char *token); +extern int afb_context_connect(struct afb_context *context, const char *uuid, const char *token); +extern void afb_context_disconnect(struct afb_context *context); +extern const char *afb_context_sent_token(struct afb_context *context); +extern const char *afb_context_sent_uuid(struct afb_context *context); + +extern void *afb_context_get(struct afb_context *context); +extern void afb_context_set(struct afb_context *context, void *value, void (*free_value)(void*)); + +extern void afb_context_close(struct afb_context *context); +extern void afb_context_refresh(struct afb_context *context); +extern int afb_context_check(struct afb_context *context); +extern int afb_context_create(struct afb_context *context); + diff --git a/src/afb-hreq.c b/src/afb-hreq.c index d0adda6e..518bc5cb 100644 --- a/src/afb-hreq.c +++ b/src/afb-hreq.c @@ -35,6 +35,7 @@ #include "afb-method.h" #include "afb-req-itf.h" #include "afb-msg-json.h" +#include "afb-context.h" #include "afb-hreq.h" #include "session.h" #include "verbose.h" @@ -55,12 +56,15 @@ static char *cookie_name = NULL; static char *cookie_setter = NULL; static char *tmp_pattern = NULL; +/* + * Structure for storing key/values read from POST requests + */ struct hreq_data { - struct hreq_data *next; - char *key; - size_t length; - char *value; - char *path; + struct hreq_data *next; /* chain to next data */ + char *key; /* key name */ + size_t length; /* length of the value (used for appending) */ + char *value; /* the value (or original filename) */ + char *path; /* path of the file saved */ }; static struct json_object *req_json(struct afb_hreq *hreq); @@ -69,9 +73,6 @@ static void req_fail(struct afb_hreq *hreq, const char *status, const char *info static void req_success(struct afb_hreq *hreq, json_object *obj, const char *info); static const char *req_raw(struct afb_hreq *hreq, size_t *size); static void req_send(struct afb_hreq *hreq, const char *buffer, size_t size); -static int req_session_create(struct afb_hreq *hreq); -static int req_session_check(struct afb_hreq *hreq, int refresh); -static void req_session_close(struct afb_hreq *hreq); static const struct afb_req_itf afb_hreq_itf = { .json = (void*)req_json, @@ -80,11 +81,10 @@ static const struct afb_req_itf afb_hreq_itf = { .fail = (void*)req_fail, .raw = (void*)req_raw, .send = (void*)req_send, - .session_create = (void*)req_session_create, - .session_check = (void*)req_session_check, - .session_close = (void*)req_session_close, .context_get = (void*)afb_context_get, - .context_set = (void*)afb_context_set + .context_set = (void*)afb_context_set, + .addref = (void*)afb_hreq_addref, + .unref = (void*)afb_hreq_unref }; static struct hreq_data *get_data(struct afb_hreq *hreq, const char *key, int create) @@ -162,7 +162,8 @@ static void afb_hreq_reply_v(struct afb_hreq *hreq, unsigned status, struct MHD_ MHD_add_response_header(response, k, v); k = va_arg(args, const char *); } - if (hreq->context != NULL && asprintf(&cookie, cookie_setter, hreq->context->uuid)) { + v = afb_context_sent_uuid(&hreq->context); + if (v != NULL && asprintf(&cookie, cookie_setter, v) > 0) { MHD_add_response_header(response, MHD_HTTP_HEADER_SET_COOKIE, cookie); free(cookie); } @@ -291,26 +292,33 @@ static const char *mimetype_fd_name(int fd, const char *filename) return result; } -void afb_hreq_free(struct afb_hreq *hreq) +void afb_hreq_addref(struct afb_hreq *hreq) +{ + hreq->refcount++; +} + +void afb_hreq_unref(struct afb_hreq *hreq) { struct hreq_data *data; - if (hreq != NULL) { - if (hreq->postform != NULL) - MHD_destroy_post_processor(hreq->postform); - for (data = hreq->data; data; data = hreq->data) { - hreq->data = data->next; - if (data->path) { - unlink(data->path); - free(data->path); - } - free(data->key); - free(data->value); - free(data); + + if (hreq == NULL || --hreq->refcount) + return; + + if (hreq->postform != NULL) + MHD_destroy_post_processor(hreq->postform); + for (data = hreq->data; data; data = hreq->data) { + hreq->data = data->next; + if (data->path) { + unlink(data->path); + free(data->path); } - ctxClientPut(hreq->context); - json_object_put(hreq->json); - free(hreq); + free(data->key); + free(data->value); + free(data); } + afb_context_disconnect(&hreq->context); + json_object_put(hreq->json); + free(hreq); } /* @@ -599,7 +607,7 @@ int afb_hreq_post_add_file(struct afb_hreq *hreq, const char *key, const char *f struct afb_req afb_hreq_to_req(struct afb_hreq *hreq) { - return (struct afb_req){ .itf = &afb_hreq_itf, .req_closure = hreq }; + return (struct afb_req){ .itf = &afb_hreq_itf, .closure = hreq }; } static struct afb_arg req_get(struct afb_hreq *hreq, const char *name) @@ -678,12 +686,9 @@ static void req_reply(struct afb_hreq *hreq, unsigned retcode, const char *statu const char *token, *uuid; struct MHD_Response *response; - if (hreq->context == NULL) { - token = uuid = NULL; - } else { - token = hreq->context->token; - uuid = hreq->context->uuid; - } + token = afb_context_sent_token(&hreq->context); + uuid = afb_context_sent_uuid(&hreq->context); + reply = afb_msg_json_reply(status, info, resp, token, uuid); response = MHD_create_response_from_callback((uint64_t)strlen(json_object_to_json_string(reply)), SIZE_RESPONSE_BUFFER, (void*)send_json_cb, reply, (void*)json_object_put); afb_hreq_reply(hreq, retcode, response, NULL); @@ -699,63 +704,27 @@ static void req_success(struct afb_hreq *hreq, json_object *obj, const char *inf req_reply(hreq, MHD_HTTP_OK, "success", info, obj); } -struct AFB_clientCtx *afb_hreq_context(struct afb_hreq *hreq) +int afb_hreq_init_context(struct afb_hreq *hreq) { const char *uuid; - - if (hreq->context == NULL) { - uuid = afb_hreq_get_header(hreq, uuid_header); - if (uuid == NULL) - uuid = afb_hreq_get_argument(hreq, uuid_arg); - if (uuid == NULL) - uuid = afb_hreq_get_cookie(hreq, cookie_name); - hreq->context = ctxClientGetForUuid(uuid); - } - return hreq->context; -} - -static int req_session_create(struct afb_hreq *hreq) -{ - struct AFB_clientCtx *context = afb_hreq_context(hreq); - if (context == NULL) - return 0; - if (context->created) - return 0; - return req_session_check(hreq, 1); -} - -static int req_session_check(struct afb_hreq *hreq, int refresh) -{ const char *token; - struct AFB_clientCtx *context = afb_hreq_context(hreq); - - if (context == NULL) + if (hreq->context.session != NULL) return 0; + uuid = afb_hreq_get_header(hreq, uuid_header); + if (uuid == NULL) + uuid = afb_hreq_get_argument(hreq, uuid_arg); + if (uuid == NULL) + uuid = afb_hreq_get_cookie(hreq, cookie_name); + token = afb_hreq_get_header(hreq, token_header); if (token == NULL) token = afb_hreq_get_argument(hreq, token_arg); if (token == NULL) token = afb_hreq_get_cookie(hreq, token_cookie); - if (token == NULL) - return 0; - if (!ctxTokenCheck (context, token)) - return 0; - - if (refresh) { - ctxTokenNew (context); - } - - return 1; -} - -static void req_session_close(struct afb_hreq *hreq) -{ - struct AFB_clientCtx *context = afb_hreq_context(hreq); - if (context != NULL) - ctxClientClose(context); + return afb_context_connect(&hreq->context, uuid, token); } int afb_hreq_init_cookie(int port, const char *path, int maxage) diff --git a/src/afb-hreq.h b/src/afb-hreq.h index 82e6b65a..3d197788 100644 --- a/src/afb-hreq.h +++ b/src/afb-hreq.h @@ -23,6 +23,8 @@ struct hreq_data; struct afb_hsrv; struct afb_hreq { + struct afb_context context; + int refcount; struct afb_hsrv *hsrv; const char *cacheTimeout; struct MHD_Connection *connection; @@ -37,14 +39,11 @@ struct afb_hreq { const char *tail; size_t lentail; struct MHD_PostProcessor *postform; - struct AFB_clientCtx *context; struct hreq_data *data; struct json_object *json; int upgrade; }; -extern void afb_hreq_free(struct afb_hreq *request); - extern int afb_hreq_unprefix(struct afb_hreq *request, const char *prefix, size_t length); extern int afb_hreq_valid_tail(struct afb_hreq *request); @@ -69,7 +68,7 @@ extern int afb_hreq_post_add(struct afb_hreq *hreq, const char *name, const char extern struct afb_req afb_hreq_to_req(struct afb_hreq *hreq); -extern struct AFB_clientCtx *afb_hreq_context(struct afb_hreq *hreq); +extern int afb_hreq_init_context(struct afb_hreq *hreq); extern int afb_hreq_init_cookie(int port, const char *path, int maxage); @@ -82,3 +81,8 @@ extern void afb_hreq_reply_free(struct afb_hreq *hreq, unsigned status, size_t s extern void afb_hreq_reply_empty(struct afb_hreq *hreq, unsigned status, ...); extern int afb_hreq_init_download_path(const char *directory); + +extern void afb_hreq_addref(struct afb_hreq *hreq); + +extern void afb_hreq_unref(struct afb_hreq *hreq); + diff --git a/src/afb-hsrv.c b/src/afb-hsrv.c index f514c97d..e64c5c9a 100644 --- a/src/afb-hsrv.c +++ b/src/afb-hsrv.c @@ -30,6 +30,7 @@ #include #include "afb-method.h" +#include "afb-context.h" #include "afb-hreq.h" #include "afb-hsrv.h" #include "afb-req-itf.h" @@ -130,6 +131,7 @@ static int access_handler( } /* init the request */ + hreq->refcount = 1; hreq->hsrv = hsrv; hreq->cacheTimeout = hsrv->cache_to; hreq->reqid = ++global_reqids; @@ -228,7 +230,7 @@ static void end_handler(void *cls, struct MHD_Connection *connection, void **rec hreq = *recordreq; if (hreq->upgrade) MHD_suspend_connection (connection); - afb_hreq_free(hreq); + afb_hreq_unref(hreq); } void run_micro_httpd(struct afb_hsrv *hsrv) diff --git a/src/afb-hswitch.c b/src/afb-hswitch.c index 606d2664..f0308455 100644 --- a/src/afb-hswitch.c +++ b/src/afb-hswitch.c @@ -21,7 +21,10 @@ #include #include +#include + #include "afb-req-itf.h" +#include "afb-context.h" #include "afb-hreq.h" #include "afb-apis.h" #include "session.h" @@ -31,7 +34,6 @@ 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, "/"); @@ -42,8 +44,10 @@ int afb_hswitch_apis(struct afb_hreq *hreq, void *data) 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); + if (afb_hreq_init_context(hreq) < 0) + afb_hreq_reply_error(hreq, MHD_HTTP_INTERNAL_SERVER_ERROR); + else + afb_apis_call(afb_hreq_to_req(hreq), &hreq->context, api, lenapi, verb, lenverb); return 1; } @@ -77,7 +81,12 @@ int afb_hswitch_websocket_switch(struct afb_hreq *hreq, void *data) if (hreq->lentail != 0) return 0; - return afb_websock_check_upgrade(hreq /* TODO: protocols here */); + if (afb_hreq_init_context(hreq) < 0) { + afb_hreq_reply_error(hreq, MHD_HTTP_INTERNAL_SERVER_ERROR); + return 1; + } + + return afb_websock_check_upgrade(hreq); } diff --git a/src/afb-websock.c b/src/afb-websock.c index 9f5619fb..0247aae6 100644 --- a/src/afb-websock.c +++ b/src/afb-websock.c @@ -28,6 +28,7 @@ #include "afb-ws-json.h" #include "afb-method.h" +#include "afb-context.h" #include "afb-hreq.h" #include "afb-websock.h" @@ -203,7 +204,7 @@ int afb_websock_check_upgrade(struct afb_hreq *hreq) return 0; ws = NULL; - rc = check_websocket_upgrade(hreq->connection, protodefs, afb_hreq_context(hreq), &ws); + rc = check_websocket_upgrade(hreq->connection, protodefs, hreq->context.session, &ws); if (rc == 1) { hreq->replied = 1; if (ws != NULL) diff --git a/src/afb-ws-json.c b/src/afb-ws-json.c index 4bdda7ef..781722fc 100644 --- a/src/afb-ws-json.c +++ b/src/afb-ws-json.c @@ -31,6 +31,7 @@ #include "session.h" #include "afb-req-itf.h" #include "afb-apis.h" +#include "afb-context.h" static void aws_on_hangup(struct afb_ws_json *ws); static void aws_on_text(struct afb_ws_json *ws, char *text, size_t size); @@ -47,7 +48,7 @@ struct afb_ws_json void (*cleanup)(void*); void *cleanup_closure; struct afb_wsreq *requests; - struct AFB_clientCtx *context; + struct AFB_clientCtx *session; struct json_tokener *tokener; struct afb_ws *ws; }; @@ -58,12 +59,12 @@ static const struct afb_event_listener_itf event_listener_itf = { .send = (void*)aws_send_event }; -struct afb_ws_json *afb_ws_json_create(int fd, struct AFB_clientCtx *context, void (*cleanup)(void*), void *cleanup_closure) +struct afb_ws_json *afb_ws_json_create(int fd, struct AFB_clientCtx *session, void (*cleanup)(void*), void *cleanup_closure) { struct afb_ws_json *result; assert(fd >= 0); - assert(context != NULL); + assert(session != NULL); result = malloc(sizeof * result); if (result == NULL) @@ -72,8 +73,8 @@ struct afb_ws_json *afb_ws_json_create(int fd, struct AFB_clientCtx *context, vo result->cleanup = cleanup; result->cleanup_closure = cleanup_closure; result->requests = NULL; - result->context = ctxClientGet(context); - if (result->context == NULL) + result->session = ctxClientAddRef(session); + if (result->session == NULL) goto error2; result->tokener = json_tokener_new(); @@ -84,7 +85,7 @@ struct afb_ws_json *afb_ws_json_create(int fd, struct AFB_clientCtx *context, vo if (result->ws == NULL) goto error4; - if (0 > ctxClientEventListenerAdd(result->context, (struct afb_event_listener){ .itf = &event_listener_itf, .closure = result })) + if (0 > ctxClientEventListenerAdd(result->session, (struct afb_event_listener){ .itf = &event_listener_itf, .closure = result })) goto error5; return result; @@ -94,7 +95,7 @@ error5: error4: json_tokener_free(result->tokener); error3: - ctxClientPut(result->context); + ctxClientUnref(result->session); error2: free(result); error: @@ -104,11 +105,12 @@ error: static void aws_on_hangup(struct afb_ws_json *ws) { - ctxClientEventListenerRemove(ws->context, (struct afb_event_listener){ .itf = &event_listener_itf, .closure = ws }); + ctxClientEventListenerRemove(ws->session, (struct afb_event_listener){ .itf = &event_listener_itf, .closure = ws }); afb_ws_destroy(ws->ws); json_tokener_free(ws->tokener); if (ws->cleanup != NULL) ws->cleanup(ws->cleanup_closure); + ctxClientUnref(ws->session); free(ws); } @@ -119,6 +121,8 @@ static void aws_on_hangup(struct afb_ws_json *ws) struct afb_wsreq { + struct afb_context context; + int refcount; struct afb_ws_json *aws; struct afb_wsreq *next; char *text; @@ -137,15 +141,14 @@ struct afb_wsreq struct json_object *root; }; +static void wsreq_addref(struct afb_wsreq *wsreq); +static void wsreq_unref(struct afb_wsreq *wsreq); static struct json_object *wsreq_json(struct afb_wsreq *wsreq); static struct afb_arg wsreq_get(struct afb_wsreq *wsreq, const char *name); static void wsreq_fail(struct afb_wsreq *wsreq, const char *status, const char *info); static void wsreq_success(struct afb_wsreq *wsreq, struct json_object *obj, const char *info); static const char *wsreq_raw(struct afb_wsreq *wsreq, size_t *size); static void wsreq_send(struct afb_wsreq *wsreq, const char *buffer, size_t size); -static int wsreq_session_create(struct afb_wsreq *wsreq); -static int wsreq_session_check(struct afb_wsreq *wsreq, int refresh); -static void wsreq_session_close(struct afb_wsreq *wsreq); static const struct afb_req_itf wsreq_itf = { @@ -155,11 +158,10 @@ static const struct afb_req_itf wsreq_itf = { .fail = (void*)wsreq_fail, .raw = (void*)wsreq_raw, .send = (void*)wsreq_send, - .session_create = (void*)wsreq_session_create, - .session_check = (void*)wsreq_session_check, - .session_close = (void*)wsreq_session_close, .context_get = (void*)afb_context_get, - .context_set = (void*)afb_context_set + .context_set = (void*)afb_context_set, + .addref = (void*)wsreq_addref, + .unref = (void*)wsreq_unref }; static int aws_wsreq_parse(struct afb_wsreq *r, char *text, size_t size) @@ -303,13 +305,20 @@ static void aws_on_text(struct afb_ws_json *ws, char *text, size_t size) goto bad_header; /* fill and record the request */ + if (wsreq->tok != NULL) + wsreq->tok[wsreq->toklen] = 0; + afb_context_init(&wsreq->context, ws->session, wsreq->tok); + if (!wsreq->context.invalidated) + wsreq->context.validated = 1; + wsreq->refcount = 1; wsreq->aws = ws; wsreq->next = ws->requests; ws->requests = wsreq; - r.req_closure = wsreq; + r.closure = wsreq; r.itf = &wsreq_itf; - afb_apis_call(r, ws->context, wsreq->api, wsreq->apilen, wsreq->verb, wsreq->verblen); + afb_apis_call(r, &wsreq->context, wsreq->api, wsreq->apilen, wsreq->verb, wsreq->verblen); + wsreq_unref(wsreq); return; bad_header: @@ -320,6 +329,20 @@ alloc_error: return; } +static void wsreq_addref(struct afb_wsreq *wsreq) +{ + wsreq->refcount++; +} + +static void wsreq_unref(struct afb_wsreq *wsreq) +{ + if (--wsreq->refcount == 0) { + afb_context_disconnect(&wsreq->context); + free(wsreq->text); + free(wsreq); + } +} + static struct json_object *wsreq_json(struct afb_wsreq *wsreq) { struct json_object *root = wsreq->root; @@ -352,41 +375,9 @@ static struct afb_arg wsreq_get(struct afb_wsreq *wsreq, const char *name) return arg; } -static int wsreq_session_create(struct afb_wsreq *wsreq) -{ - struct AFB_clientCtx *context = wsreq->aws->context; - if (context->created) - return 0; - return wsreq_session_check(wsreq, 1); -} - -static int wsreq_session_check(struct afb_wsreq *wsreq, int refresh) -{ - struct AFB_clientCtx *context = wsreq->aws->context; - - if (wsreq->tok == NULL) - return 0; - - if (!ctxTokenCheckLen (context, wsreq->tok, wsreq->toklen)) - return 0; - - if (refresh) { - ctxTokenNew (context); - } - - return 1; -} - -static void wsreq_session_close(struct afb_wsreq *wsreq) -{ - struct AFB_clientCtx *context = wsreq->aws->context; - ctxClientClose(context); -} - -static void aws_emit(struct afb_ws_json *aws, int code, const char *id, size_t idlen, struct json_object *data) +static void aws_emit(struct afb_ws_json *aws, int code, const char *id, size_t idlen, struct json_object *data, const char *token) { json_object *msg; - const char *token; const char *txt; /* pack the message */ @@ -394,7 +385,6 @@ static void aws_emit(struct afb_ws_json *aws, int code, const char *id, size_t i json_object_array_add(msg, json_object_new_int(code)); json_object_array_add(msg, json_object_new_string_len(id, (int)idlen)); json_object_array_add(msg, data); - token = aws->context->token; if (token) json_object_array_add(msg, json_object_new_string(token)); @@ -406,8 +396,8 @@ static void aws_emit(struct afb_ws_json *aws, int code, const char *id, size_t i static void wsreq_reply(struct afb_wsreq *wsreq, int retcode, const char *status, const char *info, json_object *resp) { - aws_emit(wsreq->aws, retcode, wsreq->id, wsreq->idlen, afb_msg_json_reply(status, info, resp, NULL, NULL)); - /* TODO eliminates the wsreq */ + const char *token = afb_context_sent_token(&wsreq->context); + aws_emit(wsreq->aws, retcode, wsreq->id, wsreq->idlen, afb_msg_json_reply(status, info, resp, token, NULL), token); } static void wsreq_fail(struct afb_wsreq *wsreq, const char *status, const char *info) @@ -433,6 +423,6 @@ static void wsreq_send(struct afb_wsreq *wsreq, const char *buffer, size_t size) static void aws_send_event(struct afb_ws_json *aws, const char *event, struct json_object *object) { - aws_emit(aws, EVENT, event, strlen(event), afb_msg_json_event(event, object)); + aws_emit(aws, EVENT, event, strlen(event), afb_msg_json_event(event, object), NULL); } diff --git a/src/main.c b/src/main.c index fcedd2a6..be1927e3 100644 --- a/src/main.c +++ b/src/main.c @@ -39,6 +39,7 @@ #include "afb-apis.h" #include "afb-api-so.h" #include "afb-hsrv.h" +#include "afb-context.h" #include "afb-hreq.h" #include "session.h" #include "verbose.h" diff --git a/src/session.c b/src/session.c index ae44f793..d586e517 100644 --- a/src/session.c +++ b/src/session.c @@ -31,6 +31,30 @@ #define NOW (time(NULL)) +struct client_value +{ + void *value; + void (*free_value)(void*); +}; + +struct afb_event_listener_list +{ + struct afb_event_listener_list *next; + struct afb_event_listener listener; + int refcount; +}; + +struct AFB_clientCtx +{ + unsigned refcount; + time_t expiration; // expiration time of the token + time_t access; + char uuid[37]; // long term authentication of remote client + char token[37]; // short term authentication of remote client + struct client_value *values; + struct afb_event_listener_list *listeners; +}; + // Session UUID are store in a simple array [for 10 sessions this should be enough] static struct { pthread_mutex_t mutex; // declare a mutex to protect hash table @@ -42,31 +66,17 @@ static struct { const char *initok; } sessions; -void *afb_context_get(struct afb_context *actx) -{ - return actx->context; -} - -void afb_context_set(struct afb_context *actx, void *context, void (*free_context)(void*)) -{ -fprintf(stderr, "afb_context_set(%p,%p) was (%p,%p)\n",context, free_context, actx->context, actx->free_context); - if (actx->context != NULL && actx->free_context != NULL) - actx->free_context(actx->context); - actx->context = context; - actx->free_context = free_context; -} - // Free context [XXXX Should be protected again memory abort XXXX] static void ctxUuidFreeCB (struct AFB_clientCtx *client) { int idx; // If application add a handle let's free it now - assert (client->contexts != NULL); + assert (client->values != NULL); // Free client handle with a standard Free function, with app callback or ignore it for (idx=0; idx < sessions.apicount; idx ++) - afb_context_set(&client->contexts[idx], NULL, NULL); + ctxClientValueSet(client, idx, NULL, NULL); } // Create a new store in RAM, not that is too small it will be automatically extended @@ -77,7 +87,7 @@ void ctxStoreInit (int max_session_count, int timeout, const char *initok, int c sessions.max = max_session_count; sessions.timeout = timeout; sessions.apicount = context_count; - if (strlen(initok) >= 37) { + if (strlen(initok) >= sizeof(sessions.store[0]->token)) { fprintf(stderr, "Error: initial token '%s' too long (max length 36)", initok); exit(1); } @@ -175,67 +185,80 @@ static void ctxStoreCleanUp (time_t now) } // This function will return exiting client context or newly created client context -struct AFB_clientCtx *ctxClientGetForUuid (const char *uuid) +struct AFB_clientCtx *ctxClientGetSession (const char *uuid, int *created) { uuid_t newuuid; struct AFB_clientCtx *clientCtx; time_t now; - /* search for an existing one not too old */ + /* cleaning */ now = NOW; ctxStoreCleanUp (now); - clientCtx = uuid != NULL ? ctxStoreSearch (uuid) : NULL; - if (clientCtx) { - clientCtx->refcount++; - return clientCtx; - } - /* mimic old behaviour */ -/* -TODO remove? not remove? - if (sessions.initok == NULL) - return NULL; -*/ - /* check the uuid if given */ - if (uuid != NULL && strlen(uuid) >= sizeof clientCtx->uuid) - return NULL; + /* search for an existing one not too old */ + if (uuid != NULL) { + if (strlen(uuid) >= sizeof clientCtx->uuid) { + errno = EINVAL; + goto error; + } + clientCtx = ctxStoreSearch(uuid); + if (clientCtx != NULL) { + *created = 0; + goto found; + } + } /* returns a new one */ - clientCtx = calloc(1, sizeof(struct AFB_clientCtx)); // init NULL clientContext - if (clientCtx != NULL) { - clientCtx->contexts = calloc ((unsigned)sessions.apicount, sizeof(*clientCtx->contexts)); - if (clientCtx->contexts != NULL) { - /* generate the uuid */ - if (uuid == NULL) { - uuid_generate(newuuid); - uuid_unparse_lower(newuuid, clientCtx->uuid); - } else { - strcpy(clientCtx->uuid, uuid); - } - strcpy(clientCtx->token, sessions.initok); - clientCtx->expiration = now + sessions.timeout; - clientCtx->refcount = 1; - if (ctxStoreAdd (clientCtx)) - return clientCtx; - free(clientCtx->contexts); - } - free(clientCtx); + clientCtx = calloc(1, sizeof(struct AFB_clientCtx) + ((unsigned)sessions.apicount * sizeof(*clientCtx->values))); + if (clientCtx == NULL) { + errno = ENOMEM; + goto error; + } + clientCtx->values = (void*)(clientCtx + 1); + + /* generate the uuid */ + if (uuid == NULL) { + uuid_generate(newuuid); + uuid_unparse_lower(newuuid, clientCtx->uuid); + } else { + strcpy(clientCtx->uuid, uuid); + } + + /* init the token */ + strcpy(clientCtx->token, sessions.initok); + clientCtx->expiration = now + sessions.timeout; + if (!ctxStoreAdd (clientCtx)) { + errno = ENOMEM; + goto error2; } + *created = 1; + +found: + clientCtx->access = now; + clientCtx->refcount++; + return clientCtx; + +error2: + free(clientCtx); +error: return NULL; } -struct AFB_clientCtx *ctxClientGet(struct AFB_clientCtx *clientCtx) +struct AFB_clientCtx *ctxClientAddRef(struct AFB_clientCtx *clientCtx) { if (clientCtx != NULL) clientCtx->refcount++; return clientCtx; } -void ctxClientPut(struct AFB_clientCtx *clientCtx) +void ctxClientUnref(struct AFB_clientCtx *clientCtx) { if (clientCtx != NULL) { assert(clientCtx->refcount != 0); --clientCtx->refcount; + if (clientCtx->refcount == 0 && clientCtx->uuid[0] == 0) { + ctxStoreDel (clientCtx); + } } } @@ -243,16 +266,12 @@ void ctxClientPut(struct AFB_clientCtx *clientCtx) void ctxClientClose (struct AFB_clientCtx *clientCtx) { assert(clientCtx != NULL); - if (clientCtx->created) { - clientCtx->created = 0; - ctxUuidFreeCB (clientCtx); - } - if (clientCtx->refcount == 0) - ctxStoreDel (clientCtx); + ctxUuidFreeCB (clientCtx); + clientCtx->uuid[0] = 0; } // Sample Generic Ping Debug API -int ctxTokenCheckLen (struct AFB_clientCtx *clientCtx, const char *token, size_t length) +int ctxTokenCheck (struct AFB_clientCtx *clientCtx, const char *token) { assert(clientCtx != NULL); assert(token != NULL); @@ -261,22 +280,12 @@ int ctxTokenCheckLen (struct AFB_clientCtx *clientCtx, const char *token, size_t if (ctxStoreTooOld (clientCtx, NOW)) return 0; - if (clientCtx->token[0] && (length >= sizeof(clientCtx->token) || strncmp (token, clientCtx->token, length) || clientCtx->token[length])) + if (clientCtx->token[0] && strcmp (token, clientCtx->token) != 0) return 0; - clientCtx->created = 1; /* creates by default */ return 1; } -// Sample Generic Ping Debug API -int ctxTokenCheck (struct AFB_clientCtx *clientCtx, const char *token) -{ - assert(clientCtx != NULL); - assert(token != NULL); - - return ctxTokenCheckLen(clientCtx, token, strlen(token)); -} - // generate a new token and update client context void ctxTokenNew (struct AFB_clientCtx *clientCtx) { @@ -292,13 +301,6 @@ void ctxTokenNew (struct AFB_clientCtx *clientCtx) clientCtx->expiration = NOW + sessions.timeout; } -struct afb_event_listener_list -{ - struct afb_event_listener_list *next; - struct afb_event_listener listener; - int refcount; -}; - int ctxClientEventListenerAdd(struct AFB_clientCtx *clientCtx, struct afb_event_listener listener) { struct afb_event_listener_list *iter, **prv; @@ -375,12 +377,43 @@ int ctxClientEventSend(struct AFB_clientCtx *clientCtx, const char *event, struc for (idx=0; idx < sessions.max; idx++) { clientCtx = sessions.store[idx]; if (clientCtx != NULL && !ctxStoreTooOld(clientCtx, now)) { - clientCtx = ctxClientGet(clientCtx); + clientCtx = ctxClientAddRef(clientCtx); result += send(clientCtx, event, object); - ctxClientPut(clientCtx); + ctxClientUnref(clientCtx); } } } return result; } +const char *ctxClientGetUuid (struct AFB_clientCtx *clientCtx) +{ + assert(clientCtx != NULL); + return clientCtx->uuid; +} + +const char *ctxClientGetToken (struct AFB_clientCtx *clientCtx) +{ + assert(clientCtx != NULL); + return clientCtx->token; +} + +void *ctxClientValueGet(struct AFB_clientCtx *clientCtx, int index) +{ + assert(clientCtx != NULL); + assert(index >= 0); + assert(index < sessions.apicount); + return clientCtx->values[index].value; +} + +void ctxClientValueSet(struct AFB_clientCtx *clientCtx, int index, void *value, void (*free_value)(void*)) +{ + struct client_value prev; + assert(clientCtx != NULL); + assert(index >= 0); + assert(index < sessions.apicount); + prev = clientCtx->values[index]; + clientCtx->values[index] = (struct client_value){.value = value, .free_value = free_value}; + if (prev.value != NULL && prev.free_value != NULL) + prev.free_value(prev.value); +} diff --git a/src/session.h b/src/session.h index f6d67fe6..c2722455 100644 --- a/src/session.h +++ b/src/session.h @@ -18,15 +18,7 @@ #pragma once struct json_object; - -struct afb_context -{ - void *context; - void (*free_context)(void*); -}; - -extern void *afb_context_get(struct afb_context *actx); -extern void afb_context_set(struct afb_context *actx, void *context, void (*free_context)(void*)); +struct AFB_clientCtx; struct afb_event_listener_itf { @@ -39,24 +31,11 @@ struct afb_event_listener void *closure; }; -struct afb_event_listener_list; - -struct AFB_clientCtx -{ - int created; - unsigned refcount; - time_t expiration; // expiration time of the token - struct afb_context *contexts; - char uuid[37]; // long term authentication of remote client - char token[37]; // short term authentication of remote client - struct afb_event_listener_list *listeners; -}; - extern void ctxStoreInit (int max_session_count, int timeout, const char *initok, int context_count); -extern struct AFB_clientCtx *ctxClientGetForUuid (const char *uuid); -extern struct AFB_clientCtx *ctxClientGet(struct AFB_clientCtx *clientCtx); -extern void ctxClientPut(struct AFB_clientCtx *clientCtx); +extern struct AFB_clientCtx *ctxClientGetSession (const char *uuid, int *created); +extern struct AFB_clientCtx *ctxClientAddRef(struct AFB_clientCtx *clientCtx); +extern void ctxClientUnref(struct AFB_clientCtx *clientCtx); extern void ctxClientClose (struct AFB_clientCtx *clientCtx); extern int ctxClientEventListenerAdd(struct AFB_clientCtx *clientCtx, struct afb_event_listener listener); @@ -64,6 +43,11 @@ extern void ctxClientEventListenerRemove(struct AFB_clientCtx *clientCtx, struct extern int ctxClientEventSend(struct AFB_clientCtx *clientCtx, const char *event, struct json_object *object); extern int ctxTokenCheck (struct AFB_clientCtx *clientCtx, const char *token); -extern int ctxTokenCheckLen (struct AFB_clientCtx *clientCtx, const char *token, size_t length); extern void ctxTokenNew (struct AFB_clientCtx *clientCtx); +extern const char *ctxClientGetUuid (struct AFB_clientCtx *clientCtx); +extern const char *ctxClientGetToken (struct AFB_clientCtx *clientCtx); + +extern void *ctxClientValueGet(struct AFB_clientCtx *clientCtx, int index); +extern void ctxClientValueSet(struct AFB_clientCtx *clientCtx, int index, void *value, void (*free_value)(void*)); +