[asterisk-commits] dlee: branch dlee/stasis-http r379129 - in /team/dlee/stasis-http: include/as...

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Tue Jan 15 16:28:57 CST 2013


Author: dlee
Date: Tue Jan 15 16:28:53 2013
New Revision: 379129

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=379129
Log:
API unit tests

Added:
    team/dlee/stasis-http/res/res_stasis_http.exports.in   (with props)
    team/dlee/stasis-http/tests/test_stasis_http.c   (with props)
Modified:
    team/dlee/stasis-http/include/asterisk/stasis_http.h
    team/dlee/stasis-http/res/res_stasis_http.c

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=379129&r1=379128&r2=379129
==============================================================================
--- team/dlee/stasis-http/include/asterisk/stasis_http.h (original)
+++ team/dlee/stasis-http/include/asterisk/stasis_http.h Tue Jan 15 16:28:53 2013
@@ -78,8 +78,21 @@
 };
 
 /*!
- * Handler for the root RESTful resource.
+ * \brief Handler for the root RESTful resource.
  */
 struct stasis_rest_handlers *stasis_root_handler(void);
 
+/*!
+ * \internal
+ * \brief Service function for API declarations. =
+ *
+ * Only call from res_stasis_http and test_stasis_http. Only public to allow
+ * for unit testing.
+ *
+ * \param uri Requested URI.
+ * \param headers HTTP headers.
+ * \param[out] response RESTful HTTP response.
+ */
+void stasis_http_get_api(const char *uri, struct ast_variable *headers, struct stasis_http_response *response);
+
 #endif /* _ASTERISK_STASIS_HTTP_H */

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=379129&r1=379128&r2=379129
==============================================================================
--- team/dlee/stasis-http/res/res_stasis_http.c (original)
+++ team/dlee/stasis-http/res/res_stasis_http.c Tue Jan 15 16:28:53 2013
@@ -68,7 +68,8 @@
 /*!
  * \internal
  * \brief Fill in an OK \a stasis_http_response.
- * \param response Response to fill in.
+ * \param response Response to fill in. This reference is stolen, so just
+ *                 \ref ast_json_incref if you need to keep a reference to it.
  * \param message JSON response.
  */
 static void response_ok(struct stasis_http_response *response, struct ast_json *message)
@@ -157,17 +158,19 @@
  * \param headers HTTP headers.
  * \param[out] response RESTful HTTP response.
  */
-static void stasis_http_get_api(const char *uri, struct ast_variable *headers, struct stasis_http_response *response) {
+void stasis_http_get_api(const char *uri, struct ast_variable *headers, struct stasis_http_response *response) {
 	RAII_VAR(struct ast_str *, absolute_path_builder, ast_str_create(80), ast_free);
 	RAII_VAR(char *, absolute_api_dirname, NULL, free);
 	RAII_VAR(char *, absolute_filename, NULL, free);
-	RAII_VAR(struct ast_json *, obj, NULL, ast_json_unref);
+	struct ast_json *obj = NULL;
 	char *relative_filename = NULL;
 	struct ast_variable *host = NULL;
 	struct ast_json_error error = {};
 
+	ast_log(LOG_DEBUG, "%s(%s)\n", __func__, uri);
+
 	if (absolute_path_builder == NULL) {
-		ast_log(LOG_ERROR, "Allocation failed.");
+		ast_log(LOG_ERROR, "Allocation failed\n");
 		response_error(response, "Allocation failed", 500, "Internal Server Error");
 		return;
 	}
@@ -211,14 +214,14 @@
 		/* HACKERZ! */
 		ast_log(LOG_ERROR, "Invalid attempt to access '%s' (not in %s)\n", absolute_filename, absolute_api_dirname);
 		response_error(response, "Resource not found", 404, "Not Found");
-		goto done;
+		return;
 	}
 
 	/* Load resource object from file */
 	obj = ast_json_load_new_file(absolute_filename, &error);
 	if (obj == NULL) {
 		response_error(response, "Yikes! Cannot parse resource", 500, "Internal Server Error");
-		goto done;
+		return;
 	}
 
 	/* Update the basePath properly */
@@ -261,10 +264,12 @@
 				struct ast_variable *headers)
 {
 	RAII_VAR(struct ast_str *, http_headers, ast_str_create(40), ast_free);
-	RAII_VAR(struct ast_str *, response_text, ast_str_create(256), ast_free);
+	RAII_VAR(struct ast_str *, response_body, ast_str_create(256), ast_free);
 	struct stasis_http_response response = {};
-
-	if (!http_headers || !out) {
+	int ret;
+
+	if (!http_headers || !response_body) {
+		ast_log(LOG_ERROR, "Allocation failure!\n");
 		return -1;
 	}
 
@@ -286,12 +291,21 @@
 	ast_assert(response.response_code > 0);
 
 	ast_str_set(&http_headers, 0, "Content-type: application/json\r\nAccess-Control-Allow-Origin: *\r\n");
-	response_text = ast_json_dump_str(response.message);
-
-	ast_http_send(ser, method, response.response_code, response.response_text, http_headers, response_text, 0, 0);
+
+	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;
+	}
 
 	ast_json_unref(response.message);
-	return 0;
+	return ret;
 }
 
 static struct ast_http_uri http_uri = {
@@ -313,7 +327,7 @@
  * \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);
