[svn-commits] dlee: branch dlee/stasis-http r380807 - in /team/dlee/stasis-http: apps/ incl...

SVN commits to the Digium repositories svn-commits at lists.digium.com
Mon Feb 4 10:30:32 CST 2013


Author: dlee
Date: Mon Feb  4 10:30:27 2013
New Revision: 380807

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=380807
Log:
Reorg

Added:
    team/dlee/stasis-http/include/asterisk/stasis_app.h   (with props)
    team/dlee/stasis-http/res/stasis_websocket.c   (with props)
    team/dlee/stasis-http/tests/test_stasis_app.c   (with props)
Removed:
    team/dlee/stasis-http/tests/test_stasis_core.c
Modified:
    team/dlee/stasis-http/apps/app_stasis.c
    team/dlee/stasis-http/include/asterisk/stasis.h
    team/dlee/stasis-http/include/asterisk/stasis_http.h
    team/dlee/stasis-http/res/Makefile
    team/dlee/stasis-http/res/res_stasis_core.c
    team/dlee/stasis-http/res/res_stasis_http.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=380807&r1=380806&r2=380807
==============================================================================
--- team/dlee/stasis-http/apps/app_stasis.c (original)
+++ team/dlee/stasis-http/apps/app_stasis.c Mon Feb  4 10:30:27 2013
@@ -33,7 +33,7 @@
 
 #include "asterisk/app.h"
 #include "asterisk/module.h"
-#include "asterisk/stasis.h"
+#include "asterisk/stasis_app.h"
 
 /*** DOCUMENTATION
 	<application name="Stasis" language="en_US">

Modified: team/dlee/stasis-http/include/asterisk/stasis.h
URL: http://svnview.digium.com/svn/asterisk/team/dlee/stasis-http/include/asterisk/stasis.h?view=diff&rev=380807&r1=380806&r2=380807
==============================================================================
--- team/dlee/stasis-http/include/asterisk/stasis.h (original)
+++ team/dlee/stasis-http/include/asterisk/stasis.h Mon Feb  4 10:30:27 2013
@@ -1,7 +1,7 @@
 /*
  * Asterisk -- An open source telephony toolkit.
  *
- * Copyright (C) 2012 - 2013, Digium, Inc.
+ * Copyright (C) 2013, Digium, Inc.
  *
  * David M. Lee, II <dlee at digium.com>
  *
@@ -19,8 +19,6 @@
 #ifndef _ASTERISK_STASIS_H
 #define _ASTERISK_STASIS_H
 
-#include "asterisk/json.h"
-
 /*! \file
  *
  * \brief Stasis message bus.
@@ -28,44 +26,6 @@
  * \author David M. Lee, II <dlee at digium.com>
  */
 
-/*!
- * \brief Invoke stasis application for a given channel, with the given arguments.
- * \param app_name Name of the application to invoke
- * \param chan Channel to invoke application upon
- * \param argc Number of arguments to pass into the application
- * \param argv Argument array.
- * \return 0 for success.
- * \return -1 for error.
- */
-int stasis_app_invoke(const char *app_name, struct ast_channel *chan, int argc, char *argv[]);
 
-/*!
- * \brief Callback for Stasis application handler.
- *
- * The message given to the handler is a borrowed copy. If you want to keep a
- * reference to it, you should use \c ast_ref() to keep it around.
- *
- * \param data Data ptr given when registered
- * \param message Message to handle. (borrowed copy)
- */
-typedef void (*stasis_app_handler)(void *data, const char *app_name, struct ast_json *message);
-
-/*!
- * \brief Register a new Stasis application.
- * If an application is already registered with the given name, the old
- * application is sent a 'replaced' message and unregistered.
- * \param app_name Name of this application.
- * \param handler Callback for application messages.
- * \param data Data blob to pass to the callback.
- * \return 0 for success
- * \return -1 for error.
- */
-int stasis_app_register(const char *app_name, stasis_app_handler handler, void *data);
-
-/*!
- * \brief Unregister a Stasis application.
- * \param app_name Name of the application to unregister.
- */
-void stasis_app_unregister(const char *app_name);
 
 #endif /* _ASTERISK_STASIS_H */

