mirror of
https://github.com/asterisk/asterisk.git
synced 2025-10-12 15:45:18 +00:00
ARI: REST over Websocket
This commit adds the ability to make ARI REST requests over the same websocket used to receive events. For full details on how to use the new capability, visit... https://docs.asterisk.org/Configuration/Interfaces/Asterisk-REST-Interface-ARI/ARI-REST-over-WebSocket/ Changes: * Added utilities to http.c: * ast_get_http_method_from_string(). * ast_http_parse_post_form(). * Added utilities to json.c: * ast_json_nvp_array_to_ast_variables(). * ast_variables_to_json_nvp_array(). * Added definitions for new events to carry REST responses. * Created res/ari/ari_websocket_requests.c to house the new request handlers. * Moved non-event specific code out of res/ari/resource_events.c into res/ari/ari_websockets.c * Refactored res/res_ari.c to move non-http code out of ast_ari_callback() (which is http specific) and into ast_ari_invoke() so it can be shared between both the http and websocket transports. UpgradeNote: This commit adds the ability to make ARI REST requests over the same websocket used to receive events. See https://docs.asterisk.org/Configuration/Interfaces/Asterisk-REST-Interface-ARI/ARI-REST-over-WebSocket/
This commit is contained in:
@@ -48,196 +48,9 @@
|
||||
#if defined(AST_DEVMODE)
|
||||
#include "ari/ari_model_validators.h"
|
||||
#endif
|
||||
#include "asterisk/http_websocket.h"
|
||||
|
||||
#define MAX_VALS 128
|
||||
|
||||
static int ast_ari_events_event_websocket_ws_attempted_cb(struct ast_tcptls_session_instance *ser,
|
||||
struct ast_variable *get_params, struct ast_variable *headers, const char *session_id)
|
||||
{
|
||||
struct ast_ari_events_event_websocket_args args = {};
|
||||
int res = 0;
|
||||
RAII_VAR(struct ast_ari_response *, response, NULL, ast_free);
|
||||
struct ast_variable *i;
|
||||
|
||||
response = ast_calloc(1, sizeof(*response));
|
||||
if (!response) {
|
||||
ast_log(LOG_ERROR, "Failed to create response.\n");
|
||||
goto fin;
|
||||
}
|
||||
|
||||
for (i = get_params; i; i = i->next) {
|
||||
if (strcmp(i->name, "app") == 0) {
|
||||
/* Parse comma separated list */
|
||||
char *vals[MAX_VALS];
|
||||
size_t j;
|
||||
|
||||
args.app_parse = ast_strdup(i->value);
|
||||
if (!args.app_parse) {
|
||||
ast_ari_response_alloc_failed(response);
|
||||
goto fin;
|
||||
}
|
||||
|
||||
if (strlen(args.app_parse) == 0) {
|
||||
/* ast_app_separate_args can't handle "" */
|
||||
args.app_count = 1;
|
||||
vals[0] = args.app_parse;
|
||||
} else {
|
||||
args.app_count = ast_app_separate_args(
|
||||
args.app_parse, ',', vals,
|
||||
ARRAY_LEN(vals));
|
||||
}
|
||||
|
||||
if (args.app_count == 0) {
|
||||
ast_ari_response_alloc_failed(response);
|
||||
goto fin;
|
||||
}
|
||||
|
||||
if (args.app_count >= MAX_VALS) {
|
||||
ast_ari_response_error(response, 400,
|
||||
"Bad Request",
|
||||
"Too many values for app");
|
||||
goto fin;
|
||||
}
|
||||
|
||||
args.app = ast_malloc(sizeof(*args.app) * args.app_count);
|
||||
if (!args.app) {
|
||||
ast_ari_response_alloc_failed(response);
|
||||
goto fin;
|
||||
}
|
||||
|
||||
for (j = 0; j < args.app_count; ++j) {
|
||||
args.app[j] = (vals[j]);
|
||||
}
|
||||
} else
|
||||
if (strcmp(i->name, "subscribeAll") == 0) {
|
||||
args.subscribe_all = ast_true(i->value);
|
||||
} else
|
||||
{}
|
||||
}
|
||||
|
||||
res = ast_ari_websocket_events_event_websocket_attempted(ser, headers, &args, session_id);
|
||||
|
||||
fin: __attribute__((unused))
|
||||
if (!response) {
|
||||
ast_http_error(ser, 500, "Server Error", "Memory allocation error");
|
||||
res = -1;
|
||||
} else if (response->response_code != 0) {
|
||||
/* Param parsing failure */
|
||||
RAII_VAR(char *, msg, NULL, ast_json_free);
|
||||
if (response->message) {
|
||||
msg = ast_json_dump_string(response->message);
|
||||
} else {
|
||||
ast_log(LOG_ERROR, "Missing response message\n");
|
||||
}
|
||||
|
||||
if (msg) {
|
||||
ast_http_error(ser, response->response_code, response->response_text, msg);
|
||||
}
|
||||
res = -1;
|
||||
}
|
||||
ast_free(args.app_parse);
|
||||
ast_free(args.app);
|
||||
return res;
|
||||
}
|
||||
|
||||
static void ast_ari_events_event_websocket_ws_established_cb(struct ast_websocket *ws_session,
|
||||
struct ast_variable *get_params, struct ast_variable *headers)
|
||||
{
|
||||
struct ast_ari_events_event_websocket_args args = {};
|
||||
RAII_VAR(struct ast_ari_response *, response, NULL, ast_free);
|
||||
struct ast_variable *i;
|
||||
RAII_VAR(struct ast_websocket *, s, ws_session, ast_websocket_unref);
|
||||
RAII_VAR(struct ast_ari_websocket_session *, session, NULL, ao2_cleanup);
|
||||
|
||||
SCOPED_MODULE_USE(ast_module_info->self);
|
||||
|
||||
response = ast_calloc(1, sizeof(*response));
|
||||
if (!response) {
|
||||
ast_log(LOG_ERROR, "Failed to create response.\n");
|
||||
goto fin;
|
||||
}
|
||||
|
||||
#if defined(AST_DEVMODE)
|
||||
session = ast_ari_websocket_session_create(ws_session,
|
||||
ast_ari_validate_message_fn());
|
||||
#else
|
||||
session = ast_ari_websocket_session_create(ws_session, NULL);
|
||||
#endif
|
||||
if (!session) {
|
||||
ast_log(LOG_ERROR, "Failed to create ARI session\n");
|
||||
goto fin;
|
||||
}
|
||||
|
||||
for (i = get_params; i; i = i->next) {
|
||||
if (strcmp(i->name, "app") == 0) {
|
||||
/* Parse comma separated list */
|
||||
char *vals[MAX_VALS];
|
||||
size_t j;
|
||||
|
||||
args.app_parse = ast_strdup(i->value);
|
||||
if (!args.app_parse) {
|
||||
ast_ari_response_alloc_failed(response);
|
||||
goto fin;
|
||||
}
|
||||
|
||||
if (strlen(args.app_parse) == 0) {
|
||||
/* ast_app_separate_args can't handle "" */
|
||||
args.app_count = 1;
|
||||
vals[0] = args.app_parse;
|
||||
} else {
|
||||
args.app_count = ast_app_separate_args(
|
||||
args.app_parse, ',', vals,
|
||||
ARRAY_LEN(vals));
|
||||
}
|
||||
|
||||
if (args.app_count == 0) {
|
||||
ast_ari_response_alloc_failed(response);
|
||||
goto fin;
|
||||
}
|
||||
|
||||
if (args.app_count >= MAX_VALS) {
|
||||
ast_ari_response_error(response, 400,
|
||||
"Bad Request",
|
||||
"Too many values for app");
|
||||
goto fin;
|
||||
}
|
||||
|
||||
args.app = ast_malloc(sizeof(*args.app) * args.app_count);
|
||||
if (!args.app) {
|
||||
ast_ari_response_alloc_failed(response);
|
||||
goto fin;
|
||||
}
|
||||
|
||||
for (j = 0; j < args.app_count; ++j) {
|
||||
args.app[j] = (vals[j]);
|
||||
}
|
||||
} else
|
||||
if (strcmp(i->name, "subscribeAll") == 0) {
|
||||
args.subscribe_all = ast_true(i->value);
|
||||
} else
|
||||
{}
|
||||
}
|
||||
|
||||
ast_ari_websocket_events_event_websocket_established(session, headers, &args);
|
||||
|
||||
fin: __attribute__((unused))
|
||||
if (response && response->response_code != 0) {
|
||||
/* Param parsing failure */
|
||||
RAII_VAR(char *, msg, NULL, ast_json_free);
|
||||
if (response->message) {
|
||||
msg = ast_json_dump_string(response->message);
|
||||
} else {
|
||||
ast_log(LOG_ERROR, "Missing response message\n");
|
||||
}
|
||||
if (msg) {
|
||||
ast_websocket_write(ws_session,
|
||||
AST_WEBSOCKET_OPCODE_TEXT, msg, strlen(msg));
|
||||
}
|
||||
}
|
||||
ast_free(args.app_parse);
|
||||
ast_free(args.app);
|
||||
}
|
||||
int ast_ari_events_user_event_parse_body(
|
||||
struct ast_json *body,
|
||||
struct ast_ari_events_user_event_args *args)
|
||||
@@ -425,9 +238,6 @@ static struct stasis_rest_handlers events = {
|
||||
static int unload_module(void)
|
||||
{
|
||||
ast_ari_remove_handler(&events);
|
||||
ao2_cleanup(events.ws_server);
|
||||
events.ws_server = NULL;
|
||||
ast_ari_websocket_events_event_websocket_dtor();
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -435,28 +245,7 @@ static int load_module(void)
|
||||
{
|
||||
int res = 0;
|
||||
|
||||
struct ast_websocket_protocol *protocol;
|
||||
|
||||
if (ast_ari_websocket_events_event_websocket_init() == -1) {
|
||||
return AST_MODULE_LOAD_DECLINE;
|
||||
}
|
||||
|
||||
events.ws_server = ast_websocket_server_create();
|
||||
if (!events.ws_server) {
|
||||
ast_ari_websocket_events_event_websocket_dtor();
|
||||
return AST_MODULE_LOAD_DECLINE;
|
||||
}
|
||||
|
||||
protocol = ast_websocket_sub_protocol_alloc("ari");
|
||||
if (!protocol) {
|
||||
ao2_ref(events.ws_server, -1);
|
||||
events.ws_server = NULL;
|
||||
ast_ari_websocket_events_event_websocket_dtor();
|
||||
return AST_MODULE_LOAD_DECLINE;
|
||||
}
|
||||
protocol->session_attempted = ast_ari_events_event_websocket_ws_attempted_cb;
|
||||
protocol->session_established = ast_ari_events_event_websocket_ws_established_cb;
|
||||
res |= ast_websocket_server_add_protocol2(events.ws_server, protocol);
|
||||
events.is_websocket = 1;
|
||||
|
||||
res |= ast_ari_add_handler(&events);
|
||||
if (res) {
|
||||
|
Reference in New Issue
Block a user