};
typedef struct cached_url cached_url_t;
-static cached_url_t *cached_url_create(url_cache_t *cache, const char *url);
+static cached_url_t *cached_url_create(url_cache_t *cache, const char *url, const char *filename);
static void cached_url_destroy(cached_url_t *url, switch_memory_pool_t *pool);
/**
static size_t get_header_callback(void *ptr, size_t size, size_t nmemb, void *url);
static void process_cache_control_header(cached_url_t *url, char *data);
-static switch_status_t http_put(url_cache_t *cache, http_profile_t *profile, switch_core_session_t *session, const char *url, const char *filename);
+static switch_status_t http_put(url_cache_t *cache, http_profile_t *profile, switch_core_session_t *session, const char *url, const char *filename, int cache_local_file);
/**
* Queue used for clock cache replacement algorithm. This
* @param session the (optional) session uploading the file
* @param url The URL
* @param filename The file to upload
+ * @param cache_local_file true if local file should be mapped to url in cache
* @return SWITCH_STATUS_SUCCESS if successful
*/
-static switch_status_t http_put(url_cache_t *cache, http_profile_t *profile, switch_core_session_t *session, const char *url, const char *filename)
+static switch_status_t http_put(url_cache_t *cache, http_profile_t *profile, switch_core_session_t *session, const char *url, const char *filename, int cache_local_file)
{
switch_status_t status = SWITCH_STATUS_SUCCESS;
if (httpRes == 200 || httpRes == 201 || httpRes == 204) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "%s saved to %s\n", filename, url);
+ if (cache_local_file) {
+ cached_url_t *u = NULL;
+ /* save to cache */
+ url_cache_lock(cache, session);
+ u = cached_url_create(cache, url, filename);
+ u->size = file_info.st_size;
+ u->status = CACHED_URL_AVAILABLE;
+ if (url_cache_add(cache, session, u) != SWITCH_STATUS_SUCCESS) {
+ /* This error should never happen */
+ switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_CRIT, "Failed to add URL to cache!\n");
+ cached_url_destroy(u, cache->pool);
+ }
+ url_cache_unlock(cache, session);
+ }
} else {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Received HTTP error %ld trying to save %s to %s\n", httpRes, filename, url);
status = SWITCH_STATUS_GENERR;
/* Set up URL entry and add to map to prevent simultaneous downloads */
cache->misses++;
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "Cache MISS: size = %zu (%zu MB), hit ratio = %d/%d\n", cache->queue.size, cache->size / 1000000, cache->hits, cache->hits + cache->misses);
- u = cached_url_create(cache, url);
+ u = cached_url_create(cache, url, NULL);
if (url_cache_add(cache, session, u) != SWITCH_STATUS_SUCCESS) {
/* This error should never happen */
url_cache_unlock(cache, session);
}
/**
- * Create a cached URL entry
- * @param cache the cache
- * @param url the URL to cache
- * @return the cached URL
+ * Find file extension at end of URL.
+ * @return file extension or NULL if it doesn't exist
*/
-static cached_url_t *cached_url_create(url_cache_t *cache, const char *url)
+static const char *find_extension(const char *url)
{
- switch_uuid_t uuid;
- char uuid_str[SWITCH_UUID_FORMATTED_LENGTH + 1] = { 0 };
- char *filename = NULL;
- char uuid_dir[3] = { 0 };
- char *dirname = NULL;
- cached_url_t *u = NULL;
- const char *file_extension = "";
- const char *ext = NULL;
+ const char *ext;
- if (zstr(url)) {
- return NULL;
+ /* find extension on the end of URL */
+ for (ext = &url[strlen(url) - 1]; ext != url; ext--) {
+ if (*ext == '/' || *ext == '\\') {
+ break;
+ }
+ if (*ext == '.') {
+ /* found it */
+ return ++ext;
+ }
}
+ return NULL;
+}
- switch_zmalloc(u, sizeof(cached_url_t));
+/**
+ * Create a cached URL filename.
+ * @param cache the cache
+ * @param extension the filename extension
+ * @return the cached URL filename. Free when done.
+ */
+static char *cached_url_filename_create(url_cache_t *cache, const char *extension)
+{
+ char *filename;
+ char *dirname;
+ char uuid_dir[3] = { 0 };
+ switch_uuid_t uuid;
+ char uuid_str[SWITCH_UUID_FORMATTED_LENGTH + 1] = { 0 };
/* filename is constructed from UUID and is stored in cache dir (first 2 characters of UUID) */
switch_uuid_get(&uuid);
switch_uuid_format(uuid_str, &uuid);
strncpy(uuid_dir, uuid_str, 2);
dirname = switch_mprintf("%s%s%s", cache->location, SWITCH_PATH_SEPARATOR, uuid_dir);
- filename = &uuid_str[2];
/* create sub-directory if it doesn't exist */
switch_dir_make_recursive(dirname, SWITCH_DEFAULT_DIR_PERMS, cache->pool);
- /* find extension on the end of URL */
- for (ext = &url[strlen(url) - 1]; ext != url; ext--) {
- if (*ext == '/' || *ext == '\\') {
- break;
- }
- if (*ext == '.') {
- /* found it */
- file_extension = ext;
- break;
- }
+ if (!zstr(extension)) {
+ filename = switch_mprintf("%s%s%s.%s", dirname, SWITCH_PATH_SEPARATOR, &uuid_str[2], extension);
+ } else {
+ filename = switch_mprintf("%s%s%s", dirname, SWITCH_PATH_SEPARATOR, &uuid_str[2]);
}
+ free(dirname);
+ return filename;
+}
+
+/**
+ * Create a cached URL entry
+ * @param cache the cache
+ * @param url the URL to cache
+ * @param filename (optional) pre-defined local filename
+ * @return the cached URL
+ */
+static cached_url_t *cached_url_create(url_cache_t *cache, const char *url, const char *filename)
+{
+ cached_url_t *u = NULL;
+
+ if (zstr(url)) {
+ return NULL;
+ }
+
+ switch_zmalloc(u, sizeof(cached_url_t));
/* intialize cached URL */
- u->filename = switch_mprintf("%s%s%s%s", dirname, SWITCH_PATH_SEPARATOR, filename, file_extension);
+ if (zstr(filename)) {
+ u->filename = cached_url_filename_create(cache, find_extension(url));
+ } else {
+ u->filename = strdup(filename);
+ }
u->url = switch_safe_strdup(url);
u->size = 0;
u->used = 1;
u->download_time = switch_time_now();
u->max_age = cache->default_max_age;
- switch_safe_free(dirname);
-
return u;
}
profile = url_cache_http_profile_find(&gcache, switch_event_get_header(params, "profile"));
}
- status = http_put(&gcache, profile, session, url, argv[1]);
+ status = http_put(&gcache, profile, session, url, argv[1], 0);
if (status == SWITCH_STATUS_SUCCESS) {
stream->write_function(stream, "+OK\n");
} else {
*/
struct http_context {
switch_file_handle_t fh;
+ http_profile_t *profile;
+ char *local_path;
+ const char *write_url;
};
/**
static switch_status_t http_cache_file_open(switch_file_handle_t *handle, const char *path)
{
switch_status_t status = SWITCH_STATUS_SUCCESS;
- http_profile_t *profile = NULL;
struct http_context *context = switch_core_alloc(handle->memory_pool, sizeof(*context));
- const char *local_path;
-
- if (switch_test_flag(handle, SWITCH_FILE_FLAG_WRITE)) {
- /* WRITE not supported */
- return SWITCH_STATUS_FALSE;
- }
+ int file_flags = SWITCH_FILE_DATA_SHORT;
if (handle->params) {
- profile = url_cache_http_profile_find(&gcache, switch_event_get_header(handle->params, "profile"));
+ context->profile = url_cache_http_profile_find(&gcache, switch_event_get_header(handle->params, "profile"));
}
- local_path = url_cache_get(&gcache, profile, NULL, path, 1, handle->memory_pool);
- if (!local_path) {
- return SWITCH_STATUS_FALSE;
+
+ if (switch_test_flag(handle, SWITCH_FILE_FLAG_WRITE)) {
+ /* WRITE = HTTP PUT */
+ file_flags |= SWITCH_FILE_FLAG_WRITE;
+ context->write_url = switch_core_strdup(handle->memory_pool, path);
+ /* allocate local file in cache */
+ context->local_path = cached_url_filename_create(&gcache, find_extension(context->write_url));
+ } else {
+ /* READ = HTTP GET */
+ file_flags |= SWITCH_FILE_FLAG_READ;
+ context->local_path = url_cache_get(&gcache, context->profile, NULL, path, 1, handle->memory_pool);
+ if (!context->local_path) {
+ return SWITCH_STATUS_FALSE;
+ }
}
if ((status = switch_core_file_open(&context->fh,
- local_path,
+ context->local_path,
handle->channels,
handle->samplerate,
- SWITCH_FILE_FLAG_READ | SWITCH_FILE_DATA_SHORT, NULL)) != SWITCH_STATUS_SUCCESS) {
- switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to open HTTP cache file: %s, %s\n", local_path, path);
- return status;
+ file_flags, NULL)) != SWITCH_STATUS_SUCCESS) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to open HTTP cache file: %s, %s\n", context->local_path, path);
+ if (switch_test_flag(handle, SWITCH_FILE_FLAG_WRITE)) {
+ switch_safe_free(context->local_path);
+ }
+ return status;
}
handle->private_info = context;
return switch_core_file_read(&context->fh, data, len);
}
+/**
+ * Write to HTTP file
+ * @param handle
+ * @param data
+ * @param len
+ * @return
+ */
+static switch_status_t http_file_write(switch_file_handle_t *handle, void *data, size_t *len)
+{
+ struct http_context *context = (struct http_context *)handle->private_info;
+ return switch_core_file_write(&context->fh, data, len);
+}
+
/**
* Close HTTP file
* @param handle
static switch_status_t http_file_close(switch_file_handle_t *handle)
{
struct http_context *context = (struct http_context *)handle->private_info;
- return switch_core_file_close(&context->fh);
+ switch_status_t status = switch_core_file_close(&context->fh);
+
+ if (status == SWITCH_STATUS_SUCCESS && !zstr(context->write_url)) {
+ status = http_put(&gcache, context->profile, NULL, context->write_url, context->local_path, 1);
+ }
+ if (!zstr(context->write_url)) {
+ switch_safe_free(context->local_path);
+ }
+ return status;
}
static char *http_supported_formats[] = { "http", NULL };
file_interface->file_open = http_file_open;
file_interface->file_close = http_file_close;
file_interface->file_read = http_file_read;
+ file_interface->file_write = http_file_write;
file_interface = switch_loadable_module_create_interface(*module_interface, SWITCH_FILE_INTERFACE);
file_interface->interface_name = modname;
file_interface->file_open = https_file_open;
file_interface->file_close = http_file_close;
file_interface->file_read = http_file_read;
+ file_interface->file_write = http_file_write;
}
/* create the queue from configuration */