+	RAII_VAR(char *, str, ast_json_dump_string(message), ast_free);
 
 	if (str == NULL) {
 		ast_log(LOG_ERROR, "Failed to encode JSON object\n");
@@ -399,9 +413,8 @@
 	return r;
 }
 
-AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Stasis HTTP bindings",
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "Stasis HTTP bindings",
 		.load = load_module,
 		.unload = unload_module,
-		.load_pri = AST_MODPRI_DEFAULT,
 		.nonoptreq = "app_stasis,res_http_websocket,res_stasis_core,res_json"
 	);

Added: team/dlee/stasis-http/res/res_stasis_http.exports.in
URL: http://svnview.digium.com/svn/asterisk/team/dlee/stasis-http/res/res_stasis_http.exports.in?view=auto&rev=379129
==============================================================================
--- team/dlee/stasis-http/res/res_stasis_http.exports.in (added)
+++ team/dlee/stasis-http/res/res_stasis_http.exports.in Tue Jan 15 16:28:53 2013
@@ -1,0 +1,6 @@
+{
+	global:
+		LINKER_SYMBOL_PREFIXstasis_http_get_api;
+	local:
+		*;
+};

Propchange: team/dlee/stasis-http/res/res_stasis_http.exports.in
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: team/dlee/stasis-http/res/res_stasis_http.exports.in
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Rev URL

