diff --git a/res/ari/resource_events.c b/res/ari/resource_events.c index eddcb662c94694c047c73b938bc402380274846e..e666f2e05866b889b6d648f52940bbad64fe06db 100644 --- a/res/ari/resource_events.c +++ b/res/ari/resource_events.c @@ -119,6 +119,10 @@ static void app_handler(void *data, const char *app_name, const char *msg_application = S_OR( ast_json_string_get(ast_json_object_get(message, "application")), ""); + + if (!session) { + return; + } /* Determine if we've been replaced */ if (strcmp(msg_type, "ApplicationReplaced") == 0 && @@ -168,7 +172,40 @@ static int session_register_app(struct event_session *session, return 0; } -void ast_ari_websocket_events_event_websocket(struct ast_ari_websocket_session *ws_session, +int ast_ari_websocket_events_event_websocket_attempted(struct ast_tcptls_session_instance *ser, + struct ast_variable *headers, + struct ast_ari_events_event_websocket_args *args) +{ + int res = 0; + size_t i, j; + + ast_debug(3, "/events WebSocket attempted\n"); + + if (args->app_count == 0) { + ast_http_error(ser, 400, "Bad Request", "Missing param 'app'"); + return -1; + } + + for (i = 0; i < args->app_count; ++i) { + if (ast_strlen_zero(args->app[i])) { + res = -1; + break; + } + + res |= stasis_app_register(args->app[i], app_handler, NULL); + } + + if (res) { + for (j = 0; j < i; ++j) { + stasis_app_unregister(args->app[j]); + } + ast_http_error(ser, 400, "Bad Request", "Invalid application provided in param 'app'."); + } + + return res; +} + +void ast_ari_websocket_events_event_websocket_established(struct ast_ari_websocket_session *ws_session, struct ast_variable *headers, struct ast_ari_events_event_websocket_args *args) { diff --git a/res/ari/resource_events.h b/res/ari/resource_events.h index 646cf9bfceb763fa27cc7f817bcd169b0871f12f..2b631819b266ac93fa9cb23564ed58d778f03db3 100644 --- a/res/ari/resource_events.h +++ b/res/ari/resource_events.h @@ -48,6 +48,19 @@ struct ast_ari_events_event_websocket_args { /*! Parsing context for app. */ char *app_parse; }; + +/*! + * \brief WebSocket connection for events. + * + * \param ser HTTP TCP/TLS Server Session + * \param headers HTTP headers + * \param args Swagger parameters + * + * \retval 0 success + * \retval non-zero error + */ +int ast_ari_websocket_events_event_websocket_attempted(struct ast_tcptls_session_instance *ser, struct ast_variable *headers, struct ast_ari_events_event_websocket_args *args); + /*! * \brief WebSocket connection for events. * @@ -55,7 +68,7 @@ struct ast_ari_events_event_websocket_args { * \param headers HTTP headers. * \param args Swagger parameters. */ -void ast_ari_websocket_events_event_websocket(struct ast_ari_websocket_session *session, struct ast_variable *headers, struct ast_ari_events_event_websocket_args *args); +void ast_ari_websocket_events_event_websocket_established(struct ast_ari_websocket_session *session, struct ast_variable *headers, struct ast_ari_events_event_websocket_args *args); /*! Argument struct for ast_ari_events_user_event() */ struct ast_ari_events_user_event_args { /*! Event name */ diff --git a/res/res_ari_events.c b/res/res_ari_events.c index 66b01a743d998fa77dbce78fcc6fb3dd09de7d81..454233945e7f234e208667b0e83df69868bef5d9 100644 --- a/res/res_ari_events.c +++ b/res/res_ari_events.c @@ -53,7 +53,92 @@ ASTERISK_REGISTER_FILE() #define MAX_VALS 128 -static void ast_ari_events_event_websocket_ws_cb(struct ast_websocket *ws_session, +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) +{ + 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 + {} + } + + res = ast_ari_websocket_events_event_websocket_attempted(ser, headers, &args); + +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 = {}; @@ -126,16 +211,11 @@ static void ast_ari_events_event_websocket_ws_cb(struct ast_websocket *ws_sessio {} } - ast_ari_websocket_events_event_websocket(session, headers, &args); + ast_ari_websocket_events_event_websocket_established(session, headers, &args); fin: __attribute__((unused)) if (response && response->response_code != 0) { /* Param parsing failure */ - /* TODO - ideally, this would return the error code to the - * HTTP client; but we've already done the WebSocket - * negotiation. Param parsing should happen earlier, but we - * need a way to pass it through the WebSocket code to the - * callback */ RAII_VAR(char *, msg, NULL, ast_json_free); if (response->message) { msg = ast_json_dump_string(response->message); @@ -351,12 +431,22 @@ static struct stasis_rest_handlers events = { static int load_module(void) { int res = 0; + struct ast_websocket_protocol *protocol; + events.ws_server = ast_websocket_server_create(); if (!events.ws_server) { return AST_MODULE_LOAD_FAILURE; } - res |= ast_websocket_server_add_protocol(events.ws_server, - "ari", ast_ari_events_event_websocket_ws_cb); + + protocol = ast_websocket_sub_protocol_alloc("ari"); + if (!protocol) { + ao2_ref(events.ws_server, -1); + events.ws_server = NULL; + return AST_MODULE_LOAD_FAILURE; + } + 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); stasis_app_ref(); res |= ast_ari_add_handler(&events); return res; diff --git a/res/stasis/app.c b/res/stasis/app.c index 5a1c5f8e24b121fe46d76e2a221ba87f381a2e10..b99e23205e560fef439f0854bdb1a5048b245329 100644 --- a/res/stasis/app.c +++ b/res/stasis/app.c @@ -871,8 +871,7 @@ struct stasis_app *app_create(const char *name, stasis_app_cb handler, void *dat strncpy(app->name, name, size - sizeof(*app)); app->handler = handler; - ao2_ref(data, +1); - app->data = data; + app->data = ao2_bump(data); ao2_ref(app, +1); return app; @@ -950,7 +949,7 @@ void app_update(struct stasis_app *app, stasis_app_cb handler, void *data) { SCOPED_AO2LOCK(lock, app); - if (app->handler) { + if (app->handler && app->data) { RAII_VAR(struct ast_json *, msg, NULL, ast_json_unref); ast_verb(1, "Replacing Stasis app '%s'\n", app->name); diff --git a/rest-api-templates/ari_resource.h.mustache b/rest-api-templates/ari_resource.h.mustache index 3a20776a77a11b2c1dade8dd18dfe5dc98b8753f..d3f40b6bd65e2e21b9a0edaa7092940777242726 100644 --- a/rest-api-templates/ari_resource.h.mustache +++ b/rest-api-templates/ari_resource.h.mustache @@ -89,6 +89,23 @@ int ast_ari_{{c_name}}_{{c_nickname}}_parse_body( void ast_ari_{{c_name}}_{{c_nickname}}(struct ast_variable *headers, struct ast_ari_{{c_name}}_{{c_nickname}}_args *args, struct ast_ari_response *response); {{/is_req}} {{#is_websocket}} + +/*! + * \brief {{summary}} +{{#notes}} + * + * {{{notes}}} +{{/notes}} + * + * \param ser HTTP TCP/TLS Server Session + * \param headers HTTP headers + * \param args Swagger parameters + * + * \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); + /*! * \brief {{summary}} {{#notes}} @@ -100,7 +117,7 @@ void ast_ari_{{c_name}}_{{c_nickname}}(struct ast_variable *headers, struct ast_ * \param headers HTTP headers. * \param args Swagger parameters. */ -void ast_ari_websocket_{{c_name}}_{{c_nickname}}(struct ast_ari_websocket_session *session, struct ast_variable *headers, struct ast_ari_{{c_name}}_{{c_nickname}}_args *args); +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}} diff --git a/rest-api-templates/res_ari_resource.c.mustache b/rest-api-templates/res_ari_resource.c.mustache index 7d138b7af25b2e48f71e065db26cd77fd4d8897f..7fe360e896c75983299e8d4bc2714fa3165948ea 100644 --- a/rest-api-templates/res_ari_resource.c.mustache +++ b/rest-api-templates/res_ari_resource.c.mustache @@ -137,7 +137,52 @@ fin: __attribute__((unused)) } {{/is_req}} {{#is_websocket}} -static void ast_ari_{{c_name}}_{{c_nickname}}_ws_cb(struct ast_websocket *ws_session, +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) +{ + 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); + +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 = {}; @@ -175,16 +220,11 @@ static void ast_ari_{{c_name}}_{{c_nickname}}_ws_cb(struct ast_websocket *ws_ses {{> param_parsing}} - ast_ari_websocket_{{c_name}}_{{c_nickname}}(session, headers, &args); + ast_ari_websocket_{{c_name}}_{{c_nickname}}_established(session, headers, &args); fin: __attribute__((unused)) if (response && response->response_code != 0) { /* Param parsing failure */ - /* TODO - ideally, this would return the error code to the - * HTTP client; but we've already done the WebSocket - * negotiation. Param parsing should happen earlier, but we - * need a way to pass it through the WebSocket code to the - * callback */ RAII_VAR(char *, msg, NULL, ast_json_free); if (response->message) { msg = ast_json_dump_string(response->message); @@ -211,16 +251,26 @@ static int load_module(void) { int res = 0; {{#apis}} +{{#operations}} {{#has_websocket}} + struct ast_websocket_protocol *protocol; + {{full_name}}.ws_server = ast_websocket_server_create(); if (!{{full_name}}.ws_server) { return AST_MODULE_LOAD_FAILURE; } + + protocol = ast_websocket_sub_protocol_alloc("{{websocket_protocol}}"); + if (!protocol) { + ao2_ref({{full_name}}.ws_server, -1); + {{full_name}}.ws_server = NULL; + return AST_MODULE_LOAD_FAILURE; + } + protocol->session_attempted = ast_ari_{{c_name}}_{{c_nickname}}_ws_attempted_cb; + protocol->session_established = ast_ari_{{c_name}}_{{c_nickname}}_ws_established_cb; {{/has_websocket}} -{{#operations}} {{#is_websocket}} - res |= ast_websocket_server_add_protocol({{full_name}}.ws_server, - "{{websocket_protocol}}", ast_ari_{{c_name}}_{{c_nickname}}_ws_cb); + res |= ast_websocket_server_add_protocol2({{full_name}}.ws_server, protocol); {{/is_websocket}} {{/operations}} {{/apis}}