While working on building client libraries from the Swagger API, I noticed a problem with the nicknames. channel.deleteChannel() channel.answerChannel() channel.muteChannel() Etc. We put the object name in the nickname (since we were generating C code), but it makes OO generators redundant. This patch makes the nicknames more OO friendly. This resulted in a lot of name changing within the res_ari_*.so modules, but not much else. There were a couple of other fixed I made in the process. * When reversible operations (POST /hold, POST /unhold) were made more RESTful (POST /hold, DELETE /unhold), the path for the second operation was left in the API declaration. This worked, but really the two operations should have been on the same API. * The POST /unmute operation had still not been REST-ified. Review: https://reviewboard.asterisk.org/r/2940/ ........ Merged revisions 402528 from http://svn.asterisk.org/svn/asterisk/branches/12 git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@402529 65c4cc65-6c06-0410-ace0-fbb531ad65f3
220 lines
5.3 KiB
C
220 lines
5.3 KiB
C
/*
|
|
* Asterisk -- An open source telephony toolkit.
|
|
*
|
|
* Copyright (C) 2012 - 2013, Digium, Inc.
|
|
*
|
|
* David M. Lee, II <dlee@digium.com>
|
|
*
|
|
* See http://www.asterisk.org for more information about
|
|
* the Asterisk project. Please do not directly contact
|
|
* any of the maintainers of this project for assistance;
|
|
* the project provides a web site, mailing lists and IRC
|
|
* channels for your use.
|
|
*
|
|
* This program is free software, distributed under the terms of
|
|
* the GNU General Public License Version 2. See the LICENSE file
|
|
* at the top of the source tree.
|
|
*/
|
|
|
|
/*! \file
|
|
*
|
|
* \brief /api-docs/events.{format} implementation- WebSocket resource
|
|
*
|
|
* \author David M. Lee, II <dlee@digium.com>
|
|
*/
|
|
|
|
#include "asterisk.h"
|
|
|
|
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
|
|
|
#include "asterisk/astobj2.h"
|
|
#include "asterisk/stasis_app.h"
|
|
#include "resource_events.h"
|
|
|
|
/*! Number of buckets for the Stasis application hash table. Remember to keep it
|
|
* a prime number!
|
|
*/
|
|
#define APPS_NUM_BUCKETS 7
|
|
|
|
/*! \brief A connection to the event WebSocket */
|
|
struct event_session {
|
|
struct ast_ari_websocket_session *ws_session;
|
|
struct ao2_container *websocket_apps;
|
|
};
|
|
|
|
/*!
|
|
* \brief Explicitly shutdown a session.
|
|
*
|
|
* An explicit shutdown is necessary, since stasis-app has a reference to this
|
|
* session. We also need to be sure to null out the \c ws_session field, since
|
|
* the websocket is about to go away.
|
|
*
|
|
* \param session Session info struct.
|
|
*/
|
|
static void session_shutdown(struct event_session *session)
|
|
{
|
|
struct ao2_iterator i;
|
|
char *app;
|
|
SCOPED_AO2LOCK(lock, session);
|
|
|
|
i = ao2_iterator_init(session->websocket_apps, 0);
|
|
while ((app = ao2_iterator_next(&i))) {
|
|
stasis_app_unregister(app);
|
|
ao2_cleanup(app);
|
|
}
|
|
ao2_iterator_destroy(&i);
|
|
ao2_cleanup(session->websocket_apps);
|
|
|
|
session->websocket_apps = NULL;
|
|
session->ws_session = NULL;
|
|
}
|
|
|
|
static void session_dtor(void *obj)
|
|
{
|
|
#ifdef AST_DEVMODE /* Avoid unused variable warning */
|
|
struct event_session *session = obj;
|
|
#endif
|
|
|
|
/* session_shutdown should have been called before */
|
|
ast_assert(session->ws_session == NULL);
|
|
ast_assert(session->websocket_apps == NULL);
|
|
}
|
|
|
|
static void session_cleanup(struct event_session *session)
|
|
{
|
|
session_shutdown(session);
|
|
ao2_cleanup(session);
|
|
}
|
|
|
|
static struct event_session *session_create(
|
|
struct ast_ari_websocket_session *ws_session)
|
|
{
|
|
RAII_VAR(struct event_session *, session, NULL, ao2_cleanup);
|
|
|
|
session = ao2_alloc(sizeof(*session), session_dtor);
|
|
|
|
session->ws_session = ws_session;
|
|
session->websocket_apps =
|
|
ast_str_container_alloc(APPS_NUM_BUCKETS);
|
|
|
|
if (!session->websocket_apps) {
|
|
return NULL;
|
|
}
|
|
|
|
ao2_ref(session, +1);
|
|
return session;
|
|
}
|
|
|
|
/*!
|
|
* \brief Callback handler for Stasis application messages.
|
|
*/
|
|
static void app_handler(void *data, const char *app_name,
|
|
struct ast_json *message)
|
|
{
|
|
struct event_session *session = data;
|
|
int res;
|
|
const char *msg_type = S_OR(
|
|
ast_json_string_get(ast_json_object_get(message, "type")),
|
|
"");
|
|
const char *msg_application = S_OR(
|
|
ast_json_string_get(ast_json_object_get(message, "application")),
|
|
"");
|
|
|
|
/* Determine if we've been replaced */
|
|
if (strcmp(msg_type, "ApplicationReplaced") == 0 &&
|
|
strcmp(msg_application, app_name) == 0) {
|
|
ao2_find(session->websocket_apps, msg_application,
|
|
OBJ_UNLINK | OBJ_NODATA);
|
|
}
|
|
|
|
res = ast_json_object_set(message, "application",
|
|
ast_json_string_create(app_name));
|
|
if(res != 0) {
|
|
return;
|
|
}
|
|
|
|
ao2_lock(session);
|
|
if (session->ws_session) {
|
|
ast_ari_websocket_session_write(session->ws_session, message);
|
|
}
|
|
ao2_unlock(session);
|
|
}
|
|
|
|
/*!
|
|
* \brief Register for all of the apps given.
|
|
* \param session Session info struct.
|
|
* \param app_name Name of application to register.
|
|
*/
|
|
static int session_register_app(struct event_session *session,
|
|
const char *app_name)
|
|
{
|
|
SCOPED_AO2LOCK(lock, session);
|
|
|
|
ast_assert(session->ws_session != NULL);
|
|
ast_assert(session->websocket_apps != NULL);
|
|
|
|
if (ast_strlen_zero(app_name)) {
|
|
return -1;
|
|
}
|
|
|
|
if (ast_str_container_add(session->websocket_apps, app_name)) {
|
|
ast_ari_websocket_session_write(session->ws_session,
|
|
ast_ari_oom_json());
|
|
return -1;
|
|
}
|
|
|
|
stasis_app_register(app_name, app_handler, session);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void ast_ari_websocket_events_event_websocket(struct ast_ari_websocket_session *ws_session,
|
|
struct ast_variable *headers,
|
|
struct ast_ari_events_event_websocket_args *args)
|
|
{
|
|
RAII_VAR(struct event_session *, session, NULL, session_cleanup);
|
|
struct ast_json *msg;
|
|
int res;
|
|
size_t i;
|
|
|
|
ast_debug(3, "/events WebSocket connection\n");
|
|
|
|
session = session_create(ws_session);
|
|
if (!session) {
|
|
ast_ari_websocket_session_write(ws_session, ast_ari_oom_json());
|
|
return;
|
|
}
|
|
|
|
res = 0;
|
|
for (i = 0; i < args->app_count; ++i) {
|
|
if (ast_strlen_zero(args->app[i])) {
|
|
continue;
|
|
}
|
|
res |= session_register_app(session, args->app[i]);
|
|
}
|
|
|
|
if (ao2_container_count(session->websocket_apps) == 0) {
|
|
RAII_VAR(struct ast_json *, msg, NULL, ast_json_unref);
|
|
|
|
msg = ast_json_pack("{s: s, s: [s]}",
|
|
"type", "MissingParams",
|
|
"params", "app");
|
|
if (!msg) {
|
|
msg = ast_json_ref(ast_ari_oom_json());
|
|
}
|
|
|
|
ast_ari_websocket_session_write(session->ws_session, msg);
|
|
return;
|
|
}
|
|
|
|
if (res != 0) {
|
|
ast_ari_websocket_session_write(ws_session, ast_ari_oom_json());
|
|
return;
|
|
}
|
|
|
|
/* We don't process any input, but we'll consume it waiting for EOF */
|
|
while ((msg = ast_ari_websocket_session_read(ws_session))) {
|
|
ast_json_unref(msg);
|
|
}
|
|
}
|