Propchange: team/dlee/stasis-http/res/res_stasis_http.exports.in
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: team/dlee/stasis-http/tests/test_stasis_http.c
URL: http://svnview.digium.com/svn/asterisk/team/dlee/stasis-http/tests/test_stasis_http.c?view=auto&rev=379129
==============================================================================
--- team/dlee/stasis-http/tests/test_stasis_http.c (added)
+++ team/dlee/stasis-http/tests/test_stasis_http.c Tue Jan 15 16:28:53 2013
@@ -1,0 +1,255 @@
+/*
+ * 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 HTTP API.
+ * \author\verbatim David M. Lee, II <dlee at digium.com> \endverbatim
+ *
+ * \ingroup tests
+ */
+
+/*** MODULEINFO
+	<depend>TEST_FRAMEWORK</depend>
+	<depend>res_stasis_http</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_http.h"
+
+/*!@{*/
+#if 0
+/*!
+ * These structs and functions define the restful API:
+ *  - /foo (GET)
+ *  - /foo/bar (GET, POST)
+ *  - /foo/{bam} (GET)
+ *  - /foo/{bam}/bang (GET, POST, DELETE)
+ */
+
+struct test_received {
+	char *function;
+	struct ast_variable *get_params;
+	struct ast_variable *path_vars;
+	struct ast_variable *headers;
+};
+
+struct test_received test_received = {};
+
+#define HANDLER(name)						\
+	static void name(struct ast_variable *get_params,	\
+			 struct ast_variable *path_vars,	\
+			 struct ast_variable *headers,		\
+			 struct stasis_http_response *response)	\
+	{							\
+		test_received.function = #name;			\
+		test_received.get_params = get_params;		\
+		test_received.path_vars = path_vars;		\
+		test_received.headers = headers;		\
+	}
+
+
+HANDLER(bang_get)
+HANDLER(bang_post)
+HANDLER(bang_delete)
+HANDLER(bar_get)
+HANDLER(bar_post)
+HANDLER(bam_get)
+HANDLER(foo_get)
+
+static struct stasis_rest_handlers bang = {
+	.path_segment = "bang",
+	.callbacks = {
+		[AST_HTTP_GET] = bang_get,
+		[AST_HTTP_POST] = bang_post,
+		[AST_HTTP_DELETE] = bang_delete,
+	},
+	.num_children = 0
+};
+static struct stasis_rest_handlers bar = {
+	.path_segment = "bar",
+	.callbacks = {
+		[AST_HTTP_GET] = bar_get,
+		[AST_HTTP_POST] = bar_post,
+	},
+	.num_children = 0
+};
+static struct stasis_rest_handlers bam = {
+	.path_segment = "bam",
+	.is_wildcard = 1,
+	.callbacks = {
+		[AST_HTTP_GET] = bam_get,
+	},
+	.num_children = 1,
+	.children = { &bang }
+};
+static struct stasis_rest_handlers foo = {
+	.path_segment = "foo",
+	.callbacks = {
+		[AST_HTTP_GET] = foo_get,
+	},
+	.num_children = 3,
+	.children = { &bar, &bam, &bang }
+};
+
+#endif // 0
+
+static struct stasis_http_response *response_alloc(void)
+{
+	struct stasis_http_response *ret = ast_calloc(1, sizeof(struct stasis_http_response));
+	return ret;
+}
+
+static void response_free(struct stasis_http_response *resp)
+{
+	ast_json_unref(resp->message);
+	ast_free(resp);
+}
+
+/*!@}*/
+
+AST_TEST_DEFINE(get_api)
+{
+	RAII_VAR(struct stasis_http_response *, response, response_alloc, response_free);
+	RAII_VAR(struct ast_variable *, headers, NULL, ast_variables_destroy);
+	struct ast_json *basePathJson;
+	const char *basePath;
+
+	switch (cmd) {
+	case TEST_INIT:
+		info->name = __func__;
+		info->category = "/stasis/http/";
+		info->summary = "Test simple API get.";
+		info->description = "Test Stasis HTTP binding logic.";
+		return AST_TEST_NOT_RUN;
+	case TEST_EXECUTE:
+		break;
+	}
+
+	headers = ast_variable_new("Host", "stasis.asterisk.org", __FILE__);
+	stasis_http_get_api("api/resources.json", headers, response);
+	ast_test_validate(test, 200 == response.response_code);
+
+	/* basePath should be relative to the Host header */
+	basePathJson = ast_json_object_get(response.message, "basePath");
+	ast_test_validate(test, NULL != basePathJson);
+	basePath = ast_json_string_get(basePathJson);
+	ast_test_validate(test, 0 == strcmp("http://stasis.asterisk.org/stasis", basePath));
+
+	return AST_TEST_PASS;
+}
+
+AST_TEST_DEFINE(get_api_nohost)
+{
+	RAII_VAR(struct stasis_http_response *, response, response_alloc, response_free);
+	struct ast_variable *headers = NULL;
+	struct ast_json *basePathJson;
+
+	switch (cmd) {
+	case TEST_INIT:
+		info->name = __func__;
+		info->category = "/stasis/http/";
+		info->summary = "Test API get without a Host header";
+		info->description = "Test Stasis HTTP binding logic.";
+		return AST_TEST_NOT_RUN;
+	case TEST_EXECUTE:
+		break;
+	}
+
+	stasis_http_get_api("api/resources.json", headers, response);
+	ast_test_validate(test, 200 == response.response_code);
+
+	/* basePath should be relative to the Host header */
+	basePathJson = ast_json_object_get(response.message, "basePath");
+	ast_test_validate(test, NULL == basePathJson);
+
+	return AST_TEST_PASS;
+}
+
+AST_TEST_DEFINE(get_api_notfound)
+{
+	RAII_VAR(struct stasis_http_response *, response, response_alloc, response_free);
+	struct ast_variable *headers = NULL;
+
+	switch (cmd) {
+	case TEST_INIT:
+		info->name = __func__;
+		info->category = "/stasis/http/";
+		info->summary = "Test API get for invalid resource";
+		info->description = "Test Stasis HTTP binding logic.";
+		return AST_TEST_NOT_RUN;
+	case TEST_EXECUTE:
+		break;
+	}
+
+	stasis_http_get_api("api/i-am-not-a-resource.json", headers, response);
+	ast_test_validate(test, 404 == response.response_code);
+
+	return AST_TEST_PASS;
+}
+
+AST_TEST_DEFINE(get_api_hackerz)
+{
+	RAII_VAR(struct stasis_http_response *, response, response_alloc, response_free);
+	struct ast_variable *headers = NULL;
+
+	switch (cmd) {
+	case TEST_INIT:
+		info->name = __func__;
+		info->category = "/stasis/http/";
+		info->summary = "Test API get for a file outside the rest-api path";
+		info->description = "Test Stasis HTTP binding logic.";
+		return AST_TEST_NOT_RUN;
+	case TEST_EXECUTE:
+		break;
+	}
+
+	stasis_http_get_api("api/../../../../sbin/asterisk", headers, response);
+	ast_test_validate(test, 404 == response.response_code);
+
+	return AST_TEST_PASS;
+}
+
+static int unload_module(void)
+{
+	AST_TEST_UNREGISTER(get_api);
+	AST_TEST_UNREGISTER(get_api_nohost);
+	AST_TEST_UNREGISTER(get_api_notfound);
+	AST_TEST_UNREGISTER(get_api_hackerz);
+	return 0;
+}
+
+static int load_module(void)
+{
+	AST_TEST_REGISTER(get_api);
+	AST_TEST_REGISTER(get_api_nohost);
+	AST_TEST_REGISTER(get_api_notfound);
+	AST_TEST_REGISTER(get_api_hackerz);
+	return AST_MODULE_LOAD_SUCCESS;
+}
+
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Stasis HTTP testing",
+		.load = load_module,
+		.unload = unload_module,
+		.nonoptreq = "res_stasis_http"
+	);

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

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

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




More information about the asterisk-commits mailing list