From 9c1d4cec94b47cd0b2e69e646f2fd14b84e3cf03 Mon Sep 17 00:00:00 2001 From: Daniel-Constantin Mierla Date: Fri, 30 Jun 2017 15:06:24 +0200 Subject: [PATCH] http_client: allow providing headers to http_client_query() - lost from removed http_query() in utils --- src/modules/http_client/curl_api.c | 4 +- src/modules/http_client/curl_api.h | 8 +- src/modules/http_client/functions.c | 119 +++++++++++++++---------- src/modules/http_client/functions.h | 13 +-- src/modules/http_client/http_client.c | 123 ++++++++++++++++++++------ 5 files changed, 183 insertions(+), 84 deletions(-) diff --git a/src/modules/http_client/curl_api.c b/src/modules/http_client/curl_api.c index 26ed9ad9abe..91dff5af432 100644 --- a/src/modules/http_client/curl_api.c +++ b/src/modules/http_client/curl_api.c @@ -8,7 +8,7 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version - * + * * Kamailio is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the @@ -30,7 +30,7 @@ int bind_httpc_api(httpc_api_t *api) return -1; } api->http_connect = curl_con_query_url; - api->http_query = http_query; + api->http_client_query = http_client_query; api->http_connection_exists = http_connection_exists; api->http_get_content_type = http_get_content_type; diff --git a/src/modules/http_client/curl_api.h b/src/modules/http_client/curl_api.h index 5d06396484a..936c8a6842e 100644 --- a/src/modules/http_client/curl_api.h +++ b/src/modules/http_client/curl_api.h @@ -32,15 +32,17 @@ #include "functions.h" #include "curlcon.h" -typedef int (*httpcapi_httpconnect_f)(struct sip_msg *msg, const str *connection, const str* _url, str* _result, const char *contenttype, const str* _post); -typedef int (*httpcapi_httpquery_f)(struct sip_msg* _m, char* _url, str* _dst, char* _post); +typedef int (*httpcapi_httpconnect_f)(struct sip_msg *msg, const str *connection, + const str* _url, str* _result, const char *contenttype, const str* _post); +typedef int (*httpcapi_httpquery_f)(struct sip_msg* _m, char* _url, str* _dst, + char* _post, char* _hdrs); typedef int (*httpcapi_curlcon_exists_f)(str* _name); typedef char * (*httpcapi_res_content_type_f)(const str* _name); typedef struct httpc_api { httpcapi_httpconnect_f http_connect; - httpcapi_httpquery_f http_query; + httpcapi_httpquery_f http_client_query; httpcapi_curlcon_exists_f http_connection_exists; httpcapi_res_content_type_f http_get_content_type; } httpc_api_t; diff --git a/src/modules/http_client/functions.c b/src/modules/http_client/functions.c index 09e533af5e0..f1a148748fc 100644 --- a/src/modules/http_client/functions.c +++ b/src/modules/http_client/functions.c @@ -55,6 +55,7 @@ typedef struct { char *http_proxy; char *failovercon; char *useragent; + char *hdrs; unsigned int authmethod; unsigned int http_proxy_port; unsigned int tlsversion; @@ -106,10 +107,11 @@ size_t write_function( void *ptr, size_t size, size_t nmemb, void *stream_ptr) /*! Send query to server, optionally post data. */ -static int curL_query_url(struct sip_msg* _m, const char* _url, str* _dst, const curl_query_t * const params) +static int curL_query_url(struct sip_msg* _m, const char* _url, str* _dst, + const curl_query_t * const params) { CURL *curl = NULL;; - CURLcode res; + CURLcode res; char *at = NULL; curl_res_stream_t stream; long stat; @@ -121,7 +123,8 @@ static int curL_query_url(struct sip_msg* _m, const char* _url, str* _dst, const stream.max_size = (size_t) params->maxdatasize; if(params->pconn) { - LM_DBG("****** ##### We have a pconn - keep_connections: %d!\n", params->keep_connections); + LM_DBG("****** ##### We have a pconn - keep_connections: %d!\n", + params->keep_connections); params->pconn->result_content_type[0] = '\0'; params->pconn->redirecturl[0] = '\0'; if (params->pconn->curl != NULL) { @@ -148,19 +151,20 @@ static int curL_query_url(struct sip_msg* _m, const char* _url, str* _dst, const res = curl_easy_setopt(curl, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS); if (params->post) { - char ctype[256]; + /* Now specify we want to POST data */ + res |= curl_easy_setopt(curl, CURLOPT_POST, 1L); - ctype[0] = '\0'; - snprintf(ctype, sizeof(ctype), "Content-Type: %s", params->contenttype); + if(params->contenttype) { + char ctype[256]; - /* Now specify we want to POST data */ - res |= curl_easy_setopt(curl, CURLOPT_POST, 1L); - /* Set the content-type of the DATA */ - headerlist = curl_slist_append(headerlist, ctype); - res |= curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headerlist); + ctype[0] = '\0'; + snprintf(ctype, sizeof(ctype), "Content-Type: %s", params->contenttype); - /* Tell CURL we want to upload using POST */ + /* Set the content-type of the DATA */ + headerlist = curl_slist_append(headerlist, ctype); + } + /* Tell CURL we want to upload using POST */ res |= curl_easy_setopt(curl, CURLOPT_POSTFIELDS, params->post); } else { @@ -168,9 +172,16 @@ static int curL_query_url(struct sip_msg* _m, const char* _url, str* _dst, const res |= curl_easy_setopt(curl, CURLOPT_POST, 0L); } + if (params->hdrs) { + headerlist = curl_slist_append(headerlist, params->hdrs); + } + if(headerlist) { + res |= curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headerlist); + } + if (params->maxdatasize) { - /* Maximum data size to download - we always download full response, but - cut it off before moving to pvar */ + /* Maximum data size to download - we always download full response, + * but cut it off before moving to pvar */ LM_DBG("****** ##### CURL Max datasize %u\n", params->maxdatasize); } @@ -184,45 +195,51 @@ static int curL_query_url(struct sip_msg* _m, const char* _url, str* _dst, const /* Client certificate */ if (params->clientcert != NULL && params->clientkey != NULL) { - res |= curl_easy_setopt(curl, CURLOPT_SSLCERTTYPE, "PEM"); - res |= curl_easy_setopt(curl, CURLOPT_SSLCERT, params->clientcert); + res |= curl_easy_setopt(curl, CURLOPT_SSLCERTTYPE, "PEM"); + res |= curl_easy_setopt(curl, CURLOPT_SSLCERT, params->clientcert); - res |= curl_easy_setopt(curl, CURLOPT_SSLKEYTYPE, "PEM"); - res |= curl_easy_setopt(curl, CURLOPT_SSLKEY, params->clientkey); + res |= curl_easy_setopt(curl, CURLOPT_SSLKEYTYPE, "PEM"); + res |= curl_easy_setopt(curl, CURLOPT_SSLKEY, params->clientkey); } if (params->cacert != NULL) { - res |= curl_easy_setopt(curl, CURLOPT_CAINFO, params->cacert); + res |= curl_easy_setopt(curl, CURLOPT_CAINFO, params->cacert); } if (params->tlsversion != CURL_SSLVERSION_DEFAULT) { - res |= curl_easy_setopt(curl, CURLOPT_SSLVERSION, (long) params->tlsversion); + res |= curl_easy_setopt(curl, CURLOPT_SSLVERSION, + (long) params->tlsversion); } if (params->ciphersuites != NULL) { - res |= curl_easy_setopt(curl, CURLOPT_SSL_CIPHER_LIST, params->ciphersuites); + res |= curl_easy_setopt(curl, CURLOPT_SSL_CIPHER_LIST, + params->ciphersuites); } if (params->http_proxy != NULL) { LM_DBG("****** ##### CURL proxy [%s] \n", params->http_proxy); res |= curl_easy_setopt(curl, CURLOPT_PROXY, params->http_proxy); - } else { + } else { LM_DBG("****** ##### CURL proxy NOT SET \n"); - } + } if (params->http_proxy_port > 0) { - res |= curl_easy_setopt(curl, CURLOPT_PROXYPORT, params->http_proxy_port); + res |= curl_easy_setopt(curl, CURLOPT_PROXYPORT, + params->http_proxy_port); } - res |= curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, (long) params->verify_peer); - res |= curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, (long) params->verify_host?2:0); + res |= curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, + (long) params->verify_peer); + res |= curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, + (long) params->verify_host?2:0); res |= curl_easy_setopt(curl, CURLOPT_NOSIGNAL, (long) 1); res |= curl_easy_setopt(curl, CURLOPT_TIMEOUT, (long) params->timeout); - res |= curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, (long) params->http_follow_redirect); + res |= curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, + (long) params->http_follow_redirect); if (params->http_follow_redirect) { - LM_DBG("****** ##### Following redirects for this request! \n"); + LM_DBG("Following redirects for this request! \n"); } @@ -242,7 +259,8 @@ static int curL_query_url(struct sip_msg* _m, const char* _url, str* _dst, const curl_easy_getinfo(curl, CURLINFO_TOTAL_TIME, &totaltime); curl_easy_getinfo(curl, CURLINFO_APPCONNECT_TIME, &connecttime); - LM_DBG("**** HTTP Call performed in %f s (connect time %f) \n", totaltime, connecttime); + LM_DBG("HTTP Call performed in %f s (connect time %f) \n", + totaltime, connecttime); if (params->pconn) { params->pconn->querytime = totaltime; params->pconn->connecttime = connecttime; @@ -305,7 +323,8 @@ static int curL_query_url(struct sip_msg* _m, const char* _url, str* _dst, const } counter_inc(connfail); if (params->failovercon != NULL) { - LM_ERR("FATAL FAILURE: Trying failover to curl con (%s)\n", params->failovercon); + LM_ERR("FATAL FAILURE: Trying failover to curl con (%s)\n", + params->failovercon); return (1000 + res); } return res; @@ -322,17 +341,19 @@ static int curL_query_url(struct sip_msg* _m, const char* _url, str* _dst, const res |= curl_easy_getinfo(curl, CURLINFO_EFFECTIVE_URL, &url); if(ct) { - LM_DBG("We received Content-Type: %s\n", ct); + LM_DBG("We received Content-Type: %s\n", ct); if (params->pconn) { - strncpy(params->pconn->result_content_type, ct, sizeof(params->pconn->result_content_type)); + strncpy(params->pconn->result_content_type, ct, + sizeof(params->pconn->result_content_type)); } - } + } if(url) { - LM_DBG("We visited URL: %s\n", url); + LM_DBG("We visited URL: %s\n", url); if (params->pconn) { - strncpy(params->pconn->redirecturl, url , sizeof(params->pconn->redirecturl)); + strncpy(params->pconn->redirecturl, url , + sizeof(params->pconn->redirecturl)); } - } + } } if (params->pconn) { params->pconn->last_result = stat; @@ -350,18 +371,22 @@ static int curL_query_url(struct sip_msg* _m, const char* _url, str* _dst, const /* search for line feed */ at = memchr(stream.buf, (char)10, download_size); datasize = (double) (at - stream.buf); - LM_DBG(" -- curl download size cut to first line: %d \n", (int) datasize); + LM_DBG(" -- curl download size cut to first line: %d \n", + (int) datasize); } if (at == NULL) { - if (params->maxdatasize && ((unsigned int) download_size) > params->maxdatasize) { + if (params->maxdatasize + && ((unsigned int) download_size) > params->maxdatasize) { /* Limit at maximum data size */ datasize = (double) params->maxdatasize; - LM_DBG(" -- curl download size cut to maxdatasize : %d \n", (int) datasize); + LM_DBG(" -- curl download size cut to maxdatasize : %d \n", + (int) datasize); } else { /* Limit at actual downloaded data size */ datasize = (double) download_size; - LM_DBG(" -- curl download size cut to download_size : %d \n", (int) datasize); - //at = stream.buf + (unsigned int) download_size; + LM_DBG(" -- curl download size cut to download_size : %d \n", + (int) datasize); + // at = stream.buf + (unsigned int) download_size; } } /* Create a STR object */ @@ -369,7 +394,8 @@ static int curL_query_url(struct sip_msg* _m, const char* _url, str* _dst, const rval.len = datasize; /* Duplicate string to return */ pkg_str_dup(_dst, &rval); - LM_DBG("curl query result: Length %d %.*s \n", rval.len, rval.len, rval.s); + LM_DBG("curl query result - length: %d data: [%.*s]\n", rval.len, + rval.len, rval.s); } else { _dst->s = NULL; _dst->len = 0; @@ -381,7 +407,8 @@ static int curL_query_url(struct sip_msg* _m, const char* _url, str* _dst, const counter_inc(connfail); if (stat >= 500) { if (params->failovercon != NULL) { - LM_ERR("FAILURE: Trying failover to curl con (%s)\n", params->failovercon); + LM_ERR("FAILURE: Trying failover to curl con (%s)\n", + params->failovercon); return (1000 + stat); } } @@ -568,7 +595,8 @@ int curl_con_query_url(struct sip_msg* _m, const str *connection, * to pvar. * This is the same http_query as used to be in the utils module. */ -int http_query(struct sip_msg* _m, char* _url, str* _dst, char* _post) +int http_client_query(struct sip_msg* _m, char* _url, str* _dst, char* _post, + char* _hdrs) { int res; curl_query_t query_params; @@ -577,7 +605,8 @@ int http_query(struct sip_msg* _m, char* _url, str* _dst, char* _post) query_params.username = NULL; query_params.secret = NULL; query_params.authmethod = default_authmethod; - query_params.contenttype = "text/plain"; + query_params.contenttype = NULL; + query_params.hdrs = _hdrs; query_params.post = _post; query_params.clientcert = NULL; query_params.clientkey = NULL; diff --git a/src/modules/http_client/functions.h b/src/modules/http_client/functions.h index 1909bcbc138..722560ab6b9 100644 --- a/src/modules/http_client/functions.h +++ b/src/modules/http_client/functions.h @@ -16,8 +16,8 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ @@ -44,11 +44,12 @@ int curl_get_redirect(struct sip_msg* _m, const str *connection, str* result); -/* - * Performs http_query and saves possible result (first body line of reply) - * to pvar. +/* + * Performs http_client_query and saves possible result + * (first body line of reply) to pvar. */ -int http_query(struct sip_msg* _m, char* _url, str* _dst, char* _post); +int http_client_query(struct sip_msg* _m, char* _url, str* _dst, char* _post, + char* _hdrs); char *http_get_content_type(const str *connection); diff --git a/src/modules/http_client/http_client.c b/src/modules/http_client/http_client.c index 78d05686634..d822c5019be 100644 --- a/src/modules/http_client/http_client.c +++ b/src/modules/http_client/http_client.c @@ -103,12 +103,15 @@ static int fixup_http_query_get(void** param, int param_no); static int fixup_free_http_query_get(void** param, int param_no); static int fixup_http_query_post(void** param, int param_no); static int fixup_free_http_query_post(void** param, int param_no); +static int fixup_http_query_post_hdr(void** param, int param_no); +static int fixup_free_http_query_post_hdr(void** param, int param_no); static int fixup_curl_connect(void** param, int param_no); static int fixup_free_curl_connect(void** param, int param_no); static int fixup_curl_connect_post(void** param, int param_no); static int fixup_free_curl_connect_post(void** param, int param_no); -static int w_curl_connect_post(struct sip_msg* _m, char* _con, char * _url, char* _result, char* _ctype, char* _data); +static int w_curl_connect_post(struct sip_msg* _m, char* _con, char * _url, + char* _result, char* _ctype, char* _data); static int fixup_curl_get_redirect(void** param, int param_no); static int fixup_free_curl_get_redirect(void** param, int param_no); @@ -116,7 +119,10 @@ static int w_curl_get_redirect(struct sip_msg* _m, char* _con, char* _result); /* Wrappers for http_query to be defined later */ static int w_http_query(struct sip_msg* _m, char* _url, char* _result); -static int w_http_query_post(struct sip_msg* _m, char* _url, char* _post, char* _result); +static int w_http_query_post(struct sip_msg* _m, char* _url, char* _post, + char* _result); +static int w_http_query_post_hdr(struct sip_msg* _m, char* _url, char* _post, + char* _hdrs, char* _result); static int w_curl_connect(struct sip_msg* _m, char* _con, char * _url, char* _result); /* forward function */ @@ -132,6 +138,9 @@ static cmd_export_t cmds[] = { {"http_client_query", (cmd_function)w_http_query_post, 3, fixup_http_query_post, fixup_free_http_query_post, REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE}, + {"http_query", (cmd_function)w_http_query_post_hdr, 4, fixup_http_query_post_hdr, + fixup_free_http_query_post_hdr, + REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE}, {"http_connect", (cmd_function)w_curl_connect, 3, fixup_curl_connect, fixup_free_curl_connect, REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE}, @@ -383,7 +392,7 @@ static int fixup_free_http_query_get(void** param, int param_no) if (param_no == 2) { return fixup_free_pvar_null(param, 1); } - + LM_ERR("http_query: invalid parameter number <%d>\n", param_no); return -1; } @@ -621,55 +630,86 @@ static int fixup_free_http_query_post(void** param, int param_no) return -1; } -/*! - * Wrapper for HTTP-Query (GET) +/* + * Fix http_query params: url (string that may contain pvars) and + * result (writable pvar). */ -static int w_http_query(struct sip_msg* _m, char* _url, char* _result) { - int ret = 0; - str url = {NULL, 0}; - str result = {NULL, 0}; - pv_spec_t *dst; - pv_value_t val; +static int fixup_http_query_post_hdr(void** param, int param_no) +{ + if ((param_no >= 1) && (param_no <= 3)) { + return fixup_spve_null(param, 1); + } - if (get_str_fparam(&url, _m, (gparam_p)_url) != 0) { - LM_ERR("URL undefined\n"); - return -1; + if (param_no == 4) { + if (fixup_pvar_null(param, 1) != 0) { + LM_ERR("failed to fixup result pvar\n"); + return -1; + } + if (((pv_spec_t *)(*param))->setf == NULL) { + LM_ERR("result pvar is not writeble\n"); + return -1; + } + return 0; } - ret = http_query(_m, url.s, &result, NULL); + LM_ERR("invalid parameter number <%d>\n", param_no); + return -1; +} - val.rs = result; - val.flags = PV_VAL_STR; - dst = (pv_spec_t *)_result; - dst->setf(_m, &dst->pvp, (int)EQ_T, &val); +/* + * Free http_query params. + */ +static int fixup_free_http_query_post_hdr(void** param, int param_no) +{ + if ((param_no >= 1) && (param_no <= 3)) { + return fixup_free_spve_null(param, 1); + } - if (result.s != NULL) - pkg_free(result.s); - return (ret==0)?-1:ret; -} + if (param_no == 4) { + return fixup_free_pvar_null(param, 1); + } + LM_ERR("invalid parameter number <%d>\n", param_no); + return -1; +} /*! - * Wrapper for HTTP-Query (POST-Variant) + * Wrapper for HTTP-Query function for cfg script */ -static int w_http_query_post(struct sip_msg* _m, char* _url, char* _post, char* _result) { +static int w_http_query_script(sip_msg_t* _m, char* _url, char* _post, + char* _hdrs, char* _result) +{ int ret = 0; str url = {NULL, 0}; str post = {NULL, 0}; + str hdrs = {NULL, 0}; str result = {NULL, 0}; pv_spec_t *dst; pv_value_t val; - if (get_str_fparam(&url, _m, (gparam_p)_url) != 0) { + if (get_str_fparam(&url, _m, (gparam_p)_url) != 0 || url.len<=0) { LM_ERR("URL has no value\n"); return -1; } - if (get_str_fparam(&post, _m, (gparam_p)_post) != 0) { + if (_post && get_str_fparam(&post, _m, (gparam_p)_post) != 0) { LM_ERR("DATA has no value\n"); return -1; + } else { + if(post.len==0) { + post.s = NULL; + } } + if (_hdrs && get_str_fparam(&hdrs, _m, (gparam_p)_hdrs) != 0) { + LM_ERR("HDRS has no value\n"); + return -1; + } else { + if(hdrs.len==0) { + hdrs.s = NULL; + } + } + + ret = http_client_query(_m, url.s, &result, post.s, hdrs.s); - ret = http_query(_m, url.s, &result, post.s); val.rs = result; val.flags = PV_VAL_STR; dst = (pv_spec_t *)_result; @@ -677,9 +717,36 @@ static int w_http_query_post(struct sip_msg* _m, char* _url, char* _post, char* if (result.s != NULL) pkg_free(result.s); + return (ret==0)?-1:ret; } +/*! + * Wrapper for HTTP-Query (GET) + */ +static int w_http_query(struct sip_msg* _m, char* _url, char* _result) +{ + return w_http_query_script(_m, _url, NULL, NULL, _result); +} + +/*! + * Wrapper for HTTP-Query (POST-Variant) + */ +static int w_http_query_post(struct sip_msg* _m, char* _url, char* _post, + char* _result) +{ + return w_http_query_script(_m, _url, _post, NULL, _result); +} + +/*! + * Wrapper for HTTP-Query (HDRS-Variant) + */ +static int w_http_query_post_hdr(struct sip_msg* _m, char* _url, char* _post, + char* _hdrs, char* _result) +{ + return w_http_query_script(_m, _url, _post, _hdrs, _result); +} + /*! * Parse arguments to pv $curlerror */