res_http_media_cache: Introduce options and customize
Make the existing CURL parameters configurable and allow to specify the usable protocols, proxy and DNS timeout. ASTERISK-30340 Change-Id: I2eb02ef44190e026716720419bcbdbcc8125777b
This commit is contained in:
parent
69297b59df
commit
2048877889
|
@ -0,0 +1,69 @@
|
||||||
|
;
|
||||||
|
; Sample configuration for res_http_media_cache
|
||||||
|
;
|
||||||
|
; res_http_media_cache is the HTTP backend for the core media cache. The
|
||||||
|
; following options can be used to tune the behavior of the implementation
|
||||||
|
; or left as default.
|
||||||
|
;
|
||||||
|
; See the module's and cURL's documentation for the exact meaning of these
|
||||||
|
; options.
|
||||||
|
|
||||||
|
|
||||||
|
[general]
|
||||||
|
; Maximum time in seconds the transfer is allowed to complete in.
|
||||||
|
;
|
||||||
|
; See https://curl.se/libcurl/c/CURLOPT_TIMEOUT.html for details.
|
||||||
|
;
|
||||||
|
;timeout_secs = 180
|
||||||
|
|
||||||
|
|
||||||
|
; The HTTP User-Agent to use for requests.
|
||||||
|
;
|
||||||
|
; See https://curl.se/libcurl/c/CURLOPT_USERAGENT.html for details.
|
||||||
|
;
|
||||||
|
;user_agent = asterisk-libcurl-agent/1.0
|
||||||
|
|
||||||
|
|
||||||
|
; Follow HTTP 3xx redirects on requests. This can be combined with the
|
||||||
|
; max_redirects option to limit the number of times a redirect will be
|
||||||
|
; followed per request.
|
||||||
|
;
|
||||||
|
; See https://curl.se/libcurl/c/CURLOPT_FOLLOWLOCATION.html for details.
|
||||||
|
;
|
||||||
|
;follow_location = false
|
||||||
|
|
||||||
|
|
||||||
|
; The maximum number of redirects to follow.
|
||||||
|
;
|
||||||
|
; See https://curl.se/libcurl/c/CURLOPT_MAXREDIRS.html for details.
|
||||||
|
;
|
||||||
|
;max_redirects = 8
|
||||||
|
|
||||||
|
; The HTTP/HTTPS proxy to use for requests. Leave unspecified to not use
|
||||||
|
; a proxy. This can be a URL with scheme, host and port.
|
||||||
|
;
|
||||||
|
; See https://curl.se/libcurl/c/CURLOPT_PROXY.html for details.
|
||||||
|
;
|
||||||
|
;proxy = https://localhost:1234
|
||||||
|
|
||||||
|
|
||||||
|
; The life-time for DNS cache entries.
|
||||||
|
;
|
||||||
|
; See https://curl.se/libcurl/c/CURLOPT_DNS_CACHE_TIMEOUT.html for details.
|
||||||
|
;
|
||||||
|
;dns_cache_timeout_secs = 60
|
||||||
|
|
||||||
|
|
||||||
|
; The comma separated list of allowed protocols for the request. Available with
|
||||||
|
; cURL version 7.85.0 or later.
|
||||||
|
; See https://curl.se/libcurl/c/CURLOPT_PROTOCOLS_STR.html for details.
|
||||||
|
;
|
||||||
|
;protocols = http,https
|
||||||
|
|
||||||
|
; The comma separated list of allowed protocols for redirects. Available with
|
||||||
|
; cURL version 7.85.0 or later. This can be used to prevent a redirect from
|
||||||
|
; a protocol like HTTPS to another supported protocol of cURL.
|
||||||
|
;
|
||||||
|
; See https://curl.se/libcurl/c/CURLOPT_REDIR_PROTOCOLS_STR.html for details.
|
||||||
|
;
|
||||||
|
;redirect_protocols = http,https
|
|
@ -0,0 +1,12 @@
|
||||||
|
Subject: res_http_media_cache
|
||||||
|
|
||||||
|
The res_http_media_cache module now attempts to load
|
||||||
|
configuration from the res_http_media_cache.conf file.
|
||||||
|
The following options were added:
|
||||||
|
* timeout_secs
|
||||||
|
* user_agent
|
||||||
|
* follow_location
|
||||||
|
* max_redirects
|
||||||
|
* protocols
|
||||||
|
* redirect_protocols
|
||||||
|
* dns_cache_timeout_secs
|
|
@ -31,6 +31,41 @@
|
||||||
<support_level>core</support_level>
|
<support_level>core</support_level>
|
||||||
***/
|
***/
|
||||||
|
|
||||||
|
/*** DOCUMENTATION
|
||||||
|
<configInfo name="res_http_media_cache" language="en_US">
|
||||||
|
<synopsis>HTTP media cache</synopsis>
|
||||||
|
<configFile name="http_media_cache.conf">
|
||||||
|
<configObject name="general">
|
||||||
|
<synopsis>General configuration</synopsis>
|
||||||
|
<configOption name="timeout_secs" default="180">
|
||||||
|
<synopsis>The maximum time the transfer is allowed to complete in seconds. See https://curl.se/libcurl/c/CURLOPT_TIMEOUT.html for details.</synopsis>
|
||||||
|
</configOption>
|
||||||
|
<configOption name="user_agent">
|
||||||
|
<synopsis>The HTTP User-Agent to use for requests. See https://curl.se/libcurl/c/CURLOPT_USERAGENT.html for details.</synopsis>
|
||||||
|
</configOption>
|
||||||
|
<configOption name="follow_location" default="1">
|
||||||
|
<synopsis>Follow HTTP 3xx redirects on requests. See https://curl.se/libcurl/c/CURLOPT_FOLLOWLOCATION.html for details.</synopsis>
|
||||||
|
</configOption>
|
||||||
|
<configOption name="max_redirects" default="8">
|
||||||
|
<synopsis>The maximum number of redirects to follow. See https://curl.se/libcurl/c/CURLOPT_MAXREDIRS.html for details.</synopsis>
|
||||||
|
</configOption>
|
||||||
|
<configOption name="proxy">
|
||||||
|
<synopsis>The proxy to use for requests. See https://curl.se/libcurl/c/CURLOPT_PROXY.html for details.</synopsis>
|
||||||
|
</configOption>
|
||||||
|
<configOption name="protocols">
|
||||||
|
<synopsis>The comma separated list of allowed protocols for the request. Available with cURL 7.85.0 or later. See https://curl.se/libcurl/c/CURLOPT_PROTOCOLS_STR.html for details.</synopsis>
|
||||||
|
</configOption>
|
||||||
|
<configOption name="redirect_protocols">
|
||||||
|
<synopsis>The comma separated list of allowed protocols for redirects. Available with cURL 7.85.0 or later. See https://curl.se/libcurl/c/CURLOPT_REDIR_PROTOCOLS_STR.html for details.</synopsis>
|
||||||
|
</configOption>
|
||||||
|
<configOption name="dns_cache_timeout_secs" default="60">
|
||||||
|
<synopsis>The life-time for DNS cache entries. See https://curl.se/libcurl/c/CURLOPT_DNS_CACHE_TIMEOUT.html for details.</synopsis>
|
||||||
|
</configOption>
|
||||||
|
</configObject>
|
||||||
|
</configFile>
|
||||||
|
</configInfo>
|
||||||
|
***/
|
||||||
|
|
||||||
#include "asterisk.h"
|
#include "asterisk.h"
|
||||||
|
|
||||||
#include <curl/curl.h>
|
#include <curl/curl.h>
|
||||||
|
@ -44,6 +79,123 @@
|
||||||
|
|
||||||
#define MAX_HEADER_LENGTH 1023
|
#define MAX_HEADER_LENGTH 1023
|
||||||
|
|
||||||
|
#ifdef CURL_AT_LEAST_VERSION
|
||||||
|
#if CURL_AT_LEAST_VERSION(7, 85, 0)
|
||||||
|
#define AST_CURL_HAS_PROTOCOLS_STR 1
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static int http_media_cache_config_pre_apply(void);
|
||||||
|
|
||||||
|
/*! \brief General configuration options for http media cache. */
|
||||||
|
struct conf_general_options {
|
||||||
|
/*! \brief Request timeout to use */
|
||||||
|
int curl_timeout;
|
||||||
|
|
||||||
|
/*! \brief Follow 3xx redirects automatically. */
|
||||||
|
int curl_followlocation;
|
||||||
|
|
||||||
|
/*! \brief Number of redirects to follow for one request. */
|
||||||
|
int curl_maxredirs;
|
||||||
|
|
||||||
|
/*! \brief Life-time of CURL DNS cache entries. */
|
||||||
|
int curl_dns_cache_timeout;
|
||||||
|
|
||||||
|
AST_DECLARE_STRING_FIELDS(
|
||||||
|
AST_STRING_FIELD(curl_useragent); /*! \brief User-agent to use for requests. */
|
||||||
|
AST_STRING_FIELD(curl_proxy); /*! \brief Proxy to use for requests. None by default. */
|
||||||
|
AST_STRING_FIELD(curl_protocols); /*! \brief Allowed protocols to use for requests. All by default. */
|
||||||
|
AST_STRING_FIELD(curl_redir_protocols); /*! \brief Allowed protocols to use on redirect. All by default. */
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
/*! \brief All configuration options for http media cache. */
|
||||||
|
struct conf {
|
||||||
|
/*! The general section configuration options. */
|
||||||
|
struct conf_general_options *general;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*! \brief Locking container for safe configuration access. */
|
||||||
|
static AO2_GLOBAL_OBJ_STATIC(confs);
|
||||||
|
|
||||||
|
/*! \brief Mapping of the http media cache conf struct's general to the general context in the config file. */
|
||||||
|
static struct aco_type general_option = {
|
||||||
|
.type = ACO_GLOBAL,
|
||||||
|
.name = "general",
|
||||||
|
.item_offset = offsetof(struct conf, general),
|
||||||
|
.category = "general",
|
||||||
|
.category_match = ACO_WHITELIST_EXACT,
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct aco_type *general_options[] = ACO_TYPES(&general_option);
|
||||||
|
|
||||||
|
/*! \brief Disposes of the http media cache conf object */
|
||||||
|
static void conf_destructor(void *obj)
|
||||||
|
{
|
||||||
|
struct conf *cfg = obj;
|
||||||
|
ast_string_field_free_memory(cfg->general);
|
||||||
|
ao2_cleanup(cfg->general);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! \brief Creates the http media cache conf object. */
|
||||||
|
static void *conf_alloc(void)
|
||||||
|
{
|
||||||
|
struct conf *cfg;
|
||||||
|
|
||||||
|
if (!(cfg = ao2_alloc(sizeof(*cfg), conf_destructor))) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(cfg->general = ao2_alloc(sizeof(*cfg->general), NULL))) {
|
||||||
|
ao2_ref(cfg, -1);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ast_string_field_init(cfg->general, 256)) {
|
||||||
|
ao2_ref(cfg, -1);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return cfg;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! \brief The conf file that's processed for the module. */
|
||||||
|
static struct aco_file conf_file = {
|
||||||
|
/*! The config file name. */
|
||||||
|
.filename = "res_http_media_cache.conf",
|
||||||
|
/*! The mapping object types to be processed. */
|
||||||
|
.types = ACO_TYPES(&general_option),
|
||||||
|
};
|
||||||
|
|
||||||
|
CONFIG_INFO_STANDARD(cfg_info, confs, conf_alloc,
|
||||||
|
.pre_apply_config = http_media_cache_config_pre_apply,
|
||||||
|
.files = ACO_FILES(&conf_file));
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Pre-apply callback for the config framework.
|
||||||
|
*
|
||||||
|
* This validates that used options match the ones supported by CURL.
|
||||||
|
*/
|
||||||
|
static int http_media_cache_config_pre_apply(void)
|
||||||
|
{
|
||||||
|
#ifndef AST_CURL_HAS_PROTOCOLS_STR
|
||||||
|
struct conf *cfg = aco_pending_config(&cfg_info);
|
||||||
|
|
||||||
|
if (!ast_strlen_zero(cfg->general->curl_protocols)) {
|
||||||
|
ast_log(AST_LOG_ERROR, "'protocols' not supported by linked CURL library. Please recompile against newer CURL.\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ast_strlen_zero(cfg->general->curl_redir_protocols)) {
|
||||||
|
ast_log(AST_LOG_ERROR, "'redirect_protocols' not supported by linked CURL library. Please recompile against newer CURL.\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*! \brief Data passed to cURL callbacks */
|
/*! \brief Data passed to cURL callbacks */
|
||||||
struct curl_bucket_file_data {
|
struct curl_bucket_file_data {
|
||||||
/*! The \c ast_bucket_file object that caused the operation */
|
/*! The \c ast_bucket_file object that caused the operation */
|
||||||
|
@ -334,6 +486,8 @@ static int bucket_file_expired(struct ast_bucket_file *bucket_file)
|
||||||
*/
|
*/
|
||||||
static CURL *get_curl_instance(struct curl_bucket_file_data *cb_data)
|
static CURL *get_curl_instance(struct curl_bucket_file_data *cb_data)
|
||||||
{
|
{
|
||||||
|
RAII_VAR(struct conf *, cfg, ao2_global_obj_ref(confs), ao2_cleanup);
|
||||||
|
CURLcode rc;
|
||||||
CURL *curl;
|
CURL *curl;
|
||||||
|
|
||||||
curl = curl_easy_init();
|
curl = curl_easy_init();
|
||||||
|
@ -342,14 +496,47 @@ static CURL *get_curl_instance(struct curl_bucket_file_data *cb_data)
|
||||||
}
|
}
|
||||||
|
|
||||||
curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
|
curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
|
||||||
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 180);
|
|
||||||
curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, curl_header_callback);
|
curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, curl_header_callback);
|
||||||
curl_easy_setopt(curl, CURLOPT_USERAGENT, AST_CURL_USER_AGENT);
|
|
||||||
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);
|
|
||||||
curl_easy_setopt(curl, CURLOPT_MAXREDIRS, 8);
|
|
||||||
curl_easy_setopt(curl, CURLOPT_URL, ast_sorcery_object_get_id(cb_data->bucket_file));
|
curl_easy_setopt(curl, CURLOPT_URL, ast_sorcery_object_get_id(cb_data->bucket_file));
|
||||||
curl_easy_setopt(curl, CURLOPT_HEADERDATA, cb_data);
|
curl_easy_setopt(curl, CURLOPT_HEADERDATA, cb_data);
|
||||||
|
|
||||||
|
curl_easy_setopt(curl, CURLOPT_TIMEOUT, cfg->general->curl_timeout);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_USERAGENT, cfg->general->curl_useragent);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, cfg->general->curl_followlocation ? 1 : 0);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_MAXREDIRS, cfg->general->curl_maxredirs);
|
||||||
|
|
||||||
|
if (!ast_strlen_zero(cfg->general->curl_proxy)) {
|
||||||
|
curl_easy_setopt(curl, CURLOPT_PROXY, cfg->general->curl_proxy);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ast_strlen_zero(cfg->general->curl_protocols)) {
|
||||||
|
#ifdef AST_CURL_HAS_PROTOCOLS_STR
|
||||||
|
CURLcode rc = curl_easy_setopt(curl, CURLOPT_PROTOCOLS_STR, cfg->general->curl_protocols);
|
||||||
|
if (rc != CURLE_OK) {
|
||||||
|
ast_log(AST_LOG_ERROR, "Setting protocols to '%s' failed: %d\n", cfg->general->curl_protocols, rc);
|
||||||
|
curl_easy_cleanup(curl);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
if (!ast_strlen_zero(cfg->general->curl_redir_protocols)) {
|
||||||
|
#ifdef AST_CURL_HAS_PROTOCOLS_STR
|
||||||
|
CURLcode rc = curl_easy_setopt(curl, CURLOPT_REDIR_PROTOCOLS_STR, cfg->general->curl_redir_protocols);
|
||||||
|
if (rc != CURLE_OK) {
|
||||||
|
ast_log(AST_LOG_ERROR, "Setting redirect_protocols to '%s' failed: %d\n", cfg->general->curl_redir_protocols, rc);
|
||||||
|
curl_easy_cleanup(curl);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = curl_easy_setopt(curl, CURLOPT_DNS_CACHE_TIMEOUT, cfg->general->curl_dns_cache_timeout);
|
||||||
|
if (rc != CURLE_OK) {
|
||||||
|
ast_log(AST_LOG_ERROR, "Setting dns_cache_timeout to '%d' failed: %d\n", cfg->general->curl_dns_cache_timeout, rc);
|
||||||
|
curl_easy_cleanup(curl);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
return curl;
|
return curl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -551,11 +738,73 @@ static struct ast_sorcery_wizard https_bucket_file_wizard = {
|
||||||
|
|
||||||
static int unload_module(void)
|
static int unload_module(void)
|
||||||
{
|
{
|
||||||
|
aco_info_destroy(&cfg_info);
|
||||||
|
ao2_global_obj_release(confs);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int load_module(void)
|
static int load_module(void)
|
||||||
{
|
{
|
||||||
|
if (aco_info_init(&cfg_info)) {
|
||||||
|
aco_info_destroy(&cfg_info);
|
||||||
|
return AST_MODULE_LOAD_DECLINE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
aco_option_register(&cfg_info, "timeout_secs", ACO_EXACT, general_options,
|
||||||
|
"180", OPT_INT_T, 0,
|
||||||
|
FLDSET(struct conf_general_options, curl_timeout));
|
||||||
|
|
||||||
|
aco_option_register(&cfg_info, "user_agent", ACO_EXACT, general_options,
|
||||||
|
AST_CURL_USER_AGENT, OPT_STRINGFIELD_T, 0,
|
||||||
|
STRFLDSET(struct conf_general_options, curl_useragent));
|
||||||
|
|
||||||
|
aco_option_register(&cfg_info, "follow_location", ACO_EXACT, general_options,
|
||||||
|
"yes", OPT_BOOL_T, 1,
|
||||||
|
FLDSET(struct conf_general_options, curl_followlocation));
|
||||||
|
|
||||||
|
aco_option_register(&cfg_info, "max_redirects", ACO_EXACT, general_options,
|
||||||
|
"8", OPT_INT_T, 0,
|
||||||
|
FLDSET(struct conf_general_options, curl_maxredirs));
|
||||||
|
|
||||||
|
aco_option_register(&cfg_info, "proxy", ACO_EXACT, general_options,
|
||||||
|
NULL, OPT_STRINGFIELD_T, 1,
|
||||||
|
STRFLDSET(struct conf_general_options, curl_proxy));
|
||||||
|
|
||||||
|
aco_option_register(&cfg_info, "dns_cache_timeout_secs", ACO_EXACT, general_options,
|
||||||
|
"60", OPT_INT_T, 0,
|
||||||
|
FLDSET(struct conf_general_options, curl_dns_cache_timeout));
|
||||||
|
|
||||||
|
aco_option_register(&cfg_info, "protocols", ACO_EXACT, general_options,
|
||||||
|
NULL, OPT_STRINGFIELD_T, 1,
|
||||||
|
STRFLDSET(struct conf_general_options, curl_protocols));
|
||||||
|
|
||||||
|
aco_option_register(&cfg_info, "redirect_protocols", ACO_EXACT, general_options,
|
||||||
|
NULL, OPT_STRINGFIELD_T, 1,
|
||||||
|
STRFLDSET(struct conf_general_options, curl_redir_protocols));
|
||||||
|
|
||||||
|
|
||||||
|
if (aco_process_config(&cfg_info, 0) == ACO_PROCESS_ERROR) {
|
||||||
|
struct conf *cfg;
|
||||||
|
|
||||||
|
ast_log(LOG_NOTICE, "Could not load res_http_media_cache config; using defaults\n");
|
||||||
|
cfg = conf_alloc();
|
||||||
|
if (!cfg) {
|
||||||
|
aco_info_destroy(&cfg_info);
|
||||||
|
return AST_MODULE_LOAD_DECLINE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (aco_set_defaults(&general_option, "general", cfg->general)) {
|
||||||
|
ast_log(LOG_ERROR, "Failed to initialize res_http_media_cache defaults.\n");
|
||||||
|
ao2_ref(cfg, -1);
|
||||||
|
aco_info_destroy(&cfg_info);
|
||||||
|
return AST_MODULE_LOAD_DECLINE;
|
||||||
|
}
|
||||||
|
|
||||||
|
ao2_global_obj_replace_unref(confs, cfg);
|
||||||
|
ao2_ref(cfg, -1);
|
||||||
|
}
|
||||||
|
|
||||||
if (ast_bucket_scheme_register("http", &http_bucket_wizard, &http_bucket_file_wizard,
|
if (ast_bucket_scheme_register("http", &http_bucket_wizard, &http_bucket_file_wizard,
|
||||||
NULL, NULL)) {
|
NULL, NULL)) {
|
||||||
ast_log(LOG_ERROR, "Failed to register Bucket HTTP wizard scheme implementation\n");
|
ast_log(LOG_ERROR, "Failed to register Bucket HTTP wizard scheme implementation\n");
|
||||||
|
|
Loading…
Reference in New Issue