Added: team/dlee/stasis-http/include/asterisk/stasis_app.h
URL: http://svnview.digium.com/svn/asterisk/team/dlee/stasis-http/include/asterisk/stasis_app.h?view=auto&rev=380807
==============================================================================
--- team/dlee/stasis-http/include/asterisk/stasis_app.h (added)
+++ team/dlee/stasis-http/include/asterisk/stasis_app.h Mon Feb  4 10:30:27 2013
@@ -1,0 +1,74 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2012 - 2013, Digium, Inc.
+ *
+ * David M. Lee, II <dlee at 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.
+ */
+
+#ifndef _ASTERISK_STASIS_APP_H
+#define _ASTERISK_STASIS_APP_H
+
+/*! \file
+ *
+ * \brief Stasis Application API.
+ *
+ * This is the API that binds the Stasis dialplan application to external
+ * Stasis applications.
+ *
+ * \author David M. Lee, II <dlee at digium.com>
+ */
+
+#include "asterisk/json.h"
+
+/*!
+ * \brief Invoke stasis application for a given channel, with the given arguments.
+ * \param app_name Name of the application to invoke
+ * \param chan Channel to invoke application upon
+ * \param argc Number of arguments to pass into the application
+ * \param argv Argument array.
+ * \return 0 for success.
+ * \return -1 for error.
+ */
+int stasis_app_invoke(const char *app_name, struct ast_channel *chan, int argc, char *argv[]);
+
+/*!
+ * \brief Callback for Stasis application handler.
+ *
+ * The message given to the handler is a borrowed copy. If you want to keep a
+ * reference to it, you should use \c ast_ref() to keep it around.
+ *
+ * \param data Data ptr given when registered
+ * \param message Message to handle. (borrowed copy)
+ */
+typedef void (*stasis_app_handler)(void *data, const char *app_name, struct ast_json *message);
+
+/*!
+ * \brief Register a new Stasis application.
+ * If an application is already registered with the given name, the old
+ * application is sent a 'replaced' message and unregistered.
+ * \param app_name Name of this application.
+ * \param handler Callback for application messages.
+ * \param data Data blob to pass to the callback.
+ * \return 0 for success
+ * \return -1 for error.
+ */
+int stasis_app_register(const char *app_name, stasis_app_handler handler, void *data);
+
+/*!
+ * \brief Unregister a Stasis application.
+ * \param app_name Name of the application to unregister.
+ */
+void stasis_app_unregister(const char *app_name);
+
+#endif /* _ASTERISK_STASIS_APP_H */

Propchange: team/dlee/stasis-http/include/asterisk/stasis_app.h
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: team/dlee/stasis-http/include/asterisk/stasis_app.h
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Rev URL

Propchange: team/dlee/stasis-http/include/asterisk/stasis_app.h
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Modified: team/dlee/stasis-http/include/asterisk/stasis_http.h
URL: http://svnview.digium.com/svn/asterisk/team/dlee/stasis-http/include/asterisk/stasis_http.h?view=diff&rev=380807&r1=380806&r2=380807
==============================================================================
--- team/dlee/stasis-http/include/asterisk/stasis_http.h (original)
+++ team/dlee/stasis-http/include/asterisk/stasis_http.h Mon Feb  4 10:30:27 2013
@@ -31,6 +31,7 @@
 
 #include "asterisk/http.h"
 #include "asterisk/json.h"
+#include "asterisk/http_websocket.h"
 
 struct stasis_http_response;
 
@@ -123,4 +124,13 @@
  */
 void stasis_http_get_docs(const char *uri, struct ast_variable *headers, struct stasis_http_response *response);
 
+/*!
+ * \internal
+ * \brief Stasis WebSocket connection handler
+ * \param session WebSocket session.
+ * \param parameters HTTP \c GET parameters.
+ * \param headers HTTP headers.
+ */
+void stasis_websocket_callback(struct ast_websocket *session, struct ast_variable *parameters, struct ast_variable *headers);
+
 #endif /* _ASTERISK_STASIS_HTTP_H */

Modified: team/dlee/stasis-http/res/Makefile
URL: http://svnview.digium.com/svn/asterisk/team/dlee/stasis-http/res/Makefile?view=diff&rev=380807&r1=380806&r2=380807
==============================================================================
--- team/dlee/stasis-http/res/Makefile (original)
+++ team/dlee/stasis-http/res/Makefile Mon Feb  4 10:30:27 2013
@@ -89,4 +89,4 @@
 
 # Dependencies for res_stasis_http.so are generated, so they're in this file
 include res_stasis_http.make
-res_stasis_http.so: stasis_http_resources.o
+res_stasis_http.so: stasis_http_resources.o stasis_websocket.o

