res_stasis: Add ability to switch applications.

Added the ability to move between Stasis applications within Stasis.
This can be done by calling 'move' in an application, providing (at
minimum) the channel's id and the application to switch to. If the
application is not registered or active, nothing will happen and the
channel will remain in the current application, and an event will be
triggered to let the application know that the move failed. The event
name is "ApplicationMoveFailed", and provides the "destination" that the
channel was attempting to move to, as well as the usual channel
information. Optionally, a list of arguments can be passed to the
function call for the receiving application. A full example of a 'move'
call would look like this:

client.channels.move(channelId, app, appArgs)

The control object used to control the channel in Stasis can now switch
which application it belongs to, rather than belonging to one Stasis
application for its lifetime. This allows us to use the same control
object instead of having to tear down the current one and create
another.

ASTERISK-28267 #close

Change-Id: I43d12b10045a98a8d42541889b85695be26f288a
This commit is contained in:
Ben Ford
2019-03-07 07:52:20 -06:00
parent a32f525489
commit 6626df586e
12 changed files with 703 additions and 14 deletions

View File

@@ -775,6 +775,95 @@ static void ast_ari_channels_continue_in_dialplan_cb(
}
#endif /* AST_DEVMODE */
fin: __attribute__((unused))
return;
}
int ast_ari_channels_move_parse_body(
struct ast_json *body,
struct ast_ari_channels_move_args *args)
{
struct ast_json *field;
/* Parse query parameters out of it */
field = ast_json_object_get(body, "app");
if (field) {
args->app = ast_json_string_get(field);
}
field = ast_json_object_get(body, "appArgs");
if (field) {
args->app_args = ast_json_string_get(field);
}
return 0;
}
/*!
* \brief Parameter parsing callback for /channels/{channelId}/move.
* \param get_params GET parameters in the HTTP request.
* \param path_vars Path variables extracted from the request.
* \param headers HTTP headers.
* \param[out] response Response to the HTTP request.
*/
static void ast_ari_channels_move_cb(
struct ast_tcptls_session_instance *ser,
struct ast_variable *get_params, struct ast_variable *path_vars,
struct ast_variable *headers, struct ast_json *body, struct ast_ari_response *response)
{
struct ast_ari_channels_move_args args = {};
struct ast_variable *i;
#if defined(AST_DEVMODE)
int is_valid;
int code;
#endif /* AST_DEVMODE */
for (i = get_params; i; i = i->next) {
if (strcmp(i->name, "app") == 0) {
args.app = (i->value);
} else
if (strcmp(i->name, "appArgs") == 0) {
args.app_args = (i->value);
} else
{}
}
for (i = path_vars; i; i = i->next) {
if (strcmp(i->name, "channelId") == 0) {
args.channel_id = (i->value);
} else
{}
}
if (ast_ari_channels_move_parse_body(body, &args)) {
ast_ari_response_alloc_failed(response);
goto fin;
}
ast_ari_channels_move(headers, &args, response);
#if defined(AST_DEVMODE)
code = response->response_code;
switch (code) {
case 0: /* Implementation is still a stub, or the code wasn't set */
is_valid = response->message == NULL;
break;
case 500: /* Internal Server Error */
case 501: /* Not Implemented */
case 404: /* Channel not found */
case 409: /* Channel not in a Stasis application */
is_valid = 1;
break;
default:
if (200 <= code && code <= 299) {
is_valid = ast_ari_validate_void(
response->message);
} else {
ast_log(LOG_ERROR, "Invalid error response %d for /channels/{channelId}/move\n", code);
is_valid = 0;
}
}
if (!is_valid) {
ast_log(LOG_ERROR, "Response validation failed for /channels/{channelId}/move\n");
ast_ari_response_error(response, 500,
"Internal Server Error", "Response validation failed");
}
#endif /* AST_DEVMODE */
fin: __attribute__((unused))
return;
}
@@ -2680,6 +2769,15 @@ static struct stasis_rest_handlers channels_channelId_continue = {
.children = { }
};
/*! \brief REST handler for /api-docs/channels.json */
static struct stasis_rest_handlers channels_channelId_move = {
.path_segment = "move",
.callbacks = {
[AST_HTTP_POST] = ast_ari_channels_move_cb,
},
.num_children = 0,
.children = { }
};
/*! \brief REST handler for /api-docs/channels.json */
static struct stasis_rest_handlers channels_channelId_redirect = {
.path_segment = "redirect",
.callbacks = {
@@ -2831,8 +2929,8 @@ static struct stasis_rest_handlers channels_channelId = {
[AST_HTTP_POST] = ast_ari_channels_originate_with_id_cb,
[AST_HTTP_DELETE] = ast_ari_channels_hangup_cb,
},
.num_children = 14,
.children = { &channels_channelId_continue,&channels_channelId_redirect,&channels_channelId_answer,&channels_channelId_ring,&channels_channelId_dtmf,&channels_channelId_mute,&channels_channelId_hold,&channels_channelId_moh,&channels_channelId_silence,&channels_channelId_play,&channels_channelId_record,&channels_channelId_variable,&channels_channelId_snoop,&channels_channelId_dial, }
.num_children = 15,
.children = { &channels_channelId_continue,&channels_channelId_move,&channels_channelId_redirect,&channels_channelId_answer,&channels_channelId_ring,&channels_channelId_dtmf,&channels_channelId_mute,&channels_channelId_hold,&channels_channelId_moh,&channels_channelId_silence,&channels_channelId_play,&channels_channelId_record,&channels_channelId_variable,&channels_channelId_snoop,&channels_channelId_dial, }
};
/*! \brief REST handler for /api-docs/channels.json */
static struct stasis_rest_handlers channels = {