- close(fd);
- }
- iai = iai->ai_next;
- }
- freeaddrinfo(rai);
- return -1;
-
-}
-
-static int api_ws_socket(const char *path, int server)
-{
- int fd, rc;
-
- /* check for unix socket */
- if (0 == strncmp(path, "unix:", 5))
- fd = api_ws_socket_unix(path + 5, server);
- else
- fd = api_ws_socket_inet(path, server);
-
- if (fd >= 0) {
- fcntl(fd, F_SETFD, FD_CLOEXEC);
- fcntl(fd, F_SETFL, O_NONBLOCK);
- if (server) {
- rc = 1;
- setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &rc, sizeof rc);
- rc = listen(fd, 5);
- }
- }
- return fd;
-}
-
-/******************* serialisation part **********************************/
-
-struct readbuf
-{
- char *head, *end;
-};
-
-#define WRITEBUF_COUNT_MAX 32
-struct writebuf
-{
- struct iovec iovec[WRITEBUF_COUNT_MAX];
- uint32_t uints[WRITEBUF_COUNT_MAX];
- int count;
-};
-
-static char *api_ws_read_get(struct readbuf *rb, uint32_t length)
-{
- char *before = rb->head;
- char *after = before + length;
- if (after > rb->end)
- return 0;
- rb->head = after;
- return before;
-}
-
-static int api_ws_read_uint32(struct readbuf *rb, uint32_t *value)
-{
- char *after = rb->head + sizeof *value;
- if (after > rb->end)
- return 0;
- memcpy(value, rb->head, sizeof *value);
- rb->head = after;
- *value = le32toh(*value);
- return 1;
-}
-
-static int api_ws_read_string(struct readbuf *rb, const char **value, size_t *length)
-{
- uint32_t len;
- if (!api_ws_read_uint32(rb, &len) || !len)
- return 0;
- if (length)
- *length = (size_t)(len - 1);
- return (*value = api_ws_read_get(rb, len)) != NULL && rb->head[-1] == 0;
-}
-
-static int api_ws_read_object(struct readbuf *rb, struct json_object **object)
-{
- size_t length;
- const char *string;
- return api_ws_read_string(rb, &string, &length) && ((*object = json_tokener_parse(string)) != NULL) == (strcmp(string, "null") != 0);
-}
-
-static int api_ws_write_put(struct writebuf *wb, const void *value, size_t length)
-{
- int i = wb->count;
- if (i == WRITEBUF_COUNT_MAX)
- return 0;
- wb->iovec[i].iov_base = (void*)value;
- wb->iovec[i].iov_len = length;
- wb->count = i + 1;
- return 1;
-}
-
-static int api_ws_write_char(struct writebuf *wb, char value)
-{
- int i = wb->count;
- if (i == WRITEBUF_COUNT_MAX)
- return 0;
- *(char*)&wb->uints[i] = value;
- wb->iovec[i].iov_base = &wb->uints[i];
- wb->iovec[i].iov_len = 1;
- wb->count = i + 1;
- return 1;
-}
-
-static int api_ws_write_uint32(struct writebuf *wb, uint32_t value)
-{
- int i = wb->count;
- if (i == WRITEBUF_COUNT_MAX)
- return 0;
- wb->uints[i] = htole32(value);
- wb->iovec[i].iov_base = &wb->uints[i];
- wb->iovec[i].iov_len = sizeof wb->uints[i];
- wb->count = i + 1;
- return 1;
-}
-
-static int api_ws_write_string_nz(struct writebuf *wb, const char *value, size_t length)
-{
- uint32_t len = (uint32_t)length;
- return (size_t)len == length && ++len && api_ws_write_uint32(wb, len) && api_ws_write_put(wb, value, length) && api_ws_write_char(wb, '\0');
-}
-
-static int api_ws_write_string_length(struct writebuf *wb, const char *value, size_t length)
-{
- uint32_t len = (uint32_t)++length;
- return (size_t)len == length && len && api_ws_write_uint32(wb, len) && api_ws_write_put(wb, value, length);
-}
-
-static int api_ws_write_string(struct writebuf *wb, const char *value)
-{
- return api_ws_write_string_length(wb, value, strlen(value));
-}
-
-static int api_ws_write_object(struct writebuf *wb, struct json_object *object)
-{
- const char *string = json_object_to_json_string_ext(object, JSON_C_TO_STRING_PLAIN);
- return string != NULL && api_ws_write_string(wb, string);
-}
-
-
-
-
-/******************* client part **********************************/
-
-/*
- * structure for recording query data
- */
-struct api_ws_memo {
- struct api_ws_memo *next; /* the next memo */
- struct api_ws *api; /* the ws api */
- struct afb_req req; /* the request handle */
- struct afb_context *context; /* the context of the query */
- uint32_t msgid; /* the message identifier */
-};
-
-struct api_ws_event
-{
- struct api_ws_event *next;
- struct afb_event event;
- int eventid;
- int refcount;
-};
-
-/* search a memorized request */
-static struct api_ws_memo *api_ws_client_memo_search(struct api_ws *api, uint32_t msgid)
-{
- struct api_ws_memo *memo;
-
- memo = api->client.memos;
- while (memo != NULL && memo->msgid != msgid)
- memo = memo->next;
-
- return memo;
-}
-
-/* search the event */
-static struct api_ws_event *api_ws_client_event_search(struct api_ws *api, uint32_t eventid, const char *name)
-{
- struct api_ws_event *ev;
-
- ev = api->client.events;
- while (ev != NULL && (ev->eventid != eventid || 0 != strcmp(afb_evt_event_name(ev->event), name)))
- ev = ev->next;
-
- return ev;
-}
-
-
-/* allocates and init the memorizing data */
-static struct api_ws_memo *api_ws_client_memo_make(struct api_ws *api, struct afb_req req, struct afb_context *context)
-{
- struct api_ws_memo *memo;
-
- memo = malloc(sizeof *memo);
- if (memo != NULL) {
- afb_req_addref(req);
- memo->req = req;
- memo->context = context;
- do { memo->msgid = ++api->client.id; } while(api_ws_client_memo_search(api, memo->msgid) != NULL);
- memo->api = api;
- memo->next = api->client.memos;
- api->client.memos = memo;
- }
- return memo;
-}
-
-/* free and release the memorizing data */
-static void api_ws_client_memo_destroy(struct api_ws_memo *memo)
-{
- struct api_ws_memo **prv;
-
- prv = &memo->api->client.memos;
- while (*prv != NULL) {
- if (*prv == memo) {
- *prv = memo->next;
- break;
- }
- prv = &(*prv)->next;
- }
-
- afb_req_unref(memo->req);
- free(memo);
-}
-
-/* get event data from the message */
-static int api_ws_client_msg_event_read(struct readbuf *rb, uint32_t *eventid, const char **name)
-{
- return api_ws_read_uint32(rb, eventid) && api_ws_read_string(rb, name, NULL);
-}
-
-/* get event from the message */
-static int api_ws_client_msg_event_get(struct api_ws *api, struct readbuf *rb, struct api_ws_event **ev)
-{
- const char *name;
- uint32_t eventid;
-
- /* get event data from the message */
- if (!api_ws_client_msg_event_read(rb, &eventid, &name)) {
- ERROR("Invalid message");
- return 0;
- }
-
- /* check conflicts */
- *ev = api_ws_client_event_search(api, eventid, name);
- if (*ev == NULL) {
- ERROR("event %s not found", name);
- return 0;
- }
-
- return 1;
-}
-
-/* get event from the message */
-static int api_ws_client_msg_memo_get(struct api_ws *api, struct readbuf *rb, struct api_ws_memo **memo)
-{
- uint32_t msgid;
-
- /* get event data from the message */
- if (!api_ws_read_uint32(rb, &msgid)) {
- ERROR("Invalid message");
- return 0;
- }
-
- /* get the memo */
- *memo = api_ws_client_memo_search(api, msgid);
- if (*memo == NULL) {
- ERROR("message not found");
- return 0;
- }
-
- return 1;
-}
-
-/* read a subscrition message */
-static int api_ws_client_msg_subscription_get(struct api_ws *api, struct readbuf *rb, struct api_ws_event **ev, struct api_ws_memo **memo)
-{
- return api_ws_client_msg_memo_get(api, rb, memo) && api_ws_client_msg_event_get(api, rb, ev);
-}
-
-/* adds an event */
-static void api_ws_client_event_create(struct api_ws *api, struct readbuf *rb)
-{
- size_t offset;
- const char *name;
- uint32_t eventid;
- struct api_ws_event *ev;
-
- /* get event data from the message */
- offset = api_ws_client_msg_event_read(rb, &eventid, &name);
- if (offset == 0) {
- ERROR("Invalid message");
- return;
- }
-
- /* check conflicts */
- ev = api_ws_client_event_search(api, eventid, name);
- if (ev != NULL) {
- ev->refcount++;
- return;
- }
-
- /* no conflict, try to add it */
- ev = malloc(sizeof *ev);
- if (ev != NULL) {
- ev->event = afb_evt_create_event(name);
- if (ev->event.closure == NULL)
- free(ev);
- else {
- ev->refcount = 1;
- ev->eventid = eventid;
- ev->next = api->client.events;
- api->client.events = ev;
- return;
- }
- }
- ERROR("can't create event %s, out of memory", name);
-}
-
-/* removes an event */
-static void api_ws_client_event_drop(struct api_ws *api, struct readbuf *rb)
-{
- struct api_ws_event *ev, **prv;
-
- /* retrieves the event */
- if (!api_ws_client_msg_event_get(api, rb, &ev))
- return;
-
- /* decrease the reference count */
- if (--ev->refcount)
- return;
-
- /* unlinks the event */
- prv = &api->client.events;
- while (*prv != ev)
- prv = &(*prv)->next;
- *prv = ev->next;
-
- /* destroys the event */
- afb_event_drop(ev->event);
- free(ev);
-}
-
-/* subscribes an event */
-static void api_ws_client_event_subscribe(struct api_ws *api, struct readbuf *rb)
-{
- struct api_ws_event *ev;
- struct api_ws_memo *memo;
-
- if (api_ws_client_msg_subscription_get(api, rb, &ev, &memo)) {
- /* subscribe the request from the event */
- if (afb_req_subscribe(memo->req, ev->event) < 0)
- ERROR("can't subscribe: %m");
- }
-}
-
-/* unsubscribes an event */
-static void api_ws_client_event_unsubscribe(struct api_ws *api, struct readbuf *rb)
-{
- struct api_ws_event *ev;
- struct api_ws_memo *memo;
-
- if (api_ws_client_msg_subscription_get(api, rb, &ev, &memo)) {
- /* unsubscribe the request from the event */
- if (afb_req_unsubscribe(memo->req, ev->event) < 0)
- ERROR("can't unsubscribe: %m");
- }
-}
-
-/* receives broadcasted events */
-static void api_ws_client_event_broadcast(struct api_ws *api, struct readbuf *rb)
-{
- struct json_object *object;
- const char *event;
-
- if (api_ws_read_string(rb, &event, NULL) && api_ws_read_object(rb, &object))
- afb_evt_broadcast(event, object);
- else
- ERROR("unreadable broadcasted event");
-}
-
-/* pushs an event */
-static void api_ws_client_event_push(struct api_ws *api, struct readbuf *rb)
-{
- struct api_ws_event *ev;
- struct json_object *object;
-
- if (api_ws_client_msg_event_get(api, rb, &ev) && api_ws_read_object(rb, &object))
- afb_event_push(ev->event, object);
- else
- ERROR("unreadable push event");
-}
-
-static void api_ws_client_reply_success(struct api_ws *api, struct readbuf *rb)
-{
- struct api_ws_memo *memo;
- struct json_object *object;
- const char *info;
- uint32_t flags;
-
- /* retrieve the message data */
- if (!api_ws_client_msg_memo_get(api, rb, &memo))
- return;
-
- if (api_ws_read_uint32(rb, &flags)
- && api_ws_read_string(rb, &info, NULL)
- && api_ws_read_object(rb, &object)) {
- memo->context->flags = (unsigned)flags;
- afb_req_success(memo->req, object, *info ? info : NULL);
- } else {
- /* failing to have the answer */
- afb_req_fail(memo->req, "error", "ws error");
- }
- api_ws_client_memo_destroy(memo);
-}
-
-static void api_ws_client_reply_fail(struct api_ws *api, struct readbuf *rb)
-{
- struct api_ws_memo *memo;
- const char *info, *status;
- uint32_t flags;
-
- /* retrieve the message data */
- if (!api_ws_client_msg_memo_get(api, rb, &memo))
- return;
-
- if (api_ws_read_uint32(rb, &flags)
- && api_ws_read_string(rb, &status, NULL)
- && api_ws_read_string(rb, &info, NULL)) {
- memo->context->flags = (unsigned)flags;
- afb_req_fail(memo->req, status, *info ? info : NULL);
- } else {
- /* failing to have the answer */
- afb_req_fail(memo->req, "error", "ws error");
- }
- api_ws_client_memo_destroy(memo);
-}
-
-static void api_ws_client_reply_send(struct api_ws *api, struct readbuf *rb)
-{
- struct api_ws_memo *memo;
- const char *data;
- size_t length;
- uint32_t flags;
-
- /* retrieve the message data */
- if (!api_ws_client_msg_memo_get(api, rb, &memo))
- return;
-
- if (api_ws_read_uint32(rb, &flags)
- && api_ws_read_string(rb, &data, &length)) {
- memo->context->flags = (unsigned)flags;
- afb_req_send(memo->req, data, length);
- } else {
- /* failing to have the answer */
- afb_req_fail(memo->req, "error", "ws error");
- }
- api_ws_client_memo_destroy(memo);
-}
-
-/* callback when receiving binary data */
-static void api_ws_client_on_binary(void *closure, char *data, size_t size)
-{
- if (size > 0) {
- struct readbuf rb = { .head = data, .end = data + size };
- switch (*rb.head++) {
- case 'T': /* success */
- api_ws_client_reply_success(closure, &rb);
- break;
- case 'F': /* fail */
- api_ws_client_reply_fail(closure, &rb);
- break;
- case 'X': /* send */
- api_ws_client_reply_send(closure, &rb);
- break;
- case '*': /* broadcast */
- api_ws_client_event_broadcast(closure, &rb);
- break;
- case '+': /* creates the event */
- api_ws_client_event_create(closure, &rb);
- break;
- case '-': /* drops the event */
- api_ws_client_event_drop(closure, &rb);
- break;
- case '!': /* pushs the event */
- api_ws_client_event_push(closure, &rb);
- break;
- case 'S': /* subscribe event for a request */
- api_ws_client_event_subscribe(closure, &rb);
- break;
- case 'U': /* unsubscribe event for a request */
- api_ws_client_event_unsubscribe(closure, &rb);
- break;
- default: /* unexpected message */
- break;
- }
- }
- free(data);
-}
-
-/* on call, propagate it to the ws service */
-static void api_ws_client_call_cb(void * closure, struct afb_req req, struct afb_context *context, const char *verb, size_t lenverb)
-{
- int rc;
- struct api_ws_memo *memo;
- struct writebuf wb = { .count = 0 };
- const char *raw;
- size_t szraw;
- struct api_ws *api = closure;
-
- /* create the recording data */
- memo = api_ws_client_memo_make(api, req, context);
- if (memo == NULL) {
- afb_req_fail(req, "error", "out of memory");
- return;
- }
-
- /* creates the call message */
- raw = afb_req_raw(req, &szraw);
- if (raw == NULL)
- goto internal_error;
- if (!api_ws_write_uint32(&wb, memo->msgid)
- || !api_ws_write_uint32(&wb, (uint32_t)context->flags)
- || !api_ws_write_string_nz(&wb, verb, lenverb)
- || !api_ws_write_string(&wb, ctxClientGetUuid(context->session))
- || !api_ws_write_string_length(&wb, raw, szraw))
- goto overflow;
-
- /* send */
- rc = afb_ws_binary_v(api->client.ws, wb.iovec, wb.count);
- if (rc < 0)
- goto ws_send_error;
- return;
-
-ws_send_error:
- afb_req_fail(req, "error", "websocket sending error");
- goto clean_memo;
-
-internal_error:
- afb_req_fail(req, "error", "internal: raw is NULL!");
- goto clean_memo;
-
-overflow:
- afb_req_fail(req, "error", "overflow: size doesn't match 32 bits!");
-
-clean_memo:
- api_ws_client_memo_destroy(memo);
-}
-
-static int api_ws_service_start_cb(void *closure, int share_session, int onneed)
-{
- struct api_ws *api = closure;
-
- /* not an error when onneed */
- if (onneed != 0)
- return 0;
-
- /* already started: it is an error */
- ERROR("The WS binding %s is not a startable service", api->path);
- return -1;
-}
-
-/* */
-static void api_ws_client_disconnect(struct api_ws *api)
-{
- if (api->fd >= 0) {
- afb_ws_destroy(api->client.ws);
- api->client.ws = NULL;
- close(api->fd);
- api->fd = -1;
- }
-}
-
-/* */
-static int api_ws_client_connect(struct api_ws *api)
-{
- struct afb_ws *ws;
- int fd;
-
- fd = api_ws_socket(api->path, 0);
- if (fd >= 0) {
- ws = afb_ws_create(afb_common_get_event_loop(), fd, &api_ws_client_ws_itf, api);
- if (ws != NULL) {
- api->client.ws = ws;
- api->fd = fd;
- return 0;