1010#include <stdint.h>
1111#include <inttypes.h>
1212#include <stdlib.h>
13+ #include <string.h>
1314
1415#ifdef CLOUDSYNC_NETWORK_TRACE
1516#ifdef _WIN32
@@ -45,6 +46,16 @@ static size_t cacert_len = sizeof(cacert_pem) - 1;
4546#define CLOUDSYNC_NETWORK_MINBUF_SIZE 512
4647#define CLOUDSYNC_SESSION_TOKEN_MAXSIZE 4096
4748
49+ #ifndef CLOUDSYNC_CURL_MAXCONNECTS
50+ #define CLOUDSYNC_CURL_MAXCONNECTS 2L
51+ #endif
52+ #ifndef CLOUDSYNC_CURL_MAXAGE_CONN_SECONDS
53+ #define CLOUDSYNC_CURL_MAXAGE_CONN_SECONDS 15L
54+ #endif
55+ #ifndef CLOUDSYNC_CURL_MAXLIFETIME_CONN_SECONDS
56+ #define CLOUDSYNC_CURL_MAXLIFETIME_CONN_SECONDS 60L
57+ #endif
58+
4859#define DEFAULT_SYNC_WAIT_MS 100
4960#define DEFAULT_SYNC_MAX_RETRIES 1
5061
@@ -64,6 +75,11 @@ struct network_data {
6475 char * upload_endpoint ;
6576 char * apply_endpoint ;
6677 char * status_endpoint ;
78+ #ifndef CLOUDSYNC_OMIT_CURL
79+ CURL * api_curl ;
80+ CURL * artifact_curl ;
81+ int curl_pool_enabled ;
82+ #endif
6783};
6884
6985#ifdef CLOUDSYNC_NETWORK_TRACE
@@ -105,6 +121,32 @@ void network_trace_log(network_data *data, const char *method, const char *endpo
105121 network_trace_endpoint_name (data , endpoint ), method , http_status ,
106122 network_trace_result_name (result_code ), bytes , elapsed_ms );
107123}
124+
125+ #ifndef CLOUDSYNC_OMIT_CURL
126+ void network_trace_log_curl (network_data * data , const char * method , const char * endpoint , long http_status , int result_code , size_t bytes , CURL * curl , bool pooled , double elapsed_ms ) {
127+ double namelookup = 0.0 ;
128+ double connect = 0.0 ;
129+ double appconnect = 0.0 ;
130+ double starttransfer = 0.0 ;
131+ double total = 0.0 ;
132+ long num_connects = 0 ;
133+ if (curl ) {
134+ curl_easy_getinfo (curl , CURLINFO_NAMELOOKUP_TIME , & namelookup );
135+ curl_easy_getinfo (curl , CURLINFO_CONNECT_TIME , & connect );
136+ curl_easy_getinfo (curl , CURLINFO_APPCONNECT_TIME , & appconnect );
137+ curl_easy_getinfo (curl , CURLINFO_STARTTRANSFER_TIME , & starttransfer );
138+ curl_easy_getinfo (curl , CURLINFO_TOTAL_TIME , & total );
139+ curl_easy_getinfo (curl , CURLINFO_NUM_CONNECTS , & num_connects );
140+ }
141+ fprintf (stderr ,
142+ "[cloudsync-network] endpoint=%s method=%s pool=%s http_status=%ld result=%s bytes=%zu elapsed_ms=%.2f curl_total_ms=%.2f dns_ms=%.2f connect_ms=%.2f tls_ms=%.2f starttransfer_ms=%.2f num_connects=%ld\n" ,
143+ network_trace_endpoint_name (data , endpoint ), method ,
144+ pooled ? "on" : "off" , http_status ,
145+ network_trace_result_name (result_code ), bytes , elapsed_ms ,
146+ total * 1000.0 , namelookup * 1000.0 , connect * 1000.0 ,
147+ appconnect * 1000.0 , starttransfer * 1000.0 , num_connects );
148+ }
149+ #endif
108150#endif
109151
110152typedef struct {
@@ -211,6 +253,10 @@ bool network_data_set_endpoints (network_data *data, char *auth, char *check, ch
211253void network_data_free (network_data * data ) {
212254 if (!data ) return ;
213255
256+ #ifndef CLOUDSYNC_OMIT_CURL
257+ if (data -> api_curl ) curl_easy_cleanup (data -> api_curl );
258+ if (data -> artifact_curl ) curl_easy_cleanup (data -> artifact_curl );
259+ #endif
214260 if (data -> authentication ) cloudsync_memory_free (data -> authentication );
215261 if (data -> org_id ) cloudsync_memory_free (data -> org_id );
216262 if (data -> check_endpoint ) cloudsync_memory_free (data -> check_endpoint );
@@ -223,6 +269,47 @@ void network_data_free (network_data *data) {
223269// MARK: - Utils -
224270
225271#ifndef CLOUDSYNC_OMIT_CURL
272+ static bool network_curl_pool_enabled (network_data * data ) {
273+ if (!data ) return false;
274+ if (data -> curl_pool_enabled == 0 ) {
275+ const char * value = getenv ("CLOUDSYNC_CURL_POOL" );
276+ data -> curl_pool_enabled = 1 ;
277+ if (value && (strcmp (value , "0" ) == 0 || strcmp (value , "false" ) == 0 || strcmp (value , "off" ) == 0 || strcmp (value , "no" ) == 0 )) {
278+ data -> curl_pool_enabled = -1 ;
279+ }
280+ }
281+ return data -> curl_pool_enabled > 0 ;
282+ }
283+
284+ static bool network_endpoint_is_api (network_data * data , const char * endpoint ) {
285+ if (!data || !endpoint ) return false;
286+ return (data -> check_endpoint && strcmp (endpoint , data -> check_endpoint ) == 0 ) ||
287+ (data -> upload_endpoint && strcmp (endpoint , data -> upload_endpoint ) == 0 ) ||
288+ (data -> apply_endpoint && strcmp (endpoint , data -> apply_endpoint ) == 0 ) ||
289+ (data -> status_endpoint && strcmp (endpoint , data -> status_endpoint ) == 0 );
290+ }
291+
292+ static CURL * network_curl_for_endpoint (network_data * data , const char * endpoint , bool * pooled ) {
293+ if (pooled ) * pooled = false;
294+ if (!network_curl_pool_enabled (data )) {
295+ return curl_easy_init ();
296+ }
297+
298+ CURL * * slot = network_endpoint_is_api (data , endpoint ) ? & data -> api_curl : & data -> artifact_curl ;
299+ if (!* slot ) {
300+ * slot = curl_easy_init ();
301+ } else {
302+ curl_easy_reset (* slot );
303+ }
304+ if (!* slot ) return NULL ;
305+
306+ curl_easy_setopt (* slot , CURLOPT_MAXCONNECTS , CLOUDSYNC_CURL_MAXCONNECTS );
307+ curl_easy_setopt (* slot , CURLOPT_MAXAGE_CONN , CLOUDSYNC_CURL_MAXAGE_CONN_SECONDS );
308+ curl_easy_setopt (* slot , CURLOPT_MAXLIFETIME_CONN , CLOUDSYNC_CURL_MAXLIFETIME_CONN_SECONDS );
309+ if (pooled ) * pooled = true;
310+ return * slot ;
311+ }
312+
226313static bool network_buffer_check (network_buffer * data , size_t needed ) {
227314 // alloc/resize buffer
228315 if (data -> bused + needed > data -> balloc ) {
@@ -259,12 +346,13 @@ NETWORK_RESULT network_receive_buffer (network_data *data, const char *endpoint,
259346 struct curl_slist * headers = NULL ;
260347 char errbuf [CURL_ERROR_SIZE ] = {0 };
261348 long response_code = 0 ;
349+ bool pooled = false;
262350 const char * method = (json_payload || is_post_request ) ? "POST" : "GET" ;
263351#ifdef CLOUDSYNC_NETWORK_TRACE
264352 double trace_start_ms = network_trace_now_ms ();
265353#endif
266354
267- CURL * curl = curl_easy_init ( );
355+ CURL * curl = network_curl_for_endpoint ( data , endpoint , & pooled );
268356 if (!curl ) return (NETWORK_RESULT ){CLOUDSYNC_NETWORK_ERROR , NULL , 0 , NULL , NULL };
269357
270358 // a buffer to store errors in
@@ -337,7 +425,6 @@ NETWORK_RESULT network_receive_buffer (network_data *data, const char *endpoint,
337425 }
338426
339427cleanup :
340- if (curl ) curl_easy_cleanup (curl );
341428 if (headers ) curl_slist_free_all (headers );
342429
343430 // build result
@@ -353,8 +440,9 @@ NETWORK_RESULT network_receive_buffer (network_data *data, const char *endpoint,
353440 }
354441
355442 #ifdef CLOUDSYNC_NETWORK_TRACE
356- network_trace_log (data , method , endpoint , response_code , result .code , result .blen , network_trace_now_ms () - trace_start_ms );
443+ network_trace_log_curl (data , method , endpoint , response_code , result .code , result .blen , curl , pooled , network_trace_now_ms () - trace_start_ms );
357444 #endif
445+ if (curl && !pooled ) curl_easy_cleanup (curl );
358446 return result ;
359447}
360448
@@ -378,12 +466,13 @@ bool network_send_buffer (network_data *data, const char *endpoint, const char *
378466 char errbuf [CURL_ERROR_SIZE ] = {0 };
379467 CURLcode rc = CURLE_OK ;
380468 long response_code = 0 ;
469+ bool pooled = false;
381470#ifdef CLOUDSYNC_NETWORK_TRACE
382471 double trace_start_ms = network_trace_now_ms ();
383472#endif
384473
385- // init curl
386- CURL * curl = curl_easy_init ( );
474+ // init/reuse curl
475+ CURL * curl = network_curl_for_endpoint ( data , endpoint , & pooled );
387476 if (!curl ) return false;
388477
389478 // set the URL
@@ -458,12 +547,12 @@ bool network_send_buffer (network_data *data, const char *endpoint, const char *
458547
459548cleanup :
460549 #ifdef CLOUDSYNC_NETWORK_TRACE
461- network_trace_log (data , "PUT" , endpoint , response_code ,
462- result ? CLOUDSYNC_NETWORK_OK : CLOUDSYNC_NETWORK_ERROR ,
463- result ? (size_t )blob_size : 0 ,
464- network_trace_now_ms () - trace_start_ms );
550+ network_trace_log_curl (data , "PUT" , endpoint , response_code ,
551+ result ? CLOUDSYNC_NETWORK_OK : CLOUDSYNC_NETWORK_ERROR ,
552+ result ? (size_t )blob_size : 0 ,
553+ curl , pooled , network_trace_now_ms () - trace_start_ms );
465554 #endif
466- if (curl ) curl_easy_cleanup (curl );
555+ if (curl && ! pooled ) curl_easy_cleanup (curl );
467556 if (headers ) curl_slist_free_all (headers );
468557 return result ;
469558}
0 commit comments