Modified: team/dlee/stasis-http/res/res_stasis_core.c
URL: http://svnview.digium.com/svn/asterisk/team/dlee/stasis-http/res/res_stasis_core.c?view=diff&rev=380807&r1=380806&r2=380807
==============================================================================
--- team/dlee/stasis-http/res/res_stasis_core.c (original)
+++ team/dlee/stasis-http/res/res_stasis_core.c Mon Feb  4 10:30:27 2013
@@ -37,6 +37,7 @@
 #include "asterisk/logger.h"
 #include "asterisk/module.h"
 #include "asterisk/stasis.h"
+#include "asterisk/stasis_app.h"
 #include "asterisk/utils.h"
 
 /*! Number of buckets for the Stasis application hash table. Remember to keep it a prime number! */

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=380807&r1=380806&r2=380807
==============================================================================
--- team/dlee/stasis-http/res/res_stasis_http.c (original)
+++ team/dlee/stasis-http/res/res_stasis_http.c Mon Feb  4 10:30:27 2013
@@ -34,14 +34,8 @@
 
 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
-#include "asterisk/astobj2.h"
-#include "asterisk/hashtab.h"
-#include "asterisk/http.h"
-#include "asterisk/http_websocket.h"
-#include "asterisk/json.h"
 #include "asterisk/module.h"
 #include "asterisk/paths.h"
-#include "asterisk/stasis.h"
 #include "asterisk/stasis_http.h"
 
 #include <string.h>
@@ -329,247 +323,6 @@
 	.key = __FILE__,
 };
 
