<p>Joshua Colp <strong>submitted</strong> this change.</p><p><a href="https://gerrit.asterisk.org/c/asterisk/+/16164">View Change</a></p><div style="white-space:pre-wrap">Approvals:
  Joshua Colp: Looks good to me, approved; Approved for Submit
  Kevin Harwell: Looks good to me, approved
  George Joseph: Looks good to me, but someone else must approve

</div><pre style="font-family: monospace,monospace; white-space: pre-wrap;">res_http_media_cache.c: Parse media URLs to find extensions.<br><br>Use cURL's URL parsing API, falling back to the urlparser library, to<br>parse playback URLs in order to find their file extensions.<br><br>For backwards compatibility, we first look at the full URL, then at<br>any Content-Type header, and finally at just the path portion of the<br>URL.<br><br>ASTERISK-27871 #close<br><br>Change-Id: I16d0682f6d794be96539261b3e48f237909139cb<br>---<br>M main/media_cache.c<br>M res/res_http_media_cache.c<br>M tests/test_http_media_cache.c<br>3 files changed, 276 insertions(+), 51 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/main/media_cache.c b/main/media_cache.c</span><br><span>index b303643..1899fb4 100644</span><br><span>--- a/main/media_cache.c</span><br><span>+++ b/main/media_cache.c</span><br><span>@@ -126,71 +126,30 @@</span><br><span> </span><br><span> /*!</span><br><span>  * \internal</span><br><span style="color: hsl(0, 100%, 40%);">- * \brief Normalize the value of a Content-Type header</span><br><span style="color: hsl(0, 100%, 40%);">- *</span><br><span style="color: hsl(0, 100%, 40%);">- * This will trim off any optional parameters after the type/subtype.</span><br><span style="color: hsl(0, 100%, 40%);">- */</span><br><span style="color: hsl(0, 100%, 40%);">-static void normalize_content_type_header(char *content_type)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">-        char *params = strchr(content_type, ';');</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-       if (params) {</span><br><span style="color: hsl(0, 100%, 40%);">-           *params-- = 0;</span><br><span style="color: hsl(0, 100%, 40%);">-          while (params > content_type && (*params == ' ' || *params == '\t')) {</span><br><span style="color: hsl(0, 100%, 40%);">-                       *params-- = 0;</span><br><span style="color: hsl(0, 100%, 40%);">-          }</span><br><span style="color: hsl(0, 100%, 40%);">-       }</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*!</span><br><span style="color: hsl(0, 100%, 40%);">- * \internal</span><br><span>  * \brief Update the name of the file backing a \c bucket_file</span><br><span>  * \param preferred_file_name The preferred name of the backing file</span><br><span>  */</span><br><span> static void bucket_file_update_path(struct ast_bucket_file *bucket_file,</span><br><span>       const char *preferred_file_name)</span><br><span> {</span><br><span style="color: hsl(0, 100%, 40%);">-   char *ext;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span>   if (!ast_strlen_zero(preferred_file_name) && strcmp(bucket_file->path, preferred_file_name)) {</span><br><span>            /* Use the preferred file name if available */</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span>               rename(bucket_file->path, preferred_file_name);</span><br><span>           ast_copy_string(bucket_file->path, preferred_file_name,</span><br><span>                   sizeof(bucket_file->path));</span><br><span style="color: hsl(0, 100%, 40%);">-  } else if (!strchr(bucket_file->path, '.') && (ext = strrchr(ast_sorcery_object_get_id(bucket_file), '.'))) {</span><br><span style="color: hsl(0, 100%, 40%);">-                /* If we don't have a file extension and were provided one in the URI, use it */</span><br><span style="color: hsl(0, 100%, 40%);">-            char found_ext[32];</span><br><span style="color: hsl(0, 100%, 40%);">-             char new_path[PATH_MAX + sizeof(found_ext)];</span><br><span style="color: hsl(120, 100%, 40%);">+  } else if (!strchr(bucket_file->path, '.')) {</span><br><span style="color: hsl(120, 100%, 40%);">+              struct ast_bucket_metadata *ext =</span><br><span style="color: hsl(120, 100%, 40%);">+                     ast_bucket_file_metadata_get(bucket_file, "ext");</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-         ast_bucket_file_metadata_set(bucket_file, "ext", ext);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-                /* Don't pass '.' while checking for supported extension */</span><br><span style="color: hsl(0, 100%, 40%);">-         if (!ast_get_format_for_file_ext(ext + 1)) {</span><br><span style="color: hsl(0, 100%, 40%);">-                    /* If the file extension passed in the URI isn't supported check for the</span><br><span style="color: hsl(0, 100%, 40%);">-                     * extension based on the MIME type passed in the Content-Type header before</span><br><span style="color: hsl(0, 100%, 40%);">-                     * giving up.</span><br><span style="color: hsl(0, 100%, 40%);">-                    * If a match is found then retrieve the extension from the supported list</span><br><span style="color: hsl(0, 100%, 40%);">-                       * corresponding to the mime-type and use that to rename the file */</span><br><span style="color: hsl(0, 100%, 40%);">-                    struct ast_bucket_metadata *header = ast_bucket_file_metadata_get(bucket_file, "content-type");</span><br><span style="color: hsl(0, 100%, 40%);">-                       if (header) {</span><br><span style="color: hsl(0, 100%, 40%);">-                           char *mime_type = ast_strdup(header->value);</span><br><span style="color: hsl(0, 100%, 40%);">-                         if (mime_type) {</span><br><span style="color: hsl(0, 100%, 40%);">-                                        normalize_content_type_header(mime_type);</span><br><span style="color: hsl(0, 100%, 40%);">-                                       if (!ast_strlen_zero(mime_type)) {</span><br><span style="color: hsl(0, 100%, 40%);">-                                              if (ast_get_extension_for_mime_type(mime_type, found_ext, sizeof(found_ext))) {</span><br><span style="color: hsl(0, 100%, 40%);">-                                                 ext = found_ext;</span><br><span style="color: hsl(0, 100%, 40%);">-                                                }</span><br><span style="color: hsl(0, 100%, 40%);">-                                       }</span><br><span style="color: hsl(0, 100%, 40%);">-                                       ast_free(mime_type);</span><br><span style="color: hsl(0, 100%, 40%);">-                            }</span><br><span style="color: hsl(0, 100%, 40%);">-                               ao2_ref(header, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+          if (ext) {</span><br><span style="color: hsl(120, 100%, 40%);">+                    char *new_path;</span><br><span style="color: hsl(120, 100%, 40%);">+                       if (ast_asprintf(&new_path, "%s%s", bucket_file->path, ext->value) != -1) {</span><br><span style="color: hsl(120, 100%, 40%);">+                               rename(bucket_file->path, new_path);</span><br><span style="color: hsl(120, 100%, 40%);">+                               ast_copy_string(bucket_file->path, new_path, sizeof(bucket_file->path));</span><br><span style="color: hsl(120, 100%, 40%);">+                                ast_free(new_path);</span><br><span>                  }</span><br><span style="color: hsl(120, 100%, 40%);">+                     ao2_ref(ext, -1);</span><br><span>            }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-               snprintf(new_path, sizeof(new_path), "%s%s", bucket_file->path, ext);</span><br><span style="color: hsl(0, 100%, 40%);">-              rename(bucket_file->path, new_path);</span><br><span style="color: hsl(0, 100%, 40%);">-         ast_copy_string(bucket_file->path, new_path, sizeof(bucket_file->path));</span><br><span>       }</span><br><span> }</span><br><span> </span><br><span>diff --git a/res/res_http_media_cache.c b/res/res_http_media_cache.c</span><br><span>index d761442..ba9428d 100644</span><br><span>--- a/res/res_http_media_cache.c</span><br><span>+++ b/res/res_http_media_cache.c</span><br><span>@@ -35,6 +35,7 @@</span><br><span> </span><br><span> #include <curl/curl.h></span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/file.h"</span><br><span> #include "asterisk/module.h"</span><br><span> #include "asterisk/bucket.h"</span><br><span> #include "asterisk/sorcery.h"</span><br><span>@@ -155,6 +156,176 @@</span><br><span>     ast_bucket_file_metadata_set(bucket_file, "__actual_expires", time_buf);</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+static char *file_extension_from_string(const char *str, char *buffer, size_t capacity)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       const char *ext;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    ext = strrchr(str, '.');</span><br><span style="color: hsl(120, 100%, 40%);">+      if (ext && ast_get_format_for_file_ext(ext + 1)) {</span><br><span style="color: hsl(120, 100%, 40%);">+            ast_debug(3, "Found extension '%s' at end of string\n", ext);</span><br><span style="color: hsl(120, 100%, 40%);">+               ast_copy_string(buffer, ext, capacity);</span><br><span style="color: hsl(120, 100%, 40%);">+               return buffer;</span><br><span style="color: hsl(120, 100%, 40%);">+        }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static char *file_extension_from_url(struct ast_bucket_file *bucket_file, char *buffer, size_t capacity)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+        return file_extension_from_string(ast_sorcery_object_get_id(bucket_file), buffer, capacity);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \internal</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Normalize the value of a Content-Type header</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This will trim off any optional parameters after the type/subtype.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+static void normalize_content_type_header(char *content_type)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+        char *params = strchr(content_type, ';');</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (params) {</span><br><span style="color: hsl(120, 100%, 40%);">+         *params-- = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+                while (params > content_type && (*params == ' ' || *params == '\t')) {</span><br><span style="color: hsl(120, 100%, 40%);">+                     *params-- = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+                }</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static char *file_extension_from_content_type(struct ast_bucket_file *bucket_file, char *buffer, size_t capacity)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+  /* Check for the extension based on the MIME type passed in the Content-Type</span><br><span style="color: hsl(120, 100%, 40%);">+   * header.</span><br><span style="color: hsl(120, 100%, 40%);">+     *</span><br><span style="color: hsl(120, 100%, 40%);">+     * If a match is found then retrieve the extension from the supported list</span><br><span style="color: hsl(120, 100%, 40%);">+     * corresponding to the mime-type and use that to rename the file */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        struct ast_bucket_metadata *header;</span><br><span style="color: hsl(120, 100%, 40%);">+   char *mime_type;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    header = ast_bucket_file_metadata_get(bucket_file, "content-type");</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!header) {</span><br><span style="color: hsl(120, 100%, 40%);">+                return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   mime_type = ast_strdup(header->value);</span><br><span style="color: hsl(120, 100%, 40%);">+     if (mime_type) {</span><br><span style="color: hsl(120, 100%, 40%);">+              normalize_content_type_header(mime_type);</span><br><span style="color: hsl(120, 100%, 40%);">+             if (!ast_strlen_zero(mime_type)) {</span><br><span style="color: hsl(120, 100%, 40%);">+                    if (ast_get_extension_for_mime_type(mime_type, buffer, sizeof(buffer))) {</span><br><span style="color: hsl(120, 100%, 40%);">+                             ast_debug(3, "Derived extension '%s' from MIME type %s\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                                  buffer,</span><br><span style="color: hsl(120, 100%, 40%);">+                                       mime_type);</span><br><span style="color: hsl(120, 100%, 40%);">+                           ast_free(mime_type);</span><br><span style="color: hsl(120, 100%, 40%);">+                          ao2_ref(header, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+                          return buffer;</span><br><span style="color: hsl(120, 100%, 40%);">+                        }</span><br><span style="color: hsl(120, 100%, 40%);">+             }</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+     ast_free(mime_type);</span><br><span style="color: hsl(120, 100%, 40%);">+  ao2_ref(header, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* The URL parsing API was introduced in 7.62.0 */</span><br><span style="color: hsl(120, 100%, 40%);">+#if LIBCURL_VERSION_NUM >= 0x073e00</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static char *file_extension_from_url_path(struct ast_bucket_file *bucket_file, char *buffer, size_t capacity)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     char *path;</span><br><span style="color: hsl(120, 100%, 40%);">+   CURLU *h;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   h = curl_url();</span><br><span style="color: hsl(120, 100%, 40%);">+       if (!h) {</span><br><span style="color: hsl(120, 100%, 40%);">+             ast_log(LOG_ERROR, "Failed to allocate cURL URL handle\n");</span><br><span style="color: hsl(120, 100%, 40%);">+         return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (curl_url_set(h, CURLUPART_URL, ast_sorcery_object_get_id(bucket_file), 0)) {</span><br><span style="color: hsl(120, 100%, 40%);">+              ast_log(LOG_ERROR, "Failed to parse URL: %s\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                     ast_sorcery_object_get_id(bucket_file));</span><br><span style="color: hsl(120, 100%, 40%);">+              curl_url_cleanup(h);</span><br><span style="color: hsl(120, 100%, 40%);">+          return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   curl_url_get(h, CURLUPART_PATH, &path, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      /* Just parse it as a string like before, but without the extra cruft */</span><br><span style="color: hsl(120, 100%, 40%);">+      buffer = file_extension_from_string(path, buffer, capacity);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        curl_free(path);</span><br><span style="color: hsl(120, 100%, 40%);">+      curl_url_cleanup(h);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        return buffer;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#elif defined(HAVE_URIPARSER)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include <uriparser/Uri.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static char *file_extension_from_url_path(struct ast_bucket_file *bucket_file, char *buffer, size_t capacity)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+    UriParserStateA state;</span><br><span style="color: hsl(120, 100%, 40%);">+        UriUriA full_uri;</span><br><span style="color: hsl(120, 100%, 40%);">+     char *path;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ state.uri = &full_uri;</span><br><span style="color: hsl(120, 100%, 40%);">+    if (uriParseUriA(&state, ast_sorcery_object_get_id(bucket_file)) != URI_SUCCESS</span><br><span style="color: hsl(120, 100%, 40%);">+      || !full_uri.scheme.first</span><br><span style="color: hsl(120, 100%, 40%);">+     || !full_uri.scheme.afterLast</span><br><span style="color: hsl(120, 100%, 40%);">+         || !full_uri.pathTail) {</span><br><span style="color: hsl(120, 100%, 40%);">+           ast_log(LOG_ERROR, "Failed to parse URL: %s\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                     ast_sorcery_object_get_id(bucket_file));</span><br><span style="color: hsl(120, 100%, 40%);">+              uriFreeUriMembersA(&full_uri);</span><br><span style="color: hsl(120, 100%, 40%);">+            return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (ast_asprintf(&path,</span><br><span style="color: hsl(120, 100%, 40%);">+                   "%.*s",</span><br><span style="color: hsl(120, 100%, 40%);">+                     (int) (full_uri.pathTail->text.afterLast - full_uri.pathTail->text.first),</span><br><span style="color: hsl(120, 100%, 40%);">+                      full_uri.pathTail->text.first) != -1) {</span><br><span style="color: hsl(120, 100%, 40%);">+            /* Just parse it as a string like before, but without the extra cruft */</span><br><span style="color: hsl(120, 100%, 40%);">+              file_extension_from_string(path, buffer, capacity);</span><br><span style="color: hsl(120, 100%, 40%);">+           ast_free(path);</span><br><span style="color: hsl(120, 100%, 40%);">+               uriFreeUriMembersA(&full_uri);</span><br><span style="color: hsl(120, 100%, 40%);">+            return buffer;</span><br><span style="color: hsl(120, 100%, 40%);">+        }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   uriFreeUriMembersA(&full_uri);</span><br><span style="color: hsl(120, 100%, 40%);">+    return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#else</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static char *file_extension_from_url_path(struct ast_bucket_file *bucket_file, char *buffer, size_t capacity)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+  /* NOP */</span><br><span style="color: hsl(120, 100%, 40%);">+     return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#endif</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void bucket_file_set_extension(struct ast_bucket_file *bucket_file)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+    /* We will attempt to determine an extension in the following order for backwards</span><br><span style="color: hsl(120, 100%, 40%);">+      * compatibility:</span><br><span style="color: hsl(120, 100%, 40%);">+      *</span><br><span style="color: hsl(120, 100%, 40%);">+     * 1. Look at tail end of URL for extension</span><br><span style="color: hsl(120, 100%, 40%);">+    * 2. Use the Content-Type header if present</span><br><span style="color: hsl(120, 100%, 40%);">+   * 3. Parse the URL (assuming we can) and look at the tail of the path</span><br><span style="color: hsl(120, 100%, 40%);">+         */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ char buffer[64];</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    if (file_extension_from_url(bucket_file, buffer, sizeof(buffer))</span><br><span style="color: hsl(120, 100%, 40%);">+         || file_extension_from_content_type(bucket_file, buffer, sizeof(buffer))</span><br><span style="color: hsl(120, 100%, 40%);">+      || file_extension_from_url_path(bucket_file, buffer, sizeof(buffer))) {</span><br><span style="color: hsl(120, 100%, 40%);">+            ast_bucket_file_metadata_set(bucket_file, "ext", buffer);</span><br><span style="color: hsl(120, 100%, 40%);">+   }</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> /*! \internal</span><br><span>  * \brief Return whether or not we should always revalidate against the server</span><br><span>  */</span><br><span>@@ -278,6 +449,7 @@</span><br><span> </span><br><span>   if (http_code / 100 == 2) {</span><br><span>          bucket_file_set_expiration(bucket_file);</span><br><span style="color: hsl(120, 100%, 40%);">+              bucket_file_set_extension(bucket_file);</span><br><span>              return 0;</span><br><span>    } else {</span><br><span>             ast_log(LOG_WARNING, "Failed to retrieve URL '%s': server returned %ld\n",</span><br><span>diff --git a/tests/test_http_media_cache.c b/tests/test_http_media_cache.c</span><br><span>index c197539..dfb28b7 100644</span><br><span>--- a/tests/test_http_media_cache.c</span><br><span>+++ b/tests/test_http_media_cache.c</span><br><span>@@ -41,6 +41,14 @@</span><br><span> #include "asterisk/bucket.h"</span><br><span> #include "asterisk/test.h"</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+#undef INCLUDE_URI_PARSING_TESTS</span><br><span style="color: hsl(120, 100%, 40%);">+#if defined(HAVE_CURL)</span><br><span style="color: hsl(120, 100%, 40%);">+# include <curl/curl.h></span><br><span style="color: hsl(120, 100%, 40%);">+#endif</span><br><span style="color: hsl(120, 100%, 40%);">+#if (defined(HAVE_CURL) && LIBCURL_VERSION_NUM >= 0x073e00) || defined(HAVE_URIPARSER)</span><br><span style="color: hsl(120, 100%, 40%);">+# define INCLUDE_URI_PARSING_TESTS 1</span><br><span style="color: hsl(120, 100%, 40%);">+#endif</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> #define CATEGORY "/res/http_media_cache/"</span><br><span> </span><br><span> #define TEST_URI "test_media_cache"</span><br><span>@@ -57,6 +65,7 @@</span><br><span>         struct timeval expires;</span><br><span>      const char *status_text;</span><br><span>     const char *etag;</span><br><span style="color: hsl(120, 100%, 40%);">+     const char *content_type;</span><br><span> };</span><br><span> </span><br><span> static struct test_options options;</span><br><span>@@ -125,6 +134,10 @@</span><br><span>            }</span><br><span>    }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+ if (!ast_strlen_zero(options.content_type)) {</span><br><span style="color: hsl(120, 100%, 40%);">+         ast_str_append(&http_header, 0, "Content-Type: %s\r\n", options.content_type);</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>  if (options.cache_control.maxage) {</span><br><span>          SET_OR_APPEND_CACHE_CONTROL(cache_control);</span><br><span>          ast_str_append(&cache_control, 0, "max-age=%d", options.cache_control.maxage);</span><br><span>@@ -220,6 +233,77 @@</span><br><span>  }</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+AST_TEST_DEFINE(retrieve_content_type)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ RAII_VAR(struct ast_bucket_file *, bucket_file, NULL, bucket_file_cleanup);</span><br><span style="color: hsl(120, 100%, 40%);">+   char uri[1024];</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     switch (cmd) {</span><br><span style="color: hsl(120, 100%, 40%);">+        case TEST_INIT:</span><br><span style="color: hsl(120, 100%, 40%);">+               info->name = __func__;</span><br><span style="color: hsl(120, 100%, 40%);">+             info->category = CATEGORY;</span><br><span style="color: hsl(120, 100%, 40%);">+         info->summary = "Test retrieval of a resource with a Content-Type header";</span><br><span style="color: hsl(120, 100%, 40%);">+               info->description =</span><br><span style="color: hsl(120, 100%, 40%);">+                        "This test covers retrieval of a resource whose URL does not end with\n"</span><br><span style="color: hsl(120, 100%, 40%);">+                    "a parseable extension and whose response includes a Content-Type\n"</span><br><span style="color: hsl(120, 100%, 40%);">+                        "header that we recognize.";</span><br><span style="color: hsl(120, 100%, 40%);">+                return AST_TEST_NOT_RUN;</span><br><span style="color: hsl(120, 100%, 40%);">+      case TEST_EXECUTE:</span><br><span style="color: hsl(120, 100%, 40%);">+            break;</span><br><span style="color: hsl(120, 100%, 40%);">+        }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   options.send_file = 1;</span><br><span style="color: hsl(120, 100%, 40%);">+        options.status_code = 200;</span><br><span style="color: hsl(120, 100%, 40%);">+    options.status_text = "OK";</span><br><span style="color: hsl(120, 100%, 40%);">+ options.content_type = "audio/wav";</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       snprintf(uri, sizeof(uri), "%s/%s", server_uri, "foo.wav?account_id=1234");</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     bucket_file = ast_bucket_file_retrieve(uri);</span><br><span style="color: hsl(120, 100%, 40%);">+  ast_test_validate(test, bucket_file != NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_validate(test, !strcmp(uri, ast_sorcery_object_get_id(bucket_file)));</span><br><span style="color: hsl(120, 100%, 40%);">+        ast_test_validate(test, !ast_strlen_zero(bucket_file->path));</span><br><span style="color: hsl(120, 100%, 40%);">+      VALIDATE_STR_METADATA(test, bucket_file, "ext", ".wav");</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        return AST_TEST_PASS;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#ifdef INCLUDE_URI_PARSING_TESTS</span><br><span style="color: hsl(120, 100%, 40%);">+AST_TEST_DEFINE(retrieve_parsed_uri)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ RAII_VAR(struct ast_bucket_file *, bucket_file, NULL, bucket_file_cleanup);</span><br><span style="color: hsl(120, 100%, 40%);">+   char uri[1024];</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     switch (cmd) {</span><br><span style="color: hsl(120, 100%, 40%);">+        case TEST_INIT:</span><br><span style="color: hsl(120, 100%, 40%);">+               info->name = __func__;</span><br><span style="color: hsl(120, 100%, 40%);">+             info->category = CATEGORY;</span><br><span style="color: hsl(120, 100%, 40%);">+         info->summary = "Test retrieval of a resource with a complex URI";</span><br><span style="color: hsl(120, 100%, 40%);">+               info->description =</span><br><span style="color: hsl(120, 100%, 40%);">+                        "This test covers retrieval of a resource whose URL does not end with\n"</span><br><span style="color: hsl(120, 100%, 40%);">+                    "a parseable extension, but the path portion of the URL does end with\n"</span><br><span style="color: hsl(120, 100%, 40%);">+                    "parseable extension.";</span><br><span style="color: hsl(120, 100%, 40%);">+             return AST_TEST_NOT_RUN;</span><br><span style="color: hsl(120, 100%, 40%);">+      case TEST_EXECUTE:</span><br><span style="color: hsl(120, 100%, 40%);">+            break;</span><br><span style="color: hsl(120, 100%, 40%);">+        }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   options.send_file = 1;</span><br><span style="color: hsl(120, 100%, 40%);">+        options.status_code = 200;</span><br><span style="color: hsl(120, 100%, 40%);">+    options.status_text = "OK";</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       snprintf(uri, sizeof(uri), "%s/%s", server_uri, "foo.wav?account_id=1234");</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     bucket_file = ast_bucket_file_retrieve(uri);</span><br><span style="color: hsl(120, 100%, 40%);">+  ast_test_validate(test, bucket_file != NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_validate(test, !strcmp(uri, ast_sorcery_object_get_id(bucket_file)));</span><br><span style="color: hsl(120, 100%, 40%);">+        ast_test_validate(test, !ast_strlen_zero(bucket_file->path));</span><br><span style="color: hsl(120, 100%, 40%);">+      VALIDATE_STR_METADATA(test, bucket_file, "ext", ".wav");</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        return AST_TEST_PASS;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+#endif</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> AST_TEST_DEFINE(retrieve_cache_control_directives)</span><br><span> {</span><br><span>        RAII_VAR(struct ast_bucket_file *, bucket_file, NULL, bucket_file_cleanup);</span><br><span>@@ -670,6 +754,11 @@</span><br><span>   AST_TEST_REGISTER(retrieve_etag_expired);</span><br><span>    AST_TEST_REGISTER(retrieve_cache_control_age);</span><br><span>       AST_TEST_REGISTER(retrieve_cache_control_directives);</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_TEST_REGISTER(retrieve_content_type);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#ifdef INCLUDE_URI_PARSING_TESTS</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_TEST_REGISTER(retrieve_parsed_uri);</span><br><span style="color: hsl(120, 100%, 40%);">+#endif</span><br><span> </span><br><span>  ast_test_register_init(CATEGORY, pre_test_cb);</span><br><span> </span><br><span>@@ -688,6 +777,11 @@</span><br><span>    AST_TEST_UNREGISTER(retrieve_etag_expired);</span><br><span>  AST_TEST_UNREGISTER(retrieve_cache_control_age);</span><br><span>     AST_TEST_UNREGISTER(retrieve_cache_control_directives);</span><br><span style="color: hsl(120, 100%, 40%);">+       AST_TEST_REGISTER(retrieve_content_type);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#ifdef INCLUDE_URI_PARSING_TESTS</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_TEST_REGISTER(retrieve_parsed_uri);</span><br><span style="color: hsl(120, 100%, 40%);">+#endif</span><br><span> </span><br><span>  return 0;</span><br><span> }</span><br><span></span><br></pre><div style="white-space:pre-wrap"></div><p>To view, visit <a href="https://gerrit.asterisk.org/c/asterisk/+/16164">change 16164</a>. To unsubscribe, or for help writing mail filters, visit <a href="https://gerrit.asterisk.org/settings">settings</a>.</p><div itemscope itemtype="http://schema.org/EmailMessage"><div itemscope itemprop="action" itemtype="http://schema.org/ViewAction"><link itemprop="url" href="https://gerrit.asterisk.org/c/asterisk/+/16164"/><meta itemprop="name" content="View Change"/></div></div>

<div style="display:none"> Gerrit-Project: asterisk </div>
<div style="display:none"> Gerrit-Branch: master </div>
<div style="display:none"> Gerrit-Change-Id: I16d0682f6d794be96539261b3e48f237909139cb </div>
<div style="display:none"> Gerrit-Change-Number: 16164 </div>
<div style="display:none"> Gerrit-PatchSet: 4 </div>
<div style="display:none"> Gerrit-Owner: Sean Bright <sean@seanbright.com> </div>
<div style="display:none"> Gerrit-Reviewer: Benjamin Keith Ford <bford@digium.com> </div>
<div style="display:none"> Gerrit-Reviewer: Friendly Automation </div>
<div style="display:none"> Gerrit-Reviewer: George Joseph <gjoseph@digium.com> </div>
<div style="display:none"> Gerrit-Reviewer: Joshua Colp <jcolp@sangoma.com> </div>
<div style="display:none"> Gerrit-Reviewer: Kevin Harwell <kharwell@digium.com> </div>
<div style="display:none"> Gerrit-MessageType: merged </div>