diff options
-rw-r--r-- | doc/afb-plugin-writing.html | 131 | ||||
-rw-r--r-- | doc/afb-plugin-writing.md | 4 | ||||
-rw-r--r-- | src/afb-ws-client.h | 6 | ||||
-rw-r--r-- | src/afb-wsj1.h | 145 |
4 files changed, 213 insertions, 73 deletions
diff --git a/doc/afb-plugin-writing.html b/doc/afb-plugin-writing.html index 20e25972..b80006f4 100644 --- a/doc/afb-plugin-writing.html +++ b/doc/afb-plugin-writing.html @@ -8,7 +8,7 @@ <h1>HOWTO WRITE a PLUGIN for AFB-DAEMON</h1> <pre><code>version: 1 -Date: 27 mai 2016 +Date: 29 mai 2016 Author: José Bollo </code></pre> @@ -18,14 +18,14 @@ Author: José Bollo <li><a href="#Summary">Summary</a> <ul> <li><a href="#Nature.of.a.plugin">Nature of a plugin</a></li> - <li><a href="#Kinds.of.plugins">Kinds of plugins</a> + <li><a href="#Class.of.plugins">Class of plugins</a> <ul> - <li><a href="#Application.plugins">Application plugins</a></li> - <li><a href="#Service.plugins">Service plugins</a></li> + <li><a href="#Application-plugins">Application-plugins</a></li> + <li><a href="#Service-plugins">Service-plugins</a></li> </ul> </li> - <li><a href="#Live.cycle.of.a.plugin.within.afb-daemon">Live cycle of a plugin within afb-daemon</a></li> - <li><a href="#Content.of.a.plugin">Content of a plugin</a> + <li><a href="#Live.cycle.of.plugins.within.afb-daemon">Live cycle of plugins within afb-daemon</a></li> + <li><a href="#Plugin.Contend">Plugin Contend</a> <ul> <li><a href="#The.name.of.the.plugin">The name of the plugin</a></li> <li><a href="#Names.of.verbs">Names of verbs</a></li> @@ -85,101 +85,90 @@ Author: José Bollo <a name="Summary"></a> <h2>Summary</h2> -<p>The binder afb-daemon serves files through -the HTTP protocol and offers access to API’s through +<p>The binder afb-daemon serves files through HTTP protocol +and offers to developers the capability to expose application APIs through HTTP or WebSocket protocol.</p> -<p>The plugins are used to add API’s to afb-daemon. +<p>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.</p> +by developers.</p> -<p>Before going into details, through a tiny example, -a short overview plugins basis is needed.</p> +<p>Before moving further through an example, here after +a short overview of binder plugins fundamentals.</p> <a name="Nature.of.a.plugin"></a> <h3>Nature of a plugin</h3> -<p>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.</p> +<p>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.</p> -<p>Technically, a plugin is not linked to any library of afb-daemon.</p> +<p>Technically, a binder plugins does not reference and is not linked with any library from afb-daemon.</p> -<a name="Kinds.of.plugins"></a> -<h3>Kinds of plugins</h3> +<a name="Class.of.plugins"></a> +<h3>Class of plugins</h3> -<p>There is two kinds of plugins: application plugins and service -plugins.</p> +<p>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.</p> -<a name="Application.plugins"></a> -<h4>Application plugins</h4> +<a name="Application-plugins"></a> +<h4>Application-plugins</h4> -<p>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.</p> +<p>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.</p> -<p>It means that the application plugins mainly have only one -context to manage for one client.</p> +<p>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.</p> -<a name="Service.plugins"></a> -<h4>Service plugins</h4> +<a name="Service-plugins"></a> +<h4>Service-plugins</h4> -<p>Service plugins are intended to be instanciated only one time -only and connected to many clients.</p> +<p>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.</p> -<p>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.</p> +<p>Service-plugins can either be stateless or manage client context. When managing context each client get a private context.</p> -<p>In details, it may be useful to have service plugins at a user -level.</p> +<p>Sharing may either be global to the platform (ie: GPS service) or dedicated to a given user (ie: preference management)</p> -<a name="Live.cycle.of.a.plugin.within.afb-daemon"></a> -<h3>Live cycle of a plugin within afb-daemon</h3> +<a name="Live.cycle.of.plugins.within.afb-daemon"></a> +<h3>Live cycle of plugins within afb-daemon</h3> -<p>The plugins are loaded and activated when afb-daemon starts.</p> +<p>Application and service plugins are loaded and activated each time a new afb-daemon is started.</p> -<p>At start, the plugin initialise itself. -If it fails to initialise then afb-daemon stops.</p> +<p>At launch time, every loaded plugin initialise itself. +If a single plugin initialisation fail corresponding instance of afb-daemon self aborts.</p> -<p>Conversely, if it success to initialize, it must declare -a name, that must be unique, and a list of API’s verbs.</p> +<p>Conversely, when plugin initialisation succeeds, it should register +its unique name and the list of API verbs it exposes.</p> -<p>When initialized, the functions implementing the API’s verbs -of the plugin are activated on call.</p> +<p>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.</p> -<p>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.</p> +<p>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.</p> -<a name="Content.of.a.plugin"></a> -<h3>Content of a plugin</h3> +<a name="Plugin.Contend"></a> +<h3>Plugin Contend</h3> -<p>For afb-daemon, a plugin contains 2 different -things: names and functions.</p> +<p>Afb-daemon’s plugin register two classes of objects: names and functions.</p> -<p>There is two kind of names: - - the name of the plugin, - - the names of the verbs.</p> +<p>Plugins declare categories of names: + - A unique plugin name, + - Multiple API verb’s names.</p> -<p>There is two kind of functions: - - the initialisation function - - functions implementing verbs</p> +<p>Plugins declare two categories of functions: + - initialisation function + - API functions implementing verbs</p> -<p>Afb-daemon translates the name of the method that is -invoked to a pair of API and verb names. For example, -the method named <strong>foo/bar</strong> translated to the API -name <strong>foo</strong> and the verb name <strong>bar</strong>. -To serve it, afb-daemon search the plugin that record -the name <strong>foo</strong> and if it also recorded the verb <strong>bar</strong>, -it calls the implementation function declared for this verb.</p> +<p>Afb-daemon parses URI requests to extract plugin name and API verb. +As an example, URI <strong>foo/bar</strong> translates to API verb named <strong>bar</strong> within plugin named <strong>foo</strong>. +To serve such a request, afb-daemon looks for an active plugin named <strong>foo</strong> and then within this plugin for an API verb named <strong>bar</strong>. +When find afb-daemon calls corresponding function with attached parameter if any.</p> -<p>Afb-daemon make no distinction between lower case -and upper case when searching for a method. -Thus, The names <strong>TicTacToe/Board</strong> and <strong>tictactoe/borad</strong> -are equals.</p> +<p>Afb-daemon ignores letter case when parsing URI. Thus <strong>TicTacToe/Board</strong> and <strong>tictactoe/board</strong> are equivalent.</p> <a name="The.name.of.the.plugin"></a> <h4>The name of the plugin</h4> @@ -370,7 +359,7 @@ and upper case when searching for an API by its name.</p> <p>The names of the verbs are not checked.</p> <p>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.</p> <p>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); |