Update copyright dates
[src/app-framework-binder.git] / src / afb-api-ws.c
1 /*
2  * Copyright (C) 2015-2020 "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_server
42 {
43         struct afb_apiset *apiset;      /* the apiset for calling */
44         struct fdev *fdev;              /* fdev handler */
45         uint16_t offapi;                /* api name of the interface */
46         char uri[];                     /* the uri of the server socket */
47 };
48
49 /******************************************************************************/
50 /***       C L I E N T                                                      ***/
51 /******************************************************************************/
52
53 static struct fdev *reopen_client(void *closure)
54 {
55         const char *uri = closure;
56         return afb_socket_open_fdev(uri, 0);
57 }
58
59 int afb_api_ws_add_client(const char *uri, struct afb_apiset *declare_set, struct afb_apiset *call_set, int strong)
60 {
61         struct afb_stub_ws *stubws;
62         struct fdev *fdev;
63         const char *api;
64
65         /* check the api name */
66         api = afb_socket_api(uri);
67         if (api == NULL || !afb_api_is_valid_name(api)) {
68                 ERROR("invalid (too long) ws client uri %s", uri);
69                 errno = EINVAL;
70                 goto error;
71         }
72
73         /* open the socket */
74         fdev = afb_socket_open_fdev(uri, 0);
75         if (fdev) {
76                 /* create the client stub */
77                 stubws = afb_stub_ws_create_client(fdev, api, call_set);
78                 if (!stubws) {
79                         ERROR("can't setup client ws service to %s", uri);
80                         fdev_unref(fdev);
81                 } else {
82                         if (afb_stub_ws_client_add(stubws, declare_set) >= 0) {
83 #if 1
84                                 /* it is asserted here that uri is never released */
85                                 afb_stub_ws_client_robustify(stubws, reopen_client, (void*)uri, NULL);
86 #else
87                                 /* it is asserted here that uri is released, so use a copy */
88                                 afb_stub_ws_client_robustify(stubws, reopen_client, strdup(uri), free);
89 #endif
90                                 return 0;
91                         }
92                         ERROR("can't add the client to the apiset for service %s", uri);
93                         afb_stub_ws_unref(stubws);
94                 }
95         }
96 error:
97         return -!!strong;
98 }
99
100 int afb_api_ws_add_client_strong(const char *uri, struct afb_apiset *declare_set, struct afb_apiset *call_set)
101 {
102         return afb_api_ws_add_client(uri, declare_set, call_set, 1);
103 }
104
105 int afb_api_ws_add_client_weak(const char *uri, struct afb_apiset *declare_set, struct afb_apiset *call_set)
106 {
107         return afb_api_ws_add_client(uri, declare_set, call_set, 0);
108 }
109
110 /*****************************************************************************/
111 /***       S E R V E R                                                      ***/
112 /******************************************************************************/
113
114 static void api_ws_server_accept(struct api_ws_server *apiws)
115 {
116         int fd;
117         struct sockaddr addr;
118         socklen_t lenaddr;
119         struct fdev *fdev;
120         struct afb_stub_ws *server;
121
122         lenaddr = (socklen_t)sizeof addr;
123         fd = accept(fdev_fd(apiws->fdev), &addr, &lenaddr);
124         if (fd < 0) {
125                 ERROR("can't accept connection to %s: %m", apiws->uri);
126         } else {
127                 fdev = afb_fdev_create(fd);
128                 if (!fdev) {
129                         ERROR("can't hold accepted connection to %s: %m", apiws->uri);
130                         close(fd);
131                 } else {
132                         server = afb_stub_ws_create_server(fdev, &apiws->uri[apiws->offapi], apiws->apiset);
133                         if (server)
134                                 afb_stub_ws_set_on_hangup(server, afb_stub_ws_unref);
135                         else
136                                 ERROR("can't serve accepted connection to %s: %m", apiws->uri);
137                 }
138         }
139 }
140
141 static int api_ws_server_connect(struct api_ws_server *apiws);
142
143 static void api_ws_server_listen_callback(void *closure, uint32_t revents, struct fdev *fdev)
144 {
145         struct api_ws_server *apiws = closure;
146
147         if ((revents & EPOLLHUP) != 0)
148                 api_ws_server_connect(apiws);
149         else if ((revents & EPOLLIN) != 0)
150                 api_ws_server_accept(apiws);
151 }
152
153 static void api_ws_server_disconnect(struct api_ws_server *apiws)
154 {
155         fdev_unref(apiws->fdev);
156         apiws->fdev = 0;
157 }
158
159 static int api_ws_server_connect(struct api_ws_server *apiws)
160 {
161         /* ensure disconnected */
162         api_ws_server_disconnect(apiws);
163
164         /* request the service object name */
165         apiws->fdev = afb_socket_open_fdev(apiws->uri, 1);
166         if (!apiws->fdev)
167                 ERROR("can't create socket %s", apiws->uri);
168         else {
169                 /* listen for service */
170                 fdev_set_events(apiws->fdev, EPOLLIN);
171                 fdev_set_callback(apiws->fdev, api_ws_server_listen_callback, apiws);
172                 return 0;
173         }
174         return -1;
175 }
176
177 /* create the service */
178 int afb_api_ws_add_server(const char *uri, struct afb_apiset *declare_set, struct afb_apiset *call_set)
179 {
180         int rc;
181         const char *api;
182         struct api_ws_server *apiws;
183         size_t luri, lapi, extra;
184
185         /* check the size */
186         luri = strlen(uri);
187         if (luri > 4000) {
188                 ERROR("can't create socket %s", uri);
189                 errno = E2BIG;
190                 return -1;
191         }
192
193         /* check the api name */
194         api = afb_socket_api(uri);
195         if (api == NULL || !afb_api_is_valid_name(api)) {
196                 ERROR("invalid api name in ws uri %s", uri);
197                 errno = EINVAL;
198                 goto error;
199         }
200
201         /* check api name */
202         if (!afb_apiset_lookup(call_set, api, 1)) {
203                 ERROR("Can't provide ws-server for URI %s: API %s doesn't exist", uri, api);
204                 errno = ENOENT;
205                 goto error;
206         }
207
208         /* make the structure */
209         lapi = strlen(api);
210         extra = luri == (api - uri) + lapi ? 0 : lapi + 1;
211         apiws = malloc(sizeof * apiws + 1 + luri + extra);
212         if (!apiws) {
213                 ERROR("out of memory");
214                 errno = ENOMEM;
215                 goto error;
216         }
217
218         apiws->apiset = afb_apiset_addref(call_set);
219         apiws->fdev = 0;
220         strcpy(apiws->uri, uri);
221         if (!extra)
222                 apiws->offapi = (uint16_t)(api - uri);
223         else {
224                 apiws->offapi = (uint16_t)(luri + 1);
225                 strcpy(&apiws->uri[apiws->offapi], api);
226         }
227
228         /* connect for serving */
229         rc = api_ws_server_connect(apiws);
230         if (rc >= 0)
231                 return 0;
232
233         afb_apiset_unref(apiws->apiset);
234         free(apiws);
235 error:
236         return -1;
237 }