[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