mirror of
https://github.com/asterisk/asterisk.git
synced 2025-09-02 11:06:31 +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/
(cherry picked from commit 6bc055416b
)
This commit is contained in:
committed by
Asterisk Development Team
parent
6a7038e2c5
commit
64aeb20724
@@ -96,61 +96,6 @@ void ast_ari_{{c_name}}_{{c_nickname}}(struct ast_variable *headers, struct ast_
|
||||
void ast_ari_{{c_name}}_{{c_nickname}}(struct ast_tcptls_session_instance *ser, struct ast_variable *headers, struct ast_ari_{{c_name}}_{{c_nickname}}_args *args, struct ast_ari_response *response);
|
||||
{{/is_binary_response}}
|
||||
{{/is_req}}
|
||||
{{#is_websocket}}
|
||||
|
||||
/*!
|
||||
* \brief {{{summary}}}
|
||||
{{#notes}}
|
||||
*
|
||||
* {{{notes}}}
|
||||
{{/notes}}
|
||||
*
|
||||
* \retval 0 success
|
||||
* \retval -1 error
|
||||
*/
|
||||
int ast_ari_websocket_{{c_name}}_{{c_nickname}}_init(void);
|
||||
|
||||
/*!
|
||||
* \brief {{{summary}}}
|
||||
{{#notes}}
|
||||
*
|
||||
* {{{notes}}}
|
||||
{{/notes}}
|
||||
*/
|
||||
void ast_ari_websocket_{{c_name}}_{{c_nickname}}_dtor(void);
|
||||
|
||||
/*!
|
||||
* \brief {{summary}}
|
||||
{{#notes}}
|
||||
*
|
||||
* {{{notes}}}
|
||||
{{/notes}}
|
||||
*
|
||||
* \param ser HTTP TCP/TLS Server Session
|
||||
* \param headers HTTP headers
|
||||
* \param args Swagger parameters
|
||||
* \param session_id The id of the current session.
|
||||
*
|
||||
* \retval 0 success
|
||||
* \retval non-zero error
|
||||
*/
|
||||
int ast_ari_websocket_{{c_name}}_{{c_nickname}}_attempted(struct ast_tcptls_session_instance *ser,
|
||||
struct ast_variable *headers, struct ast_ari_{{c_name}}_{{c_nickname}}_args *args, const char *session_id);
|
||||
|
||||
/*!
|
||||
* \brief {{summary}}
|
||||
{{#notes}}
|
||||
*
|
||||
* {{{notes}}}
|
||||
{{/notes}}
|
||||
*
|
||||
* \param session ARI WebSocket.
|
||||
* \param headers HTTP headers.
|
||||
* \param args Swagger parameters.
|
||||
*/
|
||||
void ast_ari_websocket_{{c_name}}_{{c_nickname}}_established(struct ast_ari_websocket_session *session,
|
||||
struct ast_variable *headers, struct ast_ari_{{c_name}}_{{c_nickname}}_args *args);
|
||||
{{/is_websocket}}
|
||||
{{/operations}}
|
||||
{{/apis}}
|
||||
|
||||
|
@@ -55,13 +55,6 @@
|
||||
#if defined(AST_DEVMODE)
|
||||
#include "ari/ari_model_validators.h"
|
||||
#endif
|
||||
{{#has_websocket}}
|
||||
{{! Only include http_websocket if necessary. Otherwise we'll do a lot of
|
||||
* unnecessary optional_api intialization, which makes optional_api harder
|
||||
* to debug
|
||||
}}
|
||||
#include "asterisk/http_websocket.h"
|
||||
{{/has_websocket}}
|
||||
|
||||
#define MAX_VALS 128
|
||||
|
||||
@@ -149,112 +142,6 @@ fin: __attribute__((unused))
|
||||
return;
|
||||
}
|
||||
{{/is_req}}
|
||||
{{#is_websocket}}
|
||||
static int ast_ari_{{c_name}}_{{c_nickname}}_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_{{c_name}}_{{c_nickname}}_args args = {};
|
||||
{{#has_parameters}}
|
||||
int res = 0;
|
||||
RAII_VAR(struct ast_ari_response *, response, NULL, ast_free);
|
||||
struct ast_variable *i;
|
||||
{{/has_parameters}}
|
||||
|
||||
{{#has_parameters}}
|
||||
response = ast_calloc(1, sizeof(*response));
|
||||
if (!response) {
|
||||
ast_log(LOG_ERROR, "Failed to create response.\n");
|
||||
goto fin;
|
||||
}
|
||||
{{/has_parameters}}
|
||||
|
||||
{{> param_parsing}}
|
||||
|
||||
res = ast_ari_websocket_{{c_name}}_{{c_nickname}}_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;
|
||||
}
|
||||
{{> param_cleanup}}
|
||||
{{#has_parameters}}
|
||||
return res;
|
||||
{{/has_parameters}}
|
||||
}
|
||||
|
||||
static void ast_ari_{{c_name}}_{{c_nickname}}_ws_established_cb(struct ast_websocket *ws_session,
|
||||
struct ast_variable *get_params, struct ast_variable *headers)
|
||||
{
|
||||
struct ast_ari_{{c_name}}_{{c_nickname}}_args args = {};
|
||||
{{#has_parameters}}
|
||||
RAII_VAR(struct ast_ari_response *, response, NULL, ast_free);
|
||||
struct ast_variable *i;
|
||||
{{/has_parameters}}
|
||||
RAII_VAR(struct ast_websocket *, s, ws_session, ast_websocket_unref);
|
||||
RAII_VAR(struct ast_ari_websocket_session *, session, NULL, ao2_cleanup);
|
||||
{{#has_path_parameters}}
|
||||
/* TODO: It's not immediately obvious how to pass path params through
|
||||
* the websocket code to this callback. Not needed right now, so we'll
|
||||
* just punt. */
|
||||
struct ast_variable *path_vars = NULL;
|
||||
{{/has_path_parameters}}
|
||||
|
||||
SCOPED_MODULE_USE(ast_module_info->self);
|
||||
|
||||
{{#has_parameters}}
|
||||
response = ast_calloc(1, sizeof(*response));
|
||||
if (!response) {
|
||||
ast_log(LOG_ERROR, "Failed to create response.\n");
|
||||
goto fin;
|
||||
}
|
||||
{{/has_parameters}}
|
||||
|
||||
#if defined(AST_DEVMODE)
|
||||
session = ast_ari_websocket_session_create(ws_session,
|
||||
ast_ari_validate_{{response_class.c_name}}_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;
|
||||
}
|
||||
|
||||
{{> param_parsing}}
|
||||
|
||||
ast_ari_websocket_{{c_name}}_{{c_nickname}}_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));
|
||||
}
|
||||
}
|
||||
{{> param_cleanup}}
|
||||
}
|
||||
{{/is_websocket}}
|
||||
{{/operations}}
|
||||
{{/apis}}
|
||||
|
||||
@@ -266,13 +153,6 @@ fin: __attribute__((unused))
|
||||
static int unload_module(void)
|
||||
{
|
||||
ast_ari_remove_handler(&{{root_full_name}});
|
||||
{{#apis}}
|
||||
{{#has_websocket}}
|
||||
ao2_cleanup({{full_name}}.ws_server);
|
||||
{{full_name}}.ws_server = NULL;
|
||||
ast_ari_websocket_events_event_websocket_dtor();
|
||||
{{/has_websocket}}
|
||||
{{/apis}}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -283,28 +163,7 @@ static int load_module(void)
|
||||
{{#apis}}
|
||||
{{#operations}}
|
||||
{{#is_websocket}}
|
||||
struct ast_websocket_protocol *protocol;
|
||||
|
||||
if (ast_ari_websocket_{{c_name}}_{{c_nickname}}_init() == -1) {
|
||||
return AST_MODULE_LOAD_DECLINE;
|
||||
}
|
||||
|
||||
{{full_name}}.ws_server = ast_websocket_server_create();
|
||||
if (!{{full_name}}.ws_server) {
|
||||
ast_ari_websocket_events_event_websocket_dtor();
|
||||
return AST_MODULE_LOAD_DECLINE;
|
||||
}
|
||||
|
||||
protocol = ast_websocket_sub_protocol_alloc("{{websocket_protocol}}");
|
||||
if (!protocol) {
|
||||
ao2_ref({{full_name}}.ws_server, -1);
|
||||
{{full_name}}.ws_server = NULL;
|
||||
ast_ari_websocket_events_event_websocket_dtor();
|
||||
return AST_MODULE_LOAD_DECLINE;
|
||||
}
|
||||
protocol->session_attempted = ast_ari_{{c_name}}_{{c_nickname}}_ws_attempted_cb;
|
||||
protocol->session_established = ast_ari_{{c_name}}_{{c_nickname}}_ws_established_cb;
|
||||
res |= ast_websocket_server_add_protocol2({{full_name}}.ws_server, protocol);
|
||||
{{full_name}}.is_websocket = 1;
|
||||
{{/is_websocket}}
|
||||
{{/operations}}
|
||||
{{/apis}}
|
||||
|
Reference in New Issue
Block a user