fd5415192703f19426b6186bdc7f3fa0486e4e8a
[AGL/documentation.git] / docs / 4_APIs_and_Services / 4.3_Application_Framework_Binder / 2_How_to_write_a_binding / 2_How_to_write_a_binding.md
1 ---
2 edit_link: ''
3 title: How to write a binding ?
4 origin_url: >-
5   https://git.automotivelinux.org/src/app-framework-binder/plain/docs/afb-binding-writing.md?h=master
6 ---
7
8 <!-- WARNING: This file is generated by fetch_docs.js using /home/boron/Documents/AGL/docs-webtemplate/site/_data/tocs/apis_services/master/app-framework-binder-developer-guides-api-services-book.yml -->
9
10 # Overview of the bindings
11
12 The ***binder*** serves files through HTTP protocol and offers developers the capability to offer application API methods through HTTP or
13 WebSocket protocol.
14
15 The ***bindings*** are used to add **API** to ***binders***.  
16 This part describes how to write a ***binding*** for ***binder***
17 or in other words how to add a new **API** to the system.
18
19 This section target developers.
20
21 This section shortly explain how to write a binding
22 using the C programming language.
23
24 It is convenient to install the ***binder*** on the
25 desktop used for writing the binding.  
26 It allows for easy debug and test.
27
28 ## Nature of a binding
29
30 A ***binding*** is an independent piece of software compiled as a shared
31 library and dynamically loaded by a ***binder***.  
32 It is intended to provide one **API** (**A**pplication **P**rogramming
33 **I**nterface).
34
35 The **API** is designated and accessed through its name.  
36 It contains several **verbs** that implement the ***binding***
37 functionalities.  
38 Each of these **verbs** is a **method** that
39 processes requests of applications and sends results.
40
41 The ***binding***'s methods are invoked by HTTP or websocket
42 requests.
43
44 The **methods** of the ***bindings*** are noted **api/verb**
45 where **api** is the **API** name of the binding and **verb** is
46 the **method**'s name within the **API**.  
47 This notation comes from HTTP invocations that rely on URL path terminated
48 with **api/verb**.
49
50 The name of an **API** can be made of any characters except:
51
52 - the control characters (\u0000 .. \u001f)
53 - the characters of the set { ' ', '"', '#', '%', '&',
54    '\'', '/', '?', '`', '\x7f' }
55
56 The names of the **verbs** can be any character.
57
58 The binder makes no distinctions between upper case and lower case
59 latin letters.  
60 So **API/VERB** matches **Api/Verb** or **api/verb**.
61
62 ## Versions of the bindings
63
64 Since introduction of the binder, the way how bindings are written
65 evolved a little. While changing, attention was made to ensure binary
66 compatibility between the different versions.
67
68 Actually it exists 3 ways of writing ***bindings***.
69 You can either write:
70
71 - a binding version 1 (not more supported);
72 - a binding version 2 (not recommended);
73 - a binding version 3 (RECOMMENDED).
74
75 A ***binder*** loads and runs any of these version in any combination.  
76 This document explain how to write bindings version 3.
77
78 <!-- pagebreak -->
79
80 ## Sample binding: tuto-1
81
82 This is the code of the binding **tuto-1.c**:
83
84 ```C
85   1 #define AFB_BINDING_VERSION 3
86   2 #include <afb/afb-binding.h>
87   3
88   4 void hello(afb_req_t req)
89   5 {
90   6         AFB_REQ_DEBUG(req, "hello world");
91   7         afb_req_reply(req, NULL, NULL, "hello world");
92   8 }
93   9
94  10 const afb_verb_t verbs[] = {
95  11         { .verb="hello", .callback=hello },
96  12         { .verb=NULL }
97  13 };
98  14
99  15 const afb_binding_t afbBindingExport = {
100  16         .api = "tuto-1",
101  17         .verbs = verbs
102  18 };
103 ```
104
105 Compiling:
106
107 ```bash
108 gcc -fPIC -shared tuto-1.c -o tuto-1.so $(pkg-config --cflags-only-I afb-daemon)
109 ```
110
111 > Note: the variable environment variable PKG_CONFIG_PATH might be necessary
112 > tuned to get **pkg-config** working properly
113
114 Running:
115
116 ```bash
117 afb-daemon --binding ./tuto-1.so --port 3333 --token ''
118 ```
119
120 At this point, afb-daemon has started, it loaded the binding tuto-1.so and now
121 listen at localhost on the port 3333.
122
123 Testing using **curl**:
124
125 ```bash
126 $ curl http://localhost:3333/api/tuto-1/hello
127 {"jtype":"afb-reply","request":{"status":"success","info":"hello world","uuid":"1e587b54-900b-49ab-9940-46141bc2e1d6"}}
128 ```
129
130 Testing using **afb-client-demo** (with option -H for
131 getting a human readable output):
132
133 ```bash
134 $ afb-client-demo -H ws://localhost:3333/api?token=x tuto-1 hello
135 ON-REPLY 1:tuto-1/hello: OK
136 {
137   "jtype":"afb-reply",
138   "request":{
139     "status":"success",
140     "info":"hello world",
141     "uuid":"03a84ad1-458a-4ace-af74-b1da917391b9"
142   }
143 }
144 ```
145
146 This shows basic things:
147
148 - The include to get for creating a binding
149 - How to declare the API offered by the binding
150 - How to handle requests made to the binding
151
152 ### Getting declarations for the binding
153
154 The lines 1 and 2 show how to get the include file **afb-binding.h**.
155
156 ```C
157   1 #define AFB_BINDING_VERSION 3
158   2 #include <afb/afb-binding.h>
159 ```
160
161 You must define the version of ***binding*** that you are using. 
162 This is done line 1 where we define that this is the version 3 (earlier
163 versions 1 and 2 are deprecated).
164
165 If you don't define it, an error is reported and the compilation aborts.
166
167 To include **afb-binding.h** successfully, the include search path
168 should be set correctly if needed (not needed only if installed in
169 /usr/include/afb directory that is the default).
170
171 Setting the include path is easy using **pkg-config**:
172
173 ```bash
174 pkg-config --cflags-only-I afb-daemon
175 ```
176
177 > Note for **C++** developers: 
178 >
179 > The ***binder*** currently expose a draft version of **C++** api.  
180 > To get it include the file <**afb/afb-binding**> (without **.h**).
181
182
183 ### Declaring the API of the binding
184
185 Lines 10 to 18 show the declaration of the ***binding***.
186
187 The ***binder*** knows that this is a ***binding*** because
188 it finds the exported symbol **afbBindingExport** that is expected to be
189 a structure of type **afb_binding_t**.
190
191 ```C
192  10 const afb_verb_t verbs[] = {
193  11         { .verb="hello", .callback=hello },
194  12         { .verb=NULL }
195  13 };
196  14
197  15 const afb_binding_t afbBindingExport = {
198  16         .api = "tuto-1",
199  17         .verbs = verbs
200  18 };
201 ```
202
203 The structure **afbBindingExport** actually tells that:
204
205 - the exported **API** name is **tuto-1** (line 16)
206 - the array of verbs is the above defined one
207
208 The exported list of verb is specified by an array of structures of
209 type **afb_verb_t**, each describing a verb, ended with a verb NULL (line 12).
210
211 The only defined verb here (line 11) is named **hello** (field **.verb**)
212 and the function that handle the related request is **hello**
213 (field **.callback**).
214
215 ### Handling binder's requests
216
217 As shown above this is by default the common include directory where
218 the AGL stuff is installed.
219
220 ```C
221   4 void hello(afb_req_t req)
222   5 {
223   6         AFB_REQ_DEBUG(req, "hello world");
224   7         afb_req_reply(req, NULL, NULL, "hello world");
225   8 }
226 ```
227
228 When the ***binder*** receives a request for the verb **hello** of
229 of the api **tuto-1**, it invoke the callback **hello** of the **binding**
230 with the argument **req** that handles the client request.
231
232 The callback has to treat synchronously or asynchronously the request and
233 should at the end emit a reply for the request.
234
235 At the line 7, the callback for **tuto-1/hello** replies to the request **req**.
236 Parameters of the reply are:
237
238  1. The first parameter is the replied request
239  2. The second parameter is a json object (here NULL)
240  3. The third parameter is the error string indication (here NULL: no error)
241  4. The fourth parameter is an informative string (that can be NULL) that can be used to provide meta data.
242
243 The 3 last parameters are sent back to the client as the reply content.
244
245 <!-- pagebreak -->
246
247 ## Sample binding: tuto-2
248
249 The second tutorial shows many important feature that can
250 commonly be used when writing a ***binding***:
251
252 - initialization, getting arguments, sending replies, pushing events.
253
254 This is the code of the binding **tuto-2.c**:
255
256 ```C
257       1 #include <string.h>
258       2 #include <json-c/json.h>
259       3 
260       4 #define AFB_BINDING_VERSION 3
261       5 #include <afb/afb-binding.h>
262       6 
263       7 afb_event_t event_login, event_logout;
264       8 
265       9 void login(afb_req_t req)
266      10 {
267      11         json_object *args, *user, *passwd;
268      12         char *usr;
269      13 
270      14         args = afb_req_json(req);
271      15         if (!json_object_object_get_ex(args, "user", &user)
272      16          || !json_object_object_get_ex(args, "password", &passwd)) {
273      17                 AFB_REQ_ERROR(req, "login, bad request: %s", json_object_get_string(args));
274      18                 afb_req_reply(req, NULL, "bad-request", NULL);
275      19         } else if (afb_req_context_get(req)) {
276      20                 AFB_REQ_ERROR(req, "login, bad state, logout first");
277      21                 afb_req_reply(req, NULL, "bad-state", NULL);
278      22         } else if (strcmp(json_object_get_string(passwd), "please")) {
279      23                 AFB_REQ_ERROR(req, "login, unauthorized: %s", json_object_get_string(args));
280      24                 afb_req_reply(req, NULL, "unauthorized", NULL);
281      25         } else {
282      26                 usr = strdup(json_object_get_string(user));
283      27                 AFB_REQ_NOTICE(req, "login user: %s", usr);
284      28                 afb_req_session_set_LOA(req, 1);
285      29                 afb_req_context_set(req, usr, free);
286      30                 afb_req_reply(req, NULL, NULL, NULL);
287      31                 afb_event_push(event_login, json_object_new_string(usr));
288      32         }
289      33 }
290      34 
291      35 void action(afb_req_t req)
292      36 {
293      37         json_object *args, *val;
294      38         char *usr;
295      39 
296      40         args = afb_req_json(req);
297      41         usr = afb_req_context_get(req);
298      42         AFB_REQ_NOTICE(req, "action for user %s: %s", usr, json_object_get_string(args));
299      43         if (json_object_object_get_ex(args, "subscribe", &val)) {
300      44                 if (json_object_get_boolean(val)) {
301      45                         AFB_REQ_NOTICE(req, "user %s subscribes to events", usr);
302      46                         afb_req_subscribe(req, event_login);
303      47                         afb_req_subscribe(req, event_logout);
304      48                 } else {
305      49                         AFB_REQ_NOTICE(req, "user %s unsubscribes to events", usr);
306      50                         afb_req_unsubscribe(req, event_login);
307      51                         afb_req_unsubscribe(req, event_logout);
308      52                 }
309      53         }
310      54         afb_req_reply(req, json_object_get(args), NULL, NULL);
311      55 }
312      56 
313      57 void logout(afb_req_t req)
314      58 {
315      59         char *usr;
316      60 
317      61         usr = afb_req_context_get(req);
318      62         AFB_REQ_NOTICE(req, "login user %s out", usr);
319      63         afb_event_push(event_logout, json_object_new_string(usr));
320      64         afb_req_session_set_LOA(req, 0);
321      65         afb_req_context_clear(req);
322      66         afb_req_reply(req, NULL, NULL, NULL);
323      67 }
324      68 
325      69 int preinit(afb_api_t api)
326      70 {
327      71         AFB_API_NOTICE(api, "preinit");
328      72         return 0;
329      73 }
330      74 
331      75 int init(afb_api_t api)
332      76 {
333      77         AFB_API_NOTICE(api, "init");
334      78         event_login = afb_api_make_event(api, "login");
335      79         event_logout = afb_api_make_event(api, "logout");
336      80         if (afb_event_is_valid(event_login) && afb_event_is_valid(event_logout))
337      81                 return 0;
338      82         AFB_API_ERROR(api, "Can't create events");
339      83         return -1;
340      84 }
341      85 
342      86 const afb_verb_t verbs[] = {
343      87         { .verb="login", .callback=login },
344      88         { .verb="action", .callback=action, .session=AFB_SESSION_LOA_1 },
345      89         { .verb="logout", .callback=logout, .session=AFB_SESSION_LOA_1 },
346      90         { .verb=NULL }
347      91 };
348      92 
349      93 const afb_binding_t afbBindingExport = {
350      94         .api = "tuto-2",
351      95         .specification = NULL,
352      96         .verbs = verbs,
353      97         .preinit = preinit,
354      98         .init = init,
355      99         .noconcurrency = 0
356     100 };
357 ```
358
359 Compiling:
360
361 ```bash
362 gcc -fPIC -shared tuto-2.c -o tuto-2.so $(pkg-config --cflags --libs afb-daemon)
363 ```
364
365 Running:
366
367 ```bash
368 afb-daemon --binding ./tuto-2.so --port 3333 --token ''
369 ```
370
371 Testing:
372
373 ```bash
374 $ afb-client-demo -H localhost:3333/api?token=toto
375 tuto-2 login {"help":true}
376 ON-REPLY 1:tuto-2/login: ERROR
377 {
378   "jtype":"afb-reply",
379   "request":{
380     "status":"bad-request",
381     "uuid":"e2b24a13-fc43-487e-a5f4-9266dd1e60a9"
382   }
383 }
384 tuto-2 login {"user":"jose","password":"please"}
385 ON-REPLY 2:tuto-2/login: OK
386 {
387   "jtype":"afb-reply",
388   "request":{
389     "status":"success"
390   }
391 }
392 tuto-2 login {"user":"jobol","password":"please"}
393 ON-REPLY 3:tuto-2/login: ERROR
394 {
395   "jtype":"afb-reply",
396   "request":{
397     "status":"bad-state"
398   }
399 }
400 tuto-2 action {"subscribe":true}
401 ON-REPLY 4:tuto-2/action: OK
402 {
403   "response":{
404     "subscribe":true
405   },
406   "jtype":"afb-reply",
407   "request":{
408     "status":"success"
409   }
410 }
411 ```
412
413 In another terminal:
414
415 ```bash
416 $ afb-client-demo -H localhost:3333/api?token=toto
417 tuto-2 login {"user":"jobol","password":"please"}
418 ON-REPLY 1:tuto-2/login: OK
419 {
420   "jtype":"afb-reply",
421   "request":{
422     "status":"success",
423     "uuid":"a09f55ff-0e89-4f4e-8415-c6e0e7f439be"
424   }
425 }
426 tuto-2 logout true
427 ON-REPLY 2:tuto-2/logout: OK
428 {
429   "jtype":"afb-reply",
430   "request":{
431     "status":"success"
432   }
433 }
434 ```
435
436 It produced in the first terminal:
437
438 ```bash
439 ON-EVENT tuto-2/login:
440 {
441   "event":"tuto-2\/login",
442   "data":"jobol",
443   "jtype":"afb-event"
444 }
445 ON-EVENT tuto-2/logout:
446 {
447   "event":"tuto-2\/logout",
448   "data":"jobol",
449   "jtype":"afb-event"
450 }
451 ```