[asterisk-commits] dlee: trunk r386232 - in /trunk: ./ configs/ include/asterisk/ main/ res/ res...
SVN commits to the Asterisk project
asterisk-commits at lists.digium.com
Mon Apr 22 09:59:04 CDT 2013
Author: dlee
Date: Mon Apr 22 09:58:53 2013
New Revision: 386232
URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=386232
Log:
This patch adds a RESTful HTTP interface to Asterisk.
The API itself is documented using Swagger, a lightweight mechanism for
documenting RESTful API's using JSON. This allows us to use swagger-ui
to provide executable documentation for the API, generate client
bindings in different languages, and generate a lot of the boilerplate
code for implementing the RESTful bindings. The API docs live in the
rest-api/ directory.
The RESTful bindings are generated from the Swagger API docs using a set
of Mustache templates. The code generator is written in Python, and
uses Pystache. Pystache has no dependencies, and be installed easily
using pip. Code generation code lives in rest-api-templates/.
The generated code reduces a lot of boilerplate when it comes to
handling HTTP requests. It also helps us have greater consistency in the
REST API.
(closes issue ASTERISK-20891)
Review: https://reviewboard.asterisk.org/r/2376/
Added:
trunk/configs/stasis_http.conf.sample
- copied unchanged from r386231, team/dlee/stasis-http/configs/stasis_http.conf.sample
trunk/include/asterisk/stasis_http.h
- copied unchanged from r386231, team/dlee/stasis-http/include/asterisk/stasis_http.h
trunk/res/res_stasis_http.c
- copied unchanged from r386231, team/dlee/stasis-http/res/res_stasis_http.c
trunk/res/res_stasis_http.exports.in
- copied, changed from r386231, team/dlee/stasis-http/res/res_stasis_http.exports.in
trunk/res/res_stasis_http_asterisk.c
- copied unchanged from r386231, team/dlee/stasis-http/res/res_stasis_http_asterisk.c
trunk/res/res_stasis_http_bridges.c
- copied unchanged from r386231, team/dlee/stasis-http/res/res_stasis_http_bridges.c
trunk/res/res_stasis_http_channels.c
- copied unchanged from r386231, team/dlee/stasis-http/res/res_stasis_http_channels.c
trunk/res/res_stasis_http_endpoints.c
- copied unchanged from r386231, team/dlee/stasis-http/res/res_stasis_http_endpoints.c
trunk/res/res_stasis_http_events.c
- copied unchanged from r386231, team/dlee/stasis-http/res/res_stasis_http_events.c
trunk/res/res_stasis_http_playback.c
- copied unchanged from r386231, team/dlee/stasis-http/res/res_stasis_http_playback.c
trunk/res/res_stasis_http_recordings.c
- copied unchanged from r386231, team/dlee/stasis-http/res/res_stasis_http_recordings.c
trunk/res/res_stasis_http_sounds.c
- copied unchanged from r386231, team/dlee/stasis-http/res/res_stasis_http_sounds.c
trunk/res/stasis_http/ (props changed)
- copied from r386231, team/dlee/stasis-http/res/stasis_http/
trunk/res/stasis_http.make
- copied unchanged from r386231, team/dlee/stasis-http/res/stasis_http.make
trunk/rest-api/
- copied from r386231, team/dlee/stasis-http/rest-api/
trunk/rest-api-templates/ (props changed)
- copied from r386231, team/dlee/stasis-http/rest-api-templates/
trunk/tests/test_stasis_http.c
- copied unchanged from r386231, team/dlee/stasis-http/tests/test_stasis_http.c
Modified:
trunk/Makefile
trunk/include/asterisk/http.h
trunk/include/asterisk/json.h
trunk/include/asterisk/stasis_app.h
trunk/include/asterisk/strings.h
trunk/main/http.c
trunk/main/json.c
trunk/res/Makefile
trunk/res/res_stasis.c
trunk/res/stasis_http/resource_channels.c
trunk/tests/test_stasis.c
trunk/tests/test_strings.c
Modified: trunk/Makefile
URL: http://svnview.digium.com/svn/asterisk/trunk/Makefile?view=diff&rev=386232&r1=386231&r2=386232
==============================================================================
--- trunk/Makefile (original)
+++ trunk/Makefile Mon Apr 22 09:58:53 2013
@@ -453,6 +453,9 @@
$(INSTALL) -m 644 $$x "$(DESTDIR)$(ASTDATADIR)/images" ; \
done
$(MAKE) -C sounds install
+ find rest-api -name "*.json" | while read x; do \
+ $(INSTALL) -m 644 $$x "$(DESTDIR)$(ASTDATADIR)/rest-api" ; \
+ done
ifneq ($(GREP),)
XML_core_en_US = $(foreach dir,$(MOD_SUBDIRS),$(shell $(GREP) -l "language=\"en_US\"" $(dir)/*.c $(dir)/*.cc 2>/dev/null))
@@ -537,8 +540,8 @@
"$(ASTLOGDIR)/cel-custom" "$(ASTDATADIR)" "$(ASTDATADIR)/documentation" \
"$(ASTDATADIR)/documentation/thirdparty" "$(ASTDATADIR)/firmware" \
"$(ASTDATADIR)/firmware/iax" "$(ASTDATADIR)/images" "$(ASTDATADIR)/keys" \
- "$(ASTDATADIR)/phoneprov" "$(ASTDATADIR)/static-http" "$(ASTDATADIR)/sounds" \
- "$(ASTDATADIR)/moh" "$(ASTMANDIR)/man8" "$(AGI_DIR)" "$(ASTDBDIR)"
+ "$(ASTDATADIR)/phoneprov" "$(ASTDATADIR)/rest-api" "$(ASTDATADIR)/static-http" \
+ "$(ASTDATADIR)/sounds" "$(ASTDATADIR)/moh" "$(ASTMANDIR)/man8" "$(AGI_DIR)" "$(ASTDBDIR)"
installdirs:
@for i in $(INSTALLDIRS); do \
@@ -958,6 +961,19 @@
@cat sounds/sounds.xml >> $@
@echo "</menu>" >> $@
+# We don't want to require Python or Pystache for every build, so this is its
+# own target.
+stasis-stubs:
+ifeq ($(PYTHON),:)
+ @echo "--------------------------------------------------------------------------"
+ @echo "--- Please install python to build Stasis HTTP stubs ---"
+ @echo "--------------------------------------------------------------------------"
+ @false
+else
+ $(PYTHON) rest-api-templates/make_stasis_http_stubs.py \
+ rest-api/resources.json res/
+endif
+
.PHONY: menuselect
.PHONY: main
.PHONY: sounds
@@ -977,6 +993,7 @@
.PHONY: installdirs
.PHONY: validate-docs
.PHONY: _clean
+.PHONY: stasis-stubs
.PHONY: $(SUBDIRS_INSTALL)
.PHONY: $(SUBDIRS_DIST_CLEAN)
.PHONY: $(SUBDIRS_CLEAN)
Modified: trunk/include/asterisk/http.h
URL: http://svnview.digium.com/svn/asterisk/trunk/include/asterisk/http.h?view=diff&rev=386232&r1=386231&r2=386232
==============================================================================
--- trunk/include/asterisk/http.h (original)
+++ trunk/include/asterisk/http.h Mon Apr 22 09:58:53 2013
@@ -58,7 +58,10 @@
AST_HTTP_GET = 0,
AST_HTTP_POST,
AST_HTTP_HEAD,
- AST_HTTP_PUT, /*!< Not supported in Asterisk */
+ AST_HTTP_PUT,
+ AST_HTTP_DELETE,
+ AST_HTTP_OPTIONS,
+ AST_HTTP_MAX_METHOD, /*!< Last entry in ast_http_method enum */
};
struct ast_http_uri;
Modified: trunk/include/asterisk/json.h
URL: http://svnview.digium.com/svn/asterisk/trunk/include/asterisk/json.h?view=diff&rev=386232&r1=386231&r2=386232
==============================================================================
--- trunk/include/asterisk/json.h (original)
+++ trunk/include/asterisk/json.h Mon Apr 22 09:58:53 2013
@@ -586,16 +586,33 @@
/*!@{*/
/*!
+ * \brief Encoding format type.
+ * \since 12.0.0
+ */
+enum ast_json_encoding_format
+{
+ /*! Compact format, low human readability */
+ AST_JSON_COMPACT,
+ /*! Formatted for human readability */
+ AST_JSON_PRETTY,
+};
+
+#define ast_json_dump_string(root) ast_json_dump_string_format(root, AST_JSON_COMPACT)
+
+/*!
* \brief Encode a JSON value to a string.
* \since 12.0.0
*
* Returned string must be freed by calling ast_free().
*
- * \param JSON value.
+ * \param root JSON value.
+ * \param format encoding format type.
* \return String encoding of \a root.
* \return \c NULL on error.
*/
-char *ast_json_dump_string(struct ast_json *root);
+char *ast_json_dump_string_format(struct ast_json *root, enum ast_json_encoding_format format);
+
+#define ast_json_dump_str(root, dst) ast_json_dump_str_format(root, dst, AST_JSON_COMPACT)
/*!
* \brief Encode a JSON value to an \ref ast_str.
@@ -605,10 +622,13 @@
*
* \param root JSON value.
* \param dst \ref ast_str to store JSON encoding.
+ * \param format encoding format type.
* \return 0 on success.
* \return -1 on error. The contents of \a dst are undefined.
*/
-int ast_json_dump_str(struct ast_json *root, struct ast_str **dst);
+int ast_json_dump_str_format(struct ast_json *root, struct ast_str **dst, enum ast_json_encoding_format format);
+
+#define ast_json_dump_file(root, output) ast_json_dump_file_format(root, output, AST_JSON_COMPACT)
/*!
* \brief Encode a JSON value to a \c FILE.
@@ -616,10 +636,13 @@
*
* \param root JSON value.
* \param output File to write JSON encoding to.
+ * \param format encoding format type.
* \return 0 on success.
* \return -1 on error. The contents of \a output are undefined.
*/
-int ast_json_dump_file(struct ast_json *root, FILE *output);
+int ast_json_dump_file_format(struct ast_json *root, FILE *output, enum ast_json_encoding_format format);
+
+#define ast_json_dump_new_file(root, path) ast_json_dump_new_file_format(root, path, AST_JSON_COMPACT)
/*!
* \brief Encode a JSON value to a file at the given location.
@@ -627,10 +650,11 @@
*
* \param root JSON value.
* \param path Path to file to write JSON encoding to.
+ * \param format encoding format type.
* \return 0 on success.
* \return -1 on error. The contents of \a output are undefined.
*/
-int ast_json_dump_new_file(struct ast_json *root, const char *path);
+int ast_json_dump_new_file_format(struct ast_json *root, const char *path, enum ast_json_encoding_format format);
#define AST_JSON_ERROR_TEXT_LENGTH 160
#define AST_JSON_ERROR_SOURCE_LENGTH 80
Modified: trunk/include/asterisk/stasis_app.h
URL: http://svnview.digium.com/svn/asterisk/trunk/include/asterisk/stasis_app.h?view=diff&rev=386232&r1=386231&r2=386232
==============================================================================
--- trunk/include/asterisk/stasis_app.h (original)
+++ trunk/include/asterisk/stasis_app.h Mon Apr 22 09:58:53 2013
@@ -66,7 +66,7 @@
* \param argv Arguments for the application.
*/
int stasis_app_exec(struct ast_channel *chan, const char *app_name, int argc,
- char *argv[]);
+ char *argv[]);
/*! @} */
@@ -126,22 +126,50 @@
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 stasis handler.
+ * \return NULL channel not in Stasis application.
+ * \return Pointer to \c res_stasis handler.
*/
struct stasis_app_control *stasis_app_control_find_by_channel(
const struct ast_channel *chan);
/*!
- * \brief Exit \c app_stasis and continue execution in the dialplan.
+ * \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 res_stasis handler.
+ */
+struct stasis_app_control *stasis_app_control_find_by_channel_id(
+ const char *channel_id);
+
+/*!
+ * \brief Exit \c res_stasis and continue execution in the dialplan.
*
- * If the channel is no longer in \c app_stasis, this function does nothing.
+ * If the channel is no longer in \c res_stasis, this function does nothing.
*
- * \param handler Handler for \c app_stasis
+ * \param control Control for \c res_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 res_stasis.
+ * \return 0 for success.
+ * \return -1 for error.
+ */
+int stasis_app_control_answer(struct stasis_app_control *control);
+
+/*! @} */
+
+/*! @{ */
+
+/*!
+ * \brief Build a JSON object from a \ref ast_channel_snapshot.
+ * \return JSON object representing channel snapshot.
+ * \return \c NULL on error
+ */
+struct ast_json *ast_channel_snapshot_to_json(const struct ast_channel_snapshot *snapshot);
/*! @} */
Modified: trunk/include/asterisk/strings.h
URL: http://svnview.digium.com/svn/asterisk/trunk/include/asterisk/strings.h?view=diff&rev=386232&r1=386231&r2=386232
==============================================================================
--- trunk/include/asterisk/strings.h (original)
+++ trunk/include/asterisk/strings.h Mon Apr 22 09:58:53 2013
@@ -82,6 +82,48 @@
*/
#define S_COR(a, b, c) ({typeof(&((b)[0])) __x = (b); (a) && !ast_strlen_zero(__x) ? (__x) : (c);})
+/*
+ \brief Checks whether a string begins with another.
+ \since 12.0.0
+ \param str String to check.
+ \param prefix Prefix to look for.
+ \param 1 if \a str begins with \a prefix, 0 otherwise.
+ */
+static int force_inline attribute_pure ast_begins_with(const char *str, const char *prefix)
+{
+ ast_assert(str != NULL);
+ ast_assert(prefix != NULL);
+ while (*str == *prefix && *prefix != '\0') {
+ ++str;
+ ++prefix;
+ }
+ return *prefix == '\0';
+}
+
+/*
+ \brief Checks whether a string ends with another.
+ \since 12.0.0
+ \param str String to check.
+ \param suffix Suffix to look for.
+ \param 1 if \a str ends with \a suffix, 0 otherwise.
+ */
+static int force_inline attribute_pure ast_ends_with(const char *str, const char *suffix)
+{
+ size_t str_len;
+ size_t suffix_len;
+
+ ast_assert(str != NULL);
+ ast_assert(suffix != NULL);
+ str_len = strlen(str);
+ suffix_len = strlen(suffix);
+
+ if (suffix_len > str_len) {
+ return 0;
+ }
+
+ return strcmp(str + str_len - suffix_len, suffix) == 0;
+}
+
/*!
* \brief return Yes or No depending on the argument.
*
Modified: trunk/main/http.c
URL: http://svnview.digium.com/svn/asterisk/trunk/main/http.c?view=diff&rev=386232&r1=386231&r2=386232
==============================================================================
--- trunk/main/http.c (original)
+++ trunk/main/http.c Mon Apr 22 09:58:53 2013
@@ -153,6 +153,8 @@
{ AST_HTTP_POST, "POST" },
{ AST_HTTP_HEAD, "HEAD" },
{ AST_HTTP_PUT, "PUT" },
+ { AST_HTTP_DELETE, "DELETE" },
+ { AST_HTTP_OPTIONS, "OPTIONS" },
};
const char *ast_get_http_method(enum ast_http_method method)
@@ -897,6 +899,10 @@
http_method = AST_HTTP_HEAD;
} else if (!strcasecmp(method,"PUT")) {
http_method = AST_HTTP_PUT;
+ } else if (!strcasecmp(method,"DELETE")) {
+ http_method = AST_HTTP_DELETE;
+ } else if (!strcasecmp(method,"OPTIONS")) {
+ http_method = AST_HTTP_OPTIONS;
}
uri = ast_skip_blanks(uri); /* Skip white space */
Modified: trunk/main/json.c
URL: http://svnview.digium.com/svn/asterisk/trunk/main/json.c?view=diff&rev=386232&r1=386231&r2=386232
==============================================================================
--- trunk/main/json.c (original)
+++ trunk/main/json.c Mon Apr 22 09:58:53 2013
@@ -338,20 +338,15 @@
/*!
* \brief Default flags for JSON encoding.
*/
-static size_t dump_flags(void)
-{
- /* There's a chance this could become a runtime flag */
- int flags = JSON_COMPACT;
-#ifdef AST_DEVMODE
- /* In dev mode, write readable JSON */
- flags = JSON_INDENT(2) | JSON_PRESERVE_ORDER;
-#endif
- return flags;
-}
-
-char *ast_json_dump_string(struct ast_json *root)
-{
- return json_dumps((json_t *)root, dump_flags());
+static size_t dump_flags(enum ast_json_encoding_format format)
+{
+ return format == AST_JSON_PRETTY ?
+ JSON_INDENT(2) | JSON_PRESERVE_ORDER : JSON_COMPACT;
+}
+
+char *ast_json_dump_string_format(struct ast_json *root, enum ast_json_encoding_format format)
+{
+ return json_dumps((json_t *)root, dump_flags(format));
}
static int write_to_ast_str(const char *buffer, size_t size, void *data)
@@ -385,25 +380,25 @@
return 0;
}
-int ast_json_dump_str(struct ast_json *root, struct ast_str **dst)
-{
- return json_dump_callback((json_t *)root, write_to_ast_str, dst, dump_flags());
-}
-
-
-int ast_json_dump_file(struct ast_json *root, FILE *output)
+int ast_json_dump_str_format(struct ast_json *root, struct ast_str **dst, enum ast_json_encoding_format format)
+{
+ return json_dump_callback((json_t *)root, write_to_ast_str, dst, dump_flags(format));
+}
+
+
+int ast_json_dump_file_format(struct ast_json *root, FILE *output, enum ast_json_encoding_format format)
{
if (!root || !output) {
return -1;
}
- return json_dumpf((json_t *)root, output, dump_flags());
-}
-int ast_json_dump_new_file(struct ast_json *root, const char *path)
+ return json_dumpf((json_t *)root, output, dump_flags(format));
+}
+int ast_json_dump_new_file_format(struct ast_json *root, const char *path, enum ast_json_encoding_format format)
{
if (!root || !path) {
return -1;
}
- return json_dump_file((json_t *)root, path, dump_flags());
+ return json_dump_file((json_t *)root, path, dump_flags(format));
}
/*!
Modified: trunk/res/Makefile
URL: http://svnview.digium.com/svn/asterisk/trunk/res/Makefile?view=diff&rev=386232&r1=386231&r2=386232
==============================================================================
--- trunk/res/Makefile (original)
+++ trunk/res/Makefile Mon Apr 22 09:58:53 2013
@@ -67,4 +67,7 @@
ael/pval.o: ael/pval.c
clean::
- rm -f snmp/*.o snmp/*.i ael/*.o ael/*.i ais/*.o ais/*.i
+ rm -f snmp/*.[oi] ael/*.[oi] ais/*.[oi] stasis_http/*.[oi]
+
+# Dependencies for res_stasis_http_*.so are generated, so they're in this file
+include stasis_http.make
Modified: trunk/res/res_stasis.c
URL: http://svnview.digium.com/svn/asterisk/trunk/res/res_stasis.c?view=diff&rev=386232&r1=386231&r2=386232
==============================================================================
--- trunk/res/res_stasis.c (original)
+++ trunk/res/res_stasis.c Mon Apr 22 09:58:53 2013
@@ -40,6 +40,9 @@
#include "asterisk/stasis_channels.h"
#include "asterisk/strings.h"
+/*! Time to wait for a frame in the application */
+#define MAX_WAIT_MS 200
+
/*!
* \brief Number of buckets for the Stasis application hash table. Remember to
* keep it a prime number!
@@ -147,7 +150,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.
*/
@@ -167,9 +230,22 @@
return NULL;
}
+ control->command_queue = ao2_container_alloc_list(0, 0, NULL, NULL);
+
strncpy(control->channel_id, uniqueid, size - sizeof(*control));
return control;
+}
+
+static void *exec_command(struct stasis_app_control *control,
+ struct stasis_app_command *command)
+{
+ ao2_lock(control);
+ ao2_ref(command, +1);
+ ao2_link(control->command_queue, command);
+ ao2_unlock(control);
+
+ return wait_for_command(command);
}
/*! AO2 hash function for \ref stasis_app_control */
@@ -199,13 +275,20 @@
struct stasis_app_control *stasis_app_control_find_by_channel(
const struct ast_channel *chan)
{
- RAII_VAR(struct ao2_container *, controls, NULL, ao2_cleanup);
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);
}
/*!
@@ -231,6 +314,33 @@
{
SCOPED_AO2LOCK(lock, control);
control->continue_to_dialplan = 1;
+}
+
+static int OK = 0;
+static int FAIL = -1;
+
+static void *__app_control_answer(struct stasis_app_control *control,
+ struct ast_channel *chan, void *data)
+{
+ ast_debug(3, "%s: Answering", control->channel_id);
+ 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;
+
+ ast_debug(3, "%s: Sending answer command\n", control->channel_id);
+
+ command = command_create(__app_control_answer, NULL);
+ retval = exec_command(control, command);
+
+ if (*retval != 0) {
+ ast_log(LOG_WARNING, "Failed to answer channel");
+ }
+
+ return *retval;
}
static struct ast_json *app_event_create(
@@ -410,6 +520,26 @@
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 */
int stasis_app_exec(struct ast_channel *chan, const char *app_name, int argc,
char *argv[])
@@ -458,8 +588,38 @@
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);
+ while (1) {
+ RAII_VAR(struct ast_frame *, f, NULL, ast_frame_dtor);
+ int r;
+
+ if (hungup) {
+ ast_debug(3, "%s: Hangup\n",
+ ast_channel_uniqueid(chan));
+ break;
+ }
+
+ if (control_continue_test_and_reset(control)) {
+ ast_debug(3, "%s: Continue\n",
+ ast_channel_uniqueid(chan));
+ break;
+ }
+
+ r = ast_waitfor(chan, MAX_WAIT_MS);
+
+ if (r < 0) {
+ ast_debug(3, "%s: Poll error\n",
+ ast_channel_uniqueid(chan));
+ break;
+ }
+
+ dispatch_commands(control, chan);
+
+ if (r == 0) {
+ /* Timeout */
+ continue;
+ }
+
+ f = ast_read(chan);
if (!f) {
ast_debug(3, "%s: No more frames. Must be done, I guess.\n", ast_channel_uniqueid(chan));
break;
@@ -468,8 +628,6 @@
switch (f->frametype) {
case AST_FRAME_CONTROL:
if (f->subclass.integer == AST_CONTROL_HANGUP) {
- ast_debug(3, "%s: Received hangup\n",
- ast_channel_uniqueid(chan));
hungup = 1;
}
break;
Copied: trunk/res/res_stasis_http.exports.in (from r386231, team/dlee/stasis-http/res/res_stasis_http.exports.in)
URL: http://svnview.digium.com/svn/asterisk/trunk/res/res_stasis_http.exports.in?view=diff&rev=386232&p1=team/dlee/stasis-http/res/res_stasis_http.exports.in&r1=386231&p2=trunk/res/res_stasis_http.exports.in&r2=386232
==============================================================================
--- team/dlee/stasis-http/res/res_stasis_http.exports.in (original)
+++ trunk/res/res_stasis_http.exports.in Mon Apr 22 09:58:53 2013
@@ -1,9 +1,6 @@
{
global:
- LINKER_SYMBOL_PREFIXstasis_http_get_api;
- LINKER_SYMBOL_PREFIXstasis_set_root_handler;
- LINKER_SYMBOL_PREFIXstasis_http_invoke;
- LINKER_SYMBOL_PREFIXstasis_http_get_docs;
+ LINKER_SYMBOL_PREFIXstasis_http_*;
local:
*;
};
Propchange: trunk/res/stasis_http/
------------------------------------------------------------------------------
--- svn:ignore (added)
+++ svn:ignore Mon Apr 22 09:58:53 2013
@@ -1,0 +1,14 @@
+*.o
+*.a
+*.d
+*.eo
+*.eoo
+*.i
+*.makeopts
+*.moduleinfo
+*.s
+*.so
+*.exports
+modules.link
+*.gcno
+*.gcda
Modified: trunk/res/stasis_http/resource_channels.c
URL: http://svnview.digium.com/svn/asterisk/trunk/res/stasis_http/resource_channels.c?view=diff&rev=386232&r1=386231&r2=386232
==============================================================================
--- trunk/res/stasis_http/resource_channels.c (original)
+++ trunk/res/stasis_http/resource_channels.c Mon Apr 22 09:58:53 2013
@@ -123,6 +123,18 @@
{
ast_log(LOG_ERROR, "TODO: stasis_http_unmute_channel\n");
}
+void stasis_http_hold_channel(struct ast_variable *headers, struct ast_hold_channel_args *args, struct stasis_http_response *response)
+{
+ ast_log(LOG_ERROR, "TODO: stasis_http_hold_channel\n");
+}
+void stasis_http_unhold_channel(struct ast_variable *headers, struct ast_unhold_channel_args *args, struct stasis_http_response *response)
+{
+ ast_log(LOG_ERROR, "TODO: stasis_http_unhold_channel\n");
+}
+void stasis_http_play_on_channel(struct ast_variable *headers, struct ast_play_on_channel_args *args, struct stasis_http_response *response)
+{
+ ast_log(LOG_ERROR, "TODO: stasis_http_play_on_channel\n");
+}
void stasis_http_record_channel(struct ast_variable *headers, struct ast_record_channel_args *args, struct stasis_http_response *response)
{
ast_log(LOG_ERROR, "TODO: stasis_http_record_channel\n");
Propchange: trunk/rest-api-templates/
------------------------------------------------------------------------------
--- svn:ignore (added)
+++ svn:ignore Mon Apr 22 09:58:53 2013
@@ -1,0 +1,1 @@
+*.pyc
Modified: trunk/tests/test_stasis.c
URL: http://svnview.digium.com/svn/asterisk/trunk/tests/test_stasis.c?view=diff&rev=386232&r1=386231&r2=386232
==============================================================================
--- trunk/tests/test_stasis.c (original)
+++ trunk/tests/test_stasis.c Mon Apr 22 09:58:53 2013
@@ -645,7 +645,6 @@
RAII_VAR(struct stasis_message *, test_message1_clear, NULL, ao2_cleanup);
int actual_len;
struct stasis_cache_update *actual_update;
- struct ao2_container *cache_dump;
switch (cmd) {
case TEST_INIT:
@@ -681,12 +680,6 @@
actual_len = consumer_wait_for(consumer, 2);
ast_test_validate(test, 2 == actual_len);
- /* Dump the cache to ensure that it has the correct number of items in it */
- cache_dump = stasis_cache_dump(caching_topic, NULL);
- ast_test_validate(test, 2 == ao2_container_count(cache_dump));
- ao2_ref(cache_dump, -1);
- cache_dump = NULL;
-
/* Check for new snapshot messages */
ast_test_validate(test, stasis_cache_update_type() == stasis_message_type(consumer->messages_rxed[0]));
actual_update = stasis_message_data(consumer->messages_rxed[0]);
@@ -722,12 +715,6 @@
/* stasis_cache_get returned a ref, so unref test_message2_2 */
ao2_ref(test_message2_2, -1);
- /* Dump the cache to ensure that it has the correct number of items in it */
- cache_dump = stasis_cache_dump(caching_topic, NULL);
- ast_test_validate(test, 2 == ao2_container_count(cache_dump));
- ao2_ref(cache_dump, -1);
- cache_dump = NULL;
-
/* Clear snapshot 1 */
test_message1_clear = stasis_cache_clear_create(cache_type, "1");
ast_test_validate(test, NULL != test_message1_clear);
@@ -742,17 +729,109 @@
ast_test_validate(test, NULL == actual_update->new_snapshot);
ast_test_validate(test, NULL == stasis_cache_get(caching_topic, cache_type, "1"));
- /* Dump the cache to ensure that it has the correct number of items in it */
+ return AST_TEST_PASS;
+}
+
+AST_TEST_DEFINE(cache_dump)
+{
+ RAII_VAR(struct stasis_message_type *, cache_type, NULL, ao2_cleanup);
+ RAII_VAR(struct stasis_topic *, topic, NULL, ao2_cleanup);
+ RAII_VAR(struct stasis_caching_topic *, caching_topic, NULL, stasis_caching_unsubscribe);
+ RAII_VAR(struct consumer *, consumer, NULL, ao2_cleanup);
+ RAII_VAR(struct stasis_subscription *, sub, NULL, stasis_unsubscribe);
+ RAII_VAR(struct stasis_message *, test_message1_1, NULL, ao2_cleanup);
+ RAII_VAR(struct stasis_message *, test_message2_1, NULL, ao2_cleanup);
+ RAII_VAR(struct stasis_message *, test_message2_2, NULL, ao2_cleanup);
+ RAII_VAR(struct stasis_message *, test_message1_clear, NULL, ao2_cleanup);
+ RAII_VAR(struct ao2_container *, cache_dump, NULL, ao2_cleanup);
+ int actual_len;
+ struct ao2_iterator i;
+ void *obj;
+
+ switch (cmd) {
+ case TEST_INIT:
+ info->name = __func__;
+ info->category = test_category;
+ info->summary = "Test passing messages through cache topic unscathed.";
+ info->description = "Test passing messages through cache topic unscathed.";
+ return AST_TEST_NOT_RUN;
+ case TEST_EXECUTE:
+ break;
+ }
+
+ cache_type = stasis_message_type_create("Cacheable");
+ ast_test_validate(test, NULL != cache_type);
+ topic = stasis_topic_create("SomeTopic");
+ ast_test_validate(test, NULL != topic);
+ caching_topic = stasis_caching_topic_create(topic, cache_test_data_id);
+ ast_test_validate(test, NULL != caching_topic);
+ consumer = consumer_create(1);
+ ast_test_validate(test, NULL != consumer);
+ sub = stasis_subscribe(stasis_caching_get_topic(caching_topic), consumer_exec, consumer);
+ ast_test_validate(test, NULL != sub);
+ ao2_ref(consumer, +1);
+
+ test_message1_1 = cache_test_message_create(cache_type, "1", "1");
+ ast_test_validate(test, NULL != test_message1_1);
+ test_message2_1 = cache_test_message_create(cache_type, "2", "1");
+ ast_test_validate(test, NULL != test_message2_1);
+
+ /* Post a couple of snapshots */
+ stasis_publish(topic, test_message1_1);
+ stasis_publish(topic, test_message2_1);
+ actual_len = consumer_wait_for(consumer, 2);
+ ast_test_validate(test, 2 == actual_len);
+
+ /* Check the cache */
cache_dump = stasis_cache_dump(caching_topic, NULL);
+ ast_test_validate(test, NULL != cache_dump);
+ ast_test_validate(test, 2 == ao2_container_count(cache_dump));
+ i = ao2_iterator_init(cache_dump, 0);
+ while ((obj = ao2_iterator_next(&i))) {
+ RAII_VAR(struct stasis_message *, actual_cache_entry, obj, ao2_cleanup);
+ ast_test_validate(test, actual_cache_entry == test_message1_1 || actual_cache_entry == test_message2_1);
+ }
+
+ /* Update snapshot 2 */
+ test_message2_2 = cache_test_message_create(cache_type, "2", "2");
+ ast_test_validate(test, NULL != test_message2_2);
+ stasis_publish(topic, test_message2_2);
+
+ actual_len = consumer_wait_for(consumer, 3);
+ ast_test_validate(test, 3 == actual_len);
+
+ /* Check the cache */
+ cache_dump = stasis_cache_dump(caching_topic, NULL);
+ ast_test_validate(test, NULL != cache_dump);
+ ast_test_validate(test, 2 == ao2_container_count(cache_dump));
+ i = ao2_iterator_init(cache_dump, 0);
+ while ((obj = ao2_iterator_next(&i))) {
+ RAII_VAR(struct stasis_message *, actual_cache_entry, obj, ao2_cleanup);
+ ast_test_validate(test, actual_cache_entry == test_message1_1 || actual_cache_entry == test_message2_2);
+ }
+
+ /* Clear snapshot 1 */
+ test_message1_clear = stasis_cache_clear_create(cache_type, "1");
+ ast_test_validate(test, NULL != test_message1_clear);
+ stasis_publish(topic, test_message1_clear);
+
+ actual_len = consumer_wait_for(consumer, 4);
+ ast_test_validate(test, 4 == actual_len);
+
+ /* Check the cache */
+ cache_dump = stasis_cache_dump(caching_topic, NULL);
+ ast_test_validate(test, NULL != cache_dump);
ast_test_validate(test, 1 == ao2_container_count(cache_dump));
- ao2_ref(cache_dump, -1);
- cache_dump = NULL;
+ i = ao2_iterator_init(cache_dump, 0);
+ while ((obj = ao2_iterator_next(&i))) {
+ RAII_VAR(struct stasis_message *, actual_cache_entry, obj, ao2_cleanup);
+ ast_test_validate(test, actual_cache_entry == test_message2_2);
+ }
/* Dump the cache to ensure that it has no subscription change items in it since those aren't cached */
+ ao2_cleanup(cache_dump);
cache_dump = stasis_cache_dump(caching_topic, stasis_subscription_change_type());
ast_test_validate(test, 0 == ao2_container_count(cache_dump));
- ao2_ref(cache_dump, -1);
- cache_dump = NULL;
return AST_TEST_PASS;
}
@@ -909,6 +988,7 @@
AST_TEST_UNREGISTER(forward);
AST_TEST_UNREGISTER(cache_passthrough);
AST_TEST_UNREGISTER(cache);
+ AST_TEST_UNREGISTER(cache_dump);
AST_TEST_UNREGISTER(route_conflicts);
AST_TEST_UNREGISTER(router);
AST_TEST_UNREGISTER(interleaving);
@@ -925,6 +1005,7 @@
AST_TEST_REGISTER(forward);
AST_TEST_REGISTER(cache_passthrough);
AST_TEST_REGISTER(cache);
+ AST_TEST_REGISTER(cache_dump);
AST_TEST_REGISTER(route_conflicts);
AST_TEST_REGISTER(router);
AST_TEST_REGISTER(interleaving);
Modified: trunk/tests/test_strings.c
URL: http://svnview.digium.com/svn/asterisk/trunk/tests/test_strings.c?view=diff&rev=386232&r1=386231&r2=386232
==============================================================================
--- trunk/tests/test_strings.c (original)
+++ trunk/tests/test_strings.c Mon Apr 22 09:58:53 2013
@@ -251,15 +251,78 @@
return res;
}
+AST_TEST_DEFINE(begins_with_test)
+{
+ switch (cmd) {
+ case TEST_INIT:
+ info->name = "begins_with";
+ info->category = "/main/strings/";
+ info->summary = "Test ast_begins_with";
+ info->description = "Test ast_begins_with";
+ return AST_TEST_NOT_RUN;
+ case TEST_EXECUTE:
+ break;
+ }
+
+ // prefixes
+ ast_test_validate(test, 1 == ast_begins_with("foobar", "foobar"));
+ ast_test_validate(test, 1 == ast_begins_with("foobar", "foo"));
+ ast_test_validate(test, 1 == ast_begins_with("foobar", ""));
+ ast_test_validate(test, 1 == ast_begins_with("", ""));
+
+ // not prefixes
+ ast_test_validate(test, 0 == ast_begins_with("foobar", "bang"));
+ ast_test_validate(test, 0 == ast_begins_with("foobar", "foobat"));
+ ast_test_validate(test, 0 == ast_begins_with("boo", "boom"));
+ ast_test_validate(test, 0 == ast_begins_with("", "blitz"));
+
+ // nothing failed; we're all good!
+ return AST_TEST_PASS;
+}
+
+AST_TEST_DEFINE(ends_with_test)
+{
+ switch (cmd) {
+ case TEST_INIT:
+ info->name = "ends_with";
+ info->category = "/main/strings/";
+ info->summary = "Test ast_ends_with";
+ info->description = "Test ast_ends_with";
+ return AST_TEST_NOT_RUN;
+ case TEST_EXECUTE:
+ break;
+ }
+
+ // prefixes
+ ast_test_validate(test, 1 == ast_ends_with("foobar", "foobar"));
+ ast_test_validate(test, 1 == ast_ends_with("foobar", "bar"));
+ ast_test_validate(test, 1 == ast_ends_with("foobar", ""));
+ ast_test_validate(test, 1 == ast_ends_with("", ""));
+
+ // not suffixes
+ ast_test_validate(test, 0 == ast_ends_with("bar", "bbar"));
+ ast_test_validate(test, 0 == ast_ends_with("foobar", "bang"));
+ ast_test_validate(test, 0 == ast_ends_with("foobar", "foobat"));
+ ast_test_validate(test, 0 == ast_ends_with("boo", "boom"));
+ ast_test_validate(test, 0 == ast_ends_with("", "blitz"));
+
+ // nothing failed; we're all good!
+ return AST_TEST_PASS;
+}
+
static int unload_module(void)
{
AST_TEST_UNREGISTER(str_test);
+ AST_TEST_UNREGISTER(begins_with_test);
+ AST_TEST_UNREGISTER(ends_with_test);
return 0;
}
static int load_module(void)
{
AST_TEST_REGISTER(str_test);
+ AST_TEST_REGISTER(begins_with_test);
+ AST_TEST_REGISTER(ends_with_test);
return AST_MODULE_LOAD_SUCCESS;
}
More information about the asterisk-commits
mailing list