[asterisk-commits] dlee: branch dlee/stasis-http r382860 - in /team/dlee/stasis-http: apps/ incl...
SVN commits to the Asterisk project
asterisk-commits at lists.digium.com
Tue Mar 12 12:12:15 CDT 2013
Author: dlee
Date: Tue Mar 12 12:12:11 2013
New Revision: 382860
URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=382860
Log:
Answer implemented, but something's screwy
Modified:
team/dlee/stasis-http/apps/app_stasis.c
team/dlee/stasis-http/include/asterisk/app_stasis.h
team/dlee/stasis-http/res/res_stasis_http.c
team/dlee/stasis-http/res/stasis_http/resource_channels.c
Modified: team/dlee/stasis-http/apps/app_stasis.c
URL: http://svnview.digium.com/svn/asterisk/team/dlee/stasis-http/apps/app_stasis.c?view=diff&rev=382860&r1=382859&r2=382860
==============================================================================
--- team/dlee/stasis-http/apps/app_stasis.c (original)
+++ team/dlee/stasis-http/apps/app_stasis.c Tue Mar 12 12:12:11 2013
@@ -36,6 +36,7 @@
#include "asterisk/app_stasis.h"
#include "asterisk/astobj2.h"
#include "asterisk/channel.h"
+#include "asterisk/lock.h"
#include "asterisk/module.h"
#include "asterisk/stasis.h"
#include "asterisk/strings.h"
@@ -163,7 +164,67 @@
app->handler(app->data, app->name, message);
}
+typedef void* (*stasis_app_command_cb)(struct stasis_app_control *control,
+ struct ast_channel *chan,
+ void *data);
+
+struct stasis_app_command {
+ ast_mutex_t lock;
+ ast_cond_t condition;
+ stasis_app_command_cb callback;
+ void *data;
+ void *retval;
+ int is_done:1;
+};
+
+static void command_dtor(void *obj)
+{
+ struct stasis_app_command *command = obj;
+ ast_mutex_destroy(&command->lock);
+ ast_cond_destroy(&command->condition);
+}
+
+static struct stasis_app_command *command_create(stasis_app_command_cb callback,
+ void *data)
+{
+ RAII_VAR(struct stasis_app_command *, command, NULL, ao2_cleanup);
+
+ command = ao2_alloc(sizeof(*command), command_dtor);
+ if (!command) {
+ return NULL;
+ }
+
+ ast_mutex_init(&command->lock);
+ ast_cond_init(&command->condition, 0);
+ command->callback = callback;
+ command->data = data;
+
+ ao2_ref(command, +1);
+ return command;
+}
+
+static void command_complete(struct stasis_app_command *command, void *retval)
+{
+ SCOPED_MUTEX(lock, &command->lock);
+
+ command->is_done = 1;
+ command->retval = retval;
+ ast_cond_signal(&command->condition);
+}
+
+static void *wait_for_command(struct stasis_app_command *command)
+{
+ SCOPED_MUTEX(lock, &command->lock);
+ while (!command->is_done) {
+ ast_cond_wait(&command->condition, &command->lock);
+ }
+
+ return command->retval;
+}
+
struct stasis_app_control {
+ /*! Queue of commands to dispatch on the channel */
+ struct ao2_container *command_queue;
/*!
* When set, /c app_stasis should exit and continue in the dialplan.
*/
@@ -183,20 +244,30 @@
return NULL;
}
+ control->command_queue = ao2_container_alloc_list(0, 0, NULL, NULL);
+
strncpy(control->channel_uniqueid, uniqueid, size - sizeof(*control));
return control;
}
-struct stasis_app_control *stasis_app_control_find_by_channel(const struct ast_channel *chan)
-{
- RAII_VAR(struct ao2_container *, controls, NULL, ao2_cleanup);
+struct stasis_app_control *stasis_app_control_find_by_channel(
+ const struct ast_channel *chan)
+{
if (chan == NULL) {
return NULL;
}
+ return stasis_app_control_find_by_channel_id(
+ ast_channel_uniqueid(chan));
+}
+
+struct stasis_app_control *stasis_app_control_find_by_channel_id(
+ const char *channel_id)
+{
+ RAII_VAR(struct ao2_container *, controls, NULL, ao2_cleanup);
controls = app_controls();
- return ao2_find(controls, ast_channel_uniqueid(chan), OBJ_KEY);
+ return ao2_find(controls, channel_id, OBJ_KEY);
}
/*!
@@ -224,9 +295,36 @@
control->continue_to_dialplan = 1;
}
-struct ast_json *stasis_app_event_create(const char *event_name,
- const struct ast_channel_snapshot *channel_info,
- const struct ast_json *extra_info) {
+static int OK = 0;
+static int FAIL = -1;
+
+static void *__app_control_answer(struct stasis_app_control *control, struct ast_channel *chan, void *data)
+{
+ return __ast_answer(chan, 0, 1) == 0 ? &OK : &FAIL;
+}
+
+int stasis_app_control_answer(struct stasis_app_control *control)
+{
+ RAII_VAR(struct stasis_app_command *, command, NULL, ao2_cleanup);
+ int *retval;
+
+ SCOPED_AO2LOCK(lock, control);
+
+ command = command_create(__app_control_answer, NULL);
+ retval = wait_for_command(command);
+
+ if (*retval != 0) {
+ ast_log(LOG_WARNING, "Failed to answer channel");
+ }
+
+ return *retval;
+}
+
+struct ast_json *stasis_app_event_create(
+ const char *event_name,
+ const struct ast_channel_snapshot *channel_info,
+ const struct ast_json *extra_info)
+{
RAII_VAR(struct ast_json *, message, NULL, ast_json_unref);
int r;
@@ -350,6 +448,25 @@
ao2_cleanup(control);
}
+static void dispatch_commands(struct stasis_app_control *control, struct ast_channel *chan)
+{
+ struct ao2_iterator i;
+ void *obj;
+
+ SCOPED_AO2LOCK(lock, control);
+
+ i = ao2_iterator_init(control->command_queue, AO2_ITERATOR_UNLINK);
+
+ while ((obj = ao2_iterator_next(&i))) {
+ RAII_VAR(struct stasis_app_command *, command, obj, ao2_cleanup);
+ void *retval = command->callback(control, chan, command->data);
+ command_complete(command, retval);
+ }
+
+ ao2_iterator_destroy(&i);
+}
+
+
/*! /brief Stasis dialplan application callback */
static int app_stasis_exec(struct ast_channel *chan, const char *data)
{
@@ -380,7 +497,8 @@
app = ao2_find(apps, args.app_name, OBJ_KEY);
if (!app) {
- ast_log(LOG_ERROR, "Stasis app '%s' not registered\n", args.app_name);
+ ast_log(LOG_ERROR,
+ "Stasis app '%s' not registered\n", args.app_name);
return -1;
}
@@ -396,21 +514,28 @@
ao2_link(controls, control);
}
- subscription = stasis_subscribe(ast_channel_topic(chan), sub_handler, app);
+ subscription =
+ stasis_subscribe(ast_channel_topic(chan), sub_handler, app);
if (subscription == NULL) {
- ast_log(LOG_ERROR, "Error subscribing app %s to channel %s\n", args.app_name, ast_channel_name(chan));
+ ast_log(LOG_ERROR, "Error subscribing app %s to channel %s\n",
+ args.app_name, ast_channel_name(chan));
return -1;
}
ao2_ref(app, +1); /* subscription now has a reference */
res = send_start_msg(app, chan, args.argc - 1, args.app_argv);
if (res != 0) {
- ast_log(LOG_ERROR, "Error sending start message to %s\n", args.app_name);
+ ast_log(LOG_ERROR,
+ "Error sending start message to %s\n", args.app_name);
return res;
}
while (!hungup && !control_continue_test_and_reset(control) && ast_waitfor(chan, -1) > -1) {
- RAII_VAR(struct ast_frame *, f, ast_read(chan), ast_frame_dtor);
+ RAII_VAR(struct ast_frame *, f, NULL, ast_frame_dtor);
+
+ dispatch_commands(control, chan);
+
+ f = ast_read(chan);
if (!f) {
ast_debug(3, "%s: No more frames. Must be done, I guess.\n", ast_channel_uniqueid(chan));
break;
@@ -446,10 +571,11 @@
app = ao2_find(apps, app_name, OBJ_KEY);
if (!app) {
- /* XXX We can do a better job handling late binding, queueing up the call for a few seconds
- * to wait for the app to register.
+ /* XXX We can do a better job handling late binding, queueing up
+ * the call for a few seconds to wait for the app to register.
*/
- ast_log(LOG_WARNING, "Stasis app '%s' not registered\n", app_name);
+ ast_log(LOG_WARNING,
+ "Stasis app '%s' not registered\n", app_name);
return -1;
}
Modified: team/dlee/stasis-http/include/asterisk/app_stasis.h
URL: http://svnview.digium.com/svn/asterisk/team/dlee/stasis-http/include/asterisk/app_stasis.h?view=diff&rev=382860&r1=382859&r2=382860
==============================================================================
--- team/dlee/stasis-http/include/asterisk/app_stasis.h (original)
+++ team/dlee/stasis-http/include/asterisk/app_stasis.h Tue Mar 12 12:12:11 2013
@@ -102,21 +102,39 @@
struct stasis_app_control;
/*!
- * \brief Returns the handler for the given channel
+ * \brief Returns the handler for the given channel.
* \param chan Channel to handle.
- * \return NULL channel not in Stasis application
- * \return Pointer to app_stasis handler.
+ * \return NULL channel not in Stasis application.
+ * \return Pointer to \c app_stasis handler.
*/
-struct stasis_app_control *stasis_app_control_find_by_channel(const struct ast_channel *chan);
+struct stasis_app_control *stasis_app_control_find_by_channel(
+ const struct ast_channel *chan);
+
+/*!
+ * \brief Returns the handler for the channel with the given id.
+ * \param channel_id Uniqueid of the channel.
+ * \return NULL channel not in Stasis application, or channel does not exist.
+ * \return Pointer to \c app_stasis handler.
+ */
+struct stasis_app_control *stasis_app_control_find_by_channel_id(
+ const char *channel_id);
/*!
* \brief Exit \c app_stasis and continue execution in the dialplan.
*
* If the channel is no longer in \c app_stasis, this function does nothing.
*
- * \param handler Handler for \c app_stasis
+ * \param control Control for \c app_stasis
*/
-void stasis_app_control_continue(struct stasis_app_control *handler);
+void stasis_app_control_continue(struct stasis_app_control *control);
+
+/*!
+ * \brief Answer the channel associated with this control.
+ * \param control Control for \c app_stasis.
+ * \return 0 for success.
+ * \return -1 for error.
+ */
+int stasis_app_control_answer(struct stasis_app_control *control);
/*! @} */
Modified: team/dlee/stasis-http/res/res_stasis_http.c
URL: http://svnview.digium.com/svn/asterisk/team/dlee/stasis-http/res/res_stasis_http.c?view=diff&rev=382860&r1=382859&r2=382860
==============================================================================
--- team/dlee/stasis-http/res/res_stasis_http.c (original)
+++ team/dlee/stasis-http/res/res_stasis_http.c Tue Mar 12 12:12:11 2013
@@ -299,7 +299,7 @@
RAII_VAR(struct ast_str *, http_headers, ast_str_create(40), ast_free);
RAII_VAR(struct ast_str *, response_body, ast_str_create(256), ast_free);
struct stasis_http_response response = {};
- int ret;
+ int ret = 0;
if (!http_headers || !response_body) {
ast_log(LOG_ERROR, "Allocation failure!\n");
@@ -310,7 +310,8 @@
char *slashless = ast_strdupa(uri);
slashless[strlen(slashless) - 1] = '\0';
- ast_str_append(&http_headers, 0, "Location: /stasis/%s\r\n", slashless);
+ ast_str_append(&http_headers, 0,
+ "Location: /stasis/%s\r\n", slashless);
response.message = ast_json_pack("{s: o}", "message", ast_json_stringf("Redirecting to %s", slashless));
response.response_code = 302;
@@ -321,7 +322,9 @@
} else {
/* Serving up API docs */
if (method != AST_HTTP_GET) {
- response.message = ast_json_pack("{s: s}", "message", "Unsupported method");
+ response.message =
+ ast_json_pack("{s: s}",
+ "message", "Unsupported method");
response.response_code = 405;
response.response_text = "Method Not Allowed";
} else {
@@ -329,23 +332,33 @@
}
}
- ast_assert(response.message != NULL);
+ ast_assert(response.response_code == 204 || response.message != NULL);
ast_assert(response.response_code > 0);
- ast_str_append(&http_headers, 0, "Content-type: application/json\r\n");
ast_str_append(&http_headers, 0, "Access-Control-Allow-Origin: *\r\n");
- if (ast_json_dump_str(response.message, &response_body) == 0) {
- ast_http_send(ser, method, response.response_code, response.response_text, http_headers, response_body, 0, 0);
- /* ast_http_send takes ownership, so we don't have to free them */
- http_headers = NULL;
- response_body = NULL;
- ret = 0;
- } else {
- ast_log(LOG_ERROR, "Failed to encode JSON response\n");
- ast_http_send(ser, method, 500, "Internal Server Error", http_headers, ast_str_alloca(0), 0, 0);
- ret = -1;
- }
+ /* response.message could be NULL, in which case the empty response_body
+ * is correct
+ */
+ if (response.message) {
+ ast_str_append(&http_headers, 0,
+ "Content-type: application/json\r\n");
+ if (ast_json_dump_str(response.message, &response_body) != 0) {
+ /* Error encoding response */
+ response.response_code = 500;
+ response.response_text = "Internal Server Error";
+ ast_str_set(&response_body, 0, "%s", "");
+ ast_str_set(&http_headers, 0, "%s", "");
+ ret = -1;
+ }
+ }
+
+ ast_http_send(ser, method, response.response_code,
+ response.response_text, http_headers, response_body,
+ 0, 0);
+ /* ast_http_send takes ownership, so we don't have to free them */
+ http_headers = NULL;
+ response_body = NULL;
ast_json_unref(response.message);
return ret;
@@ -361,7 +374,8 @@
.key = __FILE__,
};
-struct stasis_rest_handlers *stasis_set_root_handler(struct stasis_rest_handlers *new_handler)
+struct stasis_rest_handlers *stasis_set_root_handler(
+ struct stasis_rest_handlers *new_handler)
{
struct stasis_rest_handlers *old_handler = root_handler;
root_handler = new_handler;
@@ -385,7 +399,8 @@
return r;
}
-AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "Stasis HTTP bindings",
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS,
+ "Stasis HTTP bindings",
.load = load_module,
.unload = unload_module,
.nonoptreq = "app_stasis,res_json"
Modified: team/dlee/stasis-http/res/stasis_http/resource_channels.c
URL: http://svnview.digium.com/svn/asterisk/team/dlee/stasis-http/res/stasis_http/resource_channels.c?view=diff&rev=382860&r1=382859&r2=382860
==============================================================================
--- team/dlee/stasis-http/res/stasis_http/resource_channels.c (original)
+++ team/dlee/stasis-http/res/stasis_http/resource_channels.c Tue Mar 12 12:12:11 2013
@@ -43,17 +43,20 @@
response->response_code = response_code;
response->response_text = response_text;
} else {
- /* Allocation failed. Error sending the error; great */
+ /* Error sending the error; great. */
response->response_code = 500;
response->response_text = "Internal Server Error";
- response->message = ast_json_pack("{s: s}", "message", "Allocation failed");
- }
-}
-
-static void fill_ok(struct stasis_http_response *response, struct ast_json *message)
+ response->message =
+ ast_json_pack("{s: s}", "message", "Allocation failed");
+ }
+}
+
+static void fill_ok(struct stasis_http_response *response,
+ struct ast_json *message)
{
if (!message) {
- fill_error(response, 500, "Internal Server Error", "Allocation failed");
+ fill_error(response, 500, "Internal Server Error",
+ "Allocation failed");
}
response->response_code = 200;
@@ -61,6 +64,48 @@
response->message = message;
}
+static void fill_no_content(struct stasis_http_response *response)
+{
+ response->response_code = 204;
+ response->response_text = "No Content";
+}
+
+/*!
+ * \brief Finds the control object for a channel, filling the response with an
+ * error, if appropriate.
+ * \param[out] response Response to fill with an error if control is not found.
+ * \param channel_id ID of the channel to lookup.
+ * \return Channel control object.
+ * \return \c NULL if control object does not exist.
+ */
+static struct stasis_app_control *find_control(
+ struct stasis_http_response *response,
+ const char *channel_id)
+{
+ RAII_VAR(struct stasis_app_control *, control, NULL, ao2_cleanup);
+ RAII_VAR(struct ast_channel *, chan, NULL, ao2_cleanup);
+
+ ast_assert(response != NULL);
+
+ chan = ast_channel_get_by_name(channel_id);
+ if (chan == NULL) {
+ fill_error(response, 404, "Not Found",
+ "Channel not found");
+ return NULL;
+ }
+
+ control = stasis_app_control_find_by_channel_id(ast_channel_uniqueid(chan));
+ if (control == NULL) {
+ /* Distinguish between 404 and 409 errors */
+ fill_error(response, 409, "Conflict",
+ "Channel not in Stasis application");
+ return NULL;
+ }
+
+ ao2_ref(control, +1);
+ return control;
+}
+
void stasis_http_dial(struct ast_variable *headers, struct ast_dial_args *args, struct stasis_http_response *response)
{
ast_log(LOG_ERROR, "TODO: stasis_http_dial\n");
@@ -68,36 +113,44 @@
void stasis_http_continue_in_dialplan(struct ast_variable *headers, struct ast_continue_in_dialplan_args *args, struct stasis_http_response *response)
{
- RAII_VAR(struct ast_channel *, chan, NULL, ao2_cleanup);
RAII_VAR(struct stasis_app_control *, control, NULL, ao2_cleanup);
ast_assert(response != NULL);
- chan = ast_channel_get_by_name(args->channel_id);
- if (chan == NULL) {
- fill_error(response, 404, "Not Found", "Channel not found");
- return;
- }
-
- control = stasis_app_control_find_by_channel(chan);
+ control = find_control(response, args->channel_id);
if (control == NULL) {
- fill_error(response, 409, "Conflict", "Channel not in Stasis application");
return;
}
stasis_app_control_continue(control);
-
- fill_ok(response, ast_json_pack("{s: s}", "message", "Returned to dialplan"));
+ fill_no_content(response);
}
void stasis_http_reject_channel(struct ast_variable *headers, struct ast_reject_channel_args *args, struct stasis_http_response *response)
{
ast_log(LOG_ERROR, "TODO: stasis_http_reject_channel\n");
}
-void stasis_http_answer_channel(struct ast_variable *headers, struct ast_answer_channel_args *args, struct stasis_http_response *response)
-{
- ast_log(LOG_ERROR, "TODO: stasis_http_answer_channel\n");
-}
+
+void stasis_http_answer_channel(struct ast_variable *headers,
+ struct ast_answer_channel_args *args,
+ struct stasis_http_response *response)
+{
+ RAII_VAR(struct stasis_app_control *, control, NULL, ao2_cleanup);
+
+ control = find_control(response, args->channel_id);
+ if (control == NULL) {
+ return;
+ }
+
+ if (stasis_app_control_answer(control) != 0) {
+ fill_error(response, 500, "Internal Server Error",
+ "Failed to answer channel");
+ return;
+ }
+
+ fill_no_content(response);
+}
+
void stasis_http_mute_channel(struct ast_variable *headers, struct ast_mute_channel_args *args, struct stasis_http_response *response)
{
ast_log(LOG_ERROR, "TODO: stasis_http_mute_channel\n");
More information about the asterisk-commits
mailing list