mirror of https://github.com/sipwise/kamailio.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
949 lines
25 KiB
949 lines
25 KiB
/**
|
|
* Copyright 2016 (C) Federico Cabiddu <federico.cabiddu@gmail.com>
|
|
* Copyright 2016 (C) Giacomo Vacca <giacomo.vacca@gmail.com>
|
|
* Copyright 2016 (C) Orange - Camille Oudot <camille.oudot@orange.com>
|
|
*
|
|
* 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 <stdio.h>
|
|
#include <unistd.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#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;
|
|
}
|