From: José Bollo The binder afb-daemon serves files through
-the HTTP protocol and offers access to API’s through
+ The binder afb-daemon serves files through HTTP protocol
+and offers to developers the capability to expose application APIs through
HTTP or WebSocket protocol. The plugins are used to add API’s to afb-daemon.
+ Binder plugins are used to add API to afb-daemon.
This part describes how to write a plugin for afb-daemon.
Excepting this summary, this part is intended to be read
-by developpers.HOWTO WRITE a PLUGIN for AFB-DAEMON
@@ -18,14 +18,14 @@ Author: José Bollo
version: 1
-Date: 27 mai 2016
+Date: 29 mai 2016
Author: José Bollo
Summary
-
Before going into details, through a tiny example, -a short overview plugins basis is needed.
+Before moving further through an example, here after +a short overview of binder plugins fundamentals.
A plugin is a separate piece of code made of a shared library. -The plugin is loaded and activated by afb-daemon when afb-daemon -starts.
+A plugin is an independent piece of software, self contain and expose as a dynamically loadable library. +A plugin is loaded by afb-daemon that exposes contained API dynamically at runtime.
-Technically, a plugin is not linked to any library of afb-daemon.
+Technically, a binder plugins does not reference and is not linked with any library from afb-daemon.
- -There is two kinds of plugins: application plugins and service -plugins.
+Application binder supports two kinds of plugins: application plugins and service +plugins. Technically both class of plugin are equivalent and coding API is shared. Only sharing mode and security context diverge.
- -Application plugins are intended to be instanciated for each -application: when an application using that plugin is started, -its binder starts a new instance of the plugin.
+Application-plugins implements the glue in between application’s UI and services. Every AGL application +has a corresponding binder that typically activates one or many plugins to interface the application logic with lower platform services. +When an application is started by AGL application framework, a dedicate binder is started that loads/activates application plugin(s). +The API expose by application-plugin are executed within corresponding application security context.
-It means that the application plugins mainly have only one -context to manage for one client.
+Application plugins generally handle a unique context for a unique client. As the application framework start +a dedicated instance of afb_daemon for each AGL application, if a given plugin is used within multiple application each of those +application get a new and private instance of this “shared” plugin.
- -Service plugins are intended to be instanciated only one time -only and connected to many clients.
+Service-plugins enable API activation within corresponding service security context and not within calling application context. +Service-plugins are intended to run as a unique instance that is shared in between multiple clients.
-So either it does not manage context at all or otherwise, -if it manages context, it should be able to manage one context -per client.
+Service-plugins can either be stateless or manage client context. When managing context each client get a private context.
-In details, it may be useful to have service plugins at a user -level.
+Sharing may either be global to the platform (ie: GPS service) or dedicated to a given user (ie: preference management)
- -The plugins are loaded and activated when afb-daemon starts.
+Application and service plugins are loaded and activated each time a new afb-daemon is started.
-At start, the plugin initialise itself. -If it fails to initialise then afb-daemon stops.
+At launch time, every loaded plugin initialise itself. +If a single plugin initialisation fail corresponding instance of afb-daemon self aborts.
-Conversely, if it success to initialize, it must declare -a name, that must be unique, and a list of API’s verbs.
+Conversely, when plugin initialisation succeeds, it should register +its unique name and the list of API verbs it exposes.
-When initialized, the functions implementing the API’s verbs -of the plugin are activated on call.
+When initialised, on request from clients plugin’s function corresponding to expose API verbs +are activated by the afb-daemon instance attached to the application or service.
-At the end, nothing special is done by afb-daemon. -Consequently, developpers of plugins should use ‘atexit’ -or ‘on_exit’ during initialisation if they need to -perform specific actions when stopping.
+At exit time, no special action is enforced by afb-daemon. When a specific actions is required at afb-daemon stop, +developers should use ‘atexit/on_exit’ during plugin initialisation sequence to register a custom exit function.
- -For afb-daemon, a plugin contains 2 different -things: names and functions.
+Afb-daemon’s plugin register two classes of objects: names and functions.
-There is two kind of names: - - the name of the plugin, - - the names of the verbs.
+Plugins declare categories of names: + - A unique plugin name, + - Multiple API verb’s names.
-There is two kind of functions: - - the initialisation function - - functions implementing verbs
+Plugins declare two categories of functions: + - initialisation function + - API functions implementing verbs
-Afb-daemon translates the name of the method that is -invoked to a pair of API and verb names. For example, -the method named foo/bar translated to the API -name foo and the verb name bar. -To serve it, afb-daemon search the plugin that record -the name foo and if it also recorded the verb bar, -it calls the implementation function declared for this verb.
+Afb-daemon parses URI requests to extract plugin name and API verb. +As an example, URI foo/bar translates to API verb named bar within plugin named foo. +To serve such a request, afb-daemon looks for an active plugin named foo and then within this plugin for an API verb named bar. +When find afb-daemon calls corresponding function with attached parameter if any.
-Afb-daemon make no distinction between lower case -and upper case when searching for a method. -Thus, The names TicTacToe/Board and tictactoe/borad -are equals.
+Afb-daemon ignores letter case when parsing URI. Thus TicTacToe/Board and tictactoe/board are equivalent.
The names of the verbs are not checked.
However, the validity rules for verb’s names are the -same as for API’s names except that the dot (.) character +same as for API names except that the dot (.) character is forbidden.
Afb-daemon make no distinction between lower case diff --git a/doc/afb-plugin-writing.md b/doc/afb-plugin-writing.md index 32cd202d..57b26095 100644 --- a/doc/afb-plugin-writing.md +++ b/doc/afb-plugin-writing.md @@ -1,7 +1,7 @@ HOWTO WRITE a PLUGIN for AFB-DAEMON =================================== version: 1 - Date: 27 mai 2016 + Date: 29 mai 2016 Author: José Bollo TABLE-OF-CONTENT-HERE @@ -86,7 +86,7 @@ As an example, URI **foo/bar** translates to API verb named **bar** within plugi To serve such a request, afb-daemon looks for an active plugin named **foo** and then within this plugin for an API verb named **bar**. When find afb-daemon calls corresponding function with attached parameter if any. -Afb-daemon ignores letter case when parsing URI. Thus **TicTacToe/Board** and **tictactoe/borad** are equivalent. +Afb-daemon ignores letter case when parsing URI. Thus **TicTacToe/Board** and **tictactoe/board** are equivalent. #### The name of the plugin diff --git a/src/afb-ws-client.h b/src/afb-ws-client.h index 125bd6f3..f2aee7d0 100644 --- a/src/afb-ws-client.h +++ b/src/afb-ws-client.h @@ -20,4 +20,10 @@ struct afb_wsj1; struct afb_wsj1_itf; +/* + * Makes the WebSocket handshake at the 'uri' and if successful + * instanciate a wsj1 websocket for this connection using 'itf' and 'closure'. + * (see afb_wsj1_create). + * Returns NULL in case of failure with errno set appriately. + */ extern struct afb_wsj1 *afb_ws_client_connect_wsj1(const char *uri, struct afb_wsj1_itf *itf, void *closure); diff --git a/src/afb-wsj1.h b/src/afb-wsj1.h index 08bf67f7..44aa5656 100644 --- a/src/afb-wsj1.h +++ b/src/afb-wsj1.h @@ -22,44 +22,189 @@ struct afb_wsj1_msg; struct json_object; +/* + * Interface for callback functions. + * The received closure is the closure passed when creating the afb_wsj1 + * socket using afb_wsj1_create. + */ struct afb_wsj1_itf { + /* + * This function is called on hangup. + * Receives the 'closure' and the handle 'wsj1' + */ void (*on_hangup)(void *closure, struct afb_wsj1 *wsj1); + + /* + * This function is called on incoming call. + * Receives the 'closure' + */ void (*on_call)(void *closure, const char *api, const char *verb, struct afb_wsj1_msg *msg); + + /* + * This function is called on incoming event + */ void (*on_event)(void *closure, const char *event, struct afb_wsj1_msg *msg); }; +/* + * Creates the afb_wsj1 socket connected to the file descriptor 'fd' + * and having the callback interface defined by 'itf' for the 'closure'. + * Returns the created wsj1 websocket or NULL in case of error. + */ extern struct afb_wsj1 *afb_wsj1_create(int fd, struct afb_wsj1_itf *itf, void *closure); +/* + * Increases by one the count of reference to 'wsj1' + */ extern void afb_wsj1_addref(struct afb_wsj1 *wsj1); + +/* + * Decreases by one the count of reference to 'wsj1' + * and if it falls to zero releases the used resources + * and free the memory + */ extern void afb_wsj1_unref(struct afb_wsj1 *wsj1); +/* + * Sends on 'wsj1' the event of name 'event' with the + * data 'object'. If not NULL, 'object' should be a valid + * JSON string. + * Return 0 in case of success. Otherwise, returns -1 and set errno. + */ extern int afb_wsj1_send_event_s(struct afb_wsj1 *wsj1, const char *event, const char *object); + +/* + * Sends on 'wsj1' the event of name 'event' with the + * data 'object'. 'object' can be NULL. + * Return 0 in case of success. Otherwise, returns -1 and set errno. + */ extern int afb_wsj1_send_event_j(struct afb_wsj1 *wsj1, const char *event, struct json_object *object); +/* + * Sends on 'wsj1' a call to the method of 'api'/'verb' with arguments + * given by 'object'. If not NULL, 'object' should be a valid JSON string. + * On receiving the reply, the function 'on_reply' is called with 'closure' + * as its first argument and the message of the reply. + * Return 0 in case of success. Otherwise, returns -1 and set errno. + */ extern int afb_wsj1_call_s(struct afb_wsj1 *wsj1, const char *api, const char *verb, const char *object, void (*on_reply)(void *closure, struct afb_wsj1_msg *msg), void *closure); + +/* + * Sends on 'wsj1' a call to the method of 'api'/'verb' with arguments + * given by 'object'. 'object' can be NULL. + * On receiving the reply, the function 'on_reply' is called with 'closure' + * as its first argument and the message of the reply. + * Return 0 in case of success. Otherwise, returns -1 and set errno. + */ extern int afb_wsj1_call_j(struct afb_wsj1 *wsj1, const char *api, const char *verb, struct json_object *object, void (*on_reply)(void *closure, struct afb_wsj1_msg *msg), void *closure); +/* + * Sends for message 'msg' the OK reply with the 'object' and, if not NULL, the token. + * If not NULL, 'object' should be a valid JSON string. + * Return 0 in case of success. Otherwise, returns -1 and set errno. + */ extern int afb_wsj1_reply_ok_s(struct afb_wsj1_msg *msg, const char *object, const char *token); + +/* + * Sends for message 'msg' the OK reply with the 'object' and, if not NULL, the token. + * 'object' can be NULL. + * Return 0 in case of success. Otherwise, returns -1 and set errno. + */ extern int afb_wsj1_reply_ok_j(struct afb_wsj1_msg *msg, struct json_object *object, const char *token); +/* + * Sends for message 'msg' the ERROR reply with the 'object' and, if not NULL, the token. + * If not NULL, 'object' should be a valid JSON string. + * Return 0 in case of success. Otherwise, returns -1 and set errno. + */ extern int afb_wsj1_reply_error_s(struct afb_wsj1_msg *msg, const char *object, const char *token); + +/* + * Sends for message 'msg' the ERROR reply with the 'object' and, if not NULL, the token. + * 'object' can be NULL. + * Return 0 in case of success. Otherwise, returns -1 and set errno. + */ extern int afb_wsj1_reply_error_j(struct afb_wsj1_msg *msg, struct json_object *object, const char *token); +/* + * Increases by one the count of reference to 'msg'. + * Should be called if callbacks stores the message. + */ extern void afb_wsj1_msg_addref(struct afb_wsj1_msg *msg); + +/* + * Decreases by one the count of reference to 'msg'. + * and if it falls to zero releases the used resources + * and free the memory. + * Should be called if 'afb_wsj1_msg_addref' was called. + */ extern void afb_wsj1_msg_unref(struct afb_wsj1_msg *msg); +/* + * Returns 1 if 'msg' is for a CALL + * Otherwise returns 0. + */ extern int afb_wsj1_msg_is_call(struct afb_wsj1_msg *msg); + +/* + * Returns 1 if 'msg' is for a REPLY of any kind + * Otherwise returns 0. + */ extern int afb_wsj1_msg_is_reply(struct afb_wsj1_msg *msg); + +/* + * Returns 1 if 'msg' is for a REPLY OK + * Otherwise returns 0. + */ extern int afb_wsj1_msg_is_reply_ok(struct afb_wsj1_msg *msg); + +/* + * Returns 1 if 'msg' is for a REPLY ERROR + * Otherwise returns 0. + */ extern int afb_wsj1_msg_is_reply_error(struct afb_wsj1_msg *msg); + +/* + * Returns 1 if 'msg' is for an EVENT + * Otherwise returns 0. + */ extern int afb_wsj1_msg_is_event(struct afb_wsj1_msg *msg); +/* + * Returns the api of the call for 'msg' + * Returns NULL if 'msg' is not for a CALL + */ extern const char *afb_wsj1_msg_api(struct afb_wsj1_msg *msg); + +/* + * Returns the verb call for 'msg' + * Returns NULL if 'msg' is not for a CALL + */ extern const char *afb_wsj1_msg_verb(struct afb_wsj1_msg *msg); + +/* + * Returns the event name for 'msg' + * Returns NULL if 'msg' is not for an EVENT + */ extern const char *afb_wsj1_msg_event(struct afb_wsj1_msg *msg); + +/* + * Returns the token sent with 'msg' or NULL when no token was sent. + */ extern const char *afb_wsj1_msg_token(struct afb_wsj1_msg *msg); + +/* + * Returns the wsj1 of 'msg' + */ extern struct afb_wsj1 *afb_wsj1_msg_wsj1(struct afb_wsj1_msg *msg); +/* + * Returns the string representation of the object received with 'msg' + */ extern const char *afb_wsj1_msg_object_s(struct afb_wsj1_msg *msg); + +/* + * Returns the object received with 'msg' + */ extern struct json_object *afb_wsj1_msg_object_j(struct afb_wsj1_msg *msg);