[asterisk-commits] mjordan: branch mjordan/trunk-http-stuff-and-things r431402 - in /team/mjorda...
SVN commits to the Asterisk project
asterisk-commits at lists.digium.com
Thu Jan 29 08:38:35 CST 2015
Author: mjordan
Date: Thu Jan 29 08:38:23 2015
New Revision: 431402
URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=431402
Log:
Add some initial media caching stuff
- Add the media cache in the core. This acts as a thin wrapper over some bucket
implementation, which must be provided by other modules. Mostly, it provides
the actual in-memory cache of the media files and their metadata.
- Add some unit tests for the media cache core.
- Add a skeleton module for the HTTP retrieval of items for the media cache.
Mostly this just has 'create' callback implemented (although currently it
has only been compile checked).
Added:
team/mjordan/trunk-http-stuff-and-things/include/asterisk/media_cache.h (with props)
team/mjordan/trunk-http-stuff-and-things/main/media_cache.c (with props)
team/mjordan/trunk-http-stuff-and-things/res/res_http_media_cache.c (with props)
team/mjordan/trunk-http-stuff-and-things/tests/test_media_cache.c (with props)
Modified:
team/mjordan/trunk-http-stuff-and-things/main/asterisk.c
team/mjordan/trunk-http-stuff-and-things/main/bucket.c
team/mjordan/trunk-http-stuff-and-things/res/res_curl.c
Added: team/mjordan/trunk-http-stuff-and-things/include/asterisk/media_cache.h
URL: http://svnview.digium.com/svn/asterisk/team/mjordan/trunk-http-stuff-and-things/include/asterisk/media_cache.h?view=auto&rev=431402
==============================================================================
--- team/mjordan/trunk-http-stuff-and-things/include/asterisk/media_cache.h (added)
+++ team/mjordan/trunk-http-stuff-and-things/include/asterisk/media_cache.h Thu Jan 29 08:38:23 2015
@@ -1,0 +1,59 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2014, Digium, Inc.
+ *
+ * Matt Jordan <mjordan 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 AGI Extension interfaces - Asterisk Gateway Interface
+ */
+
+#ifndef _ASTERISK_MEDIA_CACHE_H
+#define _ASTERISK_MEDIA_CACHE_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+struct ast_variable;
+
+/*!
+ * \brief
+ *
+ * \param uri
+ *
+ * \retval 0 uri does not exist in cache
+ * \retval 1 uri does exist in cache
+ */
+int ast_media_cache_exists(const char *uri);
+
+int ast_media_cache_retrieve(const char *uri, const char *preferred_file_name,
+ char *file_path, size_t len);
+
+int ast_media_cache_retrieve_metadata(const char *uri, const char *key,
+ char *value, size_t len);
+
+int ast_media_cache_create_or_update(const char *uri, const char *file_path,
+ struct ast_variable *metadata);
+
+int ast_media_cache_delete(const char *uri);
+
+int ast_media_cache_init(void);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif /* _ASTERISK_MEDIA_CACHE_H */
Propchange: team/mjordan/trunk-http-stuff-and-things/include/asterisk/media_cache.h
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: team/mjordan/trunk-http-stuff-and-things/include/asterisk/media_cache.h
------------------------------------------------------------------------------
svn:keywords = Author Date Id Revision
Propchange: team/mjordan/trunk-http-stuff-and-things/include/asterisk/media_cache.h
------------------------------------------------------------------------------
svn:mime-type = text/plain
Modified: team/mjordan/trunk-http-stuff-and-things/main/asterisk.c
URL: http://svnview.digium.com/svn/asterisk/team/mjordan/trunk-http-stuff-and-things/main/asterisk.c?view=diff&rev=431402&r1=431401&r2=431402
==============================================================================
--- team/mjordan/trunk-http-stuff-and-things/main/asterisk.c (original)
+++ team/mjordan/trunk-http-stuff-and-things/main/asterisk.c Thu Jan 29 08:38:23 2015
@@ -250,6 +250,7 @@
#include "asterisk/endpoints.h"
#include "asterisk/codec.h"
#include "asterisk/format_cache.h"
+#include "asterisk/media_cache.h"
#include "../defaults.h"
@@ -4469,6 +4470,11 @@
exit(1);
}
+ if (ast_media_cache_init()) {
+ printf("%s", term_quit());
+ exit(1);
+ }
+
if ((moduleresult = load_modules(1))) { /* Load modules, pre-load only */
printf("%s", term_quit());
exit(moduleresult == -2 ? 2 : 1);
Modified: team/mjordan/trunk-http-stuff-and-things/main/bucket.c
URL: http://svnview.digium.com/svn/asterisk/team/mjordan/trunk-http-stuff-and-things/main/bucket.c?view=diff&rev=431402&r1=431401&r2=431402
==============================================================================
--- team/mjordan/trunk-http-stuff-and-things/main/bucket.c (original)
+++ team/mjordan/trunk-http-stuff-and-things/main/bucket.c Thu Jan 29 08:38:23 2015
@@ -258,7 +258,7 @@
if (ast_strlen_zero(name) || !bucket || !file ||
!bucket->create || !bucket->delete || !bucket->retrieve_id ||
- !create_cb) {
+ (!bucket->create && !create_cb)) {
return -1;
}
@@ -679,7 +679,7 @@
ast_string_field_set(file, scheme, uri_scheme);
- if (scheme->create(file)) {
+ if (scheme->create && scheme->create(file)) {
ao2_ref(file, -1);
return NULL;
}
Added: team/mjordan/trunk-http-stuff-and-things/main/media_cache.c
URL: http://svnview.digium.com/svn/asterisk/team/mjordan/trunk-http-stuff-and-things/main/media_cache.c?view=auto&rev=431402
==============================================================================
--- team/mjordan/trunk-http-stuff-and-things/main/media_cache.c (added)
+++ team/mjordan/trunk-http-stuff-and-things/main/media_cache.c Thu Jan 29 08:38:23 2015
@@ -1,0 +1,374 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) <Year>, <Your Name Here>
+ *
+ * <Your Name Here> <<Your Email Here>>
+ *
+ * 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 media_cache
+ *
+ * \author \verbatim <Your Name Here> <<Your Email Here>> \endverbatim
+ *
+ * This is a skeleton for development of an Asterisk test module
+ * \ingroup tests
+ */
+
+/*** MODULEINFO
+ <depend>TEST_FRAMEWORK</depend>
+ <support_level>core</support_level>
+ ***/
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <sys/stat.h>
+#include "asterisk/config.h"
+#include "asterisk/bucket.h"
+#include "asterisk/astdb.h"
+#include "asterisk/media_cache.h"
+
+#define AST_DB_FAMILY "MediaCache"
+
+/* Length of 'MediaCache' + 2 '/' characters */
+#define AST_DB_FAMILY_LEN 12
+
+#define AO2_BUCKETS 61
+
+static struct ao2_container *media_cache;
+
+/*! \brief Hashing function for file metadata */
+static int media_cache_hash(const void *obj, const int flags)
+{
+ const struct ast_bucket_file *object;
+ const char *key;
+
+ switch (flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
+ case OBJ_KEY:
+ key = obj;
+ return ast_str_hash(key);
+ case OBJ_POINTER:
+ object = obj;
+ return ast_str_hash(ast_sorcery_object_get_id(object));
+ default:
+ /* Hash can only work on something with a full key */
+ ast_assert(0);
+ return 0;
+ }
+}
+
+/*! \brief Comparison function for file metadata */
+static int media_cache_cmp(void *obj, void *arg, int flags)
+{
+ struct ast_bucket_file *left = obj;
+ struct ast_bucket_file *right = arg;
+ const char *name = arg;
+
+ return !strcmp(ast_sorcery_object_get_id(left), flags & OBJ_KEY ?
+ name : ast_sorcery_object_get_id(right)) ? CMP_MATCH | CMP_STOP : 0;
+}
+
+
+int ast_media_cache_exists(const char *uri)
+{
+ struct ast_bucket_file *bucket_file;
+
+ if (ast_strlen_zero(uri)) {
+ return 0;
+ }
+
+ bucket_file = ast_bucket_file_retrieve(uri);
+ if (bucket_file) {
+ ao2_ref(bucket_file, -1);
+ return 1;
+ }
+
+ return 0;
+}
+
+static void metadata_sync_to_astdb(struct ast_bucket_file *bucket_file,
+ const char *hash, const char *name)
+{
+ struct ast_bucket_metadata *metadata;
+
+ metadata = ast_bucket_file_metadata_get(bucket_file, name);
+ if (metadata) {
+ ast_db_put(hash, metadata->name, metadata->value);
+ ao2_ref(metadata, -1);
+ }
+}
+
+static void media_cache_item_sync_to_astdb(struct ast_bucket_file *bucket_file)
+{
+ char hash[41]; /* 40 character SHA1 hash */
+
+ ast_sha1_hash(hash, ast_sorcery_object_get_id(bucket_file));
+ if (ast_db_put(AST_DB_FAMILY, ast_sorcery_object_get_id(bucket_file), hash)) {
+ return;
+ }
+
+ ast_db_put(hash, "path", bucket_file->path);
+ metadata_sync_to_astdb(bucket_file, hash, "size");
+ metadata_sync_to_astdb(bucket_file, hash, "ext");
+ metadata_sync_to_astdb(bucket_file, hash, "accessed");
+}
+
+static void media_cache_item_del_from_astdb(struct ast_bucket_file *bucket_file)
+{
+ char *hash_value;
+
+ if (ast_db_get_allocated(AST_DB_FAMILY, ast_sorcery_object_get_id(bucket_file), &hash_value)) {
+ return;
+ }
+
+ ast_db_deltree(hash_value, NULL);
+ ast_db_del(AST_DB_FAMILY, hash_value);
+ ast_free(hash_value);
+}
+
+int ast_media_cache_retrieve(const char *uri, const char *preferred_file_name,
+ char *file_path, size_t len)
+{
+ struct ast_bucket_file *bucket_file;
+ SCOPED_AO2LOCK(media_lock, media_cache);
+
+ if (ast_strlen_zero(uri)) {
+ return -1;
+ }
+
+ bucket_file = ao2_find(media_cache, uri, OBJ_KEY | OBJ_NOLOCK);
+ if (bucket_file) {
+ ao2_lock(bucket_file);
+ ast_bucket_file_update(bucket_file);
+ ast_copy_string(file_path, bucket_file->path, len);
+ media_cache_item_sync_to_astdb(bucket_file);
+ ao2_unlock(bucket_file);
+ ao2_ref(bucket_file, -1);
+ return 0;
+ }
+
+ bucket_file = ast_bucket_file_retrieve(uri);
+ if (!bucket_file) {
+ bucket_file = ast_bucket_file_alloc(uri);
+ if (!bucket_file) {
+ ast_log(LOG_WARNING, "Failed to create storage for %s\n", uri);
+ return -1;
+ }
+
+ if (!ast_strlen_zero(preferred_file_name)) {
+ ast_copy_string(bucket_file->path, preferred_file_name,
+ sizeof(bucket_file->path));
+ } else if (ast_bucket_file_temporary_create(bucket_file)) {
+ ast_log(LOG_WARNING, "Failed to create temp storage for %s\n", uri);
+ ao2_ref(bucket_file, -1);
+ return -1;
+ }
+
+ if (ast_bucket_file_create(bucket_file)) {
+ ast_log(LOG_WARNING, "Failed to obtain media at %s\n", uri);
+ ao2_ref(bucket_file, -1);
+ return -1;
+ }
+ } else if (!ast_strlen_zero(preferred_file_name)) {
+ ao2_lock(bucket_file);
+ rename(bucket_file->path, preferred_file_name);
+ ast_copy_string(bucket_file->path, preferred_file_name,
+ sizeof(bucket_file->path));
+ ao2_unlock(bucket_file);
+ }
+ ast_copy_string(file_path, bucket_file->path, len);
+ media_cache_item_sync_to_astdb(bucket_file);
+ ao2_link_flags(media_cache, bucket_file, OBJ_NOLOCK);
+
+ ao2_ref(bucket_file, -1);
+
+ return 0;
+}
+
+int ast_media_cache_retrieve_metadata(const char *uri, const char *key,
+ char *value, size_t len)
+{
+ struct ast_bucket_file *bucket_file;
+ struct ast_bucket_metadata *metadata;
+
+ if (ast_strlen_zero(uri) || ast_strlen_zero(key) || !value) {
+ return -1;
+ }
+
+ bucket_file = ao2_find(media_cache, uri, OBJ_KEY);
+ if (!bucket_file) {
+ return -1;
+ }
+
+ metadata = ao2_find(bucket_file->metadata, key, OBJ_KEY);
+ if (!metadata) {
+ ao2_ref(bucket_file, -1);
+ return -1;
+ }
+ ast_copy_string(value, metadata->value, len);
+
+ ao2_ref(metadata, -1);
+ ao2_ref(bucket_file, -1);
+ return 0;
+}
+
+int ast_media_cache_create_or_update(const char *uri, const char *file_path,
+ struct ast_variable *metadata)
+{
+ struct ast_bucket_file *bucket_file;
+ struct stat st;
+ char tmp[128];
+ char *ext;
+ char *file_path_ptr;
+ struct ast_variable *it_metadata;
+ SCOPED_AO2LOCK(media_lock, media_cache);
+
+ if (ast_strlen_zero(file_path) || ast_strlen_zero(uri)) {
+ return -1;
+ }
+ file_path_ptr = ast_strdupa(file_path);
+
+ if (stat(file_path, &st)) {
+ ast_log(LOG_WARNING, "Unable to obtain information for file %s for URI %s\n",
+ file_path, uri);
+ return -1;
+ }
+
+ bucket_file = ao2_find(media_cache, uri, OBJ_KEY | OBJ_NOLOCK);
+ if (!bucket_file) {
+ bucket_file = ast_bucket_file_retrieve(uri);
+ if (!bucket_file) {
+ bucket_file = ast_bucket_file_alloc(uri);
+ if (!bucket_file) {
+ ast_log(LOG_WARNING, "Failed to create file storage for %s and %s\n",
+ uri, file_path);
+ return -1;
+ }
+ }
+ ao2_link_flags(media_cache, bucket_file, OBJ_NOLOCK);
+ }
+
+ ao2_lock(bucket_file);
+ strcpy(bucket_file->path, file_path);
+ bucket_file->created.tv_sec = st.st_ctime;
+ bucket_file->modified.tv_sec = st.st_mtime;
+
+ snprintf(tmp, sizeof(tmp), "%ld", (long)st.st_atime);
+ ast_bucket_file_metadata_set(bucket_file, "accessed", tmp);
+
+ snprintf(tmp, sizeof(tmp), "%zu", st.st_size);
+ ast_bucket_file_metadata_set(bucket_file, "size", tmp);
+
+ ext = strrchr(file_path_ptr, '.');
+ if (ext) {
+ ast_bucket_file_metadata_set(bucket_file, "ext", ext + 1);
+ }
+
+ for (it_metadata = metadata; it_metadata; it_metadata = it_metadata->next) {
+ ast_bucket_file_metadata_set(bucket_file, it_metadata->name, it_metadata->value);
+ }
+
+ if (ast_bucket_file_create(bucket_file)) {
+ ao2_unlock(bucket_file);
+ ast_log(LOG_WARNING, "Failed to create media for %s\n", uri);
+ ao2_ref(bucket_file, -1);
+ return -1;
+ }
+ media_cache_item_sync_to_astdb(bucket_file);
+ ao2_unlock(bucket_file);
+
+ ao2_ref(bucket_file, -1);
+ return 0;
+}
+
+int ast_media_cache_delete(const char *uri)
+{
+ struct ast_bucket_file *bucket_file;
+ int res;
+
+ if (ast_strlen_zero(uri)) {
+ return -1;
+ }
+
+ bucket_file = ao2_find(media_cache, uri, OBJ_KEY | OBJ_UNLINK);
+ if (!bucket_file) {
+ return -1;
+ }
+
+ res = ast_bucket_file_delete(bucket_file);
+ media_cache_item_del_from_astdb(bucket_file);
+
+ ao2_ref(bucket_file, -1);
+
+ return res;
+}
+
+static void media_cache_shutdown(void)
+{
+ ao2_ref(media_cache, -1);
+ media_cache = NULL;
+}
+
+static void media_cache_remove_from_astdb(const char *uri, const char *hash)
+{
+ ast_db_del(AST_DB_FAMILY, uri + AST_DB_FAMILY_LEN);
+ ast_db_deltree(hash, NULL);
+}
+
+static int media_cache_item_populate_from_astdb(const char *uri, const char *hash)
+{
+ char path[PATH_MAX];
+
+ if (ast_db_get(hash, "path", path, sizeof(path))) {
+ ast_log(LOG_WARNING, "Failed to restore media cache item for '%s' from AstDB: no 'path' specified\n",
+ uri);
+ return -1;
+ }
+
+ /* TODO: do we care about the metadata stored, since we are about to force
+ a refresh?
+ */
+ return ast_media_cache_create_or_update(uri + AST_DB_FAMILY_LEN, path, NULL);
+}
+
+static void media_cache_populate_from_astdb(void)
+{
+ struct ast_db_entry *db_entry;
+ struct ast_db_entry *db_tree;
+
+ db_tree = ast_db_gettree(AST_DB_FAMILY, NULL);
+ for (db_entry = db_tree; db_entry; db_entry = db_entry->next) {
+ if (media_cache_item_populate_from_astdb(db_entry->key, db_entry->data)) {
+ media_cache_remove_from_astdb(db_entry->key, db_entry->data);
+ }
+ }
+ ast_db_freetree(db_tree);
+}
+
+int ast_media_cache_init(void)
+{
+ ast_register_atexit(media_cache_shutdown);
+
+ media_cache = ao2_container_alloc_options(AO2_ALLOC_OPT_LOCK_RWLOCK, AO2_BUCKETS,
+ media_cache_hash, media_cache_cmp);
+ if (!media_cache) {
+ return -1;
+ }
+
+ media_cache_populate_from_astdb();
+
+ return 0;
+}
Propchange: team/mjordan/trunk-http-stuff-and-things/main/media_cache.c
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: team/mjordan/trunk-http-stuff-and-things/main/media_cache.c
------------------------------------------------------------------------------
svn:keywords = Author Date Id Revision
Propchange: team/mjordan/trunk-http-stuff-and-things/main/media_cache.c
------------------------------------------------------------------------------
svn:mime-type = text/plain
Modified: team/mjordan/trunk-http-stuff-and-things/res/res_curl.c
URL: http://svnview.digium.com/svn/asterisk/team/mjordan/trunk-http-stuff-and-things/res/res_curl.c?view=diff&rev=431402&r1=431401&r2=431402
==============================================================================
--- team/mjordan/trunk-http-stuff-and-things/res/res_curl.c (original)
+++ team/mjordan/trunk-http-stuff-and-things/res/res_curl.c Thu Jan 29 08:38:23 2015
@@ -51,6 +51,7 @@
static const char *dependents[] = {
"func_curl.so",
"res_config_curl.so",
+ "res_http_media_cache.so",
};
static int unload_module(void)
Added: team/mjordan/trunk-http-stuff-and-things/res/res_http_media_cache.c
URL: http://svnview.digium.com/svn/asterisk/team/mjordan/trunk-http-stuff-and-things/res/res_http_media_cache.c?view=auto&rev=431402
==============================================================================
--- team/mjordan/trunk-http-stuff-and-things/res/res_http_media_cache.c (added)
+++ team/mjordan/trunk-http-stuff-and-things/res/res_http_media_cache.c Thu Jan 29 08:38:23 2015
@@ -1,0 +1,219 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2015, Matt Jordan
+ *
+ * Matt Jordan <mjordan 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
+ *
+ * \author\verbatim <Your Name Here> <<Your Email Here>> \endverbatim
+ *
+ * This is a skeleton for development of an Asterisk test module
+ * \ingroup tests
+ */
+
+/*** MODULEINFO
+ <depend>curl</depend>
+ <depend>res_curl</depend>
+ <support_level>core</support_level>
+ ***/
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <curl/curl.h>
+
+#include "asterisk/module.h"
+#include "asterisk/bucket.h"
+#include "asterisk/sorcery.h"
+
+#define GLOBAL_USERAGENT "asterisk-libcurl-agent/1.0"
+
+struct curl_bucket_file_data {
+ struct ast_bucket_file *bucket_file;
+ FILE *out_file;
+};
+
+static size_t curl_header_callback(char *buffer, size_t size, size_t nitems, void *data)
+{
+ struct curl_bucket_file_data *cb_data = data;
+ size_t realsize;
+ size_t offset;
+ size_t value_len;
+ char *value;
+ char *dupd_value;
+ char *clean_value;
+
+ realsize = size * nitems;
+
+ /* buffer may not be NULL terminated */
+ value = memchr(buffer, ':', realsize);
+ if (!value) {
+ ast_log(LOG_WARNING, "Failed to split received header in cURL request\n");
+ return 0;
+ }
+ offset = value - buffer;
+ value_len = realsize - offset;
+ *value++ = '\0';
+
+ if (strcmp(buffer, "ETag")
+ && strcmp(buffer, "Cache-Control")
+ && strcmp(buffer, "Last-Modified")) {
+ return realsize;
+ }
+
+ dupd_value = ast_malloc(value_len + 1);
+ if (!dupd_value) {
+ return 0;
+ }
+ strncpy(dupd_value, value, value_len);
+ dupd_value[value_len] = '\0';
+ clean_value = dupd_value;
+ clean_value = ast_skip_blanks(clean_value);
+ clean_value = ast_trim_blanks(clean_value);
+
+ ast_bucket_file_metadata_set(cb_data->bucket_file, buffer, clean_value);
+
+ ast_free(dupd_value);
+ return realsize;
+}
+
+static size_t curl_body_callback(void *ptr, size_t size, size_t nitems, void *data)
+{
+ struct curl_bucket_file_data *cb_data = data;
+ size_t realsize;
+
+ realsize = fwrite(ptr, size, nitems, cb_data->out_file);
+
+ return realsize;
+}
+
+static int bucket_http_wizard_create(const struct ast_sorcery *sorcery, void *data,
+ void *object)
+{
+ char curl_errbuf[CURL_ERROR_SIZE + 1]; /* add one to be safe */
+ struct ast_bucket_file *bucket_file = object;
+ const char *uri = ast_sorcery_object_get_id(bucket_file);
+ CURL *curl;
+ struct curl_bucket_file_data cb_data = {
+ .bucket_file = bucket_file,
+ };
+
+ cb_data.out_file = fopen(bucket_file->path, "wb");
+ if (!cb_data.out_file) {
+ return -1;
+ }
+
+ /* TODO:
+ * -- force a refresh by pulling down the URI
+ * -- populate the object
+ */
+ curl = curl_easy_init();
+ if (!curl) {
+ fclose(cb_data.out_file);
+ return -1;
+ }
+
+ curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
+ curl_easy_setopt(curl, CURLOPT_TIMEOUT, 180);
+ curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_body_callback);
+ curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, curl_header_callback);
+ curl_easy_setopt(curl, CURLOPT_USERAGENT, GLOBAL_USERAGENT);
+
+ curl_easy_setopt(curl, CURLOPT_URL, uri);
+ curl_easy_setopt(curl, CURLOPT_FILE, (void*)&cb_data);
+ curl_errbuf[CURL_ERROR_SIZE] = '\0';
+ curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, curl_errbuf);
+
+ if (curl_easy_perform(curl)) {
+ ast_log(LOG_WARNING, "%s ('%s')\n", curl_errbuf, uri);
+ }
+
+ fclose(cb_data.out_file);
+ return -1;
+}
+
+static int bucket_http_wizard_update(const struct ast_sorcery *sorcery, void *data,
+ void *object)
+{
+ /*if (!strcmp(ast_sorcery_object_get_id(object), VALID_RESOURCE)) {
+ return 0;
+ }*/
+
+ return -1;
+}
+
+static void *bucket_http_wizard_retrieve_id(const struct ast_sorcery *sorcery,
+ void *data, const char *type, const char *id)
+{
+ /* TODO:
+ * -- hit the provided URI and see if we need to download it
+ * -- if we do, pull it down and update the resource
+ * -- if not, simply return what's there
+ */
+ if (strcmp(type, "file")) {
+ return NULL;
+ }
+ return NULL;
+}
+
+static int bucket_http_wizard_delete(const struct ast_sorcery *sorcery, void *data,
+ void *object)
+{
+ return -1;
+}
+
+static struct ast_sorcery_wizard bucket_wizard = {
+ .name = "http",
+ .create = bucket_http_wizard_create,
+ .retrieve_id = bucket_http_wizard_retrieve_id,
+ .delete = bucket_http_wizard_delete,
+};
+
+static struct ast_sorcery_wizard bucket_file_wizard = {
+ .name = "http",
+ .create = bucket_http_wizard_create,
+ .update = bucket_http_wizard_update,
+ .retrieve_id = bucket_http_wizard_retrieve_id,
+ .delete = bucket_http_wizard_delete,
+};
+
+
+static int unload_module(void)
+{
+ return 0;
+}
+
+static int load_module(void)
+{
+ if (ast_bucket_scheme_register("http", &bucket_wizard, &bucket_file_wizard,
+ NULL, NULL)) {
+ ast_log(LOG_ERROR, "Failed to register Bucket HTTP wizard scheme implementation\n");
+ return AST_MODULE_LOAD_FAILURE;
+ }
+
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "FOOBAR!",
+ .support_level = AST_MODULE_SUPPORT_CORE,
+ .load = load_module,
+ .unload = unload_module,
+ .load_pri = AST_MODPRI_DEFAULT,
+ );
+
Propchange: team/mjordan/trunk-http-stuff-and-things/res/res_http_media_cache.c
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: team/mjordan/trunk-http-stuff-and-things/res/res_http_media_cache.c
------------------------------------------------------------------------------
svn:keywords = Author Date Id Revision
Propchange: team/mjordan/trunk-http-stuff-and-things/res/res_http_media_cache.c
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added: team/mjordan/trunk-http-stuff-and-things/tests/test_media_cache.c
URL: http://svnview.digium.com/svn/asterisk/team/mjordan/trunk-http-stuff-and-things/tests/test_media_cache.c?view=auto&rev=431402
==============================================================================
--- team/mjordan/trunk-http-stuff-and-things/tests/test_media_cache.c (added)
+++ team/mjordan/trunk-http-stuff-and-things/tests/test_media_cache.c Thu Jan 29 08:38:23 2015
@@ -1,0 +1,395 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) <Year>, <Your Name Here>
+ *
+ * <Your Name Here> <<Your Email Here>>
+ *
+ * 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 Skeleton Test
+ *
+ * \author\verbatim <Your Name Here> <<Your Email Here>> \endverbatim
+ *
+ * This is a skeleton for development of an Asterisk test module
+ * \ingroup tests
+ */
+
+/*** MODULEINFO
+ <depend>TEST_FRAMEWORK</depend>
+ <support_level>core</support_level>
+ ***/
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/utils.h"
+#include "asterisk/module.h"
+#include "asterisk/test.h"
+#include "asterisk/bucket.h"
+#include "asterisk/media_cache.h"
+
+#define CATEGORY "/main/media_cache/"
+
+#define VALID_RESOURCE "httptest://localhost:8088/test_media_cache/monekys.wav"
+
+#define INVALID_RESOURCE "httptest://localhost:8088/test_media_cache/bad.wav"
+
+#define INVALID_SCHEME "foo://localhost:8088/test_media_cache/monkeys.wav"
+
+#define NO_SCHEME "localhost:8088/test_media_cache/monkeys.wav"
+
+static int bucket_http_test_wizard_create(const struct ast_sorcery *sorcery, void *data,
+ void *object)
+{
+ if (!strcmp(ast_sorcery_object_get_id(object), VALID_RESOURCE)) {
+ return 0;
+ }
+
+ return -1;
+}
+
+static int bucket_http_test_wizard_update(const struct ast_sorcery *sorcery, void *data,
+ void *object)
+{
+ if (!strcmp(ast_sorcery_object_get_id(object), VALID_RESOURCE)) {
+ return 0;
+ }
+
+ return -1;
+}
+
+static void *bucket_http_test_wizard_retrieve_id(const struct ast_sorcery *sorcery,
+ void *data, const char *type, const char *id)
+{
+ struct ast_bucket_file *bucket_file;
+
+ if (!strcmp(type, "file") && !strcmp(id, VALID_RESOURCE)) {
+ bucket_file = ast_bucket_file_alloc(id);
+ if (!bucket_file) {
+ return NULL;
+ }
+
+ ast_bucket_file_temporary_create(bucket_file);
+ return bucket_file;
+ }
+ return NULL;
+}
+
+static int bucket_http_test_wizard_delete(const struct ast_sorcery *sorcery, void *data,
+ void *object)
+{
+ if (!strcmp(ast_sorcery_object_get_id(object), VALID_RESOURCE)) {
+ return 0;
+ }
+
+ return -1;
+}
+
+static struct ast_sorcery_wizard bucket_test_wizard = {
+ .name = "httptest",
+ .create = bucket_http_test_wizard_create,
+ .retrieve_id = bucket_http_test_wizard_retrieve_id,
+ .delete = bucket_http_test_wizard_delete,
+};
+
+static struct ast_sorcery_wizard bucket_file_test_wizard = {
+ .name = "httptest",
+ .create = bucket_http_test_wizard_create,
+ .update = bucket_http_test_wizard_update,
+ .retrieve_id = bucket_http_test_wizard_retrieve_id,
+ .delete = bucket_http_test_wizard_delete,
+};
+
+AST_TEST_DEFINE(exists_nominal)
+{
+ int res;
+
+ switch (cmd) {
+ case TEST_INIT:
+ info->name = __func__;
+ info->category = CATEGORY;
+ info->summary = "Test nominal existance of resources in the cache";
+ info->description =
+ "This test verifies that if a known resource is in the cache, "
+ "calling ast_media_cache_exists will return logical True. If "
+ "a resource does not exist, the same function call will return "
+ "logical False.\n";
+ return AST_TEST_NOT_RUN;
+ case TEST_EXECUTE:
+ break;
+ }
+
+ res = ast_media_cache_exists(INVALID_RESOURCE);
+ ast_test_validate(test, res == 0);
+
+ res = ast_media_cache_exists(VALID_RESOURCE);
+ ast_test_validate(test, res == 1);
+
+ return AST_TEST_PASS;
+}
+
+AST_TEST_DEFINE(exists_off_nominal)
+{
+ int res;
+
+ switch (cmd) {
+ case TEST_INIT:
+ info->name = __func__;
+ info->category = CATEGORY;
+ info->summary = "Test off nominal existance of resources in the cache";
+ info->description =
+ "This test verifies that checking for bad resources (NULL, bad "
+ "scheme, etc.) does not result in false positivies.\n";
+ return AST_TEST_NOT_RUN;
+ case TEST_EXECUTE:
+ break;
+ }
+
+ res = ast_media_cache_exists("");
+ ast_test_validate(test, res != 1);
+
+ res = ast_media_cache_exists(NULL);
+ ast_test_validate(test, res != 1);
+
+ res = ast_media_cache_exists(NO_SCHEME);
+ ast_test_validate(test, res != 1);
+
+ res = ast_media_cache_exists(INVALID_SCHEME);
+ ast_test_validate(test, res != 1);
+
+ return AST_TEST_PASS;
+}
+
+AST_TEST_DEFINE(create_update_nominal)
+{
+ int res;
+ char file_path[PATH_MAX];
+ char tmp_path_one[PATH_MAX] = "/tmp/test-media-cache-XXXXXX";
+ char tmp_path_two[PATH_MAX] = "/tmp/test-media-cache-XXXXXX";
+ int fd;
+
+ switch (cmd) {
+ case TEST_INIT:
+ info->name = __func__;
+ info->category = CATEGORY;
+ info->summary = "Test nominal creation/updating of a resource";
+ info->description =
+ "This test creates a resource and associates it with a file. "
+ "It then updates the resource with a new file. In both cases, "
+ "the test verifies that the resource is associated with the "
+ "file.\n";
+ return AST_TEST_NOT_RUN;
+ case TEST_EXECUTE:
+ break;
+ }
+
+ /* Create two local files to associate with a resource */
+ fd = mkstemp(tmp_path_one);
+ if (fd < 0) {
+ ast_test_status_update(test, "Failed to create first tmp file: %s\n",
+ tmp_path_one);
+ return AST_TEST_FAIL;
+ }
+ /* We don't need anything in the file */
+ close(fd);
+
+ fd = mkstemp(tmp_path_two);
+ if (fd < 0) {
+ ast_test_status_update(test, "Failed to create second tmp file: %s\n",
+ tmp_path_two);
+ return AST_TEST_FAIL;
+ }
+ close(fd);
+
+ ast_test_status_update(test, "Creating resource with %s\n", tmp_path_one);
+ res = ast_media_cache_create_or_update(VALID_RESOURCE, tmp_path_one, NULL);
+ ast_test_validate(test, res == 0);
+
+ res = ast_media_cache_retrieve(VALID_RESOURCE, NULL, file_path, PATH_MAX);
+ ast_test_status_update(test, "Got %s for first file path\n", file_path);
+ ast_test_validate(test, res == 0);
+ ast_test_validate(test, strcmp(file_path, tmp_path_one) == 0);
+
+ ast_test_status_update(test, "Creating resource with %s\n", tmp_path_two);
+ res = ast_media_cache_create_or_update(VALID_RESOURCE, tmp_path_two, NULL);
+ ast_test_validate(test, res == 0);
+
+ res = ast_media_cache_retrieve(VALID_RESOURCE, NULL, file_path, PATH_MAX);
+ ast_test_status_update(test, "Got %s for second file path\n", file_path);
+ ast_test_validate(test, res == 0);
+ ast_test_validate(test, strcmp(file_path, tmp_path_two) == 0);
+
+ unlink(tmp_path_one);
+ unlink(tmp_path_two);
+
+ return AST_TEST_PASS;
+}
+
+AST_TEST_DEFINE(create_update_off_nominal)
+{
+ int res;
+ char tmp_path[PATH_MAX] = "/tmp/test-media-cache-XXXXXX";
+ int fd;
+
+ switch (cmd) {
+ case TEST_INIT:
+ info->name = __func__;
+ info->category = CATEGORY;
+ info->summary = "Test off nominal creation/updating of a resource";
+ info->description =
+ "Test creation/updating of a resource with a variety of invalid\n"
+ "inputs.\n";
+ return AST_TEST_NOT_RUN;
+ case TEST_EXECUTE:
+ break;
+ }
+
+ /* Create two local files to associate with a resource */
+ fd = mkstemp(tmp_path);
+ if (fd < 0) {
+ ast_test_status_update(test, "Failed to create first tmp file: %s\n",
+ tmp_path);
+ return AST_TEST_FAIL;
+ }
+ /* We don't need anything in the file */
+ close(fd);
+
+ res = ast_media_cache_create_or_update(VALID_RESOURCE, NULL, NULL);
+ ast_test_validate(test, res != 0);
+
+ res = ast_media_cache_create_or_update(VALID_RESOURCE, "", NULL);
+ ast_test_validate(test, res != 0);
+
+ res = ast_media_cache_create_or_update(VALID_RESOURCE, "I don't exist", NULL);
+ ast_test_validate(test, res != 0);
+
+ res = ast_media_cache_create_or_update(INVALID_RESOURCE, tmp_path, NULL);
+ ast_test_validate(test, res != 0);
+
+ res = ast_media_cache_create_or_update(INVALID_SCHEME, tmp_path, NULL);
+ ast_test_validate(test, res != 0);
+
+ res = ast_media_cache_create_or_update(NO_SCHEME, tmp_path, NULL);
+ ast_test_validate(test, res != 0);
+
+ unlink(tmp_path);
+
+ return AST_TEST_PASS;
+}
+
+AST_TEST_DEFINE(create_update_metadata)
+{
+ int res;
+ char tmp_path[PATH_MAX] = "/tmp/test-media-cache-XXXXXX";
+ char file_path[PATH_MAX];
+ char actual_metadata[32];
+ struct ast_variable *meta_list = NULL;
+ struct ast_variable *tmp;
+ int fd;
+
+ switch (cmd) {
+ case TEST_INIT:
+ info->name = __func__;
+ info->category = CATEGORY;
+ info->summary = "Test nominal creation/updating of a resource";
+ info->description =
+ "This test creates a resource and associates it with a file. "
+ "It then updates the resource with a new file. In both cases, "
+ "the test verifies that the resource is associated with the "
+ "file.\n";
+ return AST_TEST_NOT_RUN;
+ case TEST_EXECUTE:
+ break;
+ }
+
+ /* Create two local files to associate with a resource */
+ fd = mkstemp(tmp_path);
+ if (fd < 0) {
+ ast_test_status_update(test, "Failed to create first tmp file: %s\n",
+ tmp_path);
+ return AST_TEST_FAIL;
+ }
+ /* We don't need anything in the file */
+ close(fd);
+
+ tmp = ast_variable_new("meta1", "value1", __FILE__);
+ if (!tmp) {
+ ast_test_status_update(test, "Failed to create metadata 1 for test\n");
+ return AST_TEST_FAIL;
+ }
+ ast_variable_list_append(&meta_list, tmp);
+
+ tmp = ast_variable_new("meta2", "value2", __FILE__);
+ if (!tmp) {
+ ast_test_status_update(test, "Failed to create metadata 2 for test\n");
+ return AST_TEST_FAIL;
+ }
+ ast_variable_list_append(&meta_list, tmp);
+
+ res = ast_media_cache_create_or_update(VALID_RESOURCE, tmp_path, meta_list);
+ ast_test_validate(test, res == 0);
+
+ res = ast_media_cache_retrieve(VALID_RESOURCE, NULL, file_path, PATH_MAX);
+ ast_test_status_update(test, "Got %s for second file path\n", file_path);
+ ast_test_validate(test, res == 0);
+ ast_test_validate(test, strcmp(file_path, tmp_path) == 0);
+
+ res = ast_media_cache_retrieve_metadata(VALID_RESOURCE, "meta1",
+ actual_metadata, sizeof(actual_metadata));
+ ast_test_validate(test, res == 0);
+ ast_test_validate(test, strcmp(actual_metadata, "value1") == 0);
+
+ res = ast_media_cache_retrieve_metadata(VALID_RESOURCE, "meta2",
+ actual_metadata, sizeof(actual_metadata));
+ ast_test_validate(test, res == 0);
+ ast_test_validate(test, strcmp(actual_metadata, "value2") == 0);
+
+ unlink(tmp_path);
+
+ return AST_TEST_PASS;
+}
+
+static int unload_module(void)
+{
+ AST_TEST_UNREGISTER(exists_nominal);
+ AST_TEST_UNREGISTER(exists_off_nominal);
+
+ AST_TEST_UNREGISTER(create_update_nominal);
+ AST_TEST_UNREGISTER(create_update_metadata);
+ AST_TEST_UNREGISTER(create_update_off_nominal);
+
+ return 0;
+}
+
+static int load_module(void)
+{
+ if (ast_bucket_scheme_register("httptest", &bucket_test_wizard,
+ &bucket_file_test_wizard, NULL, NULL)) {
+ ast_log(LOG_ERROR, "Failed to register Bucket HTTP test wizard scheme implementation\n");
+ return AST_MODULE_LOAD_FAILURE;
+ }
+
+ AST_TEST_REGISTER(exists_nominal);
+ AST_TEST_REGISTER(exists_off_nominal);
+
+ AST_TEST_REGISTER(create_update_nominal);
+ AST_TEST_REGISTER(create_update_metadata);
+ AST_TEST_REGISTER(create_update_off_nominal);
+
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Media Cache Tests");
Propchange: team/mjordan/trunk-http-stuff-and-things/tests/test_media_cache.c
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: team/mjordan/trunk-http-stuff-and-things/tests/test_media_cache.c
------------------------------------------------------------------------------
svn:keywords = Author Date Id Revision
Propchange: team/mjordan/trunk-http-stuff-and-things/tests/test_media_cache.c
------------------------------------------------------------------------------
svn:mime-type = text/plain
More information about the asterisk-commits
mailing list