]> git.ipfire.org Git - thirdparty/freeswitch.git/commitdiff
FS-7496 [mod_http_cache] lookup file extension from content-type if missing in URL
authorChris Rienzo <chris.rienzo@grasshopper.com>
Thu, 30 Apr 2015 20:06:19 +0000 (16:06 -0400)
committerChris Rienzo <chris.rienzo@grasshopper.com>
Thu, 30 Apr 2015 20:06:19 +0000 (16:06 -0400)
src/mod/applications/mod_http_cache/mod_http_cache.c

index f9482672710b80a78bb61331d7229f81e628fe4d..776a80312f8c0dcbbd0d80b2c949cdfbe31f53d4 100644 (file)
@@ -87,6 +87,12 @@ struct cached_url {
        char *url;
        /** The path and name of the cached URL */
        char *filename;
+       /** File extension */
+       char *extension;
+       /** Content-Type of this URL (audio/3gpp) */
+       char *content_type;
+       /** Content-Type parameters  (codecs=samr) */
+       const char *content_type_params;
        /** The size of the cached URL, in bytes */
        size_t size;
        /** URL use flag */
@@ -120,6 +126,7 @@ static switch_status_t http_get(url_cache_t *cache, http_profile_t *profile, cac
 static size_t get_file_callback(void *ptr, size_t size, size_t nmemb, void *get);
 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 void process_content_type_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, int cache_local_file);
 
@@ -479,8 +486,32 @@ static void process_cache_control_header(cached_url_t *url, char *data)
        switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "setting max age to %u seconds from now\n", (int)max_age);
 }
 
+/**
+ * Content-Type: audio/mpeg; foo=bar
+ */
+static void process_content_type_header(cached_url_t *url, char *data)
+{
+       char *params;
+
+       /* trim whitespace and check if empty */
+       data = trim(data);
+       if (zstr(data)) {
+               return;
+       }
+
+       /* copy header, removing any params */
+       url->content_type = strdup(data);
+       params = strchr(url->content_type, ';');
+       if (params) {
+               *params = '\0';
+               url->content_type_params = trim(++params);
+       }
+}
+
 #define CACHE_CONTROL_HEADER "cache-control:"
 #define CACHE_CONTROL_HEADER_LEN (sizeof(CACHE_CONTROL_HEADER) - 1)
+#define CONTENT_TYPE_HEADER "content-type:"
+#define CONTENT_TYPE_HEADER_LEN (sizeof(CONTENT_TYPE_HEADER) - 1)
 /**
  * Called by libcurl to process headers from HTTP GET response
  * @param ptr the header data
@@ -508,6 +539,8 @@ static size_t get_header_callback(void *ptr, size_t size, size_t nmemb, void *ge
        /* check which header this is and process it */
        if (!strncasecmp(CACHE_CONTROL_HEADER, header, CACHE_CONTROL_HEADER_LEN)) {
                process_cache_control_header(url, header + CACHE_CONTROL_HEADER_LEN);
+       } else if (!strncasecmp(CONTENT_TYPE_HEADER, header, CONTENT_TYPE_HEADER_LEN)) {
+               process_content_type_header(url, header + CONTENT_TYPE_HEADER_LEN);
        }
 
        switch_safe_free(header);
