afb-socket: Improvement
[src/app-framework-binder.git] / src / afb-api-ws.c
1 /*
2  * Copyright (C) 2015-2018 "IoT.bzh"
3  * Author José Bollo <jose.bollo@iot.bzh>
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *   http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17
18 #define _GNU_SOURCE
19
20 #include <stdlib.h>
21 #include <string.h>
22 #include <assert.h>
23 #include <fcntl.h>
24 #include <unistd.h>
25 #include <errno.h>
26 #include <endian.h>
27 #include <netdb.h>
28 #include <sys/types.h>
29 #include <sys/socket.h>
30 #include <sys/un.h>
31
32 #include "afb-api.h"
33 #include "afb-apiset.h"
34 #include "afb-api-ws.h"
35 #include "afb-fdev.h"
36 #include "afb-socket.h"
37 #include "afb-stub-ws.h"
38 #include "verbose.h"
39 #include "fdev.h"
40
41 struct api_ws
42 {
43         char *uri;              /* uri of the object for the API */
44         char *api;              /* api name of the interface */
45         struct fdev *fdev;      /* fdev handler */
46         struct afb_apiset *apiset;
47 };
48
49 /******************************************************************************/
50
51 /*
52  * create a structure api_ws not connected to the 'uri'.
53  */
54 static struct api_ws *api_ws_make(const char *uri)
55 {
56         struct api_ws *api;
57         size_t length;
58
59         /* allocates the structure */
60         length = strlen(uri);
61         api = calloc(1, sizeof *api + 1 + length);
62         if (api == NULL) {
63                 errno = ENOMEM;
64                 goto error;
65         }
66
67         /* uri is copied after the struct */
68         api->uri = (char*)(api+1);
69         memcpy(api->uri, uri, length + 1);
70
71         /* api name is at the end of the uri */
72         while (length && uri[length - 1] != '/' && uri[length - 1] != ':')
73                 length = length - 1;
74         api->api = &api->uri[length];
75         if (api->api == NULL || !afb_api_is_valid_name(api->api)) {
76                 errno = EINVAL;
77                 goto error2;
78         }
79
80         return api;
81
82 error2:
83         free(api);
84 error:
85         return NULL;
86 }
87
88 /**********************************************************************************/
89
90 int afb_api_ws_add_client(const char *uri, struct afb_apiset *declare_set, struct afb_apiset *call_set, int strong)
91 {
92         struct api_ws *apiws;
93         struct afb_stub_ws *stubws;
94
95         /* create the ws client api */
96         apiws = api_ws_make(uri);
97         if (apiws == NULL)
98                 goto error;
99
100         /* connect to the service */
101         apiws->fdev = afb_socket_open_fdev(apiws->uri, 0);
102         if (!apiws->fdev)
103                 goto error2;
104
105         stubws = afb_stub_ws_create_client(apiws->fdev, apiws->api, call_set);
106         if (!stubws) {
107                 ERROR("can't setup client ws service to %s", apiws->uri);
108                 goto error3;
109         }
110         if (afb_stub_ws_client_add(stubws, declare_set) < 0) {
111                 ERROR("can't add the client to the apiset for service %s", apiws->uri);
112                 goto error3;
113         }
114         free(apiws);
115         return 0;
116 error3:
117         afb_stub_ws_unref(stubws);
118 error2:
119         free(apiws);
120 error:
121         return -!!strong;
122 }
123
124 int afb_api_ws_add_client_strong(const char *uri, struct afb_apiset *declare_set, struct afb_apiset *call_set)
125 {
126         return afb_api_ws_add_client(uri, declare_set, call_set, 1);
127 }
128
129 int afb_api_ws_add_client_weak(const char *uri, struct afb_apiset *declare_set, struct afb_apiset *call_set)
130 {
131         return afb_api_ws_add_client(uri, declare_set, call_set, 0);
132 }
133
134 static int api_ws_server_accept_client(struct api_ws *apiws, struct fdev *fdev)
135 {
136         return -!afb_stub_ws_create_server(fdev, apiws->api, apiws->apiset);
137 }
138
139 static void api_ws_server_accept(struct api_ws *apiws)
140 {
141         int rc, fd;
142         struct sockaddr addr;
143         socklen_t lenaddr;
144         struct fdev *fdev;
145
146         lenaddr = (socklen_t)sizeof addr;
147         fd = accept(fdev_fd(apiws->fdev), &addr, &lenaddr);
148         if (fd < 0) {
149                 ERROR("can't accept connection to %s: %m", apiws->uri);
150         } else {
151                 fdev = afb_fdev_create(fd);
152                 if (!fdev) {
153                         ERROR("can't hold accepted connection to %s: %m", apiws->uri);
154                         close(fd);
155                 } else {
156                         rc = api_ws_server_accept_client(apiws, fdev);
157                         if (rc < 0)
158                                 ERROR("can't serve accepted connection to %s: %m", apiws->uri);
159                 }
160         }
161 }
162
163 static int api_ws_server_connect(struct api_ws *apiws);
164
165 static void api_ws_server_listen_callback(void *closure, uint32_t revents, struct fdev *fdev)
166 {
167         struct api_ws *apiws = closure;
168
169         if ((revents & EPOLLIN) != 0)
170                 api_ws_server_accept(apiws);
171         if ((revents & EPOLLHUP) != 0)
172                 api_ws_server_connect(apiws);
173 }
174
175 static void api_ws_server_disconnect(struct api_ws *apiws)
176 {
177         fdev_unref(apiws->fdev);
178         apiws->fdev = 0;
179 }
180
181 static int api_ws_server_connect(struct api_ws *apiws)
182 {
183         /* ensure disconnected */
184         api_ws_server_disconnect(apiws);
185
186         /* request the service object name */
187         apiws->fdev = afb_socket_open_fdev(apiws->uri, 1);
188         if (!apiws->fdev)
189                 ERROR("can't create socket %s", apiws->uri);
190         else {
191                 /* listen for service */
192                 fdev_set_events(apiws->fdev, EPOLLIN);
193                 fdev_set_callback(apiws->fdev, api_ws_server_listen_callback, apiws);
194                 return 0;
195         }
196         return -1;
197 }
198
199 /* create the service */
200 int afb_api_ws_add_server(const char *uri, struct afb_apiset *declare_set, struct afb_apiset *call_set)
201 {
202         int rc;
203         struct api_ws *apiws;
204
205         /* creates the ws api object */
206         apiws = api_ws_make(uri);
207         if (apiws == NULL)
208                 goto error;
209
210         /* check api name */
211         if (!afb_apiset_lookup(call_set, apiws->api, 1)) {
212                 ERROR("Can't provide ws-server for %s: API %s doesn't exist", uri, apiws->api);
213                 goto error2;
214         }
215
216         /* connect for serving */
217         rc = api_ws_server_connect(apiws);
218         if (rc < 0)
219                 goto error2;
220
221         apiws->apiset = afb_apiset_addref(call_set);
222         return 0;
223
224 error2:
225         free(apiws);
226 error:
227         return -1;
228 }