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