From 6914f7781b42972263a417484bbeb179efe66e78 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Jos=C3=A9=20Bollo?= Date: Sun, 29 May 2016 00:12:00 +0200 Subject: [PATCH] adds documentation for websocket C clients MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Change-Id: I5507aeaf7669123eee16007af3d2fd3faeba8141 Signed-off-by: José Bollo --- doc/afb-plugin-writing.html | 131 ++++++++++++++++++--------------------- doc/afb-plugin-writing.md | 4 +- src/afb-ws-client.h | 6 ++ 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 @@

HOWTO WRITE a PLUGIN for AFB-DAEMON

version: 1
-Date:    27 mai 2016
+Date:    29 mai 2016
 Author:  José Bollo
 
@@ -18,14 +18,14 @@ Author: José Bollo
  • Summary
    • Nature of a plugin
    • -
    • Kinds of plugins +
    • Class of plugins
    • -
    • Live cycle of a plugin within afb-daemon
    • -
    • Content of a plugin +
    • Live cycle of plugins within afb-daemon
    • +
    • Plugin Contend
      • The name of the plugin
      • Names of verbs
      • @@ -85,101 +85,90 @@ Author: José Bollo

        Summary

        -

        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.

        +by developers.

        -

        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.

        Nature of a plugin

        -

        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.

        - -

        Kinds of plugins

        + +

        Class of plugins

        -

        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

        + +

        Application-plugins

        -

        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

        + +

        Service-plugins

        -

        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)

        - -

        Live cycle of a plugin within afb-daemon

        + +

        Live cycle of plugins within afb-daemon

        -

        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.

        - -

        Content of a plugin

        + +

        Plugin Contend

        -

        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 name of the plugin

        @@ -370,7 +359,7 @@ and upper case when searching for an API by its name.

        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); -- 2.16.6