@@ -819,40 +852,52 @@ static http_profile_t *url_cache_http_profile_add(url_cache_t *cache, const char
 
 /**
  * Find file extension at end of URL.
- * @return file extension or NULL if it doesn't exist
+ * @param url to search
+ * @param found_extension
+ * @param found_extension_len
  */
-static const char *find_extension(const char *url)
+static void find_extension(const char *url, const char **found_extension, size_t *found_extension_len)
 {
        const char *ext;
+       size_t ext_len = 0;
 
        /* find extension on the end of URL */
        for (ext = &url[strlen(url) - 1]; ext != url; ext--) {
                if (*ext == '/' || *ext == '\\') {
                        break;
                }
-               if (*ext == '.') {
+               if (*ext == '?' || *ext == '#') {
+                       ext_len = 0;
+               } else if (*ext == '.') {
                        /* found it */
-                       return ++ext;
+                       *found_extension_len = ext_len;
+                       *found_extension = ++ext;
+                       break;
+               } else {
+                       ext_len++;
                }
        }
-       return NULL;
 }
 
 /**
  * Create a cached URL filename.
- * @param cache the cache
- * @param extension the filename extension
+ * @param cache
+ * @param url
+ * @param extension if set, extension is duplicated here
  * @return the cached URL filename.  Free when done.
  */
-static char *cached_url_filename_create(url_cache_t *cache, const char *url)
+static char *cached_url_filename_create(url_cache_t *cache, const char *url, char **extension)
 {
        char *filename;
        char *dirname;
        char uuid_dir[3] = { 0 };
        switch_uuid_t uuid;
        char uuid_str[SWITCH_UUID_FORMATTED_LENGTH + 1] = { 0 };
-       const char *extension = find_extension(url);
-       
+       const char *found_extension = NULL;
+       size_t found_extension_len = 0;
+
+       find_extension(url, &found_extension, &found_extension_len);
+
        /* 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);
@@ -862,22 +907,47 @@ static char *cached_url_filename_create(url_cache_t *cache, const char *url)
        /* create sub-directory if it doesn't exist */
        switch_dir_make_recursive(dirname, SWITCH_DEFAULT_DIR_PERMS, cache->pool);
 
-    if (!zstr(extension)) {
-        char *p;
-               filename = switch_mprintf("%s%s%s.%s", dirname, SWITCH_PATH_SEPARATOR, &uuid_str[2], extension);
-           if ((p = strchr(filename, '?'))) {
-                       *p = '\0';
-               }
-           if ((p = strchr(filename, '#'))) {
-                       *p = '\0';
+       if (!zstr(found_extension) && found_extension_len > 0) {
+               char *found_extension_dup = strndup(found_extension, found_extension_len);
+               filename = switch_mprintf("%s%s%s.%s", dirname, SWITCH_PATH_SEPARATOR, &uuid_str[2], found_extension_dup);
+               if (extension) {
+                       *extension = found_extension_dup;
+               } else {
+                       free(found_extension_dup);
                }
        } else {
                filename = switch_mprintf("%s%s%s", dirname, SWITCH_PATH_SEPARATOR, &uuid_str[2]);
+               if (extension) {
+                       *extension = NULL;
+               }
        }
        free(dirname);
        return filename;
 }
 
+/**
+ * Rename cached URL with filename extension if one can be determined
+ * @param url the cached URL
+ */
+static void cached_url_set_extension_from_content_type(cached_url_t *url, switch_core_session_t *session)
+{
+       if (!url->extension && url->content_type) {
+               const char *new_extension = switch_core_mime_type2ext(url->content_type);
+               if (new_extension) {
+                       char *new_filename = switch_mprintf("%s.%s", url->filename, new_extension);
+                       if (rename(url->filename, new_filename) != -1) {
+                               switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "renamed cached URL to %s\n", new_filename);
+                               free(url->filename);
+                               url->filename = new_filename;
+                               url->extension = strdup(new_extension);
+                       } else {
+                               switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "rename(%s): %s\n", new_filename, strerror(errno));
+                               free(new_filename);
+                       }
+               }
+       }
+}
+
 /**
  * Create a cached URL entry
  * @param cache the cache
@@ -897,7 +967,7 @@ static cached_url_t *cached_url_create(url_cache_t *cache, const char *url, cons
 
        /* intialize cached URL */
        if (zstr(filename)) {
-               u->filename = cached_url_filename_create(cache, url);
+               u->filename = cached_url_filename_create(cache, url, &u->extension);
        } else {
                u->filename = strdup(filename);
        }
@@ -922,6 +992,8 @@ static void cached_url_destroy(cached_url_t *url, switch_memory_pool_t *pool)
                switch_file_remove(url->filename, pool);
        }
        switch_safe_free(url->filename);
+       switch_safe_free(url->extension);
+       switch_safe_free(url->content_type);
        switch_safe_free(url->url);
        switch_safe_free(url);
 }
@@ -1030,6 +1102,9 @@ static switch_status_t http_get(url_cache_t *cache, http_profile_t *profile, cac
                } else {
                        switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "URL %s downloaded in %d ms\n", url->url, duration_ms);
                }
+               if (!url->extension) {
+                       cached_url_set_extension_from_content_type(url, session);
+               }
        } else {
                url->size = 0; // nothing downloaded or download interrupted
                switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Received HTTP error %ld trying to fetch %s\n", httpRes, url->url);
@@ -1572,7 +1647,7 @@ static switch_status_t http_cache_file_open(switch_file_handle_t *handle, const
                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, context->write_url);
+               context->local_path = cached_url_filename_create(&gcache, context->write_url, NULL);
        } else {
                /* READ = HTTP GET */
                file_flags |= SWITCH_FILE_FLAG_READ;