/** * Copyright 2016 (C) Federico Cabiddu * Copyright 2016 (C) Giacomo Vacca * Copyright 2016 (C) Orange - Camille Oudot * * This file is part of Kamailio, a free SIP server. * * This file is free software; you can redistribute it and/or modify * 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 * * * This file 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 * 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 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ /*! \file * \brief Kamailio http_async_client :: The module interface file * \ingroup http_async_client */ /*! \defgroup http_async_client Kamailio :: Async module interface to Curl/HTTP * * http://curl.haxx.se * A generic library for many protocols * */ #include #include #include #include #include "../../core/globals.h" #include "../../core/sr_module.h" #include "../../core/dprint.h" #include "../../core/ut.h" #include "../../core/pt.h" #include "../../core/pvar.h" #include "../../core/mem/shm_mem.h" #include "../../core/mod_fix.h" #include "../../core/cfg/cfg_struct.h" #include "../../core/fmsg.h" #include "../../core/kemi.h" #include "../../modules/tm/tm_load.h" #include "../../modules/pv/pv_api.h" #include "async_http.h" MODULE_VERSION extern int num_workers; extern unsigned int q_idx; extern char q_id[MAX_ID_LEN + 1]; int http_timeout = 500; /* query timeout in ms */ int tcp_keepalive = 0; /* TCP keepalives (default disabled) */ int tcp_ka_idle = 0; /* TCP keep-alive idle time wait */ int tcp_ka_interval = 0; /* TCP keep-alive interval */ int hash_size = 2048; int tls_version = 0; // Use default SSL version in HTTPS requests (see curl/curl.h) int tls_verify_host = 1; // By default verify host in HTTPS requests int tls_verify_peer = 1; // By default verify peer in HTTPS requests int curl_verbose = 0; int curl_follow_redirect = 0; char *tls_client_cert = NULL; // client SSL certificate path, defaults to NULL char *tls_client_key = NULL; // client SSL certificate key path, defaults to NULL char *tls_ca_path = NULL; // certificate authority dir path, defaults to NULL static char *memory_manager = "shm"; extern int curl_memory_manager; unsigned int default_authmethod = CURLAUTH_BASIC | CURLAUTH_DIGEST; static int mod_init(void); static int child_init(int); static void mod_destroy(void); static int w_http_async_query(sip_msg_t *msg, char *query, char *rt); static int set_query_param(str *param, str input); /* pv api binding */ static int ah_get_reason( struct sip_msg *msg, pv_param_t *param, pv_value_t *res); static int ah_get_hdr(struct sip_msg *msg, pv_param_t *param, pv_value_t *res); static int w_pv_parse_hdr_name(pv_spec_p sp, str *in); static int ah_get_status( struct sip_msg *msg, pv_param_t *param, pv_value_t *res); static int ah_get_msg_body( struct sip_msg *msg, pv_param_t *param, pv_value_t *res); static int ah_get_body_size( struct sip_msg *msg, pv_param_t *param, pv_value_t *res); static int ah_get_msg_buf( struct sip_msg *msg, pv_param_t *param, pv_value_t *res); static int ah_get_msg_len( struct sip_msg *msg, pv_param_t *param, pv_value_t *res); static int ah_parse_req_name(pv_spec_p sp, str *in); static int ah_parse_time_name(pv_spec_p sp, str *in); static int ah_set_req( struct sip_msg *msg, pv_param_t *param, int op, pv_value_t *val); static int ah_get_id(struct sip_msg *msg, pv_param_t *param, pv_value_t *res); static str pv_str_1 = {"1", 1}; static str pv_str_0 = {"0", 1}; static int ah_get_ok(struct sip_msg *msg, pv_param_t *param, pv_value_t *res); static int ah_get_err(struct sip_msg *msg, pv_param_t *param, pv_value_t *res); static int ah_get_time(struct sip_msg *msg, pv_param_t *param, pv_value_t *res); /* tm */ struct tm_binds tmb; /* pv */ pv_api_t pv_api; stat_var *requests; stat_var *replies; stat_var *errors; stat_var *timeouts; enum http_req_name_t { E_HRN_ALL = 0, E_HRN_HDR, E_HRN_METHOD, E_HRN_TIMEOUT, E_HRN_TLS_CA_PATH, E_HRN_TLS_CLIENT_KEY, E_HRN_TLS_CLIENT_CERT, E_HRN_SUSPEND, E_HRN_BODY, E_HRN_AUTHMETHOD, E_HRN_USERNAME, E_HRN_PASSWORD, E_HRN_TCP_KA, E_HRN_TCP_KA_IDLE, E_HRN_TCP_KA_INTERVAL, E_HRN_FOLLOW_REDIRECT }; enum http_time_name_t { E_HT_TOTAL = 0, E_HT_LOOKUP, E_HT_CONNECT, E_HT_REDIRECT, E_HT_APPCONNECT, E_HT_PRETRANSFER, E_HT_STARTTRANSFER }; static cmd_export_t cmds[] = { {"http_async_query", (cmd_function)w_http_async_query, 2, fixup_spve_spve, 0, ANY_ROUTE}, {0, 0, 0, 0, 0, 0}}; static param_export_t params[] = {{"workers", INT_PARAM, &num_workers}, {"connection_timeout", INT_PARAM, &http_timeout}, {"hash_size", INT_PARAM, &hash_size}, {"tls_version", INT_PARAM, &tls_version}, {"tls_verify_host", INT_PARAM, &tls_verify_host}, {"tls_verify_peer", INT_PARAM, &tls_verify_peer}, {"curl_verbose", INT_PARAM, &curl_verbose}, {"curl_follow_redirect", INT_PARAM, &curl_follow_redirect}, {"tls_client_cert", PARAM_STRING, &tls_client_cert}, {"tls_client_key", PARAM_STRING, &tls_client_key}, {"tls_ca_path", PARAM_STRING, &tls_ca_path}, {"memory_manager", PARAM_STRING, &memory_manager}, {"authmethod", PARAM_INT, &default_authmethod}, {"tcp_keepalive", INT_PARAM, &tcp_keepalive}, {"tcp_ka_idle", INT_PARAM, &tcp_ka_idle}, {"tcp_ka_interval", INT_PARAM, &tcp_ka_interval}, {0, 0, 0}}; /*! \brief We expose internal variables via the statistic framework below.*/ stat_export_t mod_stats[] = {{"requests", STAT_NO_RESET, &requests}, {"replies", STAT_NO_RESET, &replies}, {"errors", STAT_NO_RESET, &errors}, {"timeouts", STAT_NO_RESET, &timeouts}, {0, 0, 0}}; static pv_export_t pvs[] = { {STR_STATIC_INIT("http_hdr"), PVT_HDR, ah_get_hdr, 0, w_pv_parse_hdr_name, pv_parse_index, 0, 0}, {STR_STATIC_INIT("http_rr"), PVT_OTHER, ah_get_reason, 0, 0, 0, 0, 0}, {STR_STATIC_INIT("http_rs"), PVT_OTHER, ah_get_status, 0, 0, 0, 0, 0}, {STR_STATIC_INIT("http_rb"), PVT_MSG_BODY, ah_get_msg_body, 0, 0, 0, 0, 0}, {STR_STATIC_INIT("http_bs"), PVT_OTHER, ah_get_body_size, 0, 0, 0, 0, 0}, {STR_STATIC_INIT("http_mb"), PVT_OTHER, ah_get_msg_buf, 0, 0, 0, 0, 0}, {STR_STATIC_INIT("http_ml"), PVT_OTHER, ah_get_msg_len, 0, 0, 0, 0, 0}, {STR_STATIC_INIT("http_ok"), PVT_OTHER, ah_get_ok, 0, 0, 0, 0, 0}, {STR_STATIC_INIT("http_err"), PVT_OTHER, ah_get_err, 0, 0, 0, 0, 0}, {STR_STATIC_INIT("http_time"), PVT_OTHER, ah_get_time, 0, ah_parse_time_name, 0, 0, 0}, {STR_STATIC_INIT("http_req"), PVT_OTHER, pv_get_null, ah_set_req, ah_parse_req_name, 0, 0, 0}, {STR_STATIC_INIT("http_req_id"), PVT_OTHER, ah_get_id, 0, 0, 0, 0, 0}, {{0, 0}, 0, 0, 0, 0, 0, 0, 0}}; struct module_exports exports = { "http_async_client", /* module name */ DEFAULT_DLFLAGS, /* dlopen flags */ cmds, /* exported functions */ params, /* exported parameters */ 0, /* RPC method exports */ pvs, /* exported pseudo-variables */ 0, /* response handling function */ mod_init, /* module initialization function */ child_init, /* per-child init function */ mod_destroy /* module destroy function */ }; /** * init module function */ static int mod_init(void) { unsigned int n; LM_INFO("Initializing Http Async module\n"); #ifdef STATISTICS /* register statistics */ if(register_module_stats("http_async_client", mod_stats) != 0) { LM_ERR("failed to register core statistics\n"); return -1; } #endif /* sanitize hash_size */ if(hash_size < 1) { LM_WARN("hash_size is smaller " "than 1 -> rounding from %d to 1\n", hash_size); hash_size = 1; } /* check that the hash table size is a power of 2 */ for(n = 0; n < (8 * sizeof(n)); n++) { if(hash_size == (1 << n)) break; if(n && hash_size < (1 << n)) { LM_WARN("hash_size is not a power " "of 2 as it should be -> rounding from %d to %d (n=%d)\n", hash_size, 1 << (n - 1), n); hash_size = 1 << (n - 1); break; } } /* check 'workers' param */ if(num_workers < 1) { LM_ERR("the 'workers' parameter must be >= 1\n"); return -1; } tls_verify_host = tls_verify_host ? 1 : 0; tls_verify_peer = tls_verify_peer ? 1 : 0; if(tcp_keepalive) { LM_INFO("TCP keepalives enabled\n"); } /* check tcp keepalive parameters */ if((tcp_ka_idle > 0 || tcp_ka_interval > 0) && !(tcp_keepalive > 0)) { LM_WARN("either 'tcp_ka_idle' or 'tcp_ka_interval' are set but " "'tcp_keepalive' is disabled: they will be ignored\n"); } /* init http parameters list */ init_query_params(&ah_params); if(strncmp("shm", memory_manager, 3) == 0) { curl_memory_manager = 0; } else if(strncmp("sys", memory_manager, 3) == 0) { curl_memory_manager = 1; } else { LM_ERR("invalid memory_manager parameter: '%s'\n", memory_manager); return -1; } set_curl_mem_callbacks(); /* init faked sip msg */ if(faked_msg_init() < 0) { LM_ERR("failed to init faked sip msg\n"); return -1; } if(load_tm_api(&tmb) < 0) { LM_INFO("cannot load the TM-functions - async relay disabled\n"); return -1; } /* allocate workers array */ workers = shm_malloc(num_workers * sizeof(*workers)); if(workers == NULL) { LM_ERR("error in shm_malloc\n"); return -1; } memset(workers, 0, num_workers * sizeof(*workers)); register_procs(num_workers); /* add child to update local config framework structures */ cfg_register_child(num_workers); return 0; } /** * @brief Initialize async module children */ static int child_init(int rank) { int pid; int i; LM_DBG("child initializing async http (rank: %d)\n", rank); if(num_workers <= 0) return 0; /* initialize query counter and id */ q_idx = 0; q_id[0] = '\0'; if(rank == PROC_INIT) { for(i = 0; i < num_workers; i++) { LM_DBG("initializing worker notification socket: %d (rank: %d)\n", i, rank); if(async_http_init_sockets(&workers[i]) < 0) { LM_ERR("failed to initialize tasks sockets\n"); return -1; } } return 0; } if(rank > 0) { for(i = 0; i < num_workers; i++) { close(workers[i].notication_socket[0]); } return 0; } if(rank != PROC_MAIN) return 0; for(i = 0; i < num_workers; i++) { if(async_http_init_worker(i + 1, &workers[i]) < 0) { LM_ERR("failed to initialize worker process: %d\n", i); return -1; } pid = fork_process(PROC_RPC, "Http Async Worker", 1); if(pid < 0) return -1; /* error */ if(pid == 0) { /* child */ /* enforce http_reply_parse=yes */ http_reply_parse = 1; /* initialize the config framework */ if(cfg_child_init()) return -1; /* init msg structure for http reply parsing */ ah_reply = pkg_malloc(sizeof(struct sip_msg)); if(!ah_reply) { LM_ERR("failed to allocate pkg memory\n"); return -1; } memset(ah_reply, 0, sizeof(struct sip_msg)); /* main function for workers */ async_http_run_worker(&workers[i]); } } return 0; } /** * destroy module function */ static void mod_destroy(void) { } /** * */ static int w_http_async_query(sip_msg_t *msg, char *query, char *rt) { str sdata; str rn; if(msg == NULL) return -1; if(fixup_get_svalue(msg, (gparam_t *)query, &sdata) != 0) { LM_ERR("unable to get data\n"); return -1; } if(sdata.s == NULL || sdata.len == 0) { LM_ERR("invalid data parameter\n"); return -1; } if(fixup_get_svalue(msg, (gparam_t *)rt, &rn) != 0) { LM_ERR("no route block name\n"); return -1; } if(rn.s == NULL || rn.len == 0) { LM_ERR("invalid route name parameter\n"); return -1; } return async_send_query(msg, &sdata, &rn); } /** * */ static int ki_http_async_query(sip_msg_t *msg, str *sdata, str *rn) { if(msg == NULL) return -1; if(sdata == NULL || sdata->len <= 0) { LM_ERR("invalid data parameter\n"); return -1; } if(rn->s == NULL || rn->len <= 0) { LM_ERR("invalid route name parameter\n"); return -1; } return async_send_query(msg, sdata, rn); } #define _IVALUE_ERROR(NAME) \ LM_ERR("invalid parameter '" #NAME "' (must be a number)\n") #define _IVALUE(NAME) \ int i_##NAME; \ if(fixup_get_ivalue(msg, (gparam_t *)NAME, &(i_##NAME)) != 0) { \ _IVALUE_ERROR(NAME); \ return -1; \ } /* * Helper to copy input string parameter into a query parameter */ static int set_query_param(str *param, str input) { if(param->s) { shm_free(param->s); param->s = NULL; param->len = 0; } if(input.s && input.len > 0) { if(shm_str_dup(param, &input) < 0) { LM_ERR("Error allocating parameter\n"); return -1; } } return 1; } /* * Helper to copy input string parameter into a query char* parameter */ static int set_query_cparam(char **param, str input) { if(*param) { shm_free(*param); *param = NULL; } if(input.s && input.len > 0) { *param = shm_malloc(input.len + 1); if(*param == NULL) { LM_ERR("error in shm_malloc\n"); return -1; } strncpy(*param, input.s, input.len); (*param)[input.len] = '\0'; LM_DBG("param set to '%s'\n", *param); } return 1; } /* module PVs */ #define AH_WRAP_GET_PV(AH_F, PV_F) \ static int AH_F(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) \ { \ if(ah_reply) { \ if(ah_error.s) { \ LM_WARN("an async variable was read after http error, use " \ "$http_ok to check the request's status\n"); \ return pv_get_null(msg, param, res); \ } else { \ return pv_api.PV_F(ah_reply, param, res); \ } \ } else { \ LM_ERR("the async variables can only be read from an async http " \ "worker\n"); \ return pv_get_null(msg, param, res); \ } \ } AH_WRAP_GET_PV(ah_get_reason, get_reason) AH_WRAP_GET_PV(ah_get_hdr, get_hdr) AH_WRAP_GET_PV(ah_get_status, get_status) AH_WRAP_GET_PV(ah_get_msg_body, get_msg_body) AH_WRAP_GET_PV(ah_get_body_size, get_body_size) AH_WRAP_GET_PV(ah_get_msg_buf, get_msg_buf) AH_WRAP_GET_PV(ah_get_msg_len, get_msg_len) static int w_pv_parse_hdr_name(pv_spec_p sp, str *in) { return pv_api.parse_hdr_name(sp, in); } static int ah_get_id(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) { return pv_get_strlval(msg, param, res, q_id, strlen(q_id)); } static int ah_get_ok(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) { if(ah_reply) { if(ah_error.s) { return pv_get_intstrval(msg, param, res, 0, &pv_str_0); } else { return pv_get_intstrval(msg, param, res, 1, &pv_str_1); } } else { LM_ERR("the async variables can only be read from an async http " "worker\n"); return pv_get_null(msg, param, res); } } static int ah_get_err(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) { if(ah_reply) { if(ah_error.s) { return pv_get_strval(msg, param, res, &ah_error); } else { return pv_get_null(msg, param, res); } } else { LM_ERR("the async variables can only be read from an async http " "worker\n"); return pv_get_null(msg, param, res); } } static int ah_get_time(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) { if(msg == NULL || param == NULL) return -1; switch(param->pvn.u.isname.name.n) { case E_HT_LOOKUP: return pv_get_uintval(msg, param, res, ah_time.lookup); case E_HT_CONNECT: return pv_get_uintval(msg, param, res, ah_time.connect); case E_HT_REDIRECT: return pv_get_uintval(msg, param, res, ah_time.redirect); case E_HT_APPCONNECT: return pv_get_uintval(msg, param, res, ah_time.appconnect); case E_HT_PRETRANSFER: return pv_get_uintval(msg, param, res, ah_time.pretransfer); case E_HT_STARTTRANSFER: return pv_get_uintval(msg, param, res, ah_time.starttransfer); default: return pv_get_uintval(msg, param, res, ah_time.total); } } static int ah_parse_req_name(pv_spec_p sp, str *in) { if(sp == NULL || in == NULL || in->len <= 0) return -1; switch(in->len) { case 3: if(strncmp(in->s, "all", 3) == 0) sp->pvp.pvn.u.isname.name.n = E_HRN_ALL; else if(strncmp(in->s, "hdr", 3) == 0) sp->pvp.pvn.u.isname.name.n = E_HRN_HDR; else goto error; break; case 4: if(strncmp(in->s, "body", 4) == 0) sp->pvp.pvn.u.isname.name.n = E_HRN_BODY; else goto error; break; case 6: if(strncmp(in->s, "method", 6) == 0) sp->pvp.pvn.u.isname.name.n = E_HRN_METHOD; else goto error; break; case 7: if(strncmp(in->s, "timeout", 7) == 0) sp->pvp.pvn.u.isname.name.n = E_HRN_TIMEOUT; else if(strncmp(in->s, "suspend", 7) == 0) sp->pvp.pvn.u.isname.name.n = E_HRN_SUSPEND; else if(strncmp(in->s, "ka-idle", 7) == 0) sp->pvp.pvn.u.isname.name.n = E_HRN_TCP_KA_IDLE; else goto error; break; case 8: if(strncmp(in->s, "username", 8) == 0) sp->pvp.pvn.u.isname.name.n = E_HRN_USERNAME; else if(strncmp(in->s, "password", 8) == 0) sp->pvp.pvn.u.isname.name.n = E_HRN_PASSWORD; else goto error; break; case 9: if(strncmp(in->s, "keepalive", 9) == 0) sp->pvp.pvn.u.isname.name.n = E_HRN_TCP_KA; else goto error; break; case 10: if(strncmp(in->s, "authmethod", 10) == 0) sp->pvp.pvn.u.isname.name.n = E_HRN_AUTHMETHOD; else goto error; break; case 11: if(strncmp(in->s, "tls_ca_path", 11) == 0) sp->pvp.pvn.u.isname.name.n = E_HRN_TLS_CA_PATH; else if(strncmp(in->s, "ka-interval", 11) == 0) sp->pvp.pvn.u.isname.name.n = E_HRN_TCP_KA_INTERVAL; else goto error; break; case 14: if(strncmp(in->s, "tls_client_key", 14) == 0) sp->pvp.pvn.u.isname.name.n = E_HRN_TLS_CLIENT_KEY; else goto error; break; case 15: if(strncmp(in->s, "tls_client_cert", 15) == 0) sp->pvp.pvn.u.isname.name.n = E_HRN_TLS_CLIENT_CERT; else if(strncmp(in->s, "follow_redirect", 15) == 0) sp->pvp.pvn.u.isname.name.n = E_HRN_FOLLOW_REDIRECT; else goto error; break; default: goto error; } sp->pvp.pvn.type = PV_NAME_INTSTR; sp->pvp.pvn.u.isname.type = 0; return 0; error: LM_ERR("unknown http_req name %.*s\n", in->len, in->s); return -1; } static int ah_parse_time_name(pv_spec_p sp, str *in) { if(sp == NULL || in == NULL || in->len < 0) return -1; switch(in->len) { case 5: if(strncmp(in->s, "total", 5) == 0) sp->pvp.pvn.u.isname.name.n = E_HT_TOTAL; else goto error; break; case 6: if(strncmp(in->s, "lookup", 6) == 0) sp->pvp.pvn.u.isname.name.n = E_HT_LOOKUP; else goto error; break; case 7: if(strncmp(in->s, "connect", 7) == 0) sp->pvp.pvn.u.isname.name.n = E_HT_CONNECT; else goto error; break; case 8: if(strncmp(in->s, "redirect", 8) == 0) sp->pvp.pvn.u.isname.name.n = E_HT_REDIRECT; else goto error; break; case 10: if(strncmp(in->s, "appconnect", 10) == 0) sp->pvp.pvn.u.isname.name.n = E_HT_APPCONNECT; else goto error; break; case 11: if(strncmp(in->s, "pretransfer", 11) == 0) sp->pvp.pvn.u.isname.name.n = E_HT_PRETRANSFER; else goto error; break; case 13: if(strncmp(in->s, "starttransfer", 13) == 0) sp->pvp.pvn.u.isname.name.n = E_HT_STARTTRANSFER; else goto error; break; default: goto error; } sp->pvp.pvn.type = PV_NAME_INTSTR; sp->pvp.pvn.u.isname.type = 0; return 0; error: LM_ERR("unknown http_time name %.*s\n", in->len, in->s); return -1; } static int ah_set_req( struct sip_msg *msg, pv_param_t *param, int op, pv_value_t *val) { pv_value_t *tval; if(param == NULL || tmb.t_request == NULL) return -1; tval = val; if((tval != NULL) && (tval->flags & PV_VAL_NULL)) { tval = NULL; } switch((enum http_req_name_t)param->pvn.u.isname.name.n) { case E_HRN_ALL: if(tval == NULL) set_query_params(&ah_params); break; case E_HRN_HDR: if(tval) { if(!(tval->flags & PV_VAL_STR)) { LM_ERR("invalid value type for $http_req(hdr)\n"); return -1; } header_list_add(&ah_params.headers, &tval->rs); } break; case E_HRN_METHOD: if(tval) { if(!(tval->flags & PV_VAL_STR)) { LM_ERR("invalid value type for $http_req(method)\n"); return -1; } query_params_set_method(&ah_params, &tval->rs); } else { ah_params.method = AH_METH_DEFAULT; } break; case E_HRN_TIMEOUT: if(tval) { if(!(tval->flags & PV_VAL_INT)) { LM_ERR("invalid value type for $http_req(timeout)\n"); return -1; } ah_params.timeout = tval->ri; } else { ah_params.timeout = http_timeout; } break; case E_HRN_TLS_CA_PATH: if(tval) { if(!(tval->flags & PV_VAL_STR)) { LM_ERR("invalid value type for $http_req(tls_ca_path)\n"); return -1; } set_query_cparam(&ah_params.tls_ca_path, tval->rs); } break; case E_HRN_TLS_CLIENT_KEY: if(tval) { if(!(tval->flags & PV_VAL_STR)) { LM_ERR("invalid value type for " "$http_req(tls_client_key)\n"); return -1; } set_query_cparam(&ah_params.tls_client_key, tval->rs); } break; case E_HRN_TLS_CLIENT_CERT: if(tval) { if(!(tval->flags & PV_VAL_STR)) { LM_ERR("invalid value type for " "$http_req(tls_client_cert)\n"); return -1; } set_query_cparam(&ah_params.tls_client_cert, tval->rs); } break; case E_HRN_SUSPEND: if(tval) { if(!(tval->flags & PV_VAL_INT)) { LM_ERR("invalid value type for $http_req(suspend)\n"); return -1; } ah_params.suspend_transaction = tval->ri ? 1 : 0; } else { ah_params.suspend_transaction = 1; } break; case E_HRN_BODY: if(tval) { if(!(tval->flags & PV_VAL_STR)) { LM_ERR("invalid value type for $http_req(body)\n"); return -1; } set_query_param(&ah_params.body, tval->rs); } break; case E_HRN_AUTHMETHOD: if(tval) { if(!(tval->flags & PV_VAL_INT)) { LM_ERR("invalid value type for $http_req(authmethod)\n"); return -1; } ah_params.authmethod = tval->ri; } else { ah_params.authmethod = default_authmethod; } break; case E_HRN_USERNAME: if(tval) { if(!(tval->flags & PV_VAL_STR)) { LM_ERR("invalid value type for $http_req(username)\n"); return -1; } set_query_cparam(&ah_params.username, tval->rs); } break; case E_HRN_PASSWORD: if(tval) { if(!(tval->flags & PV_VAL_STR)) { LM_ERR("invalid value type for $http_req(password)\n"); return -1; } set_query_cparam(&ah_params.password, tval->rs); } break; case E_HRN_TCP_KA: if(tval) { if(!(tval->flags & PV_VAL_INT)) { LM_ERR("invalid value type for $http_req(keepalive)\n"); return -1; } ah_params.tcp_keepalive = tval->ri; } else { ah_params.tcp_keepalive = tcp_keepalive; } break; case E_HRN_TCP_KA_IDLE: if(tval) { if(!(tval->flags & PV_VAL_INT)) { LM_ERR("invalid value type for $http_req(ka-idle)\n"); return -1; } ah_params.tcp_ka_idle = tval->ri; } else { ah_params.tcp_ka_idle = tcp_ka_idle; } break; case E_HRN_TCP_KA_INTERVAL: if(tval) { if(!(tval->flags & PV_VAL_INT)) { LM_ERR("invalid value type for $http_req(ka-interval)\n"); return -1; } ah_params.tcp_ka_interval = tval->ri; } else { ah_params.tcp_ka_interval = tcp_ka_interval; } break; case E_HRN_FOLLOW_REDIRECT: if(tval) { if(!(tval->flags & PV_VAL_INT)) { LM_ERR("invalid value type for " "$http_req(follow_redirect)\n"); return -1; } ah_params.follow_redirect = tval->ri ? 1 : 0; } else { ah_params.follow_redirect = curl_follow_redirect; } break; } return 1; } /** * */ /* clang-format off */ static sr_kemi_t sr_kemi_http_async_client_exports[] = { { str_init("http_async_client"), str_init("query"), SR_KEMIP_INT, ki_http_async_query, { SR_KEMIP_STR, SR_KEMIP_STR, SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE } }, { {0, 0}, {0, 0}, 0, NULL, { 0, 0, 0, 0, 0, 0 } } }; /* clang-format on */ int mod_register(char *path, int *dlflags, void *p1, void *p2) { pv_register_api_t pvra; pvra = (pv_register_api_t)find_export("pv_register_api", NO_SCRIPT, 0); if(!pvra) { LM_ERR("Cannot import pv functions (pv module must be loaded before " "this module)\n"); return -1; } pvra(&pv_api); sr_kemi_modules_add(sr_kemi_http_async_client_exports); return 0; }