Browse code

http_async_client: add http_time pseudovariable

Federico Cabiddu authored on 19/11/2020 09:34:13
Showing 6 changed files
... ...
@@ -57,6 +57,7 @@ extern struct tm_binds tmb;
57 57
 
58 58
 struct sip_msg *ah_reply = NULL;
59 59
 str ah_error = {NULL, 0};
60
+http_m_time_t ah_time = {0};
60 61
 
61 62
 async_http_worker_t *workers = NULL;
62 63
 int num_workers = 1;
... ...
@@ -142,6 +143,8 @@ void async_http_cb(struct http_m_reply *reply, void *param)
142 143
 	/* clean process-local result variables */
143 144
 	ah_error.s = NULL;
144 145
 	ah_error.len = 0;
146
+
147
+	memset(&ah_time, 0, sizeof(struct http_m_time));
145 148
 	memset(ah_reply, 0, sizeof(struct sip_msg));
146 149
 
147 150
 	keng = sr_kemi_eng_get();
... ...
@@ -163,6 +166,8 @@ void async_http_cb(struct http_m_reply *reply, void *param)
163 166
 	}
164 167
 
165 168
 	/* set process-local result variables */
169
+	ah_time = reply->time;
170
+
166 171
 	if (reply->result == NULL) {
167 172
 		/* error */
168 173
 		ah_error.s = reply->error;
... ...
@@ -56,6 +56,7 @@ extern int tcp_ka_interval;
56 56
 
57 57
 extern struct sip_msg *ah_reply;
58 58
 extern str ah_error;
59
+extern http_m_time_t ah_time;
59 60
 
60 61
 extern int tls_verify_host;
61 62
 extern int tls_verify_peer;
... ...
@@ -501,6 +501,21 @@ http_async_query("https://example.com/test.php", "HTTP_REPLY");
501 501
 		<listitem><para>
502 502
 			<emphasis>$http_rb</emphasis> and <emphasis>$http_bs</emphasis>: HTTP response body and body length
503 503
 		</para></listitem>
504
+		<listitem><para>
505
+			<emphasis>$http_time</emphasis>: provides access to broken-down transfer time in microseconds calculated by cURL (https://curl.se/libcurl/c/curl_easy_getinfo.html). 
506
+				<para>
507
+					<emphasis>'name'</emphasis> can be:
508
+					<itemizedlist>
509
+						<listitem><para>total - total time for the transfer</para></listitem>
510
+						<listitem><para>lookup - name lookup time</para></listitem>
511
+						<listitem><para>connect - time from the start until the connection to the remote host (or proxy) was completed.</para></listitem>
512
+						<listitem><para>appconnect - time from start until SSL/SSH handshake completed</para></listitem>
513
+						<listitem><para>pretransfer - time from start until just before the transfer begins</para></listitem>
514
+						<listitem><para>starttransfer - time from start until just when the first byte is received</para></listitem>
515
+						<listitem><para>redirect - time taken for all redirect steps before the final transfer.</para></listitem>
516
+					</itemizedlist>
517
+				</para>	
518
+		</para></listitem>
504 519
 	</itemizedlist>
505 520
 </section>
506 521
 <section>
... ...
@@ -31,6 +31,7 @@
31 31
 #ifndef _HM_HASH_
32 32
 #define _HM_HASH_
33 33
 
34
+#include <stdint.h>
34 35
 #include <curl/curl.h>
35 36
 #include "../../core/dprint.h"
36 37
 #include "../../core/mem/mem.h"
... ...
@@ -39,11 +40,23 @@
39 40
 
40 41
 extern struct http_m_table *hm_table;
41 42
 
43
+typedef struct http_m_time
44
+{
45
+	uint32_t total;
46
+	uint32_t lookup;
47
+	uint32_t connect;
48
+	uint32_t appconnect;
49
+	uint32_t pretransfer;
50
+	uint32_t starttransfer;
51
+	uint32_t redirect;
52
+} http_m_time_t;
53
+
42 54
 typedef struct http_m_reply
43 55
 {
44 56
 	long retcode;
45 57
 	str *result;
46 58
 	char error[CURL_ERROR_SIZE];
59
+	http_m_time_t time;
47 60
 } http_m_reply_t;
48 61
 
49 62
 typedef void (*http_multi_cbe_t)(struct http_m_reply *reply, void *param);
... ...
@@ -98,6 +98,7 @@ static int ah_get_body_size(struct sip_msg *msg, pv_param_t *param, pv_value_t *
98 98
 static int ah_get_msg_buf(struct sip_msg *msg, pv_param_t *param, pv_value_t *res);
99 99
 static int ah_get_msg_len(struct sip_msg *msg, pv_param_t *param, pv_value_t *res);
100 100
 static int ah_parse_req_name(pv_spec_p sp, str *in);
101
+static int ah_parse_time_name(pv_spec_p sp, str *in);
101 102
 static int ah_set_req(struct sip_msg* msg, pv_param_t *param, int op, pv_value_t *val);
102 103
 static int ah_get_id(struct sip_msg *msg, pv_param_t *param, pv_value_t *res);
103 104
 
... ...
@@ -107,6 +108,7 @@ static str pv_str_0 = {"0", 1};
107 108
 
108 109
 static int ah_get_ok(struct sip_msg *msg, pv_param_t *param, pv_value_t *res);
109 110
 static int ah_get_err(struct sip_msg *msg, pv_param_t *param, pv_value_t *res);
111
+static int ah_get_time(struct sip_msg *msg, pv_param_t *param, pv_value_t *res);
110 112
 
111 113
 /* tm */
112 114
 struct tm_binds tmb;
... ...
@@ -128,6 +130,12 @@ enum http_req_name_t {
128 130
 	E_HRN_TCP_KA_INTERVAL
129 131
 };
130 132
 
133
+enum http_time_name_t {
134
+	E_HT_TOTAL = 0,
135
+	E_HT_LOOKUP, E_HT_CONNECT, E_HT_REDIRECT,
136
+	E_HT_APPCONNECT, E_HT_PRETRANSFER, E_HT_STARTTRANSFER
137
+};
138
+
131 139
 static cmd_export_t cmds[]={
132 140
 	{"http_async_query",  (cmd_function)w_http_async_query, 2, fixup_spve_spve,
133 141
 		0, ANY_ROUTE},
... ...
@@ -190,6 +198,9 @@ static pv_export_t pvs[] = {
190 198
 	{STR_STATIC_INIT("http_err"),
191 199
 		PVT_OTHER, ah_get_err, 0,
192 200
 		0, 0, 0, 0},
201
+	{STR_STATIC_INIT("http_time"),
202
+		PVT_OTHER, ah_get_time, 0,
203
+		ah_parse_time_name, 0, 0, 0},
193 204
 	{STR_STATIC_INIT("http_req"),
194 205
 		PVT_OTHER, pv_get_null, ah_set_req,
195 206
 		ah_parse_req_name, 0, 0, 0},
... ...
@@ -544,6 +555,31 @@ static int ah_get_err(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) {
544 555
 	}
545 556
 }
546 557
 
558
+static int ah_get_time(struct sip_msg *msg, pv_param_t *param,
559
+		pv_value_t *res)
560
+{
561
+	if(msg==NULL || param==NULL)
562
+		return -1;
563
+
564
+	switch(param->pvn.u.isname.name.n)
565
+	{
566
+		case E_HT_LOOKUP:
567
+			return pv_get_uintval(msg, param, res, ah_time.lookup);
568
+		case E_HT_CONNECT:
569
+			return pv_get_uintval(msg, param, res, ah_time.connect);
570
+		case E_HT_REDIRECT:
571
+			return pv_get_uintval(msg, param, res, ah_time.redirect);
572
+		case E_HT_APPCONNECT:
573
+			return pv_get_uintval(msg, param, res, ah_time.appconnect);
574
+		case E_HT_PRETRANSFER:
575
+			return pv_get_uintval(msg, param, res, ah_time.pretransfer);
576
+		case E_HT_STARTTRANSFER:
577
+			return pv_get_uintval(msg, param, res, ah_time.starttransfer);
578
+		default:
579
+			return pv_get_uintval(msg, param, res, ah_time.total);
580
+	}
581
+}
582
+
547 583
 static int ah_parse_req_name(pv_spec_p sp, str *in) {
548 584
 	if(sp==NULL || in==NULL || in->len<=0)
549 585
 		return -1;
... ...
@@ -623,6 +659,60 @@ error:
623 659
 	return -1;
624 660
 }
625 661
 
662
+static int ah_parse_time_name(pv_spec_p sp, str *in) {
663
+	if(sp==NULL || in==NULL || in->len<0)
664
+		return -1;
665
+
666
+	switch(in->len)
667
+	{
668
+		case 5:
669
+			if(strncmp(in->s, "total", 5)==0)
670
+				sp->pvp.pvn.u.isname.name.n = E_HT_TOTAL;
671
+			else goto error;
672
+			break;
673
+		case 6:
674
+			if(strncmp(in->s, "lookup", 6)==0)
675
+				sp->pvp.pvn.u.isname.name.n = E_HT_LOOKUP;
676
+			else goto error;
677
+			break;
678
+		case 7:
679
+			if(strncmp(in->s, "connect", 7)==0)
680
+				sp->pvp.pvn.u.isname.name.n = E_HT_CONNECT;
681
+			else goto error;
682
+			break;
683
+		case 8:
684
+			if(strncmp(in->s, "redirect", 8)==0)
685
+				sp->pvp.pvn.u.isname.name.n = E_HT_REDIRECT;
686
+			else goto error;
687
+			break;
688
+		case 10:
689
+			if(strncmp(in->s, "appconnect", 10)==0)
690
+				sp->pvp.pvn.u.isname.name.n = E_HT_APPCONNECT;
691
+			else goto error;
692
+			break;
693
+		case 11:
694
+			if(strncmp(in->s, "pretransfer", 11)==0)
695
+				sp->pvp.pvn.u.isname.name.n = E_HT_PRETRANSFER;
696
+			else goto error;
697
+			break;
698
+		case 13:
699
+			if(strncmp(in->s, "starttransfer", 13)==0)
700
+				sp->pvp.pvn.u.isname.name.n = E_HT_STARTTRANSFER;
701
+			else goto error;
702
+			break;
703
+		default:
704
+			goto error;
705
+	}
706
+	sp->pvp.pvn.type = PV_NAME_INTSTR;
707
+	sp->pvp.pvn.u.isname.type = 0;
708
+
709
+	return 0;
710
+
711
+error:
712
+	LM_ERR("unknown http_time name %.*s\n", in->len, in->s);
713
+	return -1;
714
+}
715
+
626 716
 static int ah_set_req(struct sip_msg* msg, pv_param_t *param,
627 717
 		int op, pv_value_t *val)
628 718
 {
... ...
@@ -66,8 +66,8 @@ void timer_cb(int fd, short kind, void *userp)
66 66
 
67 67
 	LM_DBG("timeout on socket %d\n", fd);
68 68
 
69
-	rc = curl_multi_socket_action(g->multi,
70
-                                  CURL_SOCKET_TIMEOUT, 0, &g->still_running);
69
+	rc = curl_multi_socket_action(g->multi, CURL_SOCKET_TIMEOUT, 0, &g->still_running);
70
+
71 71
 	if (check_mcode(rc, error) < 0) {
72 72
 		LM_ERR("curl_multi_socket_action error: %s", error);
73 73
 	}
... ...
@@ -122,8 +122,7 @@ void event_cb(int fd, short kind, void *userp)
122 122
 		LM_DBG("removing handle %p\n", easy);
123 123
 		curl_multi_remove_handle(g->multi, easy);
124 124
 		curl_easy_cleanup(easy);
125
-		rc = curl_multi_socket_action(g->multi,
126
-                                  CURL_SOCKET_TIMEOUT, 0, &g->still_running);
125
+		rc = curl_multi_socket_action(g->multi, CURL_SOCKET_TIMEOUT, 0, &g->still_running);
127 126
 
128 127
 	} else {
129 128
 		LM_DBG("performing action %d on socket %d\n", action, fd);
... ...
@@ -568,6 +567,7 @@ void check_multi_info(struct http_m_global *g)
568 567
 	CURLcode res;
569 568
 
570 569
 	struct http_m_cell *cell;
570
+	double tmp_time;
571 571
 
572 572
 	LM_DBG("REMAINING: %d\n", g->still_running);
573 573
 	while ((msg = curl_multi_info_read(g->multi, &msgs_left))) {
... ...
@@ -576,6 +576,8 @@ void check_multi_info(struct http_m_global *g)
576 576
 			res = msg->data.result;
577 577
 			curl_easy_getinfo(easy, CURLINFO_PRIVATE, &cell);
578 578
 			curl_easy_getinfo(easy, CURLINFO_EFFECTIVE_URL, &eff_url);
579
+
580
+
579 581
 			LM_DBG("DONE: %s => (%d) %s\n", eff_url, res, cell->error);
580 582
 
581 583
 			cell = http_m_cell_lookup(easy);
... ...
@@ -584,6 +586,22 @@ void check_multi_info(struct http_m_global *g)
584 586
 				update_stat(errors, 1);
585 587
 				reply_error(cell);
586 588
 			} else {
589
+				
590
+				if (curl_easy_getinfo(cell->easy, CURLINFO_TOTAL_TIME, &tmp_time) == CURLE_OK)
591
+					cell->reply->time.total=(uint32_t)(tmp_time*1000000);
592
+				if (curl_easy_getinfo(cell->easy, CURLINFO_NAMELOOKUP_TIME, &tmp_time) == CURLE_OK)
593
+					cell->reply->time.lookup=(uint32_t)(tmp_time*1000000);
594
+				if (curl_easy_getinfo(cell->easy, CURLINFO_CONNECT_TIME, &tmp_time) == CURLE_OK)
595
+					cell->reply->time.connect=(uint32_t)(tmp_time*1000000);
596
+				if (curl_easy_getinfo(cell->easy, CURLINFO_REDIRECT_TIME, &tmp_time) == CURLE_OK)
597
+					cell->reply->time.redirect=(uint32_t)(tmp_time*1000000);
598
+				if (curl_easy_getinfo(cell->easy, CURLINFO_APPCONNECT_TIME, &tmp_time) == CURLE_OK)
599
+					cell->reply->time.appconnect=(uint32_t)(tmp_time*1000000);
600
+				if (curl_easy_getinfo(cell->easy, CURLINFO_PRETRANSFER_TIME, &tmp_time) == CURLE_OK)
601
+					cell->reply->time.pretransfer=(uint32_t)(tmp_time*1000000);
602
+				if (curl_easy_getinfo(cell->easy, CURLINFO_STARTTRANSFER_TIME, &tmp_time) == CURLE_OK)
603
+					cell->reply->time.starttransfer=(uint32_t)(tmp_time*1000000);
604
+				
587 605
 				cell->reply->error[0] = '\0';
588 606
 				cell->cb(cell->reply, cell->param);
589 607