http_async_client: add parameters to enable/set tcp keepalive
[sip-router] / src / modules / http_async_client / http_async_client_mod.c
1 /**
2  * Copyright 2016 (C) Federico Cabiddu <federico.cabiddu@gmail.com>
3  * Copyright 2016 (C) Giacomo Vacca <giacomo.vacca@gmail.com>
4  * Copyright 2016 (C) Orange - Camille Oudot <camille.oudot@orange.com>
5  *
6  * This file is part of Kamailio, a free SIP server.
7  *
8  * This file is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version
12  *
13  *
14  * This file is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22  *
23  */
24
25 /*! \file
26  * \brief  Kamailio http_async_client :: The module interface file
27  * \ingroup http_async_client
28  */
29
30 /*! \defgroup http_async_client Kamailio :: Async module interface to Curl/HTTP
31  *
32  * http://curl.haxx.se
33  * A generic library for many protocols
34  *
35  */
36
37 #include <stdio.h>
38 #include <unistd.h>
39 #include <stdlib.h>
40 #include <string.h>
41
42 #include "../../core/globals.h"
43 #include "../../core/sr_module.h"
44 #include "../../core/dprint.h"
45 #include "../../core/ut.h"
46 #include "../../core/pt.h"
47 #include "../../core/pvar.h"
48 #include "../../core/mem/shm_mem.h"
49 #include "../../core/mod_fix.h"
50 #include "../../core/pvar.h"
51 #include "../../core/cfg/cfg_struct.h"
52 #include "../../core/fmsg.h"
53 #include "../../core/kemi.h"
54
55 #include "../../modules/tm/tm_load.h"
56 #include "../../modules/pv/pv_api.h"
57
58
59 #include "async_http.h"
60
61 MODULE_VERSION
62
63 extern int  num_workers;
64
65 extern unsigned int q_idx;
66 extern char q_id[MAX_ID_LEN+1];
67
68 int http_timeout = 500; /* query timeout in ms */
69 int tcp_keepalive = 0; /* TCP keepalives (default disabled) */
70 int tcp_ka_idle = 0; /* TCP keep-alive idle time wait */
71 int tcp_ka_interval = 0; /* TCP keep-alive interval */
72 int hash_size = 2048;
73 int tls_version = 0; // Use default SSL version in HTTPS requests (see curl/curl.h)
74 int tls_verify_host = 1; // By default verify host in HTTPS requests
75 int tls_verify_peer = 1; // By default verify peer in HTTPS requests
76 int curl_verbose = 0;
77 char* tls_client_cert = NULL; // client SSL certificate path, defaults to NULL
78 char* tls_client_key = NULL; // client SSL certificate key path, defaults to NULL
79 char* tls_ca_path = NULL; // certificate authority dir path, defaults to NULL
80 static char *memory_manager = "shm";
81 extern int curl_memory_manager;
82 unsigned int default_authmethod = CURLAUTH_BASIC | CURLAUTH_DIGEST;
83
84 static int  mod_init(void);
85 static int  child_init(int);
86 static void mod_destroy(void);
87
88 static int w_http_async_query(sip_msg_t* msg, char* query, char* rt);
89 static int set_query_param(str* param, str input);
90
91 /* pv api binding */
92 static int ah_get_reason(struct sip_msg *msg, pv_param_t *param, pv_value_t *res);
93 static int ah_get_hdr(struct sip_msg *msg, pv_param_t *param, pv_value_t *res);
94 static int w_pv_parse_hdr_name(pv_spec_p sp, str *in);
95 static int ah_get_status(struct sip_msg *msg, pv_param_t *param, pv_value_t *res);
96 static int ah_get_msg_body(struct sip_msg *msg, pv_param_t *param, pv_value_t *res);
97 static int ah_get_body_size(struct sip_msg *msg, pv_param_t *param, pv_value_t *res);
98 static int ah_get_msg_buf(struct sip_msg *msg, pv_param_t *param, pv_value_t *res);
99 static int ah_get_msg_len(struct sip_msg *msg, pv_param_t *param, pv_value_t *res);
100 static int ah_parse_req_name(pv_spec_p sp, str *in);
101 static int ah_set_req(struct sip_msg* msg, pv_param_t *param, int op, pv_value_t *val);
102 static int ah_get_id(struct sip_msg *msg, pv_param_t *param, pv_value_t *res);
103
104
105 static str pv_str_1 = {"1", 1};
106 static str pv_str_0 = {"0", 1};
107
108 static int ah_get_ok(struct sip_msg *msg, pv_param_t *param, pv_value_t *res);
109 static int ah_get_err(struct sip_msg *msg, pv_param_t *param, pv_value_t *res);
110
111 /* tm */
112 struct tm_binds tmb;
113 /* pv */
114 pv_api_t pv_api;
115
116 stat_var *requests;
117 stat_var *replies;
118 stat_var *errors;
119 stat_var *timeouts;
120
121 enum http_req_name_t {
122         E_HRN_ALL = 0,
123         E_HRN_HDR, E_HRN_METHOD, E_HRN_TIMEOUT,
124         E_HRN_TLS_CA_PATH, E_HRN_TLS_CLIENT_KEY,
125         E_HRN_TLS_CLIENT_CERT, E_HRN_SUSPEND,
126         E_HRN_BODY, E_HRN_AUTHMETHOD, E_HRN_USERNAME,
127         E_HRN_PASSWORD, E_HRN_TCP_KA, E_HRN_TCP_KA_IDLE,
128         E_HRN_TCP_KA_INTERVAL
129 };
130
131 static cmd_export_t cmds[]={
132         {"http_async_query",  (cmd_function)w_http_async_query, 2, fixup_spve_spve,
133                 0, ANY_ROUTE},
134         {0, 0, 0, 0, 0, 0}
135 };
136
137 static param_export_t params[]={
138         {"workers",                             INT_PARAM,              &num_workers},
139         {"connection_timeout",  INT_PARAM,              &http_timeout},
140         {"hash_size",                   INT_PARAM,              &hash_size},
141         {"tls_version",                 INT_PARAM,              &tls_version},
142         {"tls_verify_host",             INT_PARAM,              &tls_verify_host},
143         {"tls_verify_peer",             INT_PARAM,              &tls_verify_peer},
144         {"curl_verbose",                INT_PARAM,              &curl_verbose},
145         {"tls_client_cert",             PARAM_STRING,   &tls_client_cert},
146         {"tls_client_key",              PARAM_STRING,   &tls_client_key},
147         {"tls_ca_path",                 PARAM_STRING,   &tls_ca_path},
148         {"memory_manager",              PARAM_STRING,   &memory_manager},
149         {"authmethod",                  PARAM_INT,              &default_authmethod },
150         {"tcp_keepalive",           INT_PARAM,          &tcp_keepalive},
151         {"tcp_ka_idle",         INT_PARAM,              &tcp_ka_idle},
152         {"tcp_ka_interval",         INT_PARAM,          &tcp_ka_interval},
153         {0, 0, 0}
154 };
155
156 /*! \brief We expose internal variables via the statistic framework below.*/
157 stat_export_t mod_stats[] = {
158         {"requests",    STAT_NO_RESET, &requests        },
159         {"replies",     STAT_NO_RESET, &replies         },
160         {"errors",      STAT_NO_RESET, &errors          },
161         {"timeouts",    STAT_NO_RESET, &timeouts        },
162         {0, 0, 0}
163 };
164
165 static pv_export_t pvs[] = {
166         {STR_STATIC_INIT("http_hdr"),
167                 PVT_HDR, ah_get_hdr, 0,
168                 w_pv_parse_hdr_name, pv_parse_index, 0, 0},
169         {STR_STATIC_INIT("http_rr"),
170                 PVT_OTHER, ah_get_reason, 0,
171                 0, 0, 0, 0},
172         {STR_STATIC_INIT("http_rs"),
173                 PVT_OTHER, ah_get_status, 0,
174                 0, 0, 0, 0},
175         {STR_STATIC_INIT("http_rb"),
176                 PVT_MSG_BODY, ah_get_msg_body, 0,
177                 0, 0, 0, 0},
178         {STR_STATIC_INIT("http_bs"),
179                 PVT_OTHER, ah_get_body_size, 0,
180                 0, 0, 0, 0},
181         {STR_STATIC_INIT("http_mb"),
182                 PVT_OTHER, ah_get_msg_buf, 0,
183                 0, 0, 0, 0},
184         {STR_STATIC_INIT("http_ml"),
185                 PVT_OTHER, ah_get_msg_len, 0,
186                 0, 0, 0, 0},
187         {STR_STATIC_INIT("http_ok"),
188                 PVT_OTHER, ah_get_ok, 0,
189                 0, 0, 0, 0},
190         {STR_STATIC_INIT("http_err"),
191                 PVT_OTHER, ah_get_err, 0,
192                 0, 0, 0, 0},
193         {STR_STATIC_INIT("http_req"),
194                 PVT_OTHER, pv_get_null, ah_set_req,
195                 ah_parse_req_name, 0, 0, 0},
196         {STR_STATIC_INIT("http_req_id"),
197                 PVT_OTHER, ah_get_id, 0,
198                 0, 0, 0, 0},
199         { {0, 0}, 0, 0, 0, 0, 0, 0, 0 }
200 };
201
202 struct module_exports exports = {
203         "http_async_client",    /* module name */
204         DEFAULT_DLFLAGS,                /* dlopen flags */
205         cmds,                                   /* exported functions */
206         params,                                 /* exported parameters */
207         0,                                              /* RPC method exports */
208         pvs,                                    /* exported pseudo-variables */
209         0,                                              /* response handling function */
210         mod_init,                               /* module initialization function */
211         child_init,                             /* per-child init function */
212         mod_destroy                             /* module destroy function */
213 };
214
215
216 /**
217  * init module function
218  */
219 static int mod_init(void)
220 {
221         unsigned int n;
222         LM_INFO("Initializing Http Async module\n");
223
224 #ifdef STATISTICS
225         /* register statistics */
226         if (register_module_stats( exports.name, mod_stats)!=0 ) {
227                 LM_ERR("failed to register core statistics\n");
228                 return -1;
229         }
230 #endif
231         /* sanitize hash_size */
232         if (hash_size < 1){
233                 LM_WARN("hash_size is smaller "
234                                 "than 1  -> rounding from %d to 1\n",
235                                 hash_size);
236                                 hash_size = 1;
237         }
238         /* check that the hash table size is a power of 2 */
239         for( n=0 ; n<(8*sizeof(n)) ; n++) {
240                 if (hash_size==(1<<n))
241                         break;
242                 if (n && hash_size<(1<<n)) {
243                         LM_WARN("hash_size is not a power "
244                                 "of 2 as it should be -> rounding from %d to %d (n=%d)\n",
245                                 hash_size, 1<<(n-1), n);
246                         hash_size = 1<<(n-1);
247                         break;
248                 }
249         }
250         /* check 'workers' param */
251         if (num_workers < 1) {
252                 LM_ERR("the 'workers' parameter must be >= 1\n");
253                 return -1;
254         }
255
256         tls_verify_host = tls_verify_host?1:0;
257         tls_verify_peer = tls_verify_peer?1:0;
258
259         if (tcp_keepalive) {
260                 LM_INFO("TCP keepalives enabled\n");
261         }
262         /* check tcp keepalive parameters */
263         if ((tcp_ka_idle > 0 || tcp_ka_interval > 0) && !(tcp_keepalive > 0)) {
264                 LM_WARN("either 'tcp_ka_idle' or 'tcp_ka_interval' are set but 'tcp_keepalive' is disabled: they will be ignored\n");
265         }
266         /* init http parameters list */
267         init_query_params(&ah_params);
268
269         if (strncmp("shm", memory_manager, 3) == 0) {
270                 curl_memory_manager = 0;
271         } else if (strncmp("sys", memory_manager, 3) == 0) {
272                 curl_memory_manager = 1;
273         } else {
274                 LM_ERR("invalid memory_manager parameter: '%s'\n", memory_manager);
275                 return -1;
276         }
277
278         /* init faked sip msg */
279         if(faked_msg_init()<0) {
280                 LM_ERR("failed to init faked sip msg\n");
281                 return -1;
282         }
283
284         if(load_tm_api( &tmb ) < 0) {
285                 LM_INFO("cannot load the TM-functions - async relay disabled\n");
286                 memset(&tmb, 0, sizeof(tm_api_t));
287         }
288
289         /* allocate workers array */
290         workers = shm_malloc(num_workers * sizeof(*workers));
291         if(workers == NULL) {
292                 LM_ERR("error in shm_malloc\n");
293                 return -1;
294         }
295         memset(workers, 0, num_workers * sizeof(*workers));
296
297         register_procs(num_workers);
298
299         /* add child to update local config framework structures */
300         cfg_register_child(num_workers);
301
302         return 0;
303 }
304
305 /**
306  * @brief Initialize async module children
307  */
308 static int child_init(int rank)
309 {
310         int pid;
311         int i;
312
313         LM_DBG("child initializing async http\n");
314
315         if(num_workers<=0)
316                 return 0;
317
318         /* initialize query counter and id */
319         q_idx = 0;
320         q_id[0] = '\0';
321
322         if (rank==PROC_INIT) {
323                 for(i=0; i<num_workers; i++) {
324                         LM_DBG("initializing worker notification socket: %d\n", i);
325                         if(async_http_init_sockets(&workers[i])<0) {
326                                 LM_ERR("failed to initialize tasks sockets\n");
327                                 return -1;
328                         }
329                 }
330
331                 return 0;
332         }
333
334         if(rank>0) {
335                 for(i=0; i<num_workers; i++) {
336                         close(workers[i].notication_socket[0]);
337                 }
338                 return 0;
339         }
340         if (rank!=PROC_MAIN)
341                 return 0;
342
343         for(i=0; i<num_workers; i++) {
344                 if(async_http_init_worker(i+1, &workers[i])<0) {
345                         LM_ERR("failed to initialize worker process: %d\n", i);
346                         return -1;
347                 }
348                 pid=fork_process(PROC_RPC, "Http Async Worker", 1);
349                 if (pid<0)
350                         return -1; /* error */
351                 if(pid==0) {
352                         /* child */
353                         /* enforce http_reply_parse=yes */
354                         http_reply_parse = 1;
355                         /* initialize the config framework */
356                         if (cfg_child_init())
357                                 return -1;
358                         /* init msg structure for http reply parsing */
359                         ah_reply = pkg_malloc(sizeof(struct sip_msg));
360                         if(!ah_reply) {
361                                 LM_ERR("failed to allocate pkg memory\n");
362                                 return -1;
363                         }
364                         memset(ah_reply, 0, sizeof(struct sip_msg));
365                         /* main function for workers */
366                         async_http_run_worker(&workers[i]);
367                 }
368         }
369
370         return 0;
371 }
372
373 /**
374  * destroy module function
375  */
376 static void mod_destroy(void)
377 {
378 }
379
380 /**
381  *
382  */
383 static int w_http_async_query(sip_msg_t *msg, char *query, char* rt)
384 {
385         str sdata;
386         str rn;
387
388         if(msg==NULL)
389                 return -1;
390
391         if(fixup_get_svalue(msg, (gparam_t*)query, &sdata)!=0) {
392                 LM_ERR("unable to get data\n");
393                 return -1;
394         }
395         if(sdata.s==NULL || sdata.len == 0) {
396                 LM_ERR("invalid data parameter\n");
397                 return -1;
398         }
399
400         if(fixup_get_svalue(msg, (gparam_t*)rt, &rn)!=0)
401         {
402                 LM_ERR("no route block name\n");
403                 return -1;
404         }
405         if(rn.s==NULL || rn.len == 0) {
406                 LM_ERR("invalid route name parameter\n");
407                 return -1;
408         }
409         return async_send_query(msg, &sdata, &rn);
410 }
411
412 /**
413  *
414  */
415 static int ki_http_async_query(sip_msg_t *msg, str *sdata, str *rn)
416 {
417         if(msg==NULL)
418                 return -1;
419         if(sdata==NULL || sdata->len <= 0) {
420                 LM_ERR("invalid data parameter\n");
421                 return -1;
422         }
423         if(rn->s==NULL || rn->len <= 0) {
424                 LM_ERR("invalid route name parameter\n");
425                 return -1;
426         }
427         return async_send_query(msg, sdata, rn);
428 }
429
430 #define _IVALUE_ERROR(NAME) LM_ERR("invalid parameter '" #NAME "' (must be a number)\n")
431 #define _IVALUE(NAME)\
432 int i_##NAME ;\
433 if(fixup_get_ivalue(msg, (gparam_t*)NAME, &( i_##NAME))!=0)\
434 { \
435         _IVALUE_ERROR(NAME);\
436         return -1;\
437 }
438 /*
439  * Helper to copy input string parameter into a query parameter
440  */
441 static int set_query_param(str* param, str input)
442 {
443         if (param->s) {
444                 shm_free(param->s);
445                 param->s = NULL;
446                 param->len = 0;
447         }
448
449         if (input.s && input.len > 0) {
450                 if (shm_str_dup(param, &input) < 0) {
451                         LM_ERR("Error allocating parameter\n");
452                         return -1;
453                 }
454         }
455
456         return 1;
457 }
458
459 /*
460  * Helper to copy input string parameter into a query char* parameter
461  */
462 static int set_query_cparam(char** param, str input)
463 {
464         if (*param) {
465                 shm_free(*param);
466                 *param = NULL;
467         }
468
469         if (input.s && input.len > 0) {
470                 *param = shm_malloc(input.len+1);
471         
472                 if(*param == NULL) {
473                         LM_ERR("error in shm_malloc\n");
474                         return -1;
475                 }
476
477                 strncpy(*param, input.s, input.len);
478                 (*param)[input.len] = '\0';
479                 
480                 LM_DBG("param set to '%s'\n", *param);
481         }
482
483         return 1;
484 }
485
486 /* module PVs */
487
488 #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) \
489         { \
490                 if (ah_reply) { \
491                         if (ah_error.s) { \
492                                 LM_WARN("an async variable was read after http error, use $http_ok to check the request's status\n"); \
493                                 return pv_get_null(msg, param, res); \
494                         } else { \
495                                 return pv_api.PV_F(ah_reply, param, res); \
496                         } \
497                 } else { \
498                         LM_ERR("the async variables can only be read from an async http worker\n"); \
499                         return pv_get_null(msg, param, res); \
500                 } \
501         }
502
503 AH_WRAP_GET_PV(ah_get_reason,      get_reason)
504 AH_WRAP_GET_PV(ah_get_hdr,         get_hdr)
505 AH_WRAP_GET_PV(ah_get_status,      get_status)
506 AH_WRAP_GET_PV(ah_get_msg_body,    get_msg_body)
507 AH_WRAP_GET_PV(ah_get_body_size,   get_body_size)
508 AH_WRAP_GET_PV(ah_get_msg_buf,     get_msg_buf)
509 AH_WRAP_GET_PV(ah_get_msg_len,     get_msg_len)
510
511 static int w_pv_parse_hdr_name(pv_spec_p sp, str *in) {
512         return pv_api.parse_hdr_name(sp, in);
513 }
514
515 static int ah_get_id(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) {
516         return pv_get_strlval(msg, param, res, q_id, strlen(q_id));
517 }
518
519 static int ah_get_ok(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) {
520         if (ah_reply) {
521                 if (ah_error.s) {
522                         return pv_get_intstrval(msg, param, res, 0, &pv_str_0);
523                 } else {
524                         return pv_get_intstrval(msg, param, res, 1, &pv_str_1);
525                 }
526         } else {
527                 LM_ERR("the async variables can only be read from an async http worker\n");
528                 return pv_get_null(msg, param, res);
529         }
530 }
531
532 static int ah_get_err(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) {
533         if (ah_reply) {
534                 if (ah_error.s) {
535                         return pv_get_strval(msg, param, res, &ah_error);
536                 } else {
537                         return pv_get_null(msg, param, res);
538                 }
539         } else {
540                 LM_ERR("the async variables can only be read from an async http worker\n");
541                 return pv_get_null(msg, param, res);
542         }
543 }
544
545 static int ah_parse_req_name(pv_spec_p sp, str *in) {
546         if(sp==NULL || in==NULL || in->len<=0)
547                 return -1;
548
549         switch(in->len)
550         {
551                 case 3:
552                         if(strncmp(in->s, "all", 3)==0)
553                                 sp->pvp.pvn.u.isname.name.n = E_HRN_ALL;
554                         else if(strncmp(in->s, "hdr", 3)==0)
555                                 sp->pvp.pvn.u.isname.name.n = E_HRN_HDR;
556                         else goto error;
557                         break;
558                 case 4:
559                         if(strncmp(in->s, "body", 4)==0)
560                                 sp->pvp.pvn.u.isname.name.n = E_HRN_BODY;
561                         else goto error;
562                         break;
563                 case 6:
564                         if(strncmp(in->s, "method", 6)==0)
565                                 sp->pvp.pvn.u.isname.name.n = E_HRN_METHOD;
566                         else goto error;
567                         break;
568                 case 7:
569                         if(strncmp(in->s, "timeout", 7)==0)
570                                 sp->pvp.pvn.u.isname.name.n = E_HRN_TIMEOUT;
571                         else if(strncmp(in->s, "suspend", 7)==0)
572                                 sp->pvp.pvn.u.isname.name.n = E_HRN_SUSPEND;
573                         else if(strncmp(in->s, "ka-idle", 7)==0)
574                                 sp->pvp.pvn.u.isname.name.n = E_HRN_TCP_KA_IDLE;
575                         else goto error;
576                         break;
577                 case 8:
578                         if(strncmp(in->s, "username", 8)==0)
579                                 sp->pvp.pvn.u.isname.name.n = E_HRN_USERNAME;
580                         else if(strncmp(in->s, "password", 8)==0)
581                                 sp->pvp.pvn.u.isname.name.n = E_HRN_PASSWORD;
582                         else goto error;
583                         break;
584                 case 9:
585                         if(strncmp(in->s, "keepalive", 9)==0)
586                                 sp->pvp.pvn.u.isname.name.n = E_HRN_TCP_KA;
587                         else goto error;
588                         break;
589                 case 10:
590                         if(strncmp(in->s, "authmethod", 10)==0)
591                                 sp->pvp.pvn.u.isname.name.n = E_HRN_AUTHMETHOD;
592                         else goto error;
593                         break;
594                 case 11:
595                         if(strncmp(in->s, "tls_ca_path", 11)==0)
596                                 sp->pvp.pvn.u.isname.name.n = E_HRN_TLS_CA_PATH;
597                         else if(strncmp(in->s, "ka-interval", 11)==0)
598                                 sp->pvp.pvn.u.isname.name.n = E_HRN_TCP_KA_INTERVAL;
599                         else goto error;
600                         break;
601                 case 14:
602                         if(strncmp(in->s, "tls_client_key", 14)==0)
603                                 sp->pvp.pvn.u.isname.name.n = E_HRN_TLS_CLIENT_KEY;
604                         else goto error;
605                         break;
606                 case 15:
607                         if(strncmp(in->s, "tls_client_cert", 15)==0)
608                                 sp->pvp.pvn.u.isname.name.n = E_HRN_TLS_CLIENT_CERT;
609                         else goto error;
610                         break;
611                 default:
612                         goto error;
613         }
614         sp->pvp.pvn.type = PV_NAME_INTSTR;
615         sp->pvp.pvn.u.isname.type = 0;
616
617         return 0;
618
619 error:
620         LM_ERR("unknown http_req name %.*s\n", in->len, in->s);
621         return -1;
622 }
623
624 static int ah_set_req(struct sip_msg* msg, pv_param_t *param,
625                 int op, pv_value_t *val)
626 {
627         pv_value_t *tval;
628
629         if(param==NULL || tmb.t_request==NULL)
630                 return -1;
631
632         tval = val;
633         if((tval!=NULL) && (tval->flags&PV_VAL_NULL)) {
634                 tval = NULL;
635         }
636
637         switch((enum http_req_name_t) param->pvn.u.isname.name.n) {
638         case E_HRN_ALL:
639                 if (tval == NULL)
640                         set_query_params(&ah_params);
641                 break;
642         case E_HRN_HDR:
643                 if (tval) {
644                         if (!(tval->flags & PV_VAL_STR)) {
645                                 LM_ERR("invalid value type for $http_req(hdr)\n");
646                                 return -1;
647                         }
648                         header_list_add(&ah_params.headers, &tval->rs);
649                 }
650                 break;
651         case E_HRN_METHOD:
652                 if (tval) {
653                         if (!(tval->flags & PV_VAL_STR)) {
654                                 LM_ERR("invalid value type for $http_req(method)\n");
655                                 return -1;
656                         }
657                         query_params_set_method(&ah_params, &tval->rs);
658                 } else {
659                         ah_params.method = AH_METH_DEFAULT;
660                 }
661                 break;
662         case E_HRN_TIMEOUT:
663                 if (tval) {
664                         if (!(tval->flags & PV_VAL_INT)) {
665                                 LM_ERR("invalid value type for $http_req(timeout)\n");
666                                 return -1;
667                         }
668                         ah_params.timeout = tval->ri;
669                 } else {
670                         ah_params.timeout = http_timeout;
671                 }
672                 break;
673         case E_HRN_TLS_CA_PATH:
674                 if (tval) {
675                         if (!(tval->flags & PV_VAL_STR)) {
676                                 LM_ERR("invalid value type for $http_req(tls_ca_path)\n");
677                                 return -1;
678                         }
679                         set_query_cparam(&ah_params.tls_ca_path, tval->rs);
680                 }
681                 break;
682         case E_HRN_TLS_CLIENT_KEY:
683                 if (tval) {
684                         if (!(tval->flags & PV_VAL_STR)) {
685                                 LM_ERR("invalid value type for $http_req(tls_client_key)\n");
686                                 return -1;
687                         }
688                         set_query_cparam(&ah_params.tls_client_key, tval->rs);
689                 }
690                 break;
691         case E_HRN_TLS_CLIENT_CERT:
692                 if (tval) {
693                         if (!(tval->flags & PV_VAL_STR)) {
694                                 LM_ERR("invalid value type for $http_req(tls_client_cert)\n");
695                                 return -1;
696                         }
697                         set_query_cparam(&ah_params.tls_client_cert, tval->rs);
698                 }
699                 break;
700         case E_HRN_SUSPEND:
701                 if (tval) {
702                         if (!(tval->flags & PV_VAL_INT)) {
703                                 LM_ERR("invalid value type for $http_req(suspend)\n");
704                                 return -1;
705                         }
706                         ah_params.suspend_transaction = tval->ri?1:0;
707                 } else {
708                         ah_params.suspend_transaction = 1;
709                 }
710                 break;
711         case E_HRN_BODY:
712                 if (tval) {
713                         if (!(tval->flags & PV_VAL_STR)) {
714                                 LM_ERR("invalid value type for $http_req(body)\n");
715                                 return -1;
716                         }
717                         set_query_param(&ah_params.body, tval->rs);
718                 }
719                 break;
720         case E_HRN_AUTHMETHOD:
721                 if (tval) {
722                         if (!(tval->flags & PV_VAL_INT)) {
723                                 LM_ERR("invalid value type for $http_req(authmethod)\n");
724                                 return -1;
725                         }
726                         ah_params.authmethod = tval->ri;
727                 } else {
728                         ah_params.authmethod = default_authmethod;
729                 }
730                 break;
731         case E_HRN_USERNAME:
732                 if (tval) {
733                         if (!(tval->flags & PV_VAL_STR)) {
734                                 LM_ERR("invalid value type for $http_req(username)\n");
735                                 return -1;
736                         }
737                         set_query_cparam(&ah_params.username, tval->rs);
738                 }
739                 break;
740         case E_HRN_PASSWORD:
741                 if (tval) {
742                         if (!(tval->flags & PV_VAL_STR)) {
743                                 LM_ERR("invalid value type for $http_req(password)\n");
744                                 return -1;
745                         }
746                         set_query_cparam(&ah_params.password, tval->rs);
747                 }
748                 break;
749         case E_HRN_TCP_KA:
750                 if (tval) {
751                         if (!(tval->flags & PV_VAL_INT)) {
752                                 LM_ERR("invalid value type for $http_req(keepalive)\n");
753                                 return -1;
754                         }
755                         ah_params.tcp_keepalive = tval->ri;
756                 } else {
757                         ah_params.tcp_keepalive = tcp_keepalive;
758                 }
759                 break;
760
761         case E_HRN_TCP_KA_IDLE:
762                 if (tval) {
763                         if (!(tval->flags & PV_VAL_INT)) {
764                                 LM_ERR("invalid value type for $http_req(ka-idle)\n");
765                                 return -1;
766                         }
767                         ah_params.tcp_ka_idle = tval->ri;
768                 } else {
769                         ah_params.tcp_ka_idle = tcp_ka_idle;
770                 }
771                 break;
772
773         case E_HRN_TCP_KA_INTERVAL:
774                 if (tval) {
775                         if (!(tval->flags & PV_VAL_INT)) {
776                                 LM_ERR("invalid value type for $http_req(ka-interval)\n");
777                                 return -1;
778                         }
779                         ah_params.tcp_ka_interval = tval->ri;
780                 } else {
781                         ah_params.tcp_ka_interval = tcp_ka_interval;
782                 }
783                 break;
784         }
785
786         return 1;
787 }
788
789 /**
790  *
791  */
792 /* clang-format off */
793 static sr_kemi_t sr_kemi_http_async_client_exports[] = {
794         { str_init("http_async_client"), str_init("query"),
795                 SR_KEMIP_INT, ki_http_async_query,
796                 { SR_KEMIP_STR, SR_KEMIP_STR, SR_KEMIP_NONE,
797                         SR_KEMIP_NONE, SR_KEMIP_NONE, SR_KEMIP_NONE }
798         },
799
800         { {0, 0}, {0, 0}, 0, NULL, { 0, 0, 0, 0, 0, 0 } }
801 };
802 /* clang-format on */
803
804 int mod_register(char *path, int *dlflags, void *p1, void *p2)
805 {
806         pv_register_api_t pvra;
807
808         pvra = (pv_register_api_t)find_export("pv_register_api", NO_SCRIPT, 0);
809         if (!pvra) {
810                 LM_ERR("Cannot import pv functions (pv module must be loaded before this module)\n");
811                 return -1;
812         }
813         pvra(&pv_api);
814         sr_kemi_modules_add(sr_kemi_http_async_client_exports); 
815         return 0;
816 }