-/*!
- * \internal
- * \brief Helper to write a JSON object to a WebSocket.
- * \param session WebSocket session.
- * \param message JSON message.
- * \return 0 on success.
- * \return -1 on error.
- */
-static int ast_websocket_write_json(struct ast_websocket *session, struct ast_json *message)
-{
-	RAII_VAR(char *, str, ast_json_dump_string(message), ast_free);
-
-	if (str == NULL) {
-		ast_log(LOG_ERROR, "Failed to encode JSON object\n");
-		return -1;
-	}
-
-	return ast_websocket_write(session, AST_WEBSOCKET_OPCODE_TEXT, str, strlen(str));
-}
-
-static struct ast_json *build_err(int id, const char *msg_format, ...) __attribute__((format(printf, 2, 3)));
-struct ast_json *build_err(int id, const char *msg_format, ...)
-{
-	va_list args;
-	struct ast_json *err;
-
-	va_start(args, msg_format);
-	err = ast_json_pack("{ s: { s: o }, s: i }",
-			    "error",
-			    "message", ast_json_vstringf(msg_format, args),
-			    "id", id);
-	va_end(args);
-
-	return err;
-}
-
-static struct ast_json *build_success(int id, const char *msg_format, ...) __attribute__((format(printf, 2, 3)));
-struct ast_json *build_success(int id, const char *msg_format, ...)
-{
-	va_list args;
-	struct ast_json *err;
-
-	va_start(args, msg_format);
-	err = ast_json_pack("{ s: o, s: i }",
-			    "success", ast_json_vstringf(msg_format, args),
-			    "id", id);
-	va_end(args);
-
-	return err;
-}
-
-/*! Number of buckets for the Stasis application hash table. Remember to keep it a prime number! */
-#define APPS_NUM_BUCKETS 7
-
-struct stasis_app {
-	char *name;
-};
-
-/*! Hash function for stasis_app */
-static int hash_app(const void *obj, const int flags)
-{
-	const struct stasis_app *app = obj;
-	const char *name = flags & OBJ_KEY ? obj : app->name;
-
-	return ast_hashtab_hash_string(name);
-}
-
-/*! Comparison function for stasis_app */
-static int compare_app(void *lhs, void *rhs, int flags)
-{
-	const struct stasis_app *lhs_app = lhs;
-	const struct stasis_app *rhs_app = rhs;
-	const char *rhs_name = flags & OBJ_KEY ? rhs : rhs_app->name;
-
-	if (strcmp(lhs_app->name, rhs_name) == 0) {
-		return CMP_MATCH | CMP_STOP;
-	} else {
-		return 0;
-	}
-}
-
-static void app_dtor(void *obj)
-{
-	struct stasis_app *app = obj;
-	ast_free(app->name);
-}
-
-struct stasis_ws_session_info {
-	struct ast_websocket *ws_session;
-	struct ao2_container *stasis_apps;
-};
-
-static void app_handler(void *data, const char *app_name, struct ast_json *message)
-{
-	struct stasis_ws_session_info *session = data;
-	int res;
-
-	res = ast_json_object_set(message, "application", ast_json_string_create(app_name));
-	ast_assert(res == 0);
-
-	ast_websocket_write_json(session->ws_session, message);
-}
-
-
-static struct ast_json *stasis_websocket_process_req(struct stasis_ws_session_info *session, struct ast_json *req)
-{
-	const char *command = ast_json_string_get(ast_json_object_get(req, "command"));
-	struct ast_json *id_json = ast_json_object_get(req, "id");
-	intmax_t id;
-
-	if (!id_json || ast_json_typeof(id_json) != AST_JSON_INTEGER) {
-		return ast_json_pack("{ s: { s: s, s: o }}",
-				     "error",
-				     "message", "Integer id field required",
-				     "request", req);
-	}
-
-	id = ast_json_integer_get(id_json);
-
-	if (ast_strlen_zero(command)) {
-		return build_err(id, "Missing command string");
-	}
-
-	if (strcmp("ActivateApplication", command) == 0) {
-		const char *app_name = ast_json_string_get(ast_json_object_get(req, "name"));
-		RAII_VAR(struct stasis_app *, app, NULL, ao2_cleanup);
-		if (ast_strlen_zero(app_name)) {
-			return build_err(id, "Missing application name");
-		}
-
-		app = ao2_find(session->stasis_apps, app_name, OBJ_KEY);
-		if (app) {
-			return build_err(id, "Application '%s' already registered", app_name);
-		}
-
-		app = ao2_alloc(sizeof(*app), app_dtor);
-		if (!app) {
-			ast_log(LOG_ERROR, "Failed to construct stasis_app");
-			return build_err(id, "Internal error");
-		}
-		app->name = ast_strdup(app_name);
-		ao2_link(session->stasis_apps, app);
-
-		stasis_app_register(app_name, app_handler, session);
-
-		return build_success(id, "Application activated");
-	} else if (strcmp("DeactivateApplication", command) == 0) {
-		const char *app_name = ast_json_string_get(ast_json_object_get(req, "name"));
-		RAII_VAR(struct stasis_app *, app, NULL, ao2_cleanup);
-		if (ast_strlen_zero(app_name)) {
-			return build_err(id, "Missing application name");
-		}
-		app = ao2_find(session->stasis_apps, app_name, OBJ_KEY | OBJ_UNLINK);
-		if (app == NULL) {
-			return build_err(id, "Application '%s' not activated", app_name);
-		}
-
-		stasis_app_unregister(app_name);
-
-		return build_success(id, "Application deactivated");
-	} else {
-		return build_err(id, "Unrecognized command: %s", command);
-	}
-}
-
-/*!
- * \internal
- * \brief Stasis WebSocket connection handler
- * \param session WebSocket session.
- * \param parameters HTTP \c GET parameters.
- * \param headers HTTP headers.
- */
-static void stasis_websocket_callback(struct ast_websocket *session, struct ast_variable *parameters, struct ast_variable *headers)
-{
-	struct stasis_ws_session_info stasis_session = {};
-        struct ao2_iterator i;
-	struct stasis_app *app;
-	int res;
-
-	ast_debug(3, "Stasis web socket connection\n");
-
-	if (ast_websocket_set_nonblock(session) != 0) {
-		ast_log(LOG_ERROR, "Stasis web socket failed to set nonblock; closing\n");
-		goto end;
-	}
-
-	stasis_session.stasis_apps = ao2_container_alloc(APPS_NUM_BUCKETS, hash_app, compare_app);
-	stasis_session.ws_session = session;
-
-	while ((res = ast_wait_for_input(ast_websocket_fd(session), -1)) > 0) {
-		char *payload;
-		uint64_t payload_len;
-		enum ast_websocket_opcode opcode;
-		int fragmented;
-
-		if (ast_websocket_read(session, &payload, &payload_len, &opcode, &fragmented)) {
-			ast_log(LOG_ERROR, "Stasis WebSocket read error; closing\n");
-			break;
-		}
-
-		if (opcode == AST_WEBSOCKET_OPCODE_TEXT) {
-			RAII_VAR(struct ast_json *, req, NULL, ast_json_unref);
-			RAII_VAR(struct ast_json *, resp, NULL, ast_json_unref);
-			struct ast_json_error err = {};
-
-			req = ast_json_load_buf(payload, payload_len, &err);
-
-			if (req == NULL) {
-				/* Parse error */
-				RAII_VAR(char *, payloadz, ast_strndup(payload, payload_len), ast_free);
-				resp = ast_json_pack("{ s: { s: o, s: i, s: i, s: s } }",
-						     "error",
-						     "message", ast_json_stringf("Error parsing message: %s", err.text),
-						     "line", err.line,
-						     "column", err.column,
-						     "request", payloadz ? payloadz : "(null)");
-				if (!resp) {
-					ast_log(LOG_ERROR, "Error building error message. That's just messed up.\n");
-				}
-			} else {
-				resp = stasis_websocket_process_req(&stasis_session, req);
-			}
-
-			if (resp) {
-				ast_websocket_write_json(session, resp);
-			}
-		} else if (opcode == AST_WEBSOCKET_OPCODE_CLOSE) {
-			break;
-		}
-	}
-
-end:
-	i = ao2_iterator_init(stasis_session.stasis_apps, 0);
-	while ((app = ao2_iterator_next(&i))) {
-		stasis_app_unregister(app->name);
-	}
-	ao2_cleanup(stasis_session.stasis_apps);
-
-	ast_websocket_unref(session);
-}
-
 struct stasis_rest_handlers *stasis_set_root_handler(struct stasis_rest_handlers *new_handler)
 {
 	struct stasis_rest_handlers *old_handler = root_handler;
@@ -590,6 +343,7 @@
 static int unload_module(void)
 {
 	int r = 0;
+
 	r |= ast_websocket_remove_protocol(ws_protocol, stasis_websocket_callback);
 	ast_http_uri_unlink(&http_uri);
 	return r;

Added: team/dlee/stasis-http/res/stasis_websocket.c
URL: http://svnview.digium.com/svn/asterisk/team/dlee/stasis-http/res/stasis_websocket.c?view=auto&rev=380807
==============================================================================
--- team/dlee/stasis-http/res/stasis_websocket.c (added)
+++ team/dlee/stasis-http/res/stasis_websocket.c Mon Feb  4 10:30:27 2013
@@ -1,0 +1,279 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2012 - 2013, Digium, Inc.
+ *
+ * David M. Lee, II <dlee at 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 HTTP binding for the Stasis API
+ *
+ * \author David M. Lee, II <dlee at digium.com>
+ */
+
+/*** MODULEINFO
+	<depend type="module">res_http_websocket</depend>
+	<depend type="module">res_stasis_core</depend>
+	<depend type="module">res_json</depend>
+	<support_level>core</support_level>
+ ***/
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/astobj2.h"
+#include "asterisk/hashtab.h"
+#include "asterisk/json.h"
+#include "asterisk/stasis_app.h"
+#include "asterisk/stasis_http.h"
+
+/*! We're a part of the res_stasis_http module, but the build system won't tell us that */
+#define AST_MODULE "res_stasis_http"
+
+/*! Number of buckets for the Stasis application hash table. Remember to keep it a prime number! */
+#define APPS_NUM_BUCKETS 7
+
+struct stasis_app {
+	char *name;
+};
+
+static struct ast_json *build_err(int id, const char *msg_format, ...) __attribute__((format(printf, 2, 3)));
+struct ast_json *build_err(int id, const char *msg_format, ...)
+{
+	va_list args;
+	struct ast_json *err;
+
+	va_start(args, msg_format);
+	err = ast_json_pack("{ s: { s: o }, s: i }",
+			    "error",
+			    "message", ast_json_vstringf(msg_format, args),
+			    "id", id);
+	va_end(args);
+
+	return err;
+}
+
+static struct ast_json *build_success(int id, const char *msg_format, ...) __attribute__((format(printf, 2, 3)));
+struct ast_json *build_success(int id, const char *msg_format, ...)
+{
+	va_list args;
+	struct ast_json *err;
+
+	va_start(args, msg_format);
+	err = ast_json_pack("{ s: o, s: i }",
+			    "success", ast_json_vstringf(msg_format, args),
+			    "id", id);
+	va_end(args);
+
+	return err;
+}
+
+/*!
+ * \internal
+ * \brief Helper to write a JSON object to a WebSocket.
+ * \param session WebSocket session.
+ * \param message JSON message.
+ * \return 0 on success.
+ * \return -1 on error.
+ */
+static int ast_websocket_write_json(struct ast_websocket *session, struct ast_json *message)
+{
+	RAII_VAR(char *, str, ast_json_dump_string(message), ast_free);
+
+	if (str == NULL) {
+		ast_log(LOG_ERROR, "Failed to encode JSON object\n");
+		return -1;
+	}
+
+	return ast_websocket_write(session, AST_WEBSOCKET_OPCODE_TEXT, str, strlen(str));
+}
+
+/*! Hash function for stasis_app */
+static int hash_app(const void *obj, const int flags)
+{
+	const struct stasis_app *app = obj;
+	const char *name = flags & OBJ_KEY ? obj : app->name;
+
+	return ast_hashtab_hash_string(name);
+}
+
+/*! Comparison function for stasis_app */
+static int compare_app(void *lhs, void *rhs, int flags)
+{
+	const struct stasis_app *lhs_app = lhs;
+	const struct stasis_app *rhs_app = rhs;
+	const char *rhs_name = flags & OBJ_KEY ? rhs : rhs_app->name;
+
+	if (strcmp(lhs_app->name, rhs_name) == 0) {
+		return CMP_MATCH | CMP_STOP;
+	} else {
+		return 0;
+	}
+}
+
+static void app_dtor(void *obj)
+{
+	struct stasis_app *app = obj;
+	ast_free(app->name);
+}
+
+struct stasis_ws_session_info {
+	struct ast_websocket *ws_session;
+	struct ao2_container *stasis_apps;
+};
+
+static void app_handler(void *data, const char *app_name, struct ast_json *message)
+{
+	struct stasis_ws_session_info *session = data;
+	int res;
+
+	res = ast_json_object_set(message, "application", ast_json_string_create(app_name));
+	ast_assert(res == 0);
+
+	ast_websocket_write_json(session->ws_session, message);
+}
+
+
+static struct ast_json *stasis_websocket_process_req(struct stasis_ws_session_info *session, struct ast_json *req)
+{
+	const char *command = ast_json_string_get(ast_json_object_get(req, "command"));
+	struct ast_json *id_json = ast_json_object_get(req, "id");
+	intmax_t id;
+
+	if (!id_json || ast_json_typeof(id_json) != AST_JSON_INTEGER) {
+		return ast_json_pack("{ s: { s: s, s: o }}",
+				     "error",
+				     "message", "Integer id field required",
+				     "request", req);
+	}
+
+	id = ast_json_integer_get(id_json);
+
+	if (ast_strlen_zero(command)) {
+		return build_err(id, "Missing command string");
+	}
+
+	if (strcmp("ActivateApplication", command) == 0) {
+		const char *app_name = ast_json_string_get(ast_json_object_get(req, "name"));
+		RAII_VAR(struct stasis_app *, app, NULL, ao2_cleanup);
+		if (ast_strlen_zero(app_name)) {
+			return build_err(id, "Missing application name");
+		}
+
+		app = ao2_find(session->stasis_apps, app_name, OBJ_KEY);
+		if (app) {
+			return build_err(id, "Application '%s' already registered", app_name);
+		}
+
+		app = ao2_alloc(sizeof(*app), app_dtor);
+		if (!app) {
+			ast_log(LOG_ERROR, "Failed to construct stasis_app");
+			return build_err(id, "Internal error");
+		}
+		app->name = ast_strdup(app_name);
+		ao2_link(session->stasis_apps, app);
+
+		stasis_app_register(app_name, app_handler, session);
+
+		return build_success(id, "Application activated");
+	} else if (strcmp("DeactivateApplication", command) == 0) {
+		const char *app_name = ast_json_string_get(ast_json_object_get(req, "name"));
+		RAII_VAR(struct stasis_app *, app, NULL, ao2_cleanup);
+		if (ast_strlen_zero(app_name)) {
+			return build_err(id, "Missing application name");
+		}
+		app = ao2_find(session->stasis_apps, app_name, OBJ_KEY | OBJ_UNLINK);
+		if (app == NULL) {
+			return build_err(id, "Application '%s' not activated", app_name);
+		}
+
+		stasis_app_unregister(app_name);
+
+		return build_success(id, "Application deactivated");
+	} else {
+		return build_err(id, "Unrecognized command: %s", command);
+	}
+}
+
+void stasis_websocket_callback(struct ast_websocket *session, struct ast_variable *parameters, struct ast_variable *headers)
+{
+	struct stasis_ws_session_info stasis_session = {};
+        struct ao2_iterator i;
+	struct stasis_app *app;
+	int res;
+
+	ast_debug(3, "Stasis web socket connection\n");
+
+	if (ast_websocket_set_nonblock(session) != 0) {
+		ast_log(LOG_ERROR, "Stasis web socket failed to set nonblock; closing\n");
+		goto end;
+	}
+
+	stasis_session.stasis_apps = ao2_container_alloc(APPS_NUM_BUCKETS, hash_app, compare_app);
+	stasis_session.ws_session = session;
+
+	while ((res = ast_wait_for_input(ast_websocket_fd(session), -1)) > 0) {
+		char *payload;
+		uint64_t payload_len;
+		enum ast_websocket_opcode opcode;
+		int fragmented;
+
+		if (ast_websocket_read(session, &payload, &payload_len, &opcode, &fragmented)) {
+			ast_log(LOG_ERROR, "Stasis WebSocket read error; closing\n");
+			break;
+		}
+
+		if (opcode == AST_WEBSOCKET_OPCODE_TEXT) {
+			RAII_VAR(struct ast_json *, req, NULL, ast_json_unref);
+			RAII_VAR(struct ast_json *, resp, NULL, ast_json_unref);
+			struct ast_json_error err = {};
+
+			req = ast_json_load_buf(payload, payload_len, &err);
+
+			if (req == NULL) {
+				/* Parse error */
+				RAII_VAR(char *, payloadz, ast_strndup(payload, payload_len), ast_free);
+				resp = ast_json_pack("{ s: { s: o, s: i, s: i, s: s } }",
+						     "error",
+						     "message", ast_json_stringf("Error parsing message: %s", err.text),
+						     "line", err.line,
+						     "column", err.column,
+						     "request", payloadz ? payloadz : "(null)");
+				if (!resp) {
+					ast_log(LOG_ERROR, "Error building error message. That's just messed up.\n");
+				}
+			} else {
+				resp = stasis_websocket_process_req(&stasis_session, req);
+			}
+
+			if (resp) {
+				ast_websocket_write_json(session, resp);
+			}
+		} else if (opcode == AST_WEBSOCKET_OPCODE_CLOSE) {
+			break;
+		}
+	}
+
+end:
+	i = ao2_iterator_init(stasis_session.stasis_apps, 0);
+	while ((app = ao2_iterator_next(&i))) {
+		stasis_app_unregister(app->name);
+	}
+	ao2_cleanup(stasis_session.stasis_apps);
+
+	ast_websocket_unref(session);
+}
+

Propchange: team/dlee/stasis-http/res/stasis_websocket.c
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: team/dlee/stasis-http/res/stasis_websocket.c
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Rev URL

Propchange: team/dlee/stasis-http/res/stasis_websocket.c
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: team/dlee/stasis-http/tests/test_stasis_app.c
URL: http://svnview.digium.com/svn/asterisk/team/dlee/stasis-http/tests/test_stasis_app.c?view=auto&rev=380807
==============================================================================
--- team/dlee/stasis-http/tests/test_stasis_app.c (added)
+++ team/dlee/stasis-http/tests/test_stasis_app.c Mon Feb  4 10:30:27 2013
@@ -1,0 +1,188 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2013, Digium, Inc.
+ *
+ * David M. Lee, II <dlee at 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 Test Stasis Core API.
+ * \author\verbatim David M. Lee, II <dlee at digium.com> \endverbatim
+ *
+ * \ingroup tests
+ */
+
+/*** MODULEINFO
+	<depend>TEST_FRAMEWORK</depend>
+	<depend>res_stasis_core</depend>
+	<support_level>core</support_level>
+ ***/
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/module.h"
+#include "asterisk/test.h"
+#include "asterisk/stasis_app.h"
+
+AST_TEST_DEFINE(app_invoke_dne)
+{
+	int res;
+
+	switch (cmd) {
+	case TEST_INIT:
+		info->name = __func__;
+		info->category = "/stasis/core/";
+		info->summary = "Test stasis app invocation.";
+		info->description = "Test stasis app invocation.";
+		return AST_TEST_NOT_RUN;
+	case TEST_EXECUTE:
+		break;
+	}
+
+	res = stasis_app_invoke("i-am-not-an-app", NULL, 0, NULL);
+	ast_test_validate(test, -1 == res);
+
+	return AST_TEST_PASS;
+}
+
+struct app_data {
+	int invocations;
+	struct ast_json *messages;
+};
+
+static struct app_data *app_data_create(void)
+{
+	struct app_data *res = ast_calloc(1, sizeof(struct app_data));
+	res->messages = ast_json_array_create();
+	return res;
+}
+
+static void app_data_dtor(struct app_data *actual)
+{
+	if (actual) {
+		ast_json_unref(actual->messages);
+		actual->messages = NULL;
+		ast_free(actual);
+	}
+}
+
+static void test_handler(void *data, const char *app_name, struct ast_json *message)
+{
+	struct app_data *actual = data;
+	int res;
+	++(actual->invocations);
+	res = ast_json_array_append(actual->messages, ast_json_copy(message));
+	ast_assert(res == 0);
+}
+
+AST_TEST_DEFINE(app_invoke_one)
+{
+	RAII_VAR(struct app_data *, app_data, NULL, app_data_dtor);
+	RAII_VAR(char *, app_name, "test-handler", stasis_app_unregister);
+	RAII_VAR(struct ast_json *, expected_message, NULL, ast_json_unref);
+	int res;
+
+
+	switch (cmd) {
+	case TEST_INIT:
+		info->name = __func__;
+		info->category = "/stasis/core/";
+		info->summary = "Test stasis app invocation.";
+		info->description = "Test stasis app invocation.";
+		return AST_TEST_NOT_RUN;
+	case TEST_EXECUTE:
+		break;
+	}
+
+	app_data = app_data_create();
+
+	stasis_app_register(app_name, test_handler, app_data);
+	expected_message = ast_json_pack("[{s: s, s: o, s: []}]",
+					 "command", "invoke",
+					 "channel", ast_json_null(),
+					 "args");
+
+	res = stasis_app_invoke(app_name, NULL, 0, NULL);
+	ast_test_validate(test, 0 == res);
+	ast_test_validate(test, 1 == app_data->invocations);
+	ast_test_validate(test, ast_json_equal(expected_message, app_data->messages));
+
+	return AST_TEST_PASS;
+}
+
+AST_TEST_DEFINE(app_replaced)
+{
+	RAII_VAR(struct app_data *, app_data1, NULL, app_data_dtor);
+	RAII_VAR(struct app_data *, app_data2, NULL, app_data_dtor);
+	RAII_VAR(char *, app_name, "test-handler", stasis_app_unregister);
+	RAII_VAR(struct ast_json *, expected_message1, NULL, ast_json_unref);
+	RAII_VAR(struct ast_json *, expected_message2, NULL, ast_json_unref);
+	int res;
+
+
+	switch (cmd) {
+	case TEST_INIT:
+		info->name = __func__;
+		info->category = "/stasis/core/";
+		info->summary = "Test stasis app invocation.";
+		info->description = "Test stasis app invocation.";
+		return AST_TEST_NOT_RUN;
+	case TEST_EXECUTE:
+		break;
+	}
+
+	app_data1 = app_data_create();
+	app_data2 = app_data_create();
+
+	stasis_app_register(app_name, test_handler, app_data1);
+	stasis_app_register(app_name, test_handler, app_data2);
+	expected_message1 = ast_json_pack("[{s: s}]", "event", "replaced");
+	expected_message2 = ast_json_pack("[{s: s, s: o, s: []}]",
+					  "command", "invoke",
+					  "channel", ast_json_null(),
+					  "args");
+
+	res = stasis_app_invoke(app_name, NULL, 0, NULL);
+	ast_test_validate(test, 0 == res);
+	ast_test_validate(test, 1 == app_data1->invocations);
+	ast_test_validate(test, ast_json_equal(expected_message1, app_data1->messages));
+	ast_test_validate(test, 1 == app_data2->invocations);
+	ast_test_validate(test, ast_json_equal(expected_message2, app_data2->messages));
+
+	return AST_TEST_PASS;
+}
+
+static int unload_module(void)
+{
+	AST_TEST_UNREGISTER(app_invoke_dne);
+	AST_TEST_UNREGISTER(app_invoke_one);
+	AST_TEST_UNREGISTER(app_replaced);
+	return 0;
+}
+
+static int load_module(void)
+{
+	AST_TEST_REGISTER(app_replaced);
+	AST_TEST_REGISTER(app_invoke_one);
+	AST_TEST_REGISTER(app_invoke_dne);
+	return AST_MODULE_LOAD_SUCCESS;
+}
+
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Stasis Core testing",
+		.load = load_module,
+		.unload = unload_module,
+		.nonoptreq = "res_stasis_core"
+	);

Propchange: team/dlee/stasis-http/tests/test_stasis_app.c
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: team/dlee/stasis-http/tests/test_stasis_app.c
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Rev URL

Propchange: team/dlee/stasis-http/tests/test_stasis_app.c
------------------------------------------------------------------------------
    svn:mime-type = text/plain




More information about the svn-commits mailing list