161 lines
6.6 KiB
C
161 lines
6.6 KiB
C
/*
|
|
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
|
|
* contributor license agreements. See the NOTICE file distributed with
|
|
* this work for additional information regarding copyright ownership.
|
|
* The OpenAirInterface Software Alliance licenses this file to You under
|
|
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
|
|
* except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.openairinterface.org/?page_id=698
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*-------------------------------------------------------------------------------
|
|
* For more information about the OpenAirInterface (OAI) Software Alliance:
|
|
* contact@openairinterface.org
|
|
*/
|
|
|
|
/*! \file common/utils/websrv/websrv_websockets.c
|
|
* \brief: implementation of web/websockets API
|
|
* \author Francois TABURET
|
|
* \date 2022
|
|
* \version 0.1
|
|
* \company NOKIA BellLabs France
|
|
* \email: francois.taburet@nokia-bell-labs.com
|
|
* \note
|
|
* \warning
|
|
*/
|
|
|
|
#include <gnutls/gnutls.h>
|
|
#include <gnutls/x509.h>
|
|
#include <arpa/inet.h>
|
|
#include <errno.h>
|
|
#include <jansson.h>
|
|
#include <ulfius.h>
|
|
#include "common/utils/LOG/log.h"
|
|
#include "common/utils/websrv/websrv.h"
|
|
|
|
#include "time.h"
|
|
|
|
void websrv_websocket_send_message(char msg_src, char msg_type, char *msg_data, struct _websocket_manager *websocket_manager)
|
|
{
|
|
websrv_msg_t msg;
|
|
int st;
|
|
msg.header.src = msg_src;
|
|
msg.header.msgtype = msg_type;
|
|
sprintf(msg.data, "%s", msg_data);
|
|
int len = strlen(msg.data) + WEBSOCK_HEADSIZE;
|
|
st = ulfius_websocket_send_message(websocket_manager, U_WEBSOCKET_OPCODE_BINARY, len, (char *)&msg);
|
|
if (st != U_OK)
|
|
LOG_I(UTIL, "Error sending scope message, length %i status %i\n", len, st);
|
|
}
|
|
/* websocket callbacks as set in callback_websocket, the initial url endpoint which triggers the websocket init */
|
|
/* function executed by ulfius when websocket is closed */
|
|
void websrv_websocket_onclose_callback(const struct _u_request *request, struct _websocket_manager *websocket_manager, void *websocket_onclose_user_data)
|
|
{
|
|
websrv_params_t *websrvparams = (websrv_params_t *)websocket_onclose_user_data;
|
|
websrv_dump_request("websocket close ", request, websrvparams->dbglvl);
|
|
websrvparams->wm = NULL;
|
|
websrv_scope_stop();
|
|
}
|
|
|
|
void websrv_close_ws(websrv_params_t *websrvparams, char *source)
|
|
{
|
|
LOG_W(UTIL, "[websrv] Websocket re-init, from %s....\n", source);
|
|
ulfius_websocket_send_close_signal(websrvparams->wm);
|
|
}
|
|
|
|
/* function executed by ulfius in a dedicated thread, should not terminate while client connection is up */
|
|
void websrv_websocket_manager_callback(const struct _u_request *request, struct _websocket_manager *websocket_manager, void *websocket_manager_user_data)
|
|
{
|
|
websrv_params_t *websrvparams = (websrv_params_t *)websocket_manager_user_data;
|
|
websrv_dump_request("websocket manager ", request, websrvparams->dbglvl);
|
|
while (websrvparams->wm != NULL) {
|
|
LOG_I(UTIL, "[websrv] Waitting a previous websocket instance to close...\n");
|
|
usleep(100000);
|
|
}
|
|
websrvparams->wm = websocket_manager;
|
|
uint64_t lcount = 0;
|
|
int st = U_WEBSOCKET_STATUS_OPEN;
|
|
while (st == U_WEBSOCKET_STATUS_OPEN) {
|
|
st = ulfius_websocket_status(websocket_manager /* ms */);
|
|
if (st == U_WEBSOCKET_STATUS_OPEN) {
|
|
if (websrv_scope_manager(lcount, websrvparams) < 0) {
|
|
websrv_close_ws(websrvparams, "ws scope manager");
|
|
};
|
|
lcount++;
|
|
} else if (st == U_WEBSOCKET_STATUS_ERROR) {
|
|
LOG_I(UTIL, "[websrv] manager: Websocket error, errno %s\n", strerror(errno));
|
|
break;
|
|
} else {
|
|
LOG_I(UTIL, "[websrv] manager: Websocket has been closed\n");
|
|
break;
|
|
}
|
|
usleep(100000);
|
|
} /* while */
|
|
LOG_I(UTIL, "Closing websocket_manager_callback...\n");
|
|
}
|
|
|
|
void websrv_websocket_incoming_message_callback(const struct _u_request *request,
|
|
struct _websocket_manager *websocket_manager,
|
|
const struct _websocket_message *last_message,
|
|
void *websocket_incoming_message_user_data)
|
|
{
|
|
LOG_I(UTIL, "Incoming message, opcode: 0x%02x, mask: %d, len: %zu\n", last_message->opcode, last_message->has_mask, last_message->data_len);
|
|
websrv_params_t *websrvparams = (websrv_params_t *)websocket_incoming_message_user_data;
|
|
if (websrvparams->wm != NULL && websrvparams->wm != websocket_manager) {
|
|
websrv_close_ws(websrvparams, "ws incoming message callback");
|
|
}
|
|
if (last_message->opcode == U_WEBSOCKET_OPCODE_TEXT) {
|
|
LOG_I(UTIL, "[websrv] text payload '%.*s'", (int)last_message->data_len, last_message->data);
|
|
} else if (last_message->opcode == U_WEBSOCKET_OPCODE_BINARY) {
|
|
websrv_msg_t *msg = (websrv_msg_t *)last_message->data;
|
|
LOG_I(UTIL, "[websrv] binary payload from %c type %i\n", msg->header.src, (int)msg->header.msgtype);
|
|
switch (msg->header.src) {
|
|
case 's':
|
|
websrv_websocket_process_scopemessage(msg->header.msgtype, msg->data, websocket_manager);
|
|
break;
|
|
default:
|
|
LOG_W(UTIL, "[websrv] Unknown message source: %c\n", msg->header.src);
|
|
break;
|
|
}
|
|
} else if (last_message->opcode == U_WEBSOCKET_OPCODE_CLOSE) {
|
|
LOG_I(UTIL, "[websrv] websocket closed\n");
|
|
|
|
} else {
|
|
LOG_I(UTIL, "[websrv] message ignored\n");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* callback function, called when the url corresponding to the endpoint set in
|
|
* websrv_init_websocket is requested. that simply set the websocket callbacks
|
|
*/
|
|
int websrv_callback_websocket(const struct _u_request *request, struct _u_response *response, void *user_data)
|
|
{
|
|
int ret;
|
|
|
|
websrv_params_t *websrvparams = (websrv_params_t *)user_data;
|
|
websrv_dump_request("websocket ", request, websrvparams->dbglvl);
|
|
if (websrvparams->wm != NULL) {
|
|
websrv_close_ws(websrvparams, "ws url callback");
|
|
}
|
|
if ((ret = ulfius_set_websocket_response(
|
|
response, NULL, NULL, websrv_websocket_manager_callback, user_data, websrv_websocket_incoming_message_callback, user_data, websrv_websocket_onclose_callback, user_data))
|
|
== U_OK) {
|
|
return U_CALLBACK_COMPLETE;
|
|
} else {
|
|
return U_CALLBACK_ERROR;
|
|
}
|
|
}
|
|
|
|
int websrv_init_websocket(websrv_params_t *websrvparams, char *module)
|
|
{
|
|
int status = ulfius_add_endpoint_by_val(&(websrvparams->instance), "GET", NULL, module, 1, &websrv_callback_websocket, websrvparams);
|
|
return status;
|